day760.redis常见问题③-redis核心技术与实战(代码片段)

阿昌喜欢吃黄桃 阿昌喜欢吃黄桃     2022-10-23     679

关键词:

Redis常见问题③

Hi,我是阿昌,今天学习记录Redis常见问题③的部分。

1、Redis 的只读缓存和使用直写策略的读写缓存,都会把数据同步写到后端数据库中,你觉得它们有什么区别吗?

主要的区别在于,当有缓存数据被修改时:

  • 只读缓存中,业务应用会直接修改数据库,并把缓存中的数据标记为无效;

  • 读写缓存中,业务应用需要同时修改缓存和数据库。

2、Redis 缓存在处理脏数据时,不仅会修改数据,还会把它写回数据库。Redis 的只读缓存模式和两种读写缓存模式(带同步直写的读写模式,带异步写回的读写模式)),Redis 缓存对应哪一种或哪几种模式?

如果在使用 Redis 缓存时,需要把脏数据写回数据库,这就意味着,Redis 中缓存的数据可以直接被修改,这就对应了读写缓存模式

脏数据是在被替换出缓存时写回后端数据库的,这就对应了带有异步写回策略的读写缓存模式


3、在只读缓存中对数据进行删改时,需要在缓存中删除相应的缓存值。如果在这个过程中,不是删除缓存值,而是直接更新缓存的值,你觉得,和删除缓存值相比,直接更新缓存值有什么好处和不足吗?

如果直接在缓存中更新缓存值,等到下次数据再被访问时,业务应用可以直接从缓存中读取数据,这是它的一大好处。

不足之处在于,当有数据更新操作时,我们要保证缓存和数据库中的数据是一致的,这就可以采用重试延时双删方法

不过,这样就需要在业务应用中增加额外代码,有一定的开销。


4、在缓存雪崩时,可以采用服务熔断、服务降级、请求限流三种方法来应对。这三个方法可以用来应对缓存穿透问题吗?

缓存穿透这个问题的本质是查询了 Redis 和数据库中没有的数据,而服务熔断、服务降级和请求限流的方法,本质上是为了解决 Redis 实例没有起到缓存层作用的问题,缓存雪崩和缓存击穿都属于这类问题。

在缓存穿透的场景下,业务应用是要从 Redis 和数据库中读取不存在的数据,此时,如果没有人工介入,Redis 是无法发挥缓存作用的。

一个可行的办法就是事前拦截,不让这种查询 Redis 和数据库中都没有的数据的请求发送到数据库层。使用布隆过滤器也是一个方法,布隆过滤器在判别数据不存在时,是不会误判的,而且判断速度非常快,一旦判断数据不存在,就立即给客户端返回结果。

使用布隆过滤器的好处是既降低了对 Redis 的查询压力,也避免了对数据库的无效访问。

对于缓存雪崩和击穿问题来说,服务熔断、服务降级和请求限流这三种方法属于有损方法,会降低业务吞吐量、拖慢系统响应、降低用户体验。

不过,采用这些方法后,随着数据慢慢地重新填充回 Redis,Redis 还是可以逐步恢复缓存层作用的。


5、使用了 LFU 策略后,缓存还会被污染吗?

在 Redis 中,使用了 LFU 策略后,还是有可能发生缓存污染的。

在一些极端情况下,LFU 策略使用的计数器可能会在短时间内达到一个很大值,而计数器的衰减配置项设置得较大,导致计数器值衰减很慢,在这种情况下,数据就可能在缓存中长期驻留。

例如,一个数据在短时间内被高频访问,即使使用了 LFU 策略,这个数据也有可能滞留在缓存中,造成污染。


6、使用 SSD 作为内存容量的扩展,增加 Redis 实例的数据保存量,可以使用机械硬盘来作为实例容量扩展吗?有什么好处或不足吗?

容量维度来看,机械硬盘的性价比更高,机械硬盘每 GB 的成本大约在 0.1 元左右,而 SSD 每 GB 的成本大约是 0.4~0.6 元左右。

性能角度来看,机械硬盘(例如 SAS 盘)的延迟大约在 3~5ms,而企业级 SSD 的读延迟大约是 60~80us,写延迟在 20us。

缓存的负载特征一般是小粒度数据、高并发请求,要求访问延迟低。

所以,如果使用机械硬盘作为 Pika 底层存储设备的话,缓存的访问性能就会降低。

建议是,如果业务应用需要缓存大容量数据,但是对缓存的性能要求不高,就可以使用机械硬盘,否则最好是用 SSD。


7、Redis 在执行 Lua 脚本时,是可以保证原子性的,那么,举的 Lua 脚本例子(lua.script)中,是否需要把读取客户端 ip 的访问次数,也就是 GET(ip),以及判断访问次数是否超过 20 的判断逻辑,也加到 Lua 脚本中吗?

local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
    redis.call("expire",KEYS[1],60)
end

在这个例子中,要保证原子性的操作有三个,分别是 INCR、判断访问次数是否为 1 和设置过期时间。

而对于获取 IP 以及判断访问次数是否超过 20 这两个操作来说,它们只是读操作,即使客户端有多个线程并发执行这两个操作,也不会改变任何值,所以并不需要保证原子性,也就不用把它们放到 Lua 脚本中了。


8、使用 SET 命令带上 NX 和 EX/PX 选项进行加锁操作,是否可以用下面的方式来实现加锁操作呢?

// 加锁
SETNX lock_key unique_value
EXPIRE lock_key 10S
// 业务逻辑
DO THINGS

如果使用这个方法实现加锁的话,SETNX 和 EXPIRE 两个命令虽然分别完成了对锁变量进行原子判断和值设置,以及设置锁变量的过期时间的操作,但是这两个操作一起执行时,并没有保证原子性

如果在执行了 SETNX 命令后,客户端发生了故障,但锁变量还没有设置过期时间,就无法在实例上释放了,这就会导致别的客户端无法执行加锁操作。

所以,不能使用这个方法进行加锁。


9、在执行事务时,如果 Redis 实例发生故障,而 Redis 使用的是 RDB 机制,那么,事务的原子性还能得到保证吗?

当 Redis 采用 RDB 机制保证数据可靠性时,Redis 会按照一定的周期执行内存快照。

一个事务在执行过程中,事务操作对数据所做的修改并不会实时地记录到 RDB 中,而且,Redis 也不会创建 RDB 快照。

可以根据故障发生的时机以及 RDB 是否生成,分成三种情况来讨论事务的原子性保证。

  1. 假设事务在执行到一半时,实例发生了故障,在这种情况下,上一次 RDB 快照中不会包含事务所做的修改,而下一次 RDB 快照还没有执行。所以,实例恢复后,事务修改的数据会丢失,事务的原子性能得到保证。
  2. 假设事务执行完成后,RDB 快照已经生成了,如果实例发生了故障,事务修改的数据可以从 RDB 中恢复,事务的原子性也就得到了保证。
  3. 假设事务执行已经完成,但是 RDB 快照还没有生成,如果实例发生了故障,那么,事务修改的数据就会全部丢失,也就谈不上原子性了。

10、在主从集群中,我们把 slave-read-only 设置为 no,让从库也能直接删除数据,以此来避免读到过期数据,这是一个好方法吗?

假设从库也能直接删除过期数据的话(也就是执行写操作),是不是一个好方法?

主从复制中的增删改操作都需要在主库执行,即使从库能做删除,也不要在从库删除,否则会导致数据不一致

例如,主从库上都有 a:stock 的键,客户端 A 给主库发送一个 SET 命令,修改 a:stock 的值,客户端 B 给从库发送了一个 SET 命令,也修改 a:stock 的值,此时,相同键的值就不一样了。所以,如果从库具备执行写操作的功能,就会导致主从数据不一致

即使从库可以删除过期数据,也还会有不一致的风险,有两种情况。

  • 第一种情况是,对于已经设置了过期时间的 key,主库在 key 快要过期时,使用 expire 命令重置了过期时间,例如,一个 key 原本设置为 10s 后过期,在还剩 1s 就要过期时,主库又用 expire 命令将 key 的过期时间设置为 60s 后。但是,expire 命令从主库传输到从库时,由于网络延迟导致从库没有及时收到 expire 命令(比如延后了 3s 从库才收到 expire 命令),所以,从库按照原定的过期时间删除了过期 key,这就导致主从数据不一致了。
  • 第二种情况是,主从库的时钟不同步,导致主从库删除时间不一致。另外,当 slave-read-only 设置为 no 时,如果在从库上写入的数据设置了过期时间,Redis 4.0 前的版本不会删除过期数据,而 Redis 4.0 及以上版本会在数据过期后删除。但是,对于主库同步过来的带有过期时间的数据,从库仍然不会主动进行删除。

11、假设将 min-slaves-to-write 设置为 1,min-slaves-max-lag 设置为 15s,哨兵的 down-after-milliseconds 设置为 10s,哨兵主从切换需要 5s,而主库因为某些原因卡住了 12s。此时,还会发生脑裂吗?主从切换完成后,数据会丢失吗?

主库卡住了 12s,超过了哨兵的 down-after-milliseconds 10s 阈值,所以,哨兵会把主库判断为客观下线,开始进行主从切换。

因为主从切换需要 5s,在主从切换过程中,原主库恢复正常。min-slaves-max-lag 设置的是 15s,而原主库在卡住 12s 后就恢复正常了,所以没有被禁止接收请求,客户端在原主库恢复后,又可以发送请求给原主库。

一旦在主从切换之后有新主库上线,就会出现脑裂

如果原主库在恢复正常后到降级为从库前的这段时间内,接收了写操作请求,那么,这些数据就会丢失了。


12、如何理解把 Redis 称为旁路缓存?

旁路缓存是指,写请求的处理方式是直接更新数据库,并删除缓存数据;而读请求的处理方式是查询缓存,如果缓存缺失,就读取数据库,并把数据写入缓存

“Redis 属于旁路缓存”是这个意思吗?

这是典型的只读缓存的特点。而Redis 称为旁路缓存,更多的是从“业务应用程序如何使用 Redis 缓存”这个角度来说的。

业务应用在使用 Redis 缓存时,需要在业务代码中显式地增加缓存的操作逻辑

例如,一个基本的缓存操作就是,一旦发生缓存缺失,业务应用需要自行去读取数据库,而不是缓存自身去从数据库中读取数据再返回。

再来看下和旁路缓存相对应的、计算机系统中的 CPU 缓存和 page cache。

这两种缓存默认就在应用程序访问内存和磁盘的路径上,写的应用程序都能直接使用这两种缓存。

强调 Redis 是一个旁路缓存,也是希望能够记住,在使用 Redis 缓存时,我们需要修改业务代码

13、使用 Redis 缓存时,应该用哪种模式?

采用只读缓存

通用的缓存模式有三种:

  • 只读缓存模式
  • 采用同步直写策略的读写缓存模式
  • 采用异步写回策略的读写缓存模式

一般情况下,会把 Redis 缓存用作只读缓存

只读缓存涉及的操作,包括查询缓存、缓存缺失时读数据库和回填,数据更新时删除缓存数据,这些操作都可以加到业务应用中。而且,当数据更新时,缓存直接删除数据,缓存和数据库的数据一致性较为容易保证。

当然,有时也会把 Redis 用作读写缓存,同时采用同步直写策略。在这种情况下,缓存涉及的操作也都可以加到业务应用中。

只读缓存相比有一个好处,就是数据修改后的最新值可以直接从缓存中读取。

异步写回策略的读写缓存模式,缓存系统需要能在脏数据被淘汰时,自行把数据写回数据库,但是,Redis 是无法实现这一点的,所以使用 Redis 缓存时,并不采用这个模式。


day748.redis常见问题②-redis核心技术与实战(代码片段)

Redis常见问题②Hi,我是阿昌,今天记录统计聚合一下前面学习过来的一系列Redis常见问题②。1、除了String类型和Hash类型,还有什么类型适合保存在【String数据结构内存空间消耗问题】所说的图片吗?除了String和Ha... 查看详情

day736.redis常见问题①-redis核心技术与实战(代码片段)

Redis常见问题①Hi,我是阿昌,今天记录的是之前文章中一些列Redis常见问题①一、前置文章末尾问题1、和跟Redis相比,SimpleKV还缺少什么?2、整数数组和压缩列表作为底层数据结构的优势是什么?整数数组和... 查看详情

day767.redis常见问题④-redis核心技术与实战(代码片段)

Redis常见问题④Hi,我是阿昌,今天学习记录的是关于Redis常见问题④。1、Redis集群方案:Codis问题:假设Codis集群中保存的80%的键值对都是Hash类型,每个Hash集合的元素数量在10万~20万个,每个集合元素的大... 查看详情

day746.redis内存碎片问题-redis核心技术与实战(代码片段)

Redis内存碎片问题Hi,我是阿昌,今天学习记录的是关于Redis内存碎片问题。在使用Redis时,经常会遇到这样一个问题:明明做了数据删除,数据量已经不大了,为什么使用top命令查看时,还会发现Redis占... 查看详情

day763.redis数据倾斜问题-redis核心技术与实战(代码片段)

Redis数据倾斜问题Hi,我是阿昌,今天学习记录的是关于Redis数据倾斜问题的内容。在切片集群中,数据会按照一定的分布规则分散到不同的实例上保存。比如,在使用RedisCluster或Codis时,数据都会先按照CRC算法... 查看详情

day763.redis数据倾斜问题-redis核心技术与实战(代码片段)

Redis数据倾斜问题Hi,我是阿昌,今天学习记录的是关于Redis数据倾斜问题的内容。在切片集群中,数据会按照一定的分布规则分散到不同的实例上保存。比如,在使用RedisCluster或Codis时,数据都会先按照CRC算法... 查看详情

day759.redis脑裂问题-redis核心技术与实战(代码片段)

Redis脑裂问题Hi,我是阿昌,今天学习记录的关于Redis脑裂问题。在使用主从集群时,一个问题:主从集群有1个主库、5个从库和3个哨兵实例,在使用的过程中,发现客户端发送的一些数据丢失了,这直... 查看详情

day742.redis阻塞主线程的问题-redis核心技术与实战(代码片段)

Redis阻塞主线程的问题Hi,我是阿昌,今天学习记录的内容是Redis阻塞主线程的问题。Redis之所以被广泛应用,很重要的一个原因就是它支持高性能访问。也正因为这样,我们必须要重视所有可能影响Redis性能的因素... 查看详情

day743.cpu结构影响redis的性能问题-redis核心技术与实战(代码片段)

CPU结构影响Redis的性能问题Hi,我是阿昌,今天学习记录的是关于CPU结构影响Redis的性能问题。很多人都认为Redis和CPU的关系很简单,就是Redis的线程在CPU上运行,CPU快,Redis处理请求的速度也很快。这种认知其... 查看详情

day753.缓存污染问题-redis核心技术与实战(代码片段)

缓存污染问题Hi,我是阿昌,今天学习记录的是关于缓存污染问题。应用Redis缓存时,如果能缓存会被反复访问的数据,那就能加速业务应用的访问。但是,如果发生了缓存污染,那么,缓存对业务应用... 查看详情

day744.如何应对变慢的redis①-redis核心技术与实战(代码片段)

如何应对变慢的Redis①Hi,我是阿昌,今天学习记录的内容是如何应对变慢的Redis①。在Redis的实际部署应用中,有一个非常严重的问题,那就是Redis突然变慢了。一旦出现这个问题,不仅会直接影响用户的使用... 查看详情

day755.redis的原子操作-redis核心技术与实战(代码片段)

Redis的原子操作Hi,我是阿昌,今天学习记录的是关于Redis的原子操作的内容,感谢您。在使用Redis时,不可避免地会遇到并发访问的问题,比如说如果多个用户同时下单,就会对缓存在Redis中的商品库存并... 查看详情

day751.缓存和数据库的数据不一致问题-redis核心技术与实战(代码片段)

缓存和数据库的数据不一致问题Hi,我是阿昌,今天学习记录的是关于缓存和数据库的数据不一致问题的内容,感谢你。在实际应用Redis缓存时,经常会遇到一些异常问题,概括来说有4个方面:缓存中的数... 查看详情

day774.能向redis学到什么-redis核心技术与实战(代码片段)

能向Redis学到什么Hi,我是阿昌,今天学习记录的是关于能向Redis学到什么。大多数人人都会觉得,就是掌握具体的原理,进行实战,并且学习别人的经验,解决自己在实际工作中的问题。比如说,学习R... 查看详情

day747.redis缓冲区-redis核心技术与实战(代码片段)

Redis缓存区Hi,我是阿昌,今天学习记录的是关于Redis缓冲区。缓冲区的功能其实很简单,主要就是用一块内存空间来暂时存放命令数据,以免出现因为数据和命令的处理速度慢于发送速度而导致的数据丢失和性能... 查看详情

day768.大佬推荐的经典的redis学习资料-redis核心技术与实战(代码片段)

大佬推荐的经典的Redis学习资料Hi,我是阿昌,今天学习记录的是关于经典的Redis学习资料。一、经典书籍在学习Redis时,最常见的需求有三个方面。日常使用操作:比如常见命令和配置,集群搭建等;关键... 查看详情

day737.string数据结构内存空间消耗问题-redis核心技术与实战(代码片段)

String数据结构内存空间消耗问题Hi,我是阿昌,每天都要学习哦,今天学习的是String数据结构内存空间消耗问题的内容,感谢您的关注和阅读。欢迎白嫖!这次,先了解下String类型的内存空间消耗问题,以及... 查看详情

day745.如何应对变慢的redis②-redis核心技术与实战(代码片段)

如何应对变慢的Redis②Hi,我是阿昌,今天学习记录的是关于如何应对变慢的Redis②的内容。上一篇聊了判断Redis变慢的两种方法,分别是响应延迟和基线性能。除此之外,还分享了从Redis的自身命令操作层面排查和... 查看详情