本文参考书籍:《Redis开发与运维》
1、Redis的作用
- 共享session
- 计数器
- 排行榜
- 消息队列
- 社交
- 限速
- 缓存
- 分布式锁
2、Redis可执行文件说明
可执行文件 | 作用 |
---|---|
redis-server | 启动Redis |
redis-cli | Redis命令行客户端 |
redis-benchmark | Redis基准测试工具 |
redis-check-aof | Redis AOF持久化文件检测和修复工具 |
redis-check-dump | Redis RDB持久化文件检测和修复工具 |
redis-sentinel | 启动Redis Sentinel |
Redis版本号第二位按奇偶分为开发版本和稳定版本,如2.9、3.1这样就是开发版本,3.0、3.2就是稳定版本
3、Redis对外的数据结构和内部编码
通过object encoding
Redis客户端每次执行命令都经过了三个阶段:发送命令、执行命令、返回结果
因为Redis是单线程执行命令,所以一条命令从客户端到服务端不会立即执行,所有命令都会进入一个队列,然后逐条被执行,不会存在同时执行多条命令的情况。
Redis单线程架构
Redis使用了单线程架构和I/O多路复用模型来实现高性能的内存数据库服务
为什么单线程模型性能如此高
- 纯内存访问,Redis将所有数据放在内存中,内存的响应时长大概为100纳秒,这是Redis达到每秒万级别访问的重要基础
- 非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不用再网络I/O上浪费过多的时间。
- 单线程避免了线程切换和竞态产生的消耗
- 单线程可以简化数据结构和算法的实现。
- 单线程避免了线程切换和竞态的消耗
Redis是面向快速执行场景的数据库,每个命令的执行时间不能过长。
4、Redis基础数据结构
字符串(string)
字符串是Redis中最基础的数据结构,其他几种数据结构都是在字符串类型的基础上构建的。
字符串的值实际可以是字符串(简单的字符串、复杂的字符串(JSON、XML))、数字(整数、浮点),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。
- 字符串的内部编码有三种:
- int:8个字节的长整形
- embstr:小于等于39个字节的字符串
- raw:大于39个字节的字符串
- Redis会根据当前值的类型和长度决定使用哪种内部编码实现
使用场景:
- 计数器
- 限速
哈希(hash)
哈希类型是指键值本身又是一个键值对结构。
哈希类型中的映射关系叫做field-value,这里的value是指field对应的值,不是键对应的值。
哈希的内部编码有两种:
- ziplist(压缩列表)
- 当哈希类型元素个数小于hash-max-ziplist-entries配置(512个)、同时所有值都小于hash-max-ziplist-value配置(64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable优秀。
- hashtable(哈希表)
- 当哈希类型无法满足ziplist的条件时,Redis会使hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)。
当field个数超过512或有value大于64字节,内部编码都会变成hashtable。
使用场景:
可以存储用户信息,使用哈希类型存储会变得更加直观,操作也更加便捷。同样的用户信息存在关系型数据库和Redis中的哈希结构对比:
一、哈希类型是稀疏的,而关系型数据库是完全结构化的。例如:哈希类型每个键都可以有不同的field,而关系型数据库一旦添加新列,所有行都要为其设置值(即使为null)。
二、关系型数据库可以做复杂的关系查询,而Redis去模拟关系型复杂查询开发困难,维护成本高。
-
哈希类型优点:
- 简单直观,使用合理可以减少内存空间的使用
-
哈希类型缺点:
- 要控制哈希在ziplist和hashtable两种内部编码的转换,hashtable会消耗更多内存
列表(list):
列表类型是用来存储多个有序的字符串,列表中的每个字符串称为元素(element),一个列表最多可以存储2^32 - 1个元素。
可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下的元素等。
列表是一种比较灵活的数据结构,它可以充当栈和队列的角色。
列表类型的特点:
- 列表中的元素是有序的
- 列表中的元素可以是重复的
列表的内部编码有两种:
- ziplist(压缩列表)
- 当元素个数小鱼list-max-ziplist-entries配置(512个),同时列表中每个元素的值都小于list-max-ziplist-value配置(64字节)时,Redis会选用ziplist来作为列表的内部实现来减少内存的使用。
- linkedlist(链表)
- 当列表类型无法满足ziplist条件时,Redis会使用linkedlist作为列表的内部实现。
Redis3.2版本提供了quicklist内部编码,它是以一个ziplist为节点的linkedlist,结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。
- 当列表类型无法满足ziplist条件时,Redis会使用linkedlist作为列表的内部实现。
使用场景:
- 消息队列
- Redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端lpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。
- lpush+brpop=message queue(消息队列)
- 文章列表
- 每个用户有属于自己的文章列表,现需分页展示文章列表。此刻可以考虑使用列表,因为列表不但是有序的,同时支持按索引范围获取元素。
- 使用列表类型保存和获取文章列表会存在两个问题:
- 如果每次分页获取的文章个数多,需要执行多次hgetall操作,此时可以考虑使用pipeline批量获取。
- 分页获取文章列表时,lrange命令在列表两端性能较好,但是如果列表较大,获取列表中间范围的元素性能会变差,此时可以考虑将列表做成二级拆分,或者使用Redis3.2的quicklist内部编码实现。
实际列表的使用场景很多,在选择时可以参考以下组合:
- lpush+lpop=Stack(栈)
- lpush+rpop=Queue(队列)
- lpush+ltrim=capped collection(有限集合)
集合(set)
集合类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引填表获取元素。
一个集合最多可以存储2^32 - 1个元素。Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。
集合类型的内部编码有两种:
- intset(整数集合)
- 当即和中的元素都是整数且元素个数小于set-max-intset-entries配置(512个)时,Redis会选用intset作用集合的内部实现,从而减少内存的使用。
- hashtable(哈希表)
- 当集合类型无法满足intset条件时,Redis会使用hashtable作为集合的内部实现。
使用场景:
集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同洗好的标签,这些数据对于用于体验以及增强用户黏度比较重要。可以对不同标签的用户做不同类型的推荐。
代码例子:
- 给用户添加标签
sadd user:1:tags tag1 tag2
sadd user:2:tags tag2 tag3
....
sadd user:n:tags tag5 tag6
- 给标签添加用户
sadd tag1:users user1 user2
sadd tag2:users user2 user3
...
用户和标签的关系应该维护在一个事务里,防止部分命令失败造成的数据不一致。
- 计算用户共同感兴趣的标签
sinter user:1:tags user:2:tag2
使用集合类型的应用场景可以参考如下几点:
- sadd=tagging(标签)
- spop/srandmember=random item(生成随机数,比如抽奖)
- sadd+sinert=social graph(社交需求)
有序集合(zset)
有序集合保留了集合不能重复成员的特性,但有序集合中的元素可以排序。但是它与列表的排序不同,它给每个元素设置一个分数(score)作为排序的依据。
有序集合提供了获取指定分数和元素范围查询、计算成员排名等功能。
有序集合类型的内部编码有两种:
- ziplist(压缩列表)
- 当有序集合的元素个数小于zset-max-ziplist-entries配置(128个),同时每个元素的值都小于zset-max-ziplist-value配置(64字节)时,Redis会用ziplist来作为有序集合的内部实现。
- skiplist(跳跃表)
- 当ziplist条件不满足时,有序集合会使用skiplist作为内部实现。
使用场景:
有序集合典型的使用场景是排行榜系统。例如:视频网站需要对用户上传视频做排行榜,榜单的维度可能是多个方面的:按时间、按播放量、按获得的赞数等。