目录
什么是Redis?
Redis应用场景
Redis数据类型选择
String
List
set
Sorted Set (ZSet)
hash
发布订阅
发布订阅的机制
Redis事务
事务命令
事务机制
事物的执行
Watch的执行
Redis持久化
为什么要持久化
RDB:
AOF
混合持久化
底层数据结构
RedisDB结构
RedisObject结构
重点type
什么是Redis?
- Redis (Remote Dictionary Server)远程字典服务器,是用C语言开发的一个开源的高性能键值 对( key-value )内存数据库。
- 它提供了五种数据类型来存储值:字符串类型、散列类型、列表类型、集合类型、有序集合类型
- 它是一种 NoSQL 数据存储。
Redis应用场景
- 缓存使用,减轻DB压力
- DB使用,用于临时存储数据(字典表,购买记录)
- 解决分布式场景下Session分离问题(登录信息)
- 任务队列(秒杀、抢红包等等) 乐观锁
- 应用排行榜 zset
- 签到 bitmap
- 分布式锁
- 冷热数据交换
Redis数据类型选择
String
String
Redis的String能表达3种值的类型:字符串、整数、浮点数。
常见操作:
List
List可以存储有序、可重复的元素
获取头部或者尾部附近的记录速度很快
List的元素个数最多为2^32-1个
常见操作:
set
无序、不重复
集合中最大成员数为2^32-1
常见操作:
Sorted Set (ZSet)
有序集合:元素本身不重复
每个元素关联一个分数(score)
可按分数排序,分数可重复
常见命令:
hash
Redis hash是一个string类型的field和value的映射表,它提供了字段和字段值的映射。
每个hash可以存储2^32-1键值对
常见操作:
发布订阅
Redis提供了发布订阅功能,可以用于消息的传输
Redis的发布订阅机制包括三个部分,publisher,subscriber和channel
发布者和订阅者都是Redis客户端,channel则为Redis服务器端
发布者将消息发送到某个频道,订阅了这个频道的订阅者就能接收到这条消息
发布订阅的机制
订阅某个频道或模式:
客户端(client):
属性为pubsub_channels,该属性表明了该客户端订阅的所有频道
属性为pubsub_patterns,该属性表示该客户端订阅的所有模式
服务端(RedisServer):
属性为pubsub_channels,该服务器端中的所有频道以及订阅了这个频道的客户端
属性为pubsub_patterns,该服务器端中的所有模式和订阅了这些模式的客户端
Redis事务
- Redis的事务是通过multi、exec、discard和watch这四个命令来完成的。
- Redis的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。
- Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
- Redis不支持回滚操作
事务命令
multi:用于标记事务块的开始,Redis会将后续的命令逐个放入队列中,然后使用exec原子化的执行这个命令队列
exec:执行命令队列
discard:清楚命令队列
watch:监视key
unwatch:清楚监视key
事务机制
事物的执行
1、事务开始
在RedisClient中,有属性flags,用来表示是否在事务中flags=REDIS_MULTI
2、命令入队
RedisClient将命令存放在事务队列中
(EXEC, DISCARD, WATCH, MULTI除外)
3、事务队列
multiCmd*commands 用于存放命令
4、执行事务
RedisClient向服务器端发送exec命令,RedisServer会遍历事务队列,执行队列中的命令,最后将执行的结果一次性返回给客户端。
如果某条命令在入队过程中发生错误,redisClient将flflags置为REDIS_DIRTY_EXEC,EXEC命令将会失败返回。
Watch的执行
使用WATCH命令监视数据库建
RedisDb有一个watched_keys字典,key是某个被监视的数据的key,值是一个链表。记录了所有监视这个数据的客户端。
监视机制的触发
当修改数据后,监视这个数据的客户端的flags置为REDIS_DIRTY_CAS
事务执行
RedisClient向服务器端发送exec命令,服务器判断RedisClient的flags,如果为REDIS_DIRTY_CAS,则清空事务队列。
Redis持久化
为什么要持久化
Redis是内存数据库,宕机后数据会消失
Redis重启后快速恢复数据,要提供持久化机制
Redis持久化是为了快速恢复数据而不是为了存储数据
Redis持久化机制有两种方式:RDB和AOF
注意:Redis持久化不保证数据的完整性
RDB:
RDB(Redis DataBase),是redis默认的存储方式,RDB方式是通过快照( snapshotting )完成的。
不关注过程,只保存这一刻的数据
触发快照的方式
- 符合自定义配置的快照规则
- 执行save或者bgsave命令
- 执行flflushall命令
- 执行主从复制操作 (第一次)
配置参数定期执行
在redis.conf中配置:save 多少秒内 数据变了多少(漏斗设计 提供性能,1分钟内至少10000个键被更改,5分钟10,15分钟1个)
RDB执行流程:
- Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(aof文件重写命令)的子进程,如果在执行则bgsave命令直接返回。
- 父进程执行fork(调用OS函数复制主进程)操作创建子进程,这个复制过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令。
- 父进程fork后,bgsave命令返回“Background saving started”信息并不在阻塞父进程,并可以响应其他命令。
- 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。(RDB文件始终完整)
- 子进程发送信号给父进程表示完成,父进程更新统计信息。
- 父进程fork子进程后,继续工作。
RDB的优缺点:
优点:
RDB是二进制压缩文件,占用空间小,便于传输(传给slaver)
主进程fork子进程,可以最大化利用Redis性能,主进程不能太大,Redis的数据量不能太大,复制过程中主进程会阻塞
缺点:
不保证数据的完整性,会丢失最后一次快照以后更改的所有数据
AOF
AOF默认不开启,存储的是Redis命令,同步命令到AOF文件的整个过程可分为三个阶段:
- 命令传播:Redis将执行完的命令、命令的参数、命令参数的个数等信息发送到AOF程序中。
- 缓存追加:AOF程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的AOF缓存中。
- 文件写入和保存:AOF缓存中的内容被写入到AOF文件末尾,如果设定的AOF保存条件被满足的话(默认是一秒一次),fsync函数或者fdatasync函数会被调用,讲写入的内容真正的保存到磁盘中。
因为AOF文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态
每一秒钟保存一次:
在这种模式中,save原则上每隔一秒种就会执行一次,因为save操作是由后台子线程(fork)调用的,所以它不会引起服务器主进程阻塞。
AOF重写
AOF记录数据的变化过程,越来越大,需要重写“瘦身”
Redis可以再AOF体积变得过大时,自动的在后台(Fork子进程)对AOF进行重写。重写后的AOF文件包含了恢复当前数据集所需的最小命令集合。
Redis不希望AOF重写造成服务器无法处理请求,所以Redis决定将AOF重写过程放到(后台)子进程里执行,这样处理的最大好处是:
- 子进程进行AOF重写期间,主进程可以继续处理命令请求。
- 子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。
因为子进程在进行AOF重写期间,主进程还需要继续处理命令,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的不一致。
为了解决这个问题,Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis主进程在接到新的写命令之后,除了会将这个写命令的协议内容加到现有的AOF文件之外,还会追加到这个缓存中。
当子进程完成AOF重写之后,他会向父进程发送一个完成信号,父进程在接到完成信号之后,会调用一个信号处理函数,并完成以下工作:
- 将AOF重写缓存中的内容全部写入到新AOF文件中。
- 对新的AOF文件进行改名,覆盖原有的AOF文件。
触发方式:
默认:超过64mb,当前AOF文件大小超过上一次的AOF文件大小的百分之百
AOF文件的载入与数据还原
因为AOF文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态
- 创建一个不带网络连接的为客户端(fake client):因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服 务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令 的效果和带网络连接的客户端执行命令的效果完全一样
- 从AOF文件中分析并读取出一条命令
- 使用为客户端执行被读出的写命令
- 一直执行步骤2和3,直到AOF文件中的所有写命令都被处理完毕为止
混合持久化
RDB和AOF各有优缺点,Redis 4.0 开始支持 rdb 和 aof 的混合持久化。如果把混合持久化打开,aofrewrite 的时候就直接把 rdb 的内容写到 aof 文件开头。 RDB的头+AOF的身体---->appendonly.aof
底层数据结构
RedisDB结构
Redis中存在“数据库”的概念,该结构由redis.h中的redisDB定义。
当redis服务器初始化时,会预先分配16个数据库
所有数据库保存到结构redisServer的一个成员redisServer.db数组中
redisClient中存在一个名叫db的指针指向当前使用的数据库
id
id是数据库序号,为0-15(默认Redis有16个数据库)
dict
存储数据库所有的key-value
expirs
存储key的过期时间
RedisObject结构
value是一个对象
包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象
4位type
type字段表示对象的类型,占4位;
REDIS_STRING(字符串)、REDIS_LIST(列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。
当我们执行type命令时,便是通过读取RedisObject的type字段获得对象的类型
4位encoding
encoding表示对象的内部编码,占4位
每个对象有不同的实现编码
Redis可以根据不同的使用场景来为对象设置不同的编码,大大提高了Redis的灵活性和效率。
通过Object encoding命令,可以查看对象采用的编码方式
24位LRU
lru记录的是对象最后一次被命令程序访问的时间,
高16位存储一个分钟数级别的时间戳,低8位存储访问计数(lfu:最近访问次数)
lru----> 高16位: 最后被访问的时间
lfu---> 低8位:最近访问次数
重点type
跳跃表
跳跃表是有序集合(sorted-set)的底层实现,实现简单。具有二分查找的功能。
优势:
- 可以快速查找到需要的节点O(lon n)
- 可以在O(1)的时间复杂度下,快速获得跳跃表的头结点、尾结点、长度和高度。
字典
字典dict又称散列表(hash),是用来存储键值对的一种数据结构。
Redis整个数据库是用字典来存储的。(K-V结构)
对Redis进行CRUD操作其实就是对字典中的数据进行CRUD操作。
快速列表
快速列表(quicklist)是Redis底层重要的数据结构。是列表的底层实现。



