Redis

洞悉Redis技术内幕:缓存,数据结构,并发,集群与算法
帅旋
关注
充电
IT宅站长,技术博主,共享单车手,全网id:arthinking。

Redis性能杀手:影响Redis性能的常见问题

发布于 2021-06-16 | 更新于 2024-03-03

Part III 部分,我们已经了解到,接收客户端网络执行命令的网络请求,网络开销对Redis的开销影响很小,那么在Redis中,究竟有哪些影响性能的点呢?这节,我们详细来探讨下。

不像线上性能分析,可以根据各种参数指标去挖掘定位性能点,为了摸底Redis的性能,那么就得从Redis的方法面面入手,我们将从以下各个维度去解析性能影响点。

1、内存数据操作

读操作

每当客户端请求数据的时候,都需要等待命令执行完,获取到执行的结果,所以执行命令的复杂度决定了性能的好坏,我们在实际操作中,要尽量使用复杂度低的命令,少用复杂度低的命令,控制一次读取的数据量。

以下是复杂度比较高的命令:

  • 集合统计排序:

    • SINTER 交集,复杂度O(N*M),其中 N 是最小集合的基数,M 是集合的数量;
    • SUNION 并集,复杂度O(N),其中 N 是所有给定集合中元素的总数;
    • SDIFF 差集,复杂度O(N) ,其中 N 是所有给定集合中元素的总数;
    • SORT 排序,复杂度O(N+M*log(M)),其中 N 是列表或集合中要排序的元素数,M 是返回元素的数量。
  • 大数据量查询:

    • HGETALL 返回存储在字典中所有的键值对,时间复杂度O(N),其中 N 是字典的大小;
    • SMEMBERS 返回集合中的所有成员key,时间复杂度O(N),其中N是设置的基数。

如果实在要执行此类操作,而数据量又比较大,建议将此类操作放到单独的从库中执行。

删除操作

如果我们删除了bigkey,那么就可能导致阻塞主线程。为什么呢?

因为删除操作除了会释放内存空间,还会把空闲空间插入操作系统的空闲内存块链表中,删除的key越大,那么就越耗时。

为了避免删除bigkey对主线程的阻塞,Redis 4.0开始新增了UNLINK命令实现惰性删除。删除bigkey,建议都使用UNLINK命令。

UNLINK命令是先释放掉字典和过期字典中的键值对引用,然后在符合以下任一条件的情况,决定是否需要放到lazyfree队列中进行异步删除:

  • Hash、Set底层采用哈希,并且元素个数超过64;
  • ZSET底层采用跳跃表,并且元素个数超过64;
  • List节点数量超过64。

否则,还是会直接删除。

可见惰性删除也不一定会起效,所以为了杜绝此类性能问题,最好避免在Redis中存储bigkey。

另外,如果我们执行FLUSHALL或者FLUSHDB,也会阻塞线程。为了避免此种情况,可以通过向FLUSHALL/FLUSHDB添加async异步清理选项,redis在清理整个实例或db时会以异步运行。

2、磁盘写

AOF日志落盘

如果我们的AOF写回策略是Always同步写,那么每次写数据的过程中,都会因写磁盘而阻塞住了。

如果可以容忍一秒钟的数据丢失,那么,我们可以把AOF写回策略设置为Everysec,这样就会通过异步线程去落盘了,从而避免阻塞主线程。

如果我们使用了Always策略,那么就需要注意了,如果刚好Redis在执行AOF重新,会导致大量的磁盘IO,最终导致操作系统fsync被阻塞,最终导致主线程也被fsync调用阻塞住了

为了进一步减小重写AOF被阻塞的风险,可以设置为AOF重写是,不进行fsync:

1
no-appendfsync-on-rewrite yes

3、主从同步

当我们要进行主从同步的时候,首先,主库会生成一份完整的RDB,传给从库,从库首先执行FLUSHDB清空原来数据库,然后从库在载入RDB文件,这个过程会导致从库被阻塞

4、切片集群

在切片集群场景,如果刚好有big key需要迁移到其他节点,那么就会导致主线程阻塞,因为Redis Cluster是用的同步迁移。迁移过程中会同时阻塞源节点和目标节点。

而如果使用Codis进行集群,则可以利用其异步迁移的特性减少big key迁移对集群性能的影响。

References

本文作者: 帅旋

本文链接: https://www.itzhai.com/columns/redis/performance-issues.html

版权声明: 版权归作者所有,未经许可不得转载,侵权必究!联系作者请加公众号。

×
IT宅

关注公众号及时获取网站内容更新。