网站结构组成部分有那些,网络优化工程师证书,企业网站手机端开发,微信公众平台官网电话文章目录 1 基准性能2 使用 slowlog 优化耗时命令3 big key 优化4 使用 lazy free 特性5 缩短键值对的存储长度6 设置键值的过期时间7 禁用耗时长的查询命令8 使用 Pipeline 批量操作数据9 避免大量数据同时失效10 客户端使用优化11 限制 Redis 内存大小12 使用物理机而非虚拟机… 文章目录 1 基准性能2 使用 slowlog 优化耗时命令3 big key 优化4 使用 lazy free 特性5 缩短键值对的存储长度6 设置键值的过期时间7 禁用耗时长的查询命令8 使用 Pipeline 批量操作数据9 避免大量数据同时失效10 客户端使用优化11 限制 Redis 内存大小12 使用物理机而非虚拟机13 检查数据持久化策略14 使用分布式架构来增加读写速度15 持久化16 绑定 CPU17 内存 swap 1 基准性能
什么是基准性能 简单来讲基准性能就是指 Redis 在一台负载正常的机器上其最大的响应延迟和平均响应延迟分别是怎样的 为什么要测试基准性能我参考别人提供的响应延迟判断自己的 Redis 是否变慢不行吗
答案是否定的。 因为 Redis 在不同的软硬件环境下它的性能是各不相同的。 例如我的机器配置比较低当延迟为 2ms 时我就认为 Redis 变慢了但是如果你的硬件配置比较高那么在你的运行环境下可能延迟是 0.5ms 时就可以认为 Redis 变慢了。 所以你只有了解了你的 Redis 在生产环境服务器上的基准性能才能进一步评估当其延迟达到什么程度时才认为 Redis 确实变慢了。
具体如何做 为了避免业务服务器到 Redis 服务器之间的网络延迟你需要直接在 Redis 服务器上测试实例的响应延迟情况。执行以下命令就可以测试出这个实例 60 秒内的最大响应延迟
$ redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60Max latency so far: 1 microseconds.Max latency so far: 15 microseconds.Max latency so far: 17 microseconds.Max latency so far: 18 microseconds.Max latency so far: 31 microseconds.Max latency so far: 32 microseconds.Max latency so far: 59 microseconds.Max latency so far: 72 microseconds.1428669267 total runs (avg latency: 0.0420 microseconds / 42.00 nanoseconds per run).Worst run took 1429x longer than the average latency.从输出结果可以看到这 60 秒内的最大响应延迟为 72 微秒0.072毫秒。 你还可以使用以下命令查看一段时间内 Redis 的最小、最大、平均访问延迟
$ redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1min: 0, max: 1, avg: 0.13 (100 samples) -- 1.01 seconds rangemin: 0, max: 1, avg: 0.12 (99 samples) -- 1.01 seconds rangemin: 0, max: 1, avg: 0.13 (99 samples) -- 1.01 seconds rangemin: 0, max: 1, avg: 0.10 (99 samples) -- 1.01 seconds rangemin: 0, max: 1, avg: 0.13 (98 samples) -- 1.00 seconds rangemin: 0, max: 1, avg: 0.08 (99 samples) -- 1.01 seconds range...以上输出结果是每间隔 1 秒采样 Redis 的平均操作耗时其结果分布在 0.08 ~ 0.13 毫秒之间。 了解了基准性能测试方法那么你就可以按照以下几步来判断你的 Redis 是否真的变慢了
在相同配置的服务器上测试一个正常 Redis 实例的基准性能找到你认为可能变慢的 Redis 实例测试这个实例的基准性能如果你观察到这个实例的运行延迟是正常 Redis 基准性能的 2 倍以上即可认为这个 Redis 实例确实变慢了
2 使用 slowlog 优化耗时命令
我们可以使用 slowlog 功能找出最耗时的 Redis 命令进行相关的优化以提升 Redis 的运行速度慢查询有两个重要的配置项
slowlog-log-slower-than用于设置慢查询的评定时间也就是说超过此配置项的命令将会被当成慢操作记录在慢查询日志中它执行单位是微秒1 秒等于 1000000 微秒 slowlog-max-len用来配置慢查询日志的最大记录数。 我们可以根据实际的业务情况进行相应的配置其中慢日志是按照插入的顺序倒序存入慢查询日志中我们可以使用
来获取相关的慢查询日志再找到这些慢查询对应的业务进行相关的优化。 Redis 提供了慢日志命令的统计功能它记录了有哪些命令在执行时耗时比较久。 查看 Redis 慢日志之前你需要设置慢日志的阈值。例如设置慢日志的阈值为 5 毫秒并且保留最近 500 条慢日志记录
# 命令执行耗时超过 5 毫秒记录慢日志CONFIG SET slowlog-log-slower-than 5000# 只保留最近 500 条慢日志CONFIG SET slowlog-max-len 500设置完成之后所有执行的命令如果操作耗时超过了 5 毫秒都会被 Redis 记录下来。 此时你可以执行以下命令就可以查询到最近记录的慢日志
127.0.0.1:6379 SLOWLOG get 51) 1) (integer) 32693 # 慢日志ID2) (integer) 1593763337 # 执行时间戳3) (integer) 5299 # 执行耗时(微秒)4) 1) LRANGE # 具体执行的命令和参数2) user_list:20003) 04) -12) 1) (integer) 326922) (integer) 15937633373) (integer) 50444) 1) GET2) user_info:1000...通过查看慢日志我们就可以知道在什么时间点执行了哪些命令比较耗时。 如果你的应用程序执行的 Redis 命令有以下特点那么有可能会导致操作延迟变大 经常使用 O(N) 以上复杂度的命令例如 SORT、SUNION、ZUNIONSTORE 聚合类命令 使用 O(N) 复杂度的命令但 N 的值非常大 第一种情况导致变慢的原因在于Redis 在操作内存数据时时间复杂度过高要花费更多的 CPU 资源。 第二种情况导致变慢的原因在于Redis 一次需要返回给客户端的数据过多更多时间花费在数据协议的组装和网络传输过程中。 另外我们还可以从资源使用率层面来分析如果你的应用程序操作 Redis 的 OPS 不是很大但 Redis 实例的 CPU 使用率却很高那么很有可能是使用了复杂度过高的命令导致的。 除此之外我们都知道Redis 是单线程处理客户端请求的如果你经常使用以上命令那么当 Redis 处理客户端请求时一旦前面某个命令发生耗时就会导致后面的请求发生排队对于客户端来说响应延迟也会变长。 针对这种情况你可以使用以下方法优化你的业务 尽量不使用 O(N) 以上复杂度过高的命令对于数据的聚合操作放在客户端做 执行 O(N) 命令保证 N 尽量的小推荐 N 300每次获取尽量少的数据让 Redis 可以及时处理返回
3 big key 优化
如果你查询慢日志发现并不是复杂度过高的命令导致的而都是 SET / DEL 这种简单命令出现在慢日志中那么你就要怀疑你的实例否写入了 bigkey。Redis 在写入数据时需要为新的数据分配内存相对应的当从 Redis 中删除数据时它会释放对应的内存空间。 如果一个 key 写入的 value 非常大那么 Redis 在分配内存时就会比较耗时。同样的当删除这个 key 时释放内存也会比较耗时这种类型的 key 我们一般称之为 bigkey。 此时你需要检查你的业务代码是否存在写入 bigkey 的情况。你需要评估写入一个 key 的数据大小尽量避免一个 key 存入过大的数据。 bigkey 带来问题如下
Redis使用过程中经常会有各种大key的情况 比如
单个简单的key存储的value很大hash setzsetlist 中存储过多的元素以万为单位 由于redis是单线程运行的如果一次操作的value很大会对整个redis的响应时间造成负面影响所以业务上能拆则拆阿里云 redis 的最佳实践推荐小于10 KB建议每个key不要超过M级别 过大的Value会引发数据倾斜、热点Key、实例流量或CPU性能被占满等问题应从设计源头上避免此类问题带来的影响。
大key的风险
读写大key会导致超时严重甚至阻塞服务。删除一个 bigkey 造成主库较长时间的阻塞使得其他请求无法执行并引发同步中断或主从切换Redis 内存不断变大引发 OOM或者达到 maxmemory 设置值引发写阻塞或重要 Key 被逐出Redis Cluster 中的某个 node 内存远超其余 node但因 Redis Cluster 的数据迁移最小粒度为 Key 而无法将 node 上的内存均衡化bigkey 的读请求占用过大带宽自身变慢的同时影响到该服务器上的其它服务
单个简单的key存储的value很大 改对象需要每次都整存整取 可以尝试将对象分拆成几个key-value 使用multiGet获取值这样分拆的意义在于分拆单次操作的压力将操作压力平摊到多个redis实例中降低对单个redis的IO影响 该对象每次只需要存取部分数据 可以像第一种做法一样分拆成几个key-value 也可以将这个存储在一个hash中每个field代表一个具体的属性使用hget,hmget来获取部分的value使用hsethmset来更新部分属性。 hash setzsetlist 中存储过多的元素 将大key进行分割为了均匀分割可以对field进行hash并通过质数N取余将余数加到key上面我们取质数N为997。 那么新的key则可以设置为
newKey order_20181010_String.valueOf( Math.abs(order_id.hashcode() % 997) )
field order_id
value 10
hset (newKey, field, value) ;
hget(newKey, field)如果已经写入了 bigkey那有没有什么办法可以扫描出实例中 bigkey 的分布情况呢 答案是可以的。 Redis 提供了扫描 bigkey 的命令执行以下命令就可以扫描出一个实例中 bigkey 的分布情况输出结果是以类型维度展示的
$ redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01
...-------- summary -------Sampled 829675 keys in the keyspace!Total key length in bytes is 10059825 (avg len 12.13)Biggest string found key:291880 has 10 bytesBiggest list found mylist:004 has 40 itemsBiggest set found myset:2386 has 38 membersBiggest hash found myhash:3574 has 37 fieldsBiggest zset found myzset:2704 has 42 members36313 strings with 363130 bytes (04.38% of keys, avg size 10.00)787393 lists with 896540 items (94.90% of keys, avg size 1.14)
从输出结果我们可以很清晰地看到每种数据类型所占用的最大内存 / 拥有最多元素的 key 是哪一个以及每种数据类型在整个实例中的占比和平均大小 / 元素数量。 其实使用这个命令的原理就是 Redis 在内部执行了 SCAN 命令遍历整个实例中所有的 key然后针对 key 的类型分别执行 STRLEN、LLEN、HLEN、SCARD、ZCARD 命令来获取 String 类型的长度、容器类型List、Hash、Set、ZSet的元素个数。
这里我需要提醒你的是当执行这个命令时要注意 2 个问题
对线上实例进行 bigkey 扫描时Redis 的 OPS 会突增为了降低扫描过程中对 Redis 的影响最好控制一下扫描的频率指定 -i 参数即可它表示扫描过程中每次扫描后休息的时间间隔单位是秒扫描结果中对于容器类型List、Hash、Set、ZSet的 key只能扫描出元素最多的 key。但一个 key 的元素多不一定表示占用内存也多你还需要根据业务情况进一步评估内存占用情况
那针对 bigkey 导致延迟的问题有什么好的解决方案呢 这里有两点可以优化
业务应用尽量避免写入 bigkey如果你使用的 Redis 是 4.0 以上版本用 UNLINK 命令替代 DEL此命令可以把释放 key 内存的操作放到后台线程中去执行从而降低对 Redis 的影响如果你使用的 Redis 是 6.0 以上版本可以开启 lazy-free 机制lazyfree-lazy-user-del yes在执行 DEL 命令时释放内存也会放到后台线程中执行 但即便有解决方案 2我也不建议你在实例中存入 bigkey。 这是因为 bigkey 在很多场景下依旧会产生很多前面提到性能问题。
4 使用 lazy free 特性
lazy free 特性是 Redis 4.0 新增的一个非常实用的功能它可以理解为惰性删除或延迟删除。意思是在删除的时候提供异步延时释放键值的功能把键值释放操作放在 BIOBackground I/O单独的子线程处理中以减少删除对 Redis 主线程的阻塞可以有效地避免删除 big key 时带来的性能和可用性问题。 lazy free 对应了 4 种场景默认都是关闭的
lazyfree-lazy-eviction nolazyfree-lazy-expire nolazyfree-lazy-server-del noslave-lazy-flush no它们代表的含义如下
lazyfree-lazy-eviction表示当 Redis 运行内存超过 maxmeory 时是否开启 lazy free 机制删除lazyfree-lazy-expire表示设置了过期时间的键值当过期之后是否开启 lazy free 机制删除lazyfree-lazy-server-del有些指令在处理已存在的键时会带有一个隐式的 del 键的操作比如 rename 命令当目标键已存在Redis 会先删除目标键如果这些目标键是一个 big key就会造成阻塞删除的问题此配置表示在这种场景中是否开启 lazy free 机制删除slave-lazy-flush针对 slave从节点进行全量数据同步slave 在加载 master 的 RDB 文件前会运行 flushall 来清理自己的数据它表示此时是否开启 lazy free 机制删除。
建议开启其中的 lazyfree-lazy-eviction、lazyfree-lazy-expire、lazyfree-lazy-server-del 等配置这样就可以有效的提高主线程的执行效率。
5 缩短键值对的存储长度
键值对的长度是和性能成反比的比如我们来做一组写入数据的性能测试执行结果如下 从以上数据可以看出在 key 不变的情况下value 值越大操作效率越慢因为 Redis 对于同一种数据类型会使用不同的内部编码进行存储比如字符串的内部编码就有三种int整数编码、raw优化内存分配的字符串编码、embstr动态字符串编码这是因为 Redis 的作者是想通过不同编码实现效率和空间的平衡然而数据量越大使用的内部编码就越复杂而越是复杂的内部编码存储的性能就越低。
这还只是写入时的速度当键值对内容较大时还会带来另外几个问题
内容越大需要的持久化时间就越长需要挂起的时间越长Redis 的性能就会越低内容越大在网络上传输的内容就越多需要的时间就越长整体的运行速度就越低内容越大占用的内存就越多就会更频繁地触发内存淘汰机制从而给 Redis 带来了更多的运行负担。 因此在保证完整语义的同时我们要尽量地缩短键值对的存储长度必要时要对数据进行序列化和压缩再存储以 Java 为例序列化我们可以使用 protostuff 或 kryo压缩我们可以使用 snappy。
6 设置键值的过期时间
我们应该根据实际的业务情况对键值设置合理的过期时间这样 Redis 会帮你自动清除过期的键值对以节约对内存的占用以避免键值过多的堆积频繁的触发内存淘汰策略。
7 禁用耗时长的查询命令
Redis 绝大多数读写命令的时间复杂度都在 O(1) 到 O(N) 之间在官方文档对每命令都有时间复杂度说明https://redis.io/commands 如下图所示 其中 O(1) 表示可以安全使用的而 O(N) 就应该当心了N 表示不确定数据越大查询的速度可能会越慢。因为 Redis 只用一个线程来做数据查询如果这些指令耗时很长就会阻塞 Redis造成大量延时。
要避免 O(N) 命令对 Redis 造成的影响可以从以下几个方面入手改造
决定禁止使用 keys 命令避免一次查询所有的成员要使用 scan 命令进行分批的游标式的遍历通过机制严格控制 Hash、Set、Sorted Set 等结构的数据大小将排序、并集、交集等操作放在客户端执行以减少 Redis 服务器运行压力删除del一个大数据的时候可能会需要很长时间所以建议用异步删除的方式 unlink它会启动一个新的线程来删除目标数据而不阻塞 Redis 的主线程。
8 使用 Pipeline 批量操作数据
Pipeline管道技术是客户端提供的一种批处理技术用于一次处理多个 Redis 命令从而提高整个交互的性能。 性能可以提升数十倍。
9 避免大量数据同时失效
Redis 过期键值删除使用的是贪心策略它每秒会进行 10 次过期扫描此配置可在 redis.conf 进行配置默认值是 hz 10 Redis 会随机抽取 20 个值删除这 20 个键中过期的键如果过期 key 的比例超过 25%重复执行此流程。 如果在大型系统中有大量缓存在同一时间同时过期那么会导致 Redis 循环多次持续扫描删除过期字典直到过期字典中过期键值被删除的比较稀疏为止而在整个执行过程会导致 Redis 的读写出现明显的卡顿卡顿的另一种原因是内存管理器需要频繁回收内存页因此也会消耗一定的 CPU。 为了避免这种卡顿现象的产生我们需要预防大量的缓存在同一时刻一起过期最简单的解决方案就是在过期时间的基础上添加一个指定范围的随机数。
10 客户端使用优化
在客户端的使用上我们除了要尽量使用 Pipeline 的技术外还需要注意要尽量使用 Redis 连接池而不是频繁创建销毁 Redis 连接这样就可以减少网络传输次数和减少了非必要调用指令。
11 限制 Redis 内存大小
在 64 位操作系统中 Redis 的内存大小是没有限制的也就是配置项
maxmemory bytes是被注释掉的这样就会导致在物理内存不足时使用 swap 空间既交换空间而当操心系统将 Redis 所用的内存分页移至 swap 空间时将会阻塞 Redis 进程导致 Redis 出现延迟从而影响 Redis 的整体性能。因此我们需要限制 Redis 的内存大小为一个固定的值当 Redis 的运行到达此值时会触发内存淘汰策略内存淘汰策略在
Redis 4.0 之后有 8 种 noeviction不淘汰任何数据当内存不足时新增操作会报错Redis 默认内存淘汰策略 allkeys-lru淘汰整个键值中最久未使用的键值 allkeys-random随机淘汰任意键值; volatile-lru淘汰所有设置了过期时间的键值中最久未使用的键值 volatile-random随机淘汰设置了过期时间的任意键值 volatile-ttl优先淘汰更早过期的键值。
在 Redis 4.0 版本中又新增了 2 种淘汰策略 volatile-lfu淘汰所有设置了过期时间的键值中最少使用的键值 allkeys-lfu淘汰整个键值中最少使用的键值。 其中 allkeys-xxx 表示从所有的键值中淘汰数据而 volatile-xxx 表示从设置了过期键的键值中淘汰数据。 我们可以根据实际的业务情况进行设置默认的淘汰策略不淘汰任何数据在新增时会报错。
12 使用物理机而非虚拟机
在虚拟机中运行 Redis 服务器因为和物理机共享一个物理网口并且一台物理机可能有多个虚拟机在运行因此在内存占用上和网络延迟方面都会有很糟糕的表现我们可以通过 ./redis-cli --intrinsic-latency 100 命令查看延迟时间如果对 Redis 的性能有较高要求的话应尽可能在物理机上直接部署 Redis 服务器。
13 检查数据持久化策略
Redis 的持久化策略是将内存数据复制到硬盘上这样才可以进行容灾恢复或者数据迁移但维护此持久化的功能需要很大的性能开销。 在 Redis 4.0 之后Redis 有 3 种持久化的方式
RDBRedis DataBase快照方式将某一个时刻的内存数据以二进制的方式写入磁盘AOFAppend Only File文件追加方式记录所有的操作命令并以文本的形式追加到文件中混合持久化方式Redis 4.0 之后新增的方式混合持久化是结合了 RDB 和 AOF 的优点在写入的时候先把当前的数据以 RDB 的形式写入文件的开头再将后续的操作命令以 AOF 的格式存入文件这样既能保证 Redis 重启时的速度又能减低数据丢失的风险。
RDB 和 AOF 持久化各有利弊RDB 可能会导致一定时间内的数据丢失而 AOF 由于文件较大则会影响 Redis 的启动速度为了能同时拥有 RDB 和 AOF 的优点Redis 4.0 之后新增了混合持久化的方式因此我们在必须要进行持久化操作时应该选择混合持久化的方式。 查询是否开启混合持久化可以使用
命令执行结果如下图所示 其中 yes 表示已经开启混合持久化no 表示关闭Redis 5.0 默认值为 yes。 如果是其他版本的 Redis 首先需要检查一下是否已经开启了混合持久化如果关闭的情况下可以通过以下两种方式开启 通过命令行开启 通过修改 Redis 配置文件开启 通过命令行开启
使用命令config set aof-use-rdb-preamble yes
执行结果如下图所示 命令行设置配置的缺点是重启 Redis 服务之后设置的配置就会失效。 通过修改 Redis 配置文件开启
在 Redis 的根路径下找到 redis.conf 文件把配置文件中的
aof-use-rdb-preamble no改为
aof-use-rdb-preamble yes如下图所示 配置完成之后需要重启 Redis 服务器配置才能生效但修改配置文件的方式在每次重启 Redis 服务之后配置信息不会丢失。 需要注意的是在非必须进行持久化的业务中可以关闭持久化这样可以有效地提升 Redis 的运行速度不会出现间歇性卡顿的困扰。
14 使用分布式架构来增加读写速度
Redis 分布式架构有三个重要的手段
主从同步哨兵模式Redis Cluster 集群
使用主从同步功能我们可以把写入放到主库上执行把读功能转移到从服务上因此就可以在单位时间内处理更多的请求从而提升的 Redis 整体的运行速度。 而哨兵模式是对于主从功能的升级但当主节点奔溃之后无需人工干预就能自动恢复 Redis 的正常使用。 Redis Cluster 是 Redis 3.0 正式推出的Redis 集群是通过将数据分散存储到多个节点上来平衡各个节点的负载压力。 Redis Cluster 采用虚拟哈希槽分区所有的键根据哈希函数映射到 0~16383 整数槽内计算公式
slot CRC16(key) 16383每一个节点负责维护一部分槽以及槽所映射的键值数据。这样 Redis 就可以把读写压力从一台服务器分散给多台服务器了因此性能会有很大的提升。 在这三个功能中我们只需要使用一个就行了毫无疑问 Redis Cluster 应该是首选的实现方案它可以把读写压力自动地分担给更多的服务器并且拥有自动容灾的能力。
15 持久化 一个典型的配置如下
127.0.0.1:6379 config get *append*1) no-appendfsync-on-rewrite2) no3) appendonly4) yes5) appendfsync6) everysecbgrewriteaof机制在一个子进程中进行aof的重写从而不阻塞主进程对其余命令的处理同时解决了aof文件过大问题。
有一种情况是同时在执行bgrewriteaof操作和主进程写aof文件的操作两者都会操作磁盘而bgrewriteaof往往会涉及大量磁盘操作这样就会造成主进程在写aof文件的时候出现阻塞的情形有两种思路解决
将no-appendfsync-on-rewrite设置为yes. 这样可以避免与appendfsync竞争磁盘资源但是在rewrite期间的AOF有丢失的风险。给当前Redis实例添加slave节点当前节点设置为master, 然后master节点关闭AOFslave节点开启AOF。这样的方式的风险是如果master挂掉尚没有同步到slave的数据会丢失。 no-appendfsync-on-rewrite参数设置为no是最安全的方式不会丢失数据但是要忍受潜在的阻塞的问题如果设置为yes相当于将appendfsync设置为no这说明写aof文件的时候并没有真正刷盘只是写入了内存缓冲区aof_buf因此这样并不会造成阻塞因为没有竞争磁盘但是如果这个时候redis挂掉就会丢失数据。丢失多少数据呢在linux的操作系统的默认设置下最多可能丢失30s的数据。
一种可能的方案
在master节点设置将no-appendfsync-on-rewrite设置为yes同时将auto-aof-rewrite-percentage参数设置为0关闭主动重写。为了防止AOF文件越来越大可以在服务器上设置定时任务在低峰期执行bgrewriteaof命令完成每日一次的AOF重写。在重写时为了避免硬盘空间不足或者IO使用率高影响重写功能可以添加硬盘空间和IO使用率报警确保aof重写机制能正常运行。
16 绑定 CPU
很多时候我们在部署服务时为了提高服务性能降低应用程序在多个 CPU 核心之间的上下文切换带来的性能损耗通常采用的方案是进程绑定 CPU 的方式提高性能。 但在部署 Redis 时如果你需要绑定 CPU 来提高其性能我建议你仔细斟酌后再做操作。
为什么 因为 Redis 在绑定 CPU 时是有很多考究的如果你不了解 Redis 的运行原理随意绑定 CPU 不仅不会提高性能甚至有可能会带来相反的效果。 我们都知道一般现代的服务器会有多个 CPU而每个 CPU 又包含多个物理核心每个物理核心又分为多个逻辑核心每个物理核下的逻辑核共用 L1/L2 Cache。 而 Redis Server 除了主线程服务客户端请求之外还会创建子进程、子线程。 其中子进程用于数据持久化而子线程用于执行一些比较耗时操作例如异步释放 fd、异步 AOF 刷盘、异步 lazy-free 等等。
如果你把 Redis 进程只绑定了一个 CPU 逻辑核心上那么当 Redis 在进行数据持久化时fork 出的子进程会继承父进程的 CPU 使用偏好。 而此时的子进程会消耗大量的 CPU 资源进行数据持久化把实例数据全部扫描出来需要耗费CPU这就会导致子进程会与主进程发生 CPU 争抢进而影响到主进程服务客户端请求访问延迟变大。 这就是 Redis 绑定 CPU 带来的性能问题。
那如何解决这个问题呢 如果你确实想要绑定 CPU可以优化的方案是不要让 Redis 进程只绑定在一个 CPU 逻辑核上而是绑定在多个逻辑核心上而且绑定的多个逻辑核心最好是同一个物理核心这样它们还可以共用 L1/L2 Cache。 当然即便我们把 Redis 绑定在多个逻辑核心上也只能在一定程度上缓解主线程、子进程、后台线程在 CPU 资源上的竞争。 因为这些子进程、子线程还是会在这多个逻辑核心上进行切换存在性能损耗。
如何再进一步优化 可能你已经想到了我们是否可以让主线程、子进程、后台线程分别绑定在固定的 CPU 核心上不让它们来回切换这样一来他们各自使用的 CPU 资源互不影响。 Redis 在 6.0 版本已经推出了这个功能我们可以通过以下配置对主线程、后台线程、后台 RDB 进程、AOF rewrite 进程绑定固定的 CPU 逻辑核心
# Redis Server 和 IO 线程绑定到 CPU核心 0,2,4,6server_cpulist 0-7:2# 后台子线程绑定到 CPU核心 1,3bio_cpulist 1,3# 后台 AOF rewrite 进程绑定到 CPU 核心 8,9,10,11aof_rewrite_cpulist 8-11# 后台 RDB 进程绑定到 CPU 核心 1,10,11# bgsave_cpulist 1,10-1 如果你使用的正好是 Redis 6.0 版本就可以通过以上配置来进一步提高 Redis 性能。 这里我需要提醒你的是一般来说Redis 的性能已经足够优秀除非你对 Redis 的性能有更加严苛的要求否则不建议你绑定 CPU。 绑定 CPU 需要对计算机体系结构有比较清晰的了解否则要谨慎操作。
17 内存 swap
如果你发现 Redis 突然变得非常慢每次的操作耗时都达到了几百毫秒甚至秒级那此时你就需要检查 Redis 是否使用到了 swap在这种情况下 Redis 基本上已经无法提供高性能的服务了。 操作系统使用虚拟内存有时候为了缓解内存不足对应用程序的影响允许把一部分进程内存中的数据交换到磁盘上以达到应用程序对内存使用的缓冲这些内存数据被换到磁盘上的区域就是 swap。 问题就在于当内存中的数据被换到磁盘上后Redis 再访问这些数据时就需要从磁盘上读取访问磁盘的速度要比访问内存慢几百倍 尤其是针对 Redis 这种对性能要求极高、性能极其敏感的数据库来说这个操作延时是无法接受的。 此时你需要检查 Redis 机器的内存使用情况确认是否存在使用了 Swap。 你可以通过以下方式来查看 Redis 进程是否使用到了 Swap
# 先找到 Redis 的进程 ID$ ps -aux | grep redis-server# 查看 Redis Swap 使用情况$ cat /proc/$pid/smaps | egrep ^(Swap|Size)输出结果如下
Size: 1256 kBSwap: 0 kBSize: 4 kBSwap: 0 kBSize: 132 kBSwap: 0 kBSize: 63488 kBSwap: 0 kBSize: 132 kBSwap: 0 kBSize: 65404 kBSwap: 0 kBSize: 1921024 kBSwap: 0 kB...这个结果会列出 Redis 进程的内存使用情况。(或者也可以用 vmstat 命令查看 swap 的情况) 每一行 Size 表示 Redis 所用的一块内存大小Size 下面的 Swap 就表示这块 Size 大小的内存有多少数据已经被换到磁盘上了如果这两个值相等说明这块内存的数据都已经完全被换到磁盘上了。 如果只是少量数据被换到磁盘上例如每一块 Swap 占对应 Size 的比例很小那影响并不是很大。如果是几百兆甚至上 GB 的内存被换到了磁盘上那么你就需要警惕了这种情况 Redis 的性能肯定会急剧下降。
此时的解决方案是
增加机器的内存让 Redis 有足够的内存可以使用整理内存空间释放出足够的内存供 Redis 使用然后释放 Redis 的 Swap让 Redis 重新使用内存
释放 Redis 的 Swap 过程通常要重启实例为了避免重启实例对业务的影响一般会先进行主从切换然后释放旧主节点的 Swap重启旧主节点实例待从库数据同步完成后再进行主从切换即可。
可见当 Redis 使用到 Swap 后此时的 Redis 性能基本已达不到高性能的要求你可以理解为武功被废所以你也需要提前预防这种情况。 预防的办法就是需要对 Redis 机器的内存和 Swap 使用情况进行监控在内存不足或使用到 Swap 时报警出来及时处理。