前面我们大致了解了 Redis 是什么,接下来我们就来使用 Redis。
如何使用 Redis?首先我们需要根据 Redis 提供的 7 种数据类型来了解它,分别是字符串 String、哈希 Hash、列表 List、集合 Set、有序集合 Sorted Set、发布订阅 Pub/Sub、事务 Transactions。
我们先来讲一下 Redis 的内部实现和运行机制。在使用 Redis 时,命令多使用就会很容易记住,这归功于 Redis 的命令简单。但是内部实现和它的单线程,你必须先了解原理,再了解 7 种数据类型,这样才能在实际开发中游刃有余。
内部实现
接下来,我们简单了解下 Redis 的内部实现。Redis 内部会封装一个 redisObject 实例。由这个 redisObject 来表示所有的 key 和 value。redisObject 所包含的字段中,最主要的是 type 和 encoding。
1.type 代表一个 value 对象具体是何种数据类型,包括 String、Hash、List、Set和Sorted set等数据类型。
2.encoding 则表示不同数据类型在 Redis 内部的存储方式,包括 Raw、Int、Ziplist、LinkedList、HashMap 和 Intset 存储方式。
上面说的比较抽象,为了帮助大家理解,我举个例子,比如 type 为 String 代表的是 value 存储了一个字符串,那么对应的 encoding 可以是 Int 或者 Raw。如果是 Int 则代表实际 Redis 内部是按数值类型存储的,比如你要存储“1234”、“2018”等字符串。
还有一个特别的内部字段,vm 字段。这个字段默认是关闭的,只有打开了 Redis 的虚拟内存功能,才能真正分配内存。有同学要问了,这个字段有什么用呢?因为 Redis 是 key/value 存储,是非常浪费内存的,这些内存成本主要是为了给Redis 上面的 7 种数据类型提供统一管理接口。
单线程
我们再来看下为什么 Redis 中单线程快。很多程序员应该深有体会,其实其他很多语言单线程是非常慢的,但是为什么 Redis 的单线程快呢?
我觉得最大的原因是纯内存存储。正因为这个是主要原因,所以后面两个原因显得有点不太重要,即非阻塞 IO 和避免线程切换和竞态消耗。
你要清楚,首先 Redis 一次只运行一条命令。其次我们应该减少长命令,哪些是长命令呢?如 KEYS、FLUSHALL、FLUSHDB、Slow Lua Script、MULTI/EXEC、Operate Big Value(Collection)。最后说明一点,其实 Redis 不只是单线程,如果你去读源码,你就会发现有些指令绝不是单线程能够做的。如 Fysnc File Descriptor、Close File Descriptor等。
7 种数据类型的使用
字符串 String
Redis 的 key 没什么好说,值得一提的就是 value 的五种数据类型。分别是字符串类型、数字、二进制、和 JSON 类型的数据。
那我们在实际生产环境中有哪些场景使用呢?如缓存、计数器(每次加 1 的计数)、分布式锁等场景都能看到。
接着我将列出与该数据类型相关的命令及使用说明。
1.GET、SET 和 DEL 这是 Redis 最简单的命令,如果你要得到一个 value 的值,只需要 GET key,就 ok 了。如果你要设置某个 key 的值,那就 SET key value,搞定。
2.INCR、DECR、INCRBY、DECRBY INCR key:就是 key 自增 1,不存在,自增后 get(key)=1; DECR key:就是 key 自减 1,不存在,自减后返回 -1; INCRBY key k:自增 k ,不存在,则返回 k; DECRBY key k:自减 k 。
实际使用
如果你想访问某个主页的访问量,那可以用 INCR id:pageview。
我们实际开发中,常常会使用下面的伪代码。
public VideoInfo get(long id){
String redisKey = redisPrefix + id;
VideoInfo videoInfo = redis.get(redisKey);
if(videoInfo == null){
videoInfo = mysql.get(id);
if(videoInfo != null){
// 序列化
redis.set(redisKey, serialize(videoInfo));
}
}
return videoInfo;
}
3.SET、SETNX、SET xx SET key value:不管 key 是否存在,都设置; SETNX key value:key 不存在,才设置(相当于 add); SET key value xx:key 存在,才设置(相当于 update)。 实际操作,见如下代码。
exists php --> 0
set php good -->OK
setnx php bad -->0
set php best xx -->ok
exists lua --> 0
set lua hehe xx -->(nil)
4.MGET、MSET MGET key1 key2 key3 ...:批量获取 key,原子操作; MSET key1 val2 key2 val2 key3 val3:批量设置 key-value。 实际开发的过程中,我们通常使用 MGET,因为 MGET 只有 1 次网络时间和 n 次命令时间。但是如果你使用 GET 的话,就是 n 次网络时间和 n 次命令时间。
使用 MGET 效率更高。
5.GETSET、APPEND、STRLEN GETSET key newvalue:set key newvalue 并返回旧的 value,旧的 value 会被删除; APPEND key value:将 value 追加到旧的 value 上; STRLEN key:返回字符串的长度(注意中文)。
6.INCRBYFLOAT、GETRANGE、SETRANGE INCRBYFLOAT key 3.5:在 key 上追加对应的值 3.5; GETRANGE key start end:获取字符串指定下标所有的值; SETRANGE key index value:设置指定下标所有对应的值。 哈希 Hash
说到 Hash,就要说到为什么我们要使用 Hash。我们在使用字符串的数据类型的时候,如果我们存储的是个对象,比如某个图书馆的会员,里面存储着会员的姓名、年龄、身份证信息、地址、借阅书籍、借阅时间……一系列的属性。
如果我们用 String 来存储的话,那我们就需要每次都序列化这个字符串,每次只要一修改某个属性,我们就要把一串属性都覆盖一遍。这样是不是非常麻烦?
这个时候,哈希就应运而生了。Hash 相当于 value 是一个 Map,里面的所有属性我们都可以单独新增、修改或者删除,而不需要像字符串那样全部覆盖操作。
常用的命令有HGET、HSET、HGETALL。 HGET key field:获取存储在哈希表中指定字段的值。 HSET key field value:将哈希表 key 中的字段 field 的值设为 value。 HGETALL key:获取在哈希表中指定 key 的所有字段和值,生产环境不常用。 列表 List
List 是一种简单的字符串的集合,是有顺序的。在实际生产环境中,我们时常会使用它。比如当我们需要获取某个数据的列表(例如粉丝列表)。
由于 Redis 的 List 是链表结构,我们可以非常轻松的实现消息排行等功能,还能用于消息队列等功能。
常用的命令有LPUSH、RPUSH、LPOP、RPOP、LRANGE。 LPUSH key value1 [value2]:将一个或多个值插入到列表头部; RPUSH key value1 [value2]:在列表中添加一个或多个值; LPOP key:移出并获取列表的第一个元素; RPOP key:移除并获取列表最后一个元素; LRANGE key start stop:获取列表指定范围内的元素。 集合 Set
Set 和 List 最大的不同是无序,Set 是没有顺序的。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
常用命令有SADD、SCARD、SNENVERS、SPOP。 SADD key member1:向集合添加一个或多个成员; SCARD key:获取集合的成员数; SMEMBERS key:返回集合中的所有成员; SPOP key:移除并返回集合中的一个随机元素。 Sorted Set 有序集合
Sorted Set 和 Set 最大的不同是前者是自动排序的,而后者是无序的。如果你需要一个有序的,但是不重复的数据结构的,就可以使用sorted set。
常用的命令有 ZADD、ZRANGE、ZREM、ZCARD。 ZADD key score1 member1:向有序集合添加一个或多个成员,或者更新已存在成员的分数; ZRANGE key start stop:通过索引区间返回有序集合成指定区间内的成员; ZREM key member:移除有序集合中的一个或多个成员; ZCARD key:获取有序集合的成员数。 Pub/Sub 发布订阅
即发布(Publish)与订阅(Subscribe)。在 Redis 中,你可以设定对某一个 key 值进行消息发布及消息订阅,当 key 的值进行了消息发布后,所有订阅它的客户端都会收到相应的消息,这类似于 QQ、微信。
常用的命令有PSUBSCRIBE、PUBSUB、PUBLISH、SUBSCRIBE。 PSUBSCRIBE pattern:订阅一个或多个符合给定模式的频道; PUBSUB subcommand:查看订阅与发布系统状态; PUBLISH channel message:将信息发送到指定的频道; SUBSCRIBE channel:订阅给定的一个或多个频道的信息; UNSUBSCRIBE [channel [channel ...]]:指退订给定的频道。 Transactions 事务
我们一般认为 NoSQL 数据库都没有事务,恐怕要让你失望了。Redis 就支持事务,但并不是我们一般意义上的事务,如果你执行 exec 命令,途中断电或者服务器挂掉了,我们还是会发现 Redis 里一部分插入了,一部分未插入。
不过 Redis 提供了 WATCH 命令,我们可以对某个 key 来 watch 一下,然后再执行 Transactions。如果这个被Watch 的值进行了修改,那么这个 Transactions 会发现并拒绝执行。
redis 127.0.0.1:6381> MULTI
OK
redis 127.0.0.1:6381> SET book-name "JAVA Programming Mastering Series"
QUEUED
redis 127.0.0.1:6381> GET book-name
QUEUED
redis 127.0.0.1:6381> SADD tag "java" "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6381> SMEMBERS tag
QUEUED
redis 127.0.0.1:6381> EXEC
1) OK
2) "JAVA Programming Mastering Series"
3) (integer) 3
4) 1) "java"
2) "Programming"
3) "Mastering Series"
常用命令有 MULTI、EXEC、DISCARD。 MULTI:标记一个事务块的开始; EXEC:执行所有事务块内的命令; DISCARD:取消事务,放弃执行事务块内的所有命令; UNWATCH:取消 WATCH 命令对所有 key 的监视; WATCH key:监视 key,如果在事务执行之前 key 被其他命令所改动,那么事务将被打断。
Redis 作为一个数据库,很多开发者还可以单独使用它。事实上,更多时候 Redis 是在数据库和代码中间作为一个中间件使用,如果你发现你目前的数据库出现瓶颈,那么你就可以通过 Redis 来优化。
如果你有疑问欢迎加微信咨询:
https://u.wechat.com/MDHWBbNbJRv36gtFwfExkEo (二维码自动识别)
也可以关注我的公众号想我提问: