内容简介:一直以来,Redis 都是单线程的(参考大部分 Redis 命令的执行速度都很快,所以不是问题;但也有一些命令,比如以
一、要解决的问题
一直以来,Redis 都是单线程的(参考 FAQ )。这种模型使得 Redis 简单、高效,但缺点也很明显:如果执行一个比较耗时的命令,那么在该命令执行期间,整个 Redis 服务都将被阻塞(无法并发地执行其他命令)。
大部分 Redis 命令的执行速度都很快,所以不是问题;但也有一些命令,比如 ZUNIONSTORE 、 LRANGE 、 SINTER ,以及臭名昭著的 KEYS ,根据处理数据集大小的不同,可能会阻塞 Redis 数秒或几分钟。
以 DEL 命令为例,当被删除的 key 是 list、set、sorted set 或 hash 类型时,时间复杂度为 O(M),其中 M 是 key 中包含的元素的个数。
二、非阻塞删除
Redis 的作者 Salvatore Sanfilippo 自己也意识到了上述问题,并提出了对应的解决方案:非阻塞删除(参考 Lazy Redis is better Redis )。简而言之,「非阻塞删除」就是将删除操作放到另外一个线程(而非 Redis 主线程)去处理。
最终「非阻塞删除」在 Redis 4.0 中得以实现(参考 Redis 4.0 release notes ),从此 Redis 开启了 “多线程” 时代。
新增实现的「非阻塞删除」包括以下命令:
命令 | (原来的)阻塞版本 |
---|---|
UNLINK | DEL |
FLUSHALL ASYNC | FLUSHALL |
FLUSHDB ASYNC | FLUSHDB |
三、DEL vs UNLINK
1. 源码实现
参考 Redis 源码 可以发现, DEL 和 UNLINK 分别对应不同的处理函数:
命令 | 处理函数 |
---|---|
DEL | dbSyncDelete |
UNLINK | dbAsyncDelete |
具体的实现细节请自行研读源码。
2. 耗时对比
下面我们来实际对比一下 DEL 和 UNLINK 的耗时差异。
开启 Slow log
设置 Slow log 记录每条命令的耗时(参考 SLOWLOG ):
127.0.0.1:6379> CONFIG SET slowlog-log-slower-than 0 OK 127.0.0.1:6379> CONFIG GET slowlog-log-slower-than 1) "slowlog-log-slower-than" 2) "0"
创建两个大 hash
准备一个 Lua 脚本:
local bulk = 1000 local fvs = {} local j for i = 1, ARGV[1] do j = i % bulk if j == 0 then fvs[2 * bulk - 1] = "field" .. i fvs[2 * bulk] = "value" .. i redis.call("HMSET", KEYS[1], unpack(fvs)) fvs = {} else fvs[2 * j - 1] = "field" .. i fvs[2 * j] = "value" .. i end end if #fvs > 0 then redis.call("HMSET", KEYS[1], unpack(fvs)) end return "OK"
将上述脚本保存为 huge_hmset.lua
,然后借助该脚本创建两个大 hash(参考 how to load lua script from file for redis
),分别为 hash1
和 hash2
,它们各自拥有 100 万个 field:
$ redis-cli --eval huge_hmset.lua hash1 , 1000000 "OK" $ redis-cli --eval huge_hmset.lua hash2 , 1000000 "OK"
上述操作会在 Slow log 中产生大量 HMSET 命令,这里先清除掉:
127.0.0.1:6379> SLOWLOG RESET OK
DEL hash1
127.0.0.1:6379> DEL hash1 (integer) 1 (0.63s)
UNLINK hash2
127.0.0.1:6379> UNLINK hash2 (integer) 1
查看 Slow log
127.0.0.1:6379> SLOWLOG GET 2 1) 1) (integer) 5089 2) (integer) 1534653951 3) (integer) 17 4) 1) "UNLINK" 2) "hash2" 5) "127.0.0.1:56560" 6) "" 2) 1) (integer) 5088 2) (integer) 1534653948 3) (integer) 630305 4) 1) "DEL" 2) "hash1" 5) "127.0.0.1:56560" 6) ""
耗时对比结果:
命令 | 耗时 |
---|---|
DEL hash1 | 630305 us |
UNLINK hash2 | 17 us |
值得注意的是: UNLINK 执行如此之快,并非使用了什么快速算法,而是因为它将真正的删除操作异步化了。
以上所述就是小编给大家介绍的《Redis 4.0 非阻塞删除》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Node.js 指南(阻塞与非阻塞概述)
- Node.js 回调函数 阻塞与非阻塞
- 明明白白学 同步、异步、阻塞与非阻塞
- 从 Linux 源码看 socket 的阻塞和非阻塞
- 分布式系统关注点——阻塞与非阻塞有什么区别?
- Netty基础系列(2) --彻底理解阻塞非阻塞与同步异步的区别
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。