命令学习可以使用 在线redis环境。
数据类型
Redis 比较常见的数据结构有 string、list、hash、set、sorted set 等,但是 Redis 还有比较高级的3种数据结构:HyperLogLog、Geo、BloomFilter。
String(字符串)
String 是 Redis 最简单最常用的数据结构,也是 Memcached 唯一的数据结构。在平时的开发中,String 可以说是使用最频繁的了。
底层实现:
- 如果一个字符串对象保存的是整数值, 并且这个整数值可以用 long 类型来表示, 那么字符串对象会将整数值保存在字符串对象结构的 ptr 属性里面(将 void* 转换成 long ), 并将字符串对象的编码设置为 int 。
- 如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度大于 39 字节, 那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值, 并将对象的编码设置为 raw。
- 如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度小于等于 39 字节, 那么字符串对象将使用 embstr 编码的方式来保存这个字符串值。
应用场景:
- 缓存:string 最常用的就是缓存功能,会将一些更新不频繁但是查询频繁的数据缓存起来,以此来减轻 DB 的压力。
- 计数器:可以用来计数,通过 incr 操作,如统计网站的访问量、文章访问量等。
List(列表)
基本概念:
list 是有序可重复列表,和 Java 的 LinkedList 比较像,可以通过索引查询;插入删除速度快。
底层实现:
Redis 3.2之前:
- 列表对象的编码可以是压缩列表 ziplist 或者 双向循环链表 linkedlist 。
- 列表对象保存的所有字符串元素的长度都小于 64 字节并且保存的元素数量小于 512 个,使用 ziplist 编码;否则使用 linkedlist;
Redis 3.2之后:
使用quicklist,它是一个双向链表,而且是一个基于ziplist的双向链表,quicklist的每个节点都是一个ziplist,结合了双向链表和ziplist的优点。
使用场景:
- 消息队列:Redis 的 list 是有序的列表结构,可以实现阻塞队列,使用左进右出的方式。Lpush 用来生产 从左侧插入数据,Brpop 用来消费,用来从右侧 阻塞的消费数据。
- 数据的分页展示: lrange 命令需要两个索引来获取数据,这个就可以用来实现分页,可以在代码中计算两个索引值,然后来 redis 中取数据。
- 可以用来实现粉丝列表以及最新消息排行等功能。
Hash(散列)
基本概念:
Redis 散列可以存储多个键值对之间的映射。和字符串一样,散列存储的值既可以是字符串又可以是数值,并且用户同样可以对散列存储的数字值执行自增或自减操作。这个和 Java 的 HashMap 很像,每个 HashMap 有自己的名字,同时可以存储多个 k/v 对。
底层实现:
- 哈希对象的编码可以是 ziplist 或者 hashtable 。
- 哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节并且保存的键值对数量小于 512 个,使用ziplist 编码;否则使用 hashtable;
应用场景:
- Hash 更适合存储结构化的数据,比如 Java 中的对象;其实 Java 中的对象也可以用 string 进行存储,只需要将 对象 序列化成 json 串就可以,但是如果这个对象的某个属性更新比较频繁的话,那么每次就需要重新将整个对象序列化存储,这样消耗开销比较大。可如果用 hash 来存储 对象的每个属性,那么每次只需要更新要更新的属性就可以。
- 购物车场景:可以以用户的 id 为 key ,商品的 id 为存储的 field ,商品数量为键值对的value,这样就构成了购物车的三个要素。
Set(集合)
基本概念:
Redis 的 set 和 list 都可以存储多个字符串,他们之间的不同之处在于,list是有序可重复,而set是无序不可重复。
底层实现:
- 集合对象的编码可以是 intset 或者 hashtable 。
- 如果集合对象保存的所有元素都是整数值并且保存的元素数量不超过 512 个,则使用 intset 编码;否则使用 hashtable;
应用场景:
- 标签:可以将博客网站每个人的标签用 set 集合存储,然后还按每个标签 将用户进行归并。
- 存储好友/粉丝:set 具有去重功能;还可以利用set并集功能得到共同好友之类的功能。
命令:
> sadd family mother # 尝试将 mother 添加进 family 集合中
(integer)1 # 返回 1 表示添加成功,0 表示元素已经存在集合中
> sadd family father
(integer)1
> sadd family father
(intger)0
> smembers family # 获取集合中所有的元素
1)"mother"
2)"father"
> sismember family father # 判断 father 是否在 family 集合中
(integer)1 # 1 存在;0 不存在
> sismber family son
(integer)0
> srem family son # 移除 family 集合中元素 son
(integer)1 # 1 表示存在并且移除成功;0 表示存在该元素
> srem family som
(integer)0
> sadd family1 mother
(integer)1
> smembers family
1)"mother"
2)"father"
> smember family1
1)"mother"
> sinter family family1 # 获取 family 和 family1 的交集
1)"mother"
> sadd family1 son
(integer)1
> sunion family family1 # 获取 family 和 family1 的并集
1)"mother"
2)"father"
> sdiff family family1 # 获取 family 和 family1 的差集(就是family有但是family1没有的元素)
1)"father"
zset(有序集合)
基本概念:
有序集合和散列一样,都用于存储键值对:其中有序集合的每个键称为成员(member),都是独一无二的,而有序集合的每个值称为分值(score),都必须是浮点数。可以根据分数进行排序,有序集合是Redis里面唯一既可以根据成员访问元素(这一点和散列一样),又可以根据分值以及分值的排列顺序来访问元素的结构。和Redis的其他结构一样,用户可以对有序集合执行添加、移除和获取等操作。
底层实现:
- 有序集合的编码可以是 ziplist 或者 skiplist
- 如果有序集合保存的元素数量小于 128 个并且保存的所有元素成员的长度都小于 64 字节,用 ziplist 编码;否则使用skiplist;
- 当
ziplist
作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的 ziplist 节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值。 - 当
skiplist
作为zset的底层存储结构的时候,使用skiplist
按序保存元素及分值,使用dict
来保存元素和分值的映射关系。
应用场景:
- 排行榜:有序集合最常用的场景。如新闻网站对热点新闻排序,比如根据点击量、点赞量等。
- 带权重的消息队列:重要的消息 score 大一些,普通消息 score 小一些,可以实现优先级高的任务先执行。
命令:
> zadd class 100 member1 # 将member1元素及其score值100加入到 有序集合 class中
(integer)1
> zadd class 90 member2 80