本文将大概介绍Redis的一些特性、使用场景。
特性
- Redis是一直基于键值对的NoSQL数据库;
- Redis支持5种主要数据结构:string、hash、list、set、zset以及bitmaps、hyperLoglog、GEO等特化的数据结构;
- Redis是内存数据库,因此它有足够好的读写性能;
- Redis支持持久化,redis支持AOF和RDB两种持久化方式,确保了内存中的数据不会“丢失”;
- Redis的sentinel和复制功能保证了Redis的高可用;
- Redis支持key维度的数据过期;
- Redis支持发布订阅、“事务”、pipeline、Lua脚本等附加功能。
使用场景
Redis 适合做什么
- 缓存,Redis本身是内存数据库,注定有极高的读写速度和吞吐,加上数据过期功能以及完善的数据淘汰策略使得Redis拥有与生俱来的缓存潜质。
- 排行榜系统,Redis提供了zset、list等复杂数据结构,以及极佳的性能,可以做出时间、数量等各种维度的排行榜系统。
- 计数器系统,对于视频(音乐)网站的视频播放量、网页浏览量等高频操作,传统的关系型数据库不能够满足需求,Redis本身知道
incr
、incrby
等命令很好的支持了这计数功能。 - 社交网络,Redis支持多种复杂数据结构,比如一个用户有自己的粉丝,同时也会关注其他人,这些多可以使用set来存储,如果需要有序,可以使用zset来存储,这些复杂的数据结构传统的关系型数据库并不能很好的支持,同时,由于社交网络网站本身访问量比较大,传统数据在性能上也是不能够满足的。
- 消息队列,Redis提供了消息队列功能,能够满足一般的消息队列需求。
- 分布式锁,Redis提供了
SET key value [EX seconds] [PX milliseconds] [NX|XX]
命令,以及Lua脚本功能,基于此能够很好的实现分布式锁功能。
Redis 不适合做什么
每种产品都有自己的特定的应用领域。Redis也不是万能的。
- Redis是内存数据库,相比磁盘类型的数据库成本要高不少,注定了Redis不能用于存储大规模的数据(土豪忽略)。
- Redis有足够高的性能,因此对于热数据能够很好满足需求,但如果冷数据存在Redis里不免过于浪费(土豪忽略)。
- Redis数据存储在内存中,对于可用性要求极高,且需要永久保存的数据不建议放在Redis中,虽然Redis提供持久化、复制等功能保证数据落盘,但持久化、复制等也存在时间差,这段时间的数据也不是能够完全保证不丢失的。
- Redis是单线程的,对于数据比较大的数据的读写操作会阻塞整个数据库,因此Redis不适合存储单个value比较大的数据。
深探入微
作为最佳实践本章将会把主要的关注点放和在Redis用户相关的一些Redis基本知识,这部分知识是Redis用户需要了解并在实际使用Redis过程中要考虑的。比如,如果不考虑Redis单线程的特性可能会遇到请求阻塞导致性能急剧下降的问题;不了解Redis的数据结构,可能导致使用存储膨胀、大value、热key的问题等等。
单线程
不管是单线程或者多线程都是为了提升Redis的性能,Redis作为一个基于内存的数据库,不仅仅需要进行数据的读写操作,还要处理大量的外部的网络请求,这就不可避免的要进行多次IO。Redis选择了IO多路复用 单线程的架构来处理请求。简单说:
- 使用 I/O 多路复用机制同时监听多个文件描述符的可读和可写状态。
- 一旦受到网络请求就会在内存中快速处理,由于绝大多数的操作都是纯内存的,所以处理的速度会非常地快。总之,单线程模式下,即使连接的网络处理很多,因为有IO多路复用,依然可以在高速的内存处理中得到忽略,所以选择Redis选择单线程的架构。当然,Redis在6.0之后加入了多线程,因为读写网络的read/write系统调用在Redis执行期间占用了大部分CPU时间,如果把网络读写做成多线程的方式对性能会有很大提升。
数据结构
相比其他KV数据库,Redis提供了丰富的数据结构来满足用户的不同需求。同时,可以说Redis在内存使用是锱铢必较。为了最大可能的节约内存,Redis的每一种数据结构都拥有2~3种(截止Redis6.0,后续可能会更多)的底层实现。比如,在list
、hash
、set
、zset
等复杂数据结构在数据量较小的情况下都会使用ziplist
这种数据结构等,由于ziplist
是连续空间,不影响指针等附加消耗,在数据量较小的时候读写速度劣势也并不明显,但是可以节约不少存储,尤其是在实际使用场景种往往小数据占比较大的情况下内存节约更为明显。表-1 Redis数据结构与编码
类型 | 编码 | 决定条件 | 说明 |
---|---|---|---|
string | int | 8 个字节的带符号长整型(2^63-1) | 整数编码 |
string | embstr | 小于等于 44 个字节的字符串,3.0.0 是 39 个字节 | 优化内存分配的字符串编码,和StringObject连续,一起分配一起释放,分别减少一次内存操作 |
string | raw | 大于 44 个字节的字符串 | 动态字符串编码 |
list | ziplist | value最大字节数 <= list-max-ziplist-value 且 链表长度 <= list-max-ziplist-entries | |
list | linkedlist | value最大字节数 > list-max-ziplist-value 或者 链表长度 > list-max-ziplist-entries | |
list | quicklist | 3.2版本之后新增。废弃list-max-ziplist-value 和 list-max-ziplist-value。使用新配置项:list-max-ziplist-size表示最大压缩空间或者ziplist长度,取值范围为[-5, -1]([64kb, 4kb])默认是-2(8kb);list-compress-depth 表示压缩深度,默认是0,不压缩 | 考虑到链表附加空间相对太高,使用quicklist代替ziplist 和 linklist |
hash | ziplist | value最大字节数 <= hash-max-ziplist-value 且 field个数 <= hash-max-ziplist-entries | |
hash | hashtable | value最大字节数 > hash-max-ziplist-value 或者 field个数 > hash-max-ziplist-entries | |
set | intset | 元素必须是整型,且集合长度<=set-max-inset-entries | |
set | hashtable | 集合长度>set-max-inset-entries | |
zset | ziplist | value最大字节数 <= zset-max-ziplist-value 且 链表长度 <= zset-max-ziplist-entries | |
zset | skiplist | value最大字节数 > zset-max-ziplist-value 或者 链表长度 > zset-max-ziplist-entries | |
stream | rax listpack | listpack 是 ziplist 优化版本,目前(Redis 5.0.0)只使用在 stream 中 |
表1 Redis数据结构内部实现
持久化
Redis支持RDB和AOF两种持久化机制。持久化功能能够有效的避免Redis服务异常退出导致数据完全丢失的情况。当重启Redis服务会自动加载持久化文件即可完成数据恢复。
RDB
RDB持久化是将当前Redis进程数据快照保存到硬盘的过程,触发RDB生成有两种方式:手动触发和自动触发。
- 手动触发:对应
save
和bgsave
两个命令,其中save
是阻塞的,已逐步淘汰。 - 自动触发:自动触发大概有四种场景
-
- 使用
save
相关配置,如save m n
,表示m
秒内数据发生了n
次修改,则字段执行bgsave
;
- 使用
-
- 从节点执行全量复制操作,主节点字自动执行
bgsave
生成RDB文件并发送给从节点。
- 从节点执行全量复制操作,主节点字自动执行
-
- 执行
debug reload
命令重新加载Redis,会触发save
操作(注意,这里是save
)。
- 执行
-
- 默认执行
shutdown
命令,如果没有开启AOF持久化也会自动执行bgsave
。RDB是一个紧凑压缩的二进制文件(Redis默认使用LZF压缩算法对RDB进行压缩),是Redis在某一时刻的数据快照,有如下优点:
- 默认执行
- 适合全量复制、和冷备。
- Redis加载RDB恢复数据速度远快于AOF方式。同样,RDB持久化方式也有诸多缺点:
- RDB持久化方式无法做到实时持久化。执行
bgsave
都执行fork
操作创建子进程,属于重量级操作,成本较高。 - RDB格式不兼容。Redis发展过程种有多个格式RDB版本,存在老版本Redis不兼容新格式RDB文件的问题。
AOF
AOF(append only file)持久化是以日志的方式记录每次写操作命令。重启Redis时,会通过重新执行这些命令的方式来恢复数据。AOF持久化有三点需要说明:
- AOF直接使用文本协议格式。
- AOF持久化首先会将写操作命令写入
aof_buf
,然后通过提供的不同策略(always
、everysec
、no
)来将aof_buf
中的数据同步到磁盘上。 - AOF支持重新机制以压缩文件体积,AOF文件重新逻辑是将Redis进程内的数据转换成写操作命令同步到AOF文件中。
复制
为了解决分布式系统中服务单点问题,Redis引入了复制机制,实现主从架构以满足容灾和负载均衡等问题。复制本质上是数据同步的过程,Redis支持两种数据同步方式:
- 全量同步,Redis最早只支持全量同步,全量同步顾名思义,会将主节点的所有数据一次性的同步给从节点,这样会导致主从之间的带宽消耗负担加重。
- 部分同步,为了解决全量复制存在的问题,Redis提供了部分复制的机制,当主从断开连接,再次建立连接,只需要同步断开连接期间的数据。当主节点把当前数据同步给从节点过后,就将复制流程建立起来了,后续主节点将会持续的把写操作命令发送给从节点,从而保证主从节点数据一致性。
reference
Redis官网
Redis开发与运维
How Twitter Uses Redis To Scale - 105TB RAM, 39MM QPS, 10,000 Instances
Latency Numbers Every Programmer Should Know