Redis数据倾斜问题
数据倾斜数据倾斜有两类。
数据量倾斜:在某些情况下,实例上的数据分布不均衡,某个实例上的数据特别多。
数据访问倾斜:虽然每个集群实例上的数据量相差不大,但是某个实例上的数据是热点数据,被访问得非常频繁。
数据量倾斜
bigkey 导致倾斜
bigkey 的 value 值很大(String 类型),或者是 bigkey 保存了大量集合元素(集合类型),会导致这个实例的数据量增加,内存资源消耗也相应增加。
解决方法:
我们在业务层生成数据时,要尽量避免把过多的数据保存在同一个键值对中。
如果 bigkey 正好是集合类型,我们还有一个方法,就是把 bigkey 拆分成很多个小的集合类型数据,分散保存在不同的实例上。
Slot 分配不均衡导致倾斜
如果没有均衡地分配 Slot,就会有大量的数据被分配到同一个 Slot 中,而同一个 Slot 只会在一个实例上分布,这就会导致,大量数据被集中到一个实例上,造成数据倾斜。
解决方法:
在分配之前,我们就要避免把过多的 Slot 分配到同一个实例。
如果已经分配,可以对不合理的slot进行迁移
Hash Tag导致倾斜
Hash ...
秒杀场景模拟
秒杀场景秒杀场景的负载特征对支撑系统的要求
特征是瞬时并发访问量非常高。
一般数据库每秒只能支撑千级别的并发请求,而 Redis 的并发处理能力(每秒处理请求数)能达到万级别,甚至更高。所以,当有大量并发请求涌入秒杀系统时,我们就需要使用 Redis 先拦截大部分请求,避免大量请求直接发送给数据库,把数据库压垮。
特征是读多写少,而且读操作是简单的查询操作。
库存查验操作是典型的键值对查询,而 Redis 对键值对查询的高效支持,正好和这个操作的要求相匹配。
秒杀的三个阶段
秒杀活动前
在这个阶段,用户会不断刷新商品详情页,这会导致详情页的瞬时请求量剧增。这个阶段的应对方案,一般是尽量把商品详情页的页面元素静态化,然后使用 CDN 或是浏览器把这些静态化的元素缓存起来。
秒杀活动开始
这个阶段的操作就是三个:库存查验、库存扣减和订单处理。(并发主要是在库存查验操作上)
库存查验:
为了支撑大量高并发的库存查验请求,我们需要在这个环节使用 Redis 保存库存量,这样一来,请求可以直接从 Redis 中读取库存并进行查验。
库存扣减:
需要使用Redis来执行;
因为放在数据 ...
Redis主从问题
主从问题
数据不一致主要原因:
主从库间的网络可能会有传输延迟,所以从库不能及时地收到主库发送的命令,从库上执行同步命令的时间就会被延后。
另一方面,即使从库及时收到了主库的命令,但是,也可能会因为正在处理其它复杂度高的命令(例如集合操作命令)而阻塞。此时,从库需要处理完当前的命令,才能执行主库发送的命令操作,这就会造成主从数据不一致。
两种方法:
在硬件环境配置方面,我们要尽量保证主从库间的网络连接状况良好。
我们还可以开发一个外部程序来监控主从库间的复制进度。
因为 Redis 的 INFO replication 命令可以查看主库接收写命令的进度信息(master_repl_offset)和从库复制写命令的进度信息(slave_repl_offset),所以,我们就可以开发一个监控程序,先用 INFO replication 命令查到主、从库的进度,然后,我们用 master_repl_offset 减去 slave_repl_offset,这样就能得到从库和主库间的复制进度差值了。
如果某个从库的进度差值大于我们预设的阈值,我们可以让客户端不再和这个从库连接进行数据读取 ...
Redis主从同步
主从同步同步流程当我们启动多个 Redis 实例的时候,它们相互之间就可以通过 replicaof(Redis 5.0 之前使用 slaveof)命令形成主库和从库的关系,之后会按照三个阶段完成数据的第一次同步。
第一阶段:是主从库间建立连接、协商同步的过程,主要是为全量复制做准备。(从库给主库发送 psync 命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。psync 命令包含了主库的 runID 和复制进度 offset 两个参数。)
在第二阶段:主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载。这个过程依赖于内存快照生成的 RDB 文件。(从库接收到 RDB 文件后,会先清空当前数据库,然后加载 RDB 文件)
第三个阶段:主库会把第二阶段执行过程中新收到的写命令,再发送给从库。
存在问题问题一:
一次全量复制中,对于主库来说,需要完成两个耗时的操作:生成 RDB 文件和传输 RDB 文件。如果从库数量很多,而且都要和主库进行全量复制的话,就会导致主库忙于 fork 子进程生成 RDB 文件,进行数据全量同步,导致fork操作阻塞主线程。
解决:通 ...
Redis事务
Redis事务Redis 提供了 MULTI、EXEC 两个命令来进行事务操作。
MULTI:表示一个事务的开启,之后客户端发过来的命令都会暂存到一个命令队列中,并不会立即执行。
EXEC:当服务器端收到 EXEC 命令后,才会实际执行命令队列中的所有命令。
Redis 的事务机制能保证哪些属性?原子性
没有发生错误:那操作可以保证原子性
执行发生错误:需要分3种情况
在执行 EXEC 命令前,客户端发送的操作命令本身就有错误。(例如语法错误等)
此时,我们还能继续提交命令操作。等到执行了 EXEC 命令之后,Redis 就会拒绝执行所有提交的命令操作。
在执行 EXEC 命令前,命令和操作的数据类型不匹配,但 Redis 实例没有检查出错误。
Redis会对错误命令报错,但还是会把正确的命令执行完。在这种情况下,事务的原子性就无法得到保证了。
如果有错误语句可以使用DISCAR命令来让事务放弃
在执行事务的 EXEC 命令时,Redis 实例发生了故障,导致事务执行失败。
如果 Redis 开启了 AOF 日志,使用 redis-check-aof 工具检查 AOF ...
Redis应对高并发
应对高并发我们在使用 Redis 时,不可避免地会遇到并发访问的问题。
为了保证并发访问的正确性,Redis 提供了两种方法,分别是加锁和原子操作。
加锁加锁是一种常用的方法,在读取数据前,客户端需要先获得锁,否则就无法进行操作。当一个客户端获得锁后,就会一直持有这把锁,直到客户端完成数据更新,才释放这把锁。
缺点:
如果加锁操作多,会降低系统的并发访问性能。
Redis 客户端要加锁时,需要用到分布式锁,而分布式锁实现复杂,需要用额外的存储系统来提供加解锁操作。
分布式锁
基于单个 Redis 节点实现分布式锁
用Redis存锁变量,key为变量名,value为锁的值;例如:lock_key:0
为了保证锁操作的原子性,需要使用原子操作的语句来实现加锁与释放锁。
加锁:SET key value [EX seconds | PX milliseconds] [NX]例如:SET lock_key unique_value NX PX 10000其中,unique_value 是客户端的唯一标识,可以用一个随机生成的字符串来表示,PX 10000 则表示 lock_key 会在 ...
Redis缓存与数据库一致性问题
缓存与数据库一致性问题概述这里的“一致性”包含了两种情况:
缓存中有数据,那么,缓存的数据值需要和数据库中的值相同;
缓存中本身没有数据,那么,数据库中的值必须是最新值。
不符合这两种情况的,就属于缓存和数据库的数据不一致问题了。
解决方案重试机制具体来说,可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用 Kafka 消息队列)。当应用没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
两种不同顺序存在的问题:
情况一:先删除缓存,再更新数据库。
延迟双删方案:在线程 A 更新完数据库值以后,我们可以让它先 sleep 一小段时间,再进行一次缓存删除操作。
情况二:先更新数据库值,再删除缓存值。
这种情况对业务的影响较小。
总结:
Redis内存替换策略
内存替换策略策略分类Redis 4.0 之前一共实现了 6 种内存淘汰策略,在 4.0 之后,又增加了 2 种策略。
我们可以按照是否会进行数据淘汰把它们分成两类:
不进行数据淘汰的策略,只有 noeviction 这一种。
会进行淘汰的 7 种其他策略。
在设置了过期时间的数据中进行淘汰,包括 volatile-random、volatile-ttl、volatile-lru、volatile-lfu(Redis 4.0 后新增)四种。
在所有数据范围内进行淘汰,包括 allkeys-lru、allkeys-random、allkeys-lfu(Redis 4.0 后新增)三种。
策略详讲 noeviction 策略
Redis 在使用的内存空间超过 maxmemory 值时,并不会淘汰数据,也就是设定的 noeviction 策略。
对应到 Redis 缓存,也就是指,一旦缓存被写满了,再有写请求来时,Redis 不再提供服务,而是直接返回错误。
基于过期时间的四种策略
volatile-ttl:在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除, ...
Redis做缓冲
Redis做缓存缓存的特征
第一个特征:在一个层次化的系统中,缓存一定是一个快速子系统,数据存在缓存中时,能避免每次从慢速子系统中存取数据。
第二个特征:缓存系统的容量大小总是小于后端慢速系统的,我们不可能把所有数据都放在缓存系统中。
Redis 缓存处理请求的两种情况
缓存命中:Redis 中有相应数据,就直接读取 Redis,性能非常快。
缓存缺失:Redis 中没有保存相应数据,就从后端数据库中读取数据,性能就会变慢。而且,一旦发生缓存缺失,为了让后续请求能从缓存中读取到数据,我们需要把缺失的数据写入 Redis,这个过程叫作缓存更新。
因为需要在应用程序新增程序代码来使用缓存,所以,Redis 并不适用于那些无法获得源码的应用。
缓存类型只读缓存当 Redis 用作只读缓存时,应用要读取数据的话,会先调用 Redis GET 接口,查询数据是否存在。而所有的数据写请求,会直接发往后端的数据库,在数据库中增删改。对于删改的数据来说,如果 Redis 已经缓存了相应的数据,应用需要把这些缓存的数据删除,Redis 中就没有这些数据了。当应用再次读取这些数据时,会发 ...
Redis缓冲区
缓冲区概述缓冲区的功能其实很简单,主要就是用一块内存空间来暂时存放命令数据,以免出现因为数据和命令的处理速度慢于发送速度而导致的数据丢失和性能问题。
Redis 是典型的client-server 架构,所有的操作命令都需要通过客户端发送给服务器端。所以,缓冲区在 Redis 中的一个主要应用场景,就是在客户端和服务器端之间进行通信时,用来暂存客户端发送的命令数据,或者是服务器端返回给客户端的数据结果。此外,缓冲区的另一个主要应用场景,是在主从节点间进行数据同步时,用来暂存主节点接收的写命令和数据。
输入缓冲区溢出可能导致溢出的情况主要是下面两种:
写入了 bigkey,比如一下子写入了多个百万级别的集合类型数据。
服务器端处理请求的速度过慢,例如,Redis 主线程出现了间歇性阻塞,无法及时处理正常发送的请求,导致客户端发送的请求在缓冲区越积越多。
要查看和服务器端相连的每个客户端对输入缓冲区的使用情况,我们可以使用 CLIENT LIST 命令:
重点关注两类信息:
一类是与服务器端连接的客户端的信息。
另一类是与输入缓冲区相关的三个参数:
cmd,表示客户端最新执行的命 ...