什么是事务的ACID
事务是由一系列对系统中数据进行访问或更新的操作组成的程序执行逻辑单元。这些操作要么都执行,要么都不执行。
为了保证数据库的一致性,在事务处理之前和之后,都应该遵循某些规则,也就是大家耳熟能详的ACID。
- 原子性:一个事务的多个操作必须都完成,或者都不完成。事务执行过程中如果发生错误,就回滚到事务开始前的状态,好像没有执行过一样。
- 一致性:事务执行结束后,数据库的完整性约束没有被破坏。数据库的完整性约束包括但不限于以下内容。
- 实体完整性(如行的主键存在且唯一)。
- 列完整性(如字段的类型、大小、长度符合要求)。
- 外键约束。
- 用户自定义完整性(如转账前后,两个账户余额的和应该不变)。
- 隔离性:事务内部的操作于其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性:事务一旦提交,所有的修改就永久保存在数据库中,即使系统崩溃重启,数据也不会丢失。
Redis如何实现事务
总的来说,MULTI、EXEC、DISCARD和WATCH命令是redis实现事务的基础。redis事务的执行过程包含如下步骤。
- 使用MULTI命令开启一个事务。
- 将命令逐个加入等待执行的事务队列里,命令并不会马上执行。
- 使用EXEC执行事务中的所有命令或使用DISCARD丢弃第二步的命令。
- 在执行EXEC命令时,可以使用WATCH监控key的变化,如果发生变化则自动放弃执行事务。
async performTransaction(): Promise<any> {
const client = this.redisService.getClient();
const transaction = client.multi();
// 假设我们要在事务中执行以下命令
transaction.set('key1', 'value1');
transaction.set('key2', 'value2');
transaction.incr('counter');
// 提交事务
const results = await transaction.exec();
// 返回事务结果
return results;
}
Redis事务满足ACID吗
Redis事务做了三个重要保证。
- 事务支持一次执行多个命令,所有命令都会被序列化并按照顺序串行执行。
- 执行EXEC命令,进入事务执行过程,其他客户端发送的请求不会被插入该事务执行命令序列中。
- EXEC命令会触发事务中所有命令的执行,如果在调用EXEC命令之前,客户端与服务器失去连接,则不会执行任何操作。如果在执行过程中,任意命令执行失败,则其余命依然会执行。
结论:redis事务在一些场景下具备原子性,不支持回滚。可以具备一致性并通过WATCH机制保证隔离性,但是无法保证持久性。
-
原子性
在redis事务执行期间,可能遇到以下三种错误。- 语法错误:在执行EXEC命令前,入队的命令语法是错误的(参数数量错误、命令名称错误等),或者内存不足(Redis实例使用maxmemory1GB命令配置最大内存)。
- 命令错误:执行EXEC命令后,某些命令可能出错。例如,命令和操作的数据类型不匹配(对字符串类型的key执行List操作)。
- Redis忽然宕机:在执行EXEC命令后,忽然断电宕机,只有部分事务操作被记录到AOF日志。
语法错误(EXEC命令执行前)
从redis2.6.5开始,redis会在命令入队时检测命令语法错误,你可以继续将命令提交到队列。但是在执行EXEC命令后,Redis将拒绝执行所有的命令,并向客户端返回一个事务失败的信息,保证原子性。命令报错(EXEC命令执行后)
命令和操作的数据类型不匹配,redis实例没有检查出错误,可以正常入队,在EXEC命令之后运行命令报错,但是队列中其他正确的命令会被执行。redis虽然会对错误命令报错,但是事务依然会把正确的命令执行完,错误的命令本身没有意义,我们可以将其理解为保证原子性。redis在事务失败时不进行回滚,而是继续执行余下的正确命令。因为支持回滚会对redis的简单性和性能产生重大影响。redis的事务在运行exec命令后报错是因为将错误的语法或目录用在了错误类型的key上面,也就是说事务中失败的命令是由编程错误造成的,这些错误应该在开发过程中就被发现,而不应该出现在生产中。
Redis忽然宕机
宕机后,开启AOF,只有部分事务的命令被记录到AOF中,我们可以使用redis-check-aof工具检查AOF日志文件,这个工具可以把未完成的事务操作从AOF文件去除,相当于事务不会被执行,从而保证了原子性。 -
一致性
- 入队错误:执行EXEC命令之前,客户端发送的操作命令错误,事务终止执行,可以保证一致性。
- 执行错误:EXEC命令执行之后,命令和操作的的数据类型不匹配,错误的命令会报错,正确的命令会继续执行,从这个角度看,可以保证一致性。
- 服务宕机:在执行事务过程中,redis服务宕机,要根据持久化的模式来分别讨论。
- 无持久化的内存模式:没有开启RDB快照和APF持久化。实例故障重启,redis没有保存任何数据,可以保证一致性。
- 开启RDB快照:可以根据RDB文件恢复数据,如果找不到RDB文件,则Redis中没有数据,保证一致性。
- 开启AOF持久化:事务的操作还没有被记录到AOF中就宕机,重启时使用AOF日志加载数据以保证一致性;如果只有部分操作被记录到AOF中,则可以使用redis-check-aof清除事务中已经完成的操作,数据恢复后也能保证一致性。
-
隔离性
redis的隔离性指在并发场景中,事务之间可以做到互不干扰。- 并发操作在EXEC命令执行前执行,隔离性通过WATCH机制保证。
- 并发操作在EXEC命令执行后执行,隔离性可以保证。
-
持久性
如果redis没有开启RDB快照或AOF持久化,那么事务肯定不能保证持久性。
如果开启RDB快照,那么在一个事务执行完成后,下一个RDB文件还未执行前,忽然发生宕机,数据就会丢失,无法保证持久性。
如果开启AOF持久化,那么AOF模式的三种配置选项no、everysec和always都可能存在丢失数据的情况。
综上,redis事务的持久性无法保证。
Redis内存管理
- 可以通过redis.conf配置maxmemory 4gb限制最大内存使用量。需要注意的是,如果maxmemory为0,则在64位操作系统上没有限制,而在32位os上有3gb的隐式限制。
- 当redis主库内存超过限制时,命令处理会触发淘汰机制淘汰数据,该机制由maxmemory-policy配置的策略来控制,直到内存使用量小于限制阈值或拒绝服务。
- redis有两种删除过期数据的策略。
- 定时任务选取部分数据删除。
- 惰性删除:当有客户端请求该key时,检查该key是否过期,如果过期,则删除。
淘汰策略概述
淘汰的目标数据可以分为数据库中有key-value的数据和数据库中配置了过期事件的key-value数据两种。
针对这两种目标数据,一共有8种淘汰策略。
- noeviction:默认策略,不淘汰任何数据。
- allkeys-lru:使用近似LRU算法淘汰长时间没有使用的key。
- allkeys-lfu:使用近似LFU算法,保留常用的键,淘汰数据库中最不常用的键。
- volatile-lru:使用近似LRU算法淘汰配置了过期时间,最近最少使用的key。
- volatile-lfu:使用近似LFU算法淘汰配置了过期时间,使用频率最低的key。
- allkeys-random:对所有key随机淘汰,为添加的新数据腾出空间。
- volatile-random:随机淘汰配置了过期时间的key。
- volatile-ttl:淘汰最接近过期时间的key,越早过期的越先被淘汰。
需要注意的是,LRU、LFU和volatile-ttl都是使用近似随机算法来采样数据并进行淘汰的。
- 淘汰策略如何选择
如果数据没有明显的冷热分别,所有的数据分布查询比较均匀,都会被随机查询,就使用allkeys-random策略,让其随机淘汰数据。
使用volatile-lru策略时,业务场景中有一些数据不能淘汰,例如置顶新闻和视频。这时,只要我们不为这些数据配置过期时间,数据就不会被淘汰,该策略会根据LRU算法淘汰那些配置了过期时间且最近最少被访问的数据。
allkeys-lru策略的使用场景时所有的数据都可以淘汰,不管数据是否配置了过期时间,都会按照最近最少被访问的原则淘汰。
对于需要确保数据不能淘汰和全部数据都可以淘汰的业务系统,分别使用不同的redis集群时更好的方案。
volatile-lfu和allkeys-lfu适用于业务场景访问频率差异明显,且可以淘汰低频数据的场景。 - 数据淘汰过程概述
- 客户端将新命令发送到服务端。
- 服务端收到客户端命令,redis检查内存使用量,如果大于或等于maxmemory限制,则根据maxmemory-policy配置的策略来淘汰数据。
- 如果内存使用量小于maxmemory限制,则执行新命令。
过期删除策略
使用EXPIRE key seconds [NX|XX|GT|LT]命令可以为key配置过期时间,如果没有配置过期时间,那么key将一直存在,除非我们明确将其删除,例如执行DEL命令。
- NX:当key未过期时就配置过期时间。
- XX:当key过期时才配置过期时间。
- GT:当新配置的过期时间大于当前过期时间时才配置。
- LT:当新配置的过期时间小于当前过期时间时才配置。
在内存未被占满时,redis是如何把过期的key-value数据删除以优化内存占用量。
- 惰性删除:当key被访问时,检查key的过期时间,若已过期则删除。
- 定期删除:每隔一段时间随机采样检查配置了过期时间的key,删除已过期的数据。
注意:不管是定时删除,还是惰性删除,当数据被删除后,master都会生成删除的命令并记录到AOF和slave上。
如果过期的数据太多,通过定时删除难以保证效果,那么就会根据redis配置的淘汰策略来删除数据,避免内存耗尽。
key的过期信息时用UNIX绝对时间戳表示的。为了让过期操作正常执行,机器之间的时间必须保证稳定同步,否则会出现过期时间不准的情况。例如,两台时钟严重不同步的机器进行RDB传输,slave的时间比实际提前2000s,假设master的一个key配置的生存时间是1000s,当slave加载RDB时就会认为该key超时,但实际未超时。
当redis中的key已过期但是未被删除时,redis并不会把过期的key持久化到RDB文件或AOF文件中。为了确保一致性,在AOF文件中,当key过期时,会生成一个DEL命令存储。slave不会主动删除过期的key,除非晋升为master或者收到master发送过来的DEL命令。
Redis发布/订阅机制深度解析
-
发布/订阅机制简介
redis的发布/订阅机制是一种消息通信模式:发布者通过PUBLISH发布消息,订阅者通过SUBSCRIBE订阅或通过UNSUBSCRIBE取消订阅。
发布/订阅到频道主要包含三个部分:- 发布者:发送消息到频道中,每次只能向一个频道发送一条消息。
- 订阅者:可以同时订阅多个频道。
- 频道:将发布者发布的消息转发给当前订阅此频道的订阅者。
发布者和订阅者属于客户端,频道属于redis服务端,发布者将消息发布到频道,订阅这个频道的订阅者则收到消息。
-
发布/订阅机制实战
- 通过发布/订阅到频道(channel)实现发布/订阅机制。
- 通过发布/订阅到模式(pattern)实现发布/订阅机制。
需要注意的是,发布/订阅机制与db空间无关,例如在db10发布,db0的订阅者也会收到消息。
首先创建一个用于管理 Redis 连接的服务:
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; import * as Redis from 'ioredis'; @Injectable() export class RedisService implements OnModuleInit, OnModuleDestroy { private publisher: Redis.Redis; private subscriber: Redis.Redis; onModuleInit() { this.publisher = new Redis(); this.subscriber = new Redis(); } onModuleDestroy() { this.publisher.quit(); this.subscriber.quit(); } getPublisher(): Redis.Redis { return this.publisher; } getSubscriber(): Redis.Redis { return this.subscriber; } }
基于频道的发布/订阅
创建一个服务来实现基于频道的发布/订阅:
import { Injectable } from '@nestjs/common'; import { RedisService } from './redis.service'; @Injectable() export class ChannelPubSubService { constructor(private readonly redisService: RedisService) {} subscribe(channel: string, callback: (message: string) => void) { const subscriber = this.redisService.getSubscriber(); subscriber.subscribe(channel); subscriber.on('message', (chan, message) => { if (chan === channel) { callback(message); } }); } publish(channel: string, message: string) { const publisher = this.redisService.getPublisher(); publisher.publish(channel, message); } }
使用服务
在控制器中使用这些服务:
import { Controller, Get, Post, Query } from '@nestjs/common'; import { ChannelPubSubService } from './channel-pubsub.service'; @Controller('channel') export class ChannelController { constructor(private readonly channelPubSubService: ChannelPubSubService) {} @Post('publish') publish(@Query('channel') channel: string, @Query('message') message: string) { this.channelPubSubService.publish(channel, message); return 'Message published'; } @Get('subscribe') subscribe(@Query('channel') channel: string) { this.channelPubSubService.subscribe(channel, (message) => { console.log(`Received message: ${message}`); }); return `Subscribed to channel: ${channel}`; } }
基于模式的发布/订阅
创建一个服务来实现基于模式的发布/订阅:
import { Injectable } from '@nestjs/common'; import { RedisService } from './redis.service'; @Injectable() export class PatternPubSubService { constructor(private readonly redisService: RedisService) {} psubscribe(pattern: string, callback: (channel: string, message: string) => void) { const subscriber = this.redisService.getSubscriber(); subscriber.psubscribe(pattern); subscriber.on('pmessage', (pattern, chan, message) => { callback(chan, message); }); } publish(channel: string, message: string) { const publisher = this.redisService.getPublisher(); publisher.publish(channel, message); } }
使用服务
在控制器中使用这些服务:
import { Controller, Get, Post, Query } from '@nestjs/common'; import { PatternPubSubService } from './pattern-pubsub.service'; @Controller('pattern') export class PatternController { constructor(private readonly patternPubSubService: PatternPubSubService) {} @Post('publish') publish(@Query('channel') channel: string, @Query('message') message: string) { this.patternPubSubService.publish(channel, message); return 'Message published'; } @Get('psubscribe') psubscribe(@Query('pattern') pattern: string) { this.patternPubSubService.psubscribe(pattern, (channel, message) => { console.log(`Received message from ${channel}: ${message}`); }); return `Subscribed to pattern: ${pattern}`; } }
-
使用场景
哨兵间通信
在哨兵集群中,每个哨兵节点利用发布/订阅机制实现哨兵之间的相互发现并找到slave。哨兵与master建立通信后,利用master提供的发布/订阅机制在__sentinel__:hello发布自己的消息,例如ip地址,端口,同时订阅这个频道来获取其他哨兵的消息,以此实现哨兵间通信。消息队列
可以使用redis的发布/订阅机制实现轻量级简单的MQ功能,实现上下游解耦。需要注意,redis发布/订阅的消息不会被持久化,所以新订阅的客户端不会收到历史消息。同时,redis发布/订阅不支持ACK机制,如果当前业务不能容忍这些缺点,那么需要使用专业的消息队列。
性能必杀技之客户端缓存
客户端缓存是创建极致性能服务的必杀技,在此技术下,应用程序把数据库的数据缓存在应用程序端的内存中,当应用程序访问数据时,可直接从本地内存读取,无须从数据库端查询数据,减少了网络I/O和数据库的压力,提升了应用程序的响应速度。
客户端缓存最核心的作用就是在redis中的数据变更或者失效后,能够保证redis和客户端数据的一致性。
应用程序先查询本地缓存是否命中,若命中则直接把数据返回给应用程序。若没有命中则访问redis,没有命中redis缓存才查询源数据库,并把数据同步到本地缓存和redis中。我们通常使用Memcachced、Guava Cache和Caffeine等进行一级缓存(本地缓存),使用redis进行二级缓存(缓存服务),本地内存避免了网络连接、查询、网络传输和序列化等操作,性能比redis服务好很多,这种模式大大降低了数据延迟。
客户端缓存技术适用于数据访问量大、读多写少的场景。反之,访问量少且不断被INCR修改的全局计数器,不应使用缓存。
客户端缓存实现原理
Redis 提供了一种名为“Tracking”的特性,允许客户端缓存数据并在数据发生更改时接收通知以保持缓存的最新状态。
-
普通模式
默认模式下,当Tracking开启时,redis会记录每个客户端访问过哪些key,当key的value发生变化时,服务端可以通过RESP3发送失效消息给客户端。这种方式会消耗服务端的内存。- redis服务端将客户端访问的key以及该key对应的客户端ID列表存储在一个全局唯一的表——TrackingTable中。如果TrackTable满了,就移除最老的记录,同时向这个客户端发送已过期的消息,通知进程更新本地缓存。
- 每个redis客户端都有唯一的数字ID标识,TrackingTable存储所有客户端ID,当连接断开后,清除该ID对应的记录。
- TrackingTable中记录的key信息不考虑对应哪个database,当访问db1的key时,如果修改db2的同名key,那么客户端也会收到过期提示,这样做的目的是减少系统的复杂性和表的数据存储量。
-
广播模式
当广播模式开启时,服务器不会记住给定客户端访问了哪些key,因此这种模式在服务器端不会消耗过多内存,而是发送更多的失效消息给客户端,即使变更的key没有被客户端缓存。
在这种模式下,服务端会向客户端广播所有的key的失效情况,如果key被频繁修改,服务端就会发送大量的失效广播消息,消耗大量的网络带宽资源。
所以,在实际应用中,我们配置让客户端注册只跟踪指定前缀的key,当注册跟踪的key的前缀匹配被修改时,服务端就会把失效消息广播给关注这个key的前缀的客户端。
与普通模式获取一次key的规则不同,在广播模式下,只要key被修改或删除,符合规则的客户端就会获取到失效消息,而且可以多次获取。如果不指定前缀,客户端就会默认接收所有key的失效消息。
与普通模式类似,在广播模式下,redis也使用Radix Tree PrefixTable保存客户端订阅的key的前缀字符串与客户端ID的映射关系,每个前缀字符串映射一些客户端ID。
Redis I/O多线程模型
redis使用全局dict+内存数据库+丰富高效的数据结构+单线程模型+I/O多路复用事件驱动框架“快到飞起”。
Redis的网络I/O以及key-value命令读/写是由单个线程来执行的,避免了不必要的线程上下文切换和资源竞争,对于提升性能有很大帮助。
-
单线程模型真的是只有一个线程吗
非也,我们通常说的单线程模型指的是redis在处理客户端的请求时,包括读取(socket读)、解析、执行、内容返回(socket写)等都由一个顺序串行的主线程处理;而清理过期key-value数据、释放无用连接、执行内存淘汰策略、bgsave生成RDB文件、AOF Rewrite等都是由其他线程处理。
在命令执行阶段、命令并不会被立刻执行,而是进入一个个socket队列,一旦socket事件就绪,事件分发器就将它们分发到对应的事件处理器,单线程模型的命令处理过程如下图所示。
-
I/O多线程模型解读
redis6.0的多线程默认是禁用的,如需开启需要修改redis.conf配置文件的配置io-threads-do-reads yes。开启I/O多线程模型后,还要配置线程数才能生效,同样需要修改redis.conf配置文件。io-threads 4
当然不是,关于线程数的配置,官方有一个建议:线程数的数量最好小于CPU核心数,起码留一个空闲核,因为redis由主线程处理命令,如果系统频繁出现上下文切换,效率会降低。例如:4个核心数的机器建议配置2个或3个线程,8核的机器建议配置为6个线程。
主线程与I/O线程如何实现协作呢?
思路:将主线程I/O的读/写任务拆分出来给一组独立的线程处理,使得多个socket读/写可以并行化,但是redis命令还是由主线程串行执行的。
Redis内存碎片深度解析与优化策略
通过CONFIG SET maxmemory 100mb或者在redis.conf配置文件配置maxmemory 100mb限制redis内存使用。当内存占用达到最大值时,会触发内存淘汰策略淘汰数据。
除此之外,当key达到过期时间时,redis会使用以下两种方法删除过期数据。
- 后台定时任务选取部分数据删除。
- 惰性删除。
删除了数据,redis进程占用的内存(也叫做RSS,进程消耗内存页数)不一定会降低。在长时间运行时,可能面临内存碎片问题。
数据已删,释放的内存去哪了
redis进程内存消耗主要由以下部分组成
- redis自身启动运行占用的内存。
- 存储数据占用的内存。
- 缓冲区内存:主要包括client-output-buffer-limit客户端输出缓冲区,复制积压缓冲区和AOF缓冲区。
- 内存碎片。
Redis进程占用的内存很小,可以忽略不计,存储数据是占比最大的一块。缓冲区内存在大流量场景容易失控,造成redis内存不稳定,需要重点关注。
内存碎片过大会导致明明有空间可用,却无法存储数据。碎片率=used_memory_rss(实际使用的物理内存,RSS值)/used_memory(实际存储数据的内存)。
什么是内存碎片
内存碎片指内存空间中的小块空闲区域,它们由于大小不一致或位置不连续而无法被有效地利用。内存碎片会占用redis的物理内存,但是不计入redis的逻辑内存。
内存碎片的形成原因
- 内存分配器的分配策略。
- 频繁的数据更新操作:redis频繁对大小不同的key-value进行更新,大量过期数据被删除,释放的空间不够连续导致无法复用。
- 内存分配器的分配策略
默认的内存分配器是jemalloc,它是以固定大小的块为单位进行连续内存分配的,而不是按需分配的。当申请的内存接近某个固定值时,例如8字节、16字节,jemalloc会给它分配对应的固定大小的空间,这样就会出现内存碎片。例如程序只需要1.5kb空间,内存分配器会分配给它2kb空间,从而产生0.5kb碎片。
这么做的目的是减少内存分配次数,例如申请22字节的空间保存数据,jemalloc会分配32字节,如果后续还要写入10字节,就不需要再向os申请空间了,可以使用之前申请的32字节。
在删除key时,redis并不会立刻把内存归还给os,出现这个情况的原因是底层内存分配器管理机制,例如大多数已经删除的key依然与其他有效的key被分配在同一个内存页中。
除此之外,分配器还可以复用空闲的内存块:原有的5gb数据被删除了2gb,当再次将数据添加到实例中时,会复用之前释放的2gb内存。redis的RSS会保持稳定,不会增长太多。 - key-value大小不一和删改操作
redis频繁对大小不同的key-value做更新操作,并删除大量过期数据,释放的空间不够连续导致内存无法被复用。例如,将原本占用32字节的字符串修改为占用20字节的字符串,那么释放出来的12字节就是空闲空间。如果下一个字符串需要申请13字节的存储空间,那么刚刚释放的12字节无法被使用,导致碎片。
内存空间总量足够大,但是其中的内存不是连续的,可能导致无法存储数据。
内存碎片解决之道
redis提供了info memory命令,可以查看Redis的内存使用情况,其中有一个字段mem_fragmentation_ratio,它表示Redis的内存碎片率,计算公式如下:
mem_fragmentation_ratio =used_memory_rss /used_memory
其中,used_memory_rss表示操作系统实际分配给Redis的物理内存,里面包含了碎片;used_memory表示Redis为了保存数据实际申请的空间。如果碎片率大于1,就说明存在内存碎片,这个值越大,内存碎片就越多。当1<碎片率<1.5时,可以认为是合理的;当碎片率≥1.5时,说明碎片已经超过50%,我们需要采取一些措施解决碎片率过大的问题。
- 重启大法
redis4.0之前的版本没有内置内存碎片清理工具,只能通过重启的方式来清理内存碎片。如果没有开启持久化,数据就会丢失,那么就需要使用RDB文件或者AOF恢复数据。当只有一个实例时,数据过大会导致恢复阶段长时间无法提供服务,高可用大打折扣。 - 内存碎片自动管理
首先,你需要确定Redis的内存分配器是否是jemalloc,可以通过info memory命令查看em_allocator字段。如果不是,那么你需要重新编译Redis,并指定MALLOC=jemalloc参数。
接着,可以通过如下命令动态修改Redis配置,而不需要重启Redis。
CONFIG SET activedefrag yes
如果想永久开启这个功能,就需要修改redis.conf配置文件,配置activedefrag yes。
除此以外,我们还可以根据需要调整一些参数,来控制内存碎片自动清理功能的触发条件速度。
-
内存碎片自动清理功能的触发条件
- active-defrag-ignore-bytes 200mb:表示当碎片占用的内存达到200MB时开始清理,默认值是100MB。
- active-defrag-threshold-lower 20:表示当内存碎片占用操作系统分配给Redis总空间的比例达到20%时开始清理,默认值是10%。
-
清理的速度
内存碎片自动清理虽好,可不要肆意妄为,把数据移动到新位置,再释放原有空间是需要消耗资源的。Redis操作数据的命令是单线程的,所以要等待数据复制移动完成才能处理请求,造成性能损耗。
以下两个参数可以控制内存碎片自动清理开始和结束的时机,避免过多占用CPU,减少内存碎片自动清理对Redis处理性能的影响。- active-defrag-cycle-min 20:表示内存碎片自动清理过程所占用CPU的比例不低20%,保证清理能正常进行,默认值是5%。
- active-defrag-cycle-max50:表示内存碎片自动清理过程所占用CPU的比例不能高于50%,一旦超过就立即停止,避免Redis阻塞,造成高延迟。默认值是75%,可以粮据实际情况调整。
这些参数可以通过config set命令动态地修改,也可以通过配置文件永久地修改。