3万字聊聊什么是Redis(八)
共 3947字,需浏览 8分钟
·
2021-12-31 08:53
大家好,我是Leo
上一篇我们介绍了
Redis处理并发的问题。 Redis分布式锁的实现。 RedLock算法。
继上篇Redis技术总结七,我们继续聊聊Redis的相关技术!
这篇主要是介绍一下Redis事务机制ACID的实现,Redis主从同步的实战细节问题,
推荐阅读
Redis如何实现事务ACID
什么是ACID
Redis能否实现事务ACID属性呢?
我们可以先来解释一下什么是ACID
A原子性:要不全部成功,要不全部失败 C一致性:事务执行前后是一致的,不能因为事务A执行时看到的字段A是1,准备提交时,字段A已经被事务B改成了2。这样是不行的。 I隔离性:执行事务时,其他操作无法存取到正在执行事务访问的数据。 D持久性:数据库执行事务后,数据的修改要被持久化保存下来。当数据库重启后,数据的值需要是被修改后的值。
Redis如何实现事务
事务执行的过程我们可以分为三步走
客户端要下达一个命令表示一个事务的开启 MULTI
客户端把本身要执行的操作和指令发给服务器端,这些也就是读写命令。服务器接收读写命令把他暂存在命令队列中 业务代码 set get incr 等
客户端向服务器端发起一个提交事务的命令让Redis去消化刚刚命令队列里的命令。 EXEC
事务机制的ACID的分析
原子性
对于Redis的原子性操作,主要分两种情况 执行报错 和 入队报错
执行报错: 执行报错的话,说明入队的时候是不报错的。执行过程中必然会有正确的指令,Redis在执行过程中正确的会正常执行,报错的指令会返回报错。原子性就无法保证了
入队报错: 入队报错的话,Redis就不会执行这段指令,所以直接返回错误,可以保证原子性!
扩展一下MySQL,MySQL事务中报错的话会有回滚机制,Redis中是不存在回滚机制的。一旦使用过程中Redis发生了这种情况,我们可以使用Redis提供的
redis-check-aof
工具检查 AOF 日志文件,这个工具可以把未完成的事务操作从 AOF 文件中去除。这样一来,我们使用 AOF 恢复实例后,事务操作不会再被执行,从而保证了原子性。如果AOF,RDB都不开启就不要谈数据安全性持久化这些概念了
一致性
事务的一致性保证会受到错误命令、实例故障的影响。所以,我们按照命令出错和实例故障的发生时机,分成三种情况来看。
入队就报错,Redis会放弃执行,同时也保证了数据库的一致性。 执行就报错,有错误的命令不会被执行,正确的命令可以正常执行,也不会改变数据库的一致性。 实例故障报错,实例故障重启后我们要根据用户是否开启了AOF和RDB进行分情况讨论。
如果我们使用了 RDB 快照,因为 RDB 快照不会在事务执行时执行,所以,事务命令操作的结果不会被保存到 RDB 快照中,使用 RDB 快照进行恢复时,数据库里的数据也是一致的。
如果我们使用了 AOF 日志,而事务操作还没有被记录到 AOF 日志时,实例就发生了故障,那么,使用 AOF 日志恢复的数据库数据是一致的。如果只有部分操作被记录到了 AOF 日志,我们可以使用 redis-check-aof 清除事务中已经完成的操作,数据库恢复后也是一致的。
Redis事务机制对一致性是有保证的
隔离性
事务的隔离性主要和并发有关。并发过程中我们还可以细分两个执行阶段。EXEC执行前 和 EXEC执行后
执行前: 我们可以通过Redis提供的watch机制来实现隔离性
执行后: 无法保证
什么是watch机制?
在事务执行前,监控一个或多个键的值变化情况,当事务调用 EXEC 命令执行时,WATCH 机制会先检查监控的键是否被其它客户端修改了。如果修改了,就放弃事务执行,避免事务的隔离性被破坏。然后,客户端可以再次执行事务,此时,如果没有并发修改事务数据的操作了,事务就能正常执行,隔离性也得到了保证。
如果在执行前我们 没有使用watch机制,同时发生了并发请求,就会对数据进行读写,隔离性就没有得到保障
如果在EXEC执行后,虽然无法保证,但是Redis的单线程的。按照入队的先后顺序执行,所以后一个请求不会排的前面一个请求。于是 也不会破坏事务的隔离性。
持久化
Redis 是内存数据库,所以,数据是否持久化保存完全取决于 Redis 的持久化配置模式。
如果 Redis 没有使用 RDB 或 AOF,那么事务的持久化属性肯定得不到保证。 如果 Redis 使用了 RDB 模式,那么,在一个事务执行后,而下一次的 RDB 快照还未执行前,如果发生了实例宕机,这种情况下,事务修改的数据也是不能保证持久化的。 如果 Redis 采用了 AOF 模式,因为 AOF 模式的三种配置选项 no、everysec 和 always 都会存在数据丢失的情况,所以,事务的持久性属性也还是得不到保证。
Redis主从同步的那些问题
主从数据不一致
主从同步时,采用的是异步同步。所以无法保证主从库数据的实时一致性。 主从同步时,网络因素导致主从数据实时性的延迟 主从同步时,从库接收到了主库的命令。但是从库正在处理其他复杂度过高的命令而阻塞,从库只有处理完当前任务后才能处理主库的新命令。这就造成了主从延迟
解决方案
在硬件方面,我们要尽量保证主从库间的网络连接状况良好。例如,我们要避免把主从库部署在不同的机房,或者是避免把网络通信密集的应用和Redis 主从库部署在一起。 监控主从库间的复制差值,如果主从库差值过大我们就可以通过设置阈值的方式。干预解决主从同步的延迟问题
Redis 的 INFO replication 命令可以查看主库接收写命令的进度信息(master_repl_offset)和从库复制写命令的进度信息(slave_repl_offset),所以,我们就可以开发一个监控程序,先用 INFO replication 命令查到主、从库的进度,然后,我们用 master_repl_offset 减去 slave_repl_offset,这样就能得到从库和主库间的复制进度差值了
读到过期数据
平时应用中读到过期数据是比较常见的,我们分析一下为什么会读到过期数据。
假如一个key的过期时间是19:51:49,刚好有个请求访问了这个key,访问时间是19:51:50。
key过期了正等待被回收,但是还没有回收这段期间就被读取了。这主要是由Redis的过期策略引起的。
过期策略分 惰性删除和 定期删除
惰性删除。当一个数据的过期时间到了以后,并不会立即删除数据,而是等到再有请求来读写这个数据时,对数据进行检查,如果发现数据已经过期了,再删除这个数据。
这个策略的好处是尽量减少删除操作对 CPU 资源的使用,对于用不到的数据,就不再浪费时间进行检查和删除了。但是,这个策略会导致大量已经过期的数据留存在内存中,占用较多的内存资源。所以,Redis 在使用这个策略的同时,还使用了第二种策略:定期删除策略。
定期删除策略 是指Redis 每隔一段时间(默认 100ms),就会随机选出一定数量的数据,检查它们是否过期,并把其中过期的数据删除,这样就可以及时释放一些内存。
清楚了这两个删除策略,我们再来看看它们为什么会导致读取到过期数据。
首先,虽然定期删除策略可以释放一些内存,但是,Redis 为了避免过多删除操作对性能产生影响,每次随机检查数据的数量并不多。如果过期数据很多,并且一直没有再被访问的话,这些数据就会留存在 Redis 实例中。业务应用之所以会读到过期数据,这些留存数据就是一个重要因素。
其次,惰性删除策略实现后,数据只有被再次访问时,才会被实际删除。如果客户端从主库上读取留存的过期数据,主库会触发删除操作,此时,客户端并不会读到过期数据。但是,从库本身不会执行删除操作,如果客户端在从库中访问留存的过期数据,从库并不会触发数据删除。那么,从库会给客户端返回过期数据吗?这就和版本有关了!
版本问题
3.2 之前的版本,从库在服务读请求时,并不会判断数据是否过期,而是会返回过期数据。 3.2 版本后,如果读取的数据已经过期了,从库虽然不会删除,但是会返回空值,这就避免了客户端读到过期数据
除了版本的问题还有设置过期时间的命令有关,有些命令给数据设置的过期时间在从库上可能会被延后,导致应该过期的数据又在从库上被读取到了。
当主从库全量同步时,如果主库接收到了一条 EXPIRE 命令,那么,主库会直接执行这条命令。这条命令会在全量同步完成后,发给从库执行。而从库在执行时,就会在当前时间的基础上加上数据的存活时间,这样一来,从库上数据的过期时间就会比主库上延后了。
为了避免这种情况,我给你的建议是,在业务应用中使用 EXPIREAT/PEXPIREAT 命令,把数据的过期时间设置为具体的时间点,避免读到过期数据。
结尾
大概总结了
Redis在事务机制ACID的相关实现保证 分析了使用Redis时,Redis主从同步的那些问题
由主从同步问题展开了主从数据不一致的原因以及解决方案,过期数据的原因以及解决方案。
这篇文章大概算是Redis第二阶段的一个收尾吧。下面将从RocketMQ或者Mybatis进行技术的分享!
非常欢迎大家加我个人微信有关后端方面的问题我们在群内一起讨论! 我们下期再见!
长按上方扫码二维码,加我微信,拉你进群