事务
常用命令
DISCARD
取消事务,放弃执行事务块内的所有命令。
UNWATCH
取消 WATCH 命令对所有 key 的监视
WATCH key [key ...]
监视一个(或多个) key ,如果在事务执行(EXEC)之前这个(或这些) key 被其他命令所改动,那么事务将被打断
一旦执行了EXEC,之前加的监控锁都会失效
相当于java锁机制,乐观锁
只支持部分事务
编译(语法错误)时出错,那么全部失败(回滚)
执行时有错误,那么继续执行,失败的失效
好处:使得Redis内部更加简单,而且运行速度更快
Redis数据持久化
两种持久化方式
AOF
概念:以日志形式记录每一次对redis的写命令,可以通过重复执行记录的命令来进行数据恢复
AOF的重写机制:AOF提供了重写机制,去除无用指令以及合并多条指令达到压缩的目的。<br>更小的文件也意味着更快的加载速率,重写不会阻塞redis<br>
优点:<br>1.AOF默认一秒钟执行一次fsync操作,即使发生故障,也最多丢失那一秒钟没来得及写入日志的命令数据。<br>2.AOF对误操作有更强的容错性。由于AOF是逐条命令的记录,因此当发生灾难性的数据误操作时(比如清库),只要这个时候AOF还没有被重写(重写机制),先把redis停掉,然后把AOF文件拷贝出来,将最后的误操作命令删除(比如清库的FLUSHALL),然后用修改过的AOF做数据恢复即可。
缺点:<br>1.AOF文件记录的更详尽,意味着文件占用空间大<br>2.AOF数据恢复的速度相对于RDB更慢
RDB
概念:生成某个时刻的redis数据快照,恢复时,直接加载快照即可
优点:<br>数据恢复速度相当AOF更快
缺点:<br>1.因为是某个时刻的快照,在这个时刻和下一个备份点之间插入的数据会丢失,不适用于对数据敏感的场景<br>2.RDB经常需要fork子进程来保存快照,创建的过程是阻塞的重量级操作。如果数据量过大,可能造成线上服务卡顿
持久化的选择,官方推荐是RDB和AOF同时开启,如果系统可以容忍几分钟的数据丢失,可以只用RDB。不建议只用AOF,因为AOF加载较慢。
数据一致性
先操作 Redis,再操作数据库
问题焦点
如果删除了缓存Redis,还没有来得及写库MySQL,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据
延时双删策略
在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。具体步骤是:<br> 1)先删除缓存 <br> 2)再写数据库 <br> 3)休眠500毫秒(根据具体的业务时间来定) <br> 4)再次删除缓存。<br>那么,这个500毫秒怎么确定的,具体该休眠多久呢? 需要评估自己的项目的读数据业务逻辑的耗时。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。 当然,这种策略还要考虑 redis 和数据库主从同步的耗时。最后的写数据的休眠时间:则在读数据业务逻辑的耗时的基础上,加上几百ms即可。比如:休眠1秒。<br>
先操作数据库,再操作 Redis
1、不做任何操作,等着Redis里的缓存数据过期后,自动从数据库同步最新的数据,此时最严重的数据不一致性周期就是在缓存过期的一段时间(考虑一下这个过期时间的范围);如果在这个时间段内,又有新的更新请求,也许这次就更新缓存成功了。<br>2、如果数据一致性要求比较高,那么 Redis 操作失败后,我们把这个操作记录下来,异步处理,用 Redis 的数据去和数据库比对,如果不一致,再次更新缓存确保缓存数据与数据库数据一致。<br>
设置缓存的过期时间
最终一致性的解决方案
所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存
强一致性、弱一致性、最终一致性
概念
从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。
最终一致性细分
因果一致性。
如果进程A通知进程B它已更新了一个数据项,那么进程B的后续访问将返回更新后的值,且一次写入将保证取代前一次写入。与进程A无因果关系的进程C的访问遵守一般的最终一致性规则。
读己之所写(read-your-writes)”一致性。
当进程A自己更新一个数据项之后,它总是访问到更新过的值,绝不会看到旧值。这是因果一致性模型的一个特例。
会话(Session)一致性。
这是上一个模型的实用版本,它把访问存储系统的进程放到会话的上下文中。只要会话还存在,系统就保证“读己之所写”一致性。如果由于某些失败情形令会话终止,就要建立新的会话,而且系统的保证不会延续到新的会话。
单调(Monotonic)读一致性。
如果进程已经看到过数据对象的某个值,那么任何后续访问都不会返回在那个值之前的值。
单调写一致性。
系统保证来自同一个进程的写操作顺序执行。要是系统不能保证这种程度的一致性,就非常难以编程了。
Redis 核心源码
核心键值对 ReidsDB源码详解
Redis 5 大基本数据类型底层编码源码精讲
Redis 6.0多线程分派机制底源码分析
Redis 跳表树结构底层源码分析
Redis GEO 实现原理及源码分析
Redis BitMap 高效统计算法及O(1) 存取速度,动态扩容机制详解
Redis 6.0 客户端缓存实现原理
Redis IO 模型
Redis常见架构
Redis主从模式
Redis主从架构设计
Redis主从数据同步原理
Redis主从实现读写分离
Redis数据同步阻塞分析
Redis哨兵机制
选举原理的介绍
实现Redis环境的HA高可用
主观下线与客观下线的介绍
最佳实践:Spring与Redis哨兵模式集群实战
Raft协议
集群模式
Redis集群架构设计过程剖析,槽的概念和键槽的细节概述
分片hash算法
CRC16(key) mod 16384(redis使用)
一致性hash的缺点
一致性hash算法
一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,就是对2^32去模
假设有三台服务器部署集群,首先通过hash(服务器A的IP地址) % 2^32,确定他们在环上的位置。数据访问时: 将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器
优点:<br>这样删除一个服务器节点或者新增一个服务器节点影响的数据不再是全部数据<br>
未使用一致性hash原因
一致性哈希算法对于数据分布、节点位置的控制并不是很友好,而Redis Cluster的槽位空间是自定义分配的
集群新增和删除节点
新增节点
1.启动节点(主节点)<br>2.redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000将节点加入集群<br>3.执行redis-cli --cluster reshard 127.0.0.1:7000重新分配集群slot<br>4.新增节点8为节点7的从节点 redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7000 --cluster-slave --cluster-master-id ef1bcdb677b1c8f8c3d290a9b1ce2e54f8589835<br>
删除节点
1.先删除从节点再删除主节点,不然哨兵会执行故障转移<br>2.redis-cli --cluster del-node 127.0.0.1:7000 f24b935a50a788692479c6beaf7c556f6d082253 (7000代表的是集群) 后接删除的节点id<br>3.下线主节点的时候记得执行reshard,把slot分配给剩余节点
在节点变化过程中数据不会受到影响
优点:高可用,可扩展。横向扩容可支持海量数据
Gossip 协议详解
集群脑裂
Redis中执行lua
1:eval 脚本内容 key个数 key列表 参数列表
eg:eval 'return "hello" .. KEY[1] .. ARGV[1]' 1 redis world
返回 hello redisworld
2:evalsha
1:加载redis脚本,返回SHA1值
2:执行对应的SHA1值
避免每次发送脚本的开销,脚本常驻服务端复用
eg>1:script load "$(cat lua_get.lua)"
返回SHA1,比如abcde123
2:evalsha abcde123
lua实现对redis访问
redis.call,eg:redis.call("set","hello","world")
redis.pcall,和上述一致
区别:redis.call如果执行失败,那么脚本执行结束后会直接返回错误,则pcall会忽略错误继续执行脚本
优势
1:lua在redis中执行是原子执行的,不会插入其他命令
2:可以帮助开发运维同事创建/定制自己的命令,并且常驻在内存中
3:将多个命令一次性打包,减少网络开销
redis管理脚本
script load:加载脚本到内存
script exists sha1 .. :判断脚本是否已加载到内存
script flush :清除所有已加载到内存的脚本
script kill :杀掉正则执行的脚本
注意:1:redis提供lua-time-limit指定脚本执行超时时间,但是此时间只是向其他命令发送BUSY信号
注意:2:上述情况需要使用script kill杀掉脚本,但注意若脚本已执行过写操作,kill命令将不生效