redis
2021-07-28 21:03:37 1 举报
AI智能生成
登录查看完整内容
redis 知识梳理
作者其他创作
大纲/内容
memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
redis的速度比memcached快很多
redis可以持久化其数据
比较memcache
常用命令
redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60
本机60秒内响应延迟
redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1
最大最小平均相应延迟
# 命令执行耗时超过 5 毫秒,记录慢日志CONFIG SET slowlog-log-slower-than 5000# 只保留最近 500 条慢日志CONFIG SET slowlog-max-len 500
127.0.0.1:6379> SLOWLOG get 51) 1) (integer) 32693 # 慢日志ID 2) (integer) 1593763337 # 执行时间戳 3) (integer) 5299 # 执行耗时(微秒) 4) 1) \"LRANGE\" # 具体执行的命令和参数 2) \"user_list:2000\" 3) \"0\" 4) \"-1\"2) 1) (integer) 32692 2) (integer) 1593763337 3) (integer) 5044 4) 1) \"GET\" 2) \"user_info:1000\"
SLOWLOG get 5
慢日志
尽量不使用 O(N) 以上复杂度过高的命令,对于数据的聚合操作,放在客户端做
执行 O(N) 命令,保证 N 尽量的小(推荐 N <= 300),每次获取尽量少的数据,让 Redis 可以及时处理返回
业务优化
001负载检查
如果一个 key 写入的 value 非常大,那么 Redis 在分配内存时就会比较耗时。同样的,当删除这个 key 时,释放内存也会比较耗时,这种类型的 key 我们一般称之为 bigkey。
redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01
对线上实例进行 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 命令时,释放内存也会放到后台线程中执行
这是因为 bigkey 在很多场景下,依旧会产生性能问题。例如,bigkey 在分片集群模式下,对于数据的迁移也会有性能影响,以及我后面即将讲到的数据过期、数据淘汰、透明大页,都会受到 bigkey 的影响。
优化
002操作bigkey
变慢的时间点很有规律,例如某个整点,或者每间隔多久就会发生一波延迟。
只有当访问某个 key 时,才判断这个 key 是否已过期,如果已过期,则从实例中删除
被动过期
Redis 内部维护了一个定时任务,默认每隔 100 毫秒(1秒10次)就会从全局的过期哈希表中随机取出 20 个 key,然后删除其中过期的 key,如果过期 key 的比例超过了 25%,则继续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒,才会退出循环
这个主动过期 key 的定时任务,是在 Redis 主线程中执行的
如果此时需要过期删除的是一个 bigkey,那么这个耗时会更久。而且,这个操作延迟的命令并不会记录在慢日志中。
主动过期
需要检查你的业务代码,是否存在集中过期 key 的逻辑
一般集中过期使用的是 expireat / pexpireat 命令,你需要在代码中搜索这个关键字。
集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力
# 释放过期 key 的内存,放到后台线程执行lazyfree-lazy-expire yes
如果你使用的 Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程
expired_keys 监控指标 看过期key的数量变化
避免方式
过期规则
003集中过期
如果你的 Redis 实例设置了内存上限 maxmemory,那么也有可能导致 Redis 变慢。
当我们把 Redis 当做纯缓存使用时,通常会给这个实例设置一个内存上限 maxmemory,然后设置一个数据淘汰策略。
当 Redis 内存达到 maxmemory 后,每次写入新的数据之前,Redis 必须先从实例中踢出一部分数据,让整个实例的内存维持在 maxmemory 之下,然后才能把新数据写进来。
allkeys-lru:不管 key 是否设置了过期,淘汰最近最少访问的 key
volatile-lru:只淘汰最近最少访问、并设置了过期时间的 key
allkeys-random:不管 key 是否设置了过期,随机淘汰 key
volatile-random:只随机淘汰设置了过期时间的 key
allkeys-ttl:不管 key 是否设置了过期,淘汰即将过期的 key
noeviction:不淘汰任何 key,实例内存达到 maxmeory 后,再写入新数据直接返回错误
allkeys-lfu:不管 key 是否设置了过期,淘汰访问频率最低的 key(4.0+版本支持
volatile-lfu:只淘汰访问频率最低、并设置了过期时间 key(4.0+版本支持)
淘汰策略
避免存储 bigkey,降低释放内存的耗时
淘汰策略改为随机淘汰,随机淘汰比 LRU 要快很多(视业务情况调整
拆分实例,把淘汰 key 的压力分摊到多个实例上
如果使用的是 Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行(配置 lazyfree-lazy-eviction = yes)
避免建议
004实例内存达到上限
为了保证 Redis 数据的安全性,我们可能会开启后台定时 RDB 和 AOF rewrite 功能
但如果你发现,操作 Redis 延迟变大,都发生在 Redis 后台 RDB 和 AOF rewrite 期间,那你就需要排查,在这期间有可能导致变慢的情况。
当 Redis 开启了后台 RDB 和 AOF rewrite 后,在执行时,它们都需要主进程创建出一个子进程进行数据的持久化
主进程创建子进程,会调用操作系统提供的 fork 函数
而 fork 在执行过程中,主进程需要拷贝自己的内存页表给子进程,如果这个实例很大,那么这个拷贝的过程也会比较耗时。
而且这个 fork 过程会消耗大量的 CPU 资源,在完成 fork 之前,整个 Redis 实例会被阻塞住,无法处理任何客户端请求。
如果此时你的 CPU 资源本来就很紧张,那么 fork 的耗时会更长,甚至达到秒级,这会严重影响 Redis 的性能。
你可以在 Redis 上执行 INFO 命令,查看 latest_fork_usec 项,单位微秒
# 上一次 fork 耗时,单位微秒latest_fork_usec:59477
这个时间就是主进程在 fork 子进程期间,整个实例阻塞无法处理客户端请求的时间。
如果你发现这个耗时很久,就要警惕起来了,这意味在这期间,你的整个 Redis 实例都处于不可用的状态。
除了数据持久化会生成 RDB 之外,当主从节点第一次建立数据同步时,主节点也创建子进程生成 RDB,然后发给从节点进行一次全量同步,所以,这个过程也会对 Redis 产生性能影响。
那如何确认确实是因为 fork 耗时导致的 Redis 延迟变大呢?
控制 Redis 实例的内存:尽量在 10G 以下,执行 fork 的耗时与实例大小有关,实例越大,耗时越久
合理配置数据持久化策略:在 slave 节点执行 RDB 备份,推荐在低峰期执行,而对于丢失数据不敏感的业务(例如把 Redis 当做纯缓存使用),可以关闭 AOF 和 AOF rewrite
Redis 实例不要部署在虚拟机上:fork 的耗时也与系统也有关,虚拟机比物理机耗时更久
降低主从库全量同步的概率:适当调大 repl-backlog-size 参数,避免主从全量同步
005fork耗时严重
应用程序向操作系统申请内存时,是按内存页进行申请的,而常规的内存页大小是 4KB。
Linux 内核从 2.6.38 开始,支持了内存大页机制,该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存。
应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长
当 Redis 在执行后台 RDB 和 AOF rewrite 时,采用 fork 子进程的方式来处理。但主进程 fork 子进程后,此时的主进程依旧是可以接收写请求的,而进来的写请求,会采用 Copy On Write(写时复制)的方式操作内存数据。
也就是说,主进程一旦有数据需要修改,Redis 并不会直接修改现有内存中的数据,而是先将这块内存数据拷贝出来,再修改这块新内存的数据,这就是所谓的「写时复制」。
写时复制你也可以理解成,谁需要发生写操作,谁就需要先拷贝,再修改。
这样做的好处是,父进程有任何写操作,并不会影响子进程的数据持久化(子进程只持久化 fork 这一瞬间整个实例中的所有数据即可,不关心新的数据变更,因为子进程只需要一份内存快照,然后持久化到磁盘上)。
但是请注意,主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修改 10B 的数据,Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加,影响到 Redis 性能。
同样地,如果这个写请求操作的是一个 bigkey,那主进程在拷贝这个 bigkey 内存块时,一次申请的内存会更大,时间也会更久。可见,bigkey 在这里又一次影响到了性能。
只需要关闭内存大页机制就可以
$ cat /sys/kernel/mm/transparent_hugepage/enabled[always] madvise never
如果输出选项是 always,就表示目前开启了内存大页机制,我们需要关掉它:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
首先,你需要查看 Redis 机器是否开启了内存大页
解决方式
其实,操作系统提供的内存大页机制,其优势是,可以在一定程序上降低应用程序申请内存的次数。
但是对于 Redis 这种对性能和延迟极其敏感的数据库来说,我们希望 Redis 在每次申请内存时,耗时尽量短,所以我不建议你在 Redis 机器上开启这个机制。
006开启内存大页
RDB 和 AOF rewrite 对 Redis 性能的影响,主要关注点在 fork 上
AOF 数据持久化 对性能的影响
Redis 执行写命令后,把这个命令写入到 AOF 文件内存中(write 系统调用)
Redis 根据配置的 AOF 刷盘策略,把 AOF 内存数据刷到磁盘上(fsync 系统调用)
Redis 开启 AOF 后,其工作原理如下
appendfsync always:主线程每次执行写操作后立即刷盘,此方案会占用比较大的磁盘 IO 资源,但数据安全性最高
appendfsync no:主线程每次写操作只写内存就返回,内存数据什么时候刷到磁盘,交由操作系统决定,此方案对性能影响最小,但数据安全性也最低,Redis 宕机时丢失的数据取决于操作系统刷盘时机
这种方案还是存在导致 Redis 延迟变大的情况发生,甚至会阻塞整个 Redis
当 Redis 后台线程在执行 AOF 文件刷盘时,如果此时磁盘的 IO 负载很高,那这个后台线程在执行刷盘操作(fsync系统调用)时就会被阻塞住。
此时的主线程依旧会接收写请求,紧接着,主线程又需要把数据写到文件内存中(write 系统调用),但此时的后台子线程由于磁盘负载过高,导致 fsync 发生阻塞,迟迟不能返回,那主线程在执行 write 系统调用时,也会被阻塞住,直到后台线程 fsync 执行完成后,主线程执行 write 才能成功返回。
尽管你的 AOF 配置为 appendfsync everysec,也不能掉以轻心,要警惕磁盘压力过大导致的 Redis 有性能问题。
appendfsync everysec:主线程每次写操作只写内存就返回,然后由后台线程每隔 1 秒执行一次刷盘操作(触发fsync系统调用),此方案对性能影响相对较小,但当 Redis 宕机时会丢失 1 秒的数据
为了保证 AOF 文件数据的安全性,Redis 提供了 3 种刷盘机制
Redis 的 AOF 后台子线程刷盘操作,撞上了子进程 AOF rewrite!
Redis 提供了一个配置项,当子进程在 AOF rewrite 期间,可以让后台子线程不执行刷盘(不触发 fsync 系统调用)操作。
这相当于在 AOF rewrite 期间,临时把 appendfsync 设置为了 none,配置如下:
# AOF rewrite 期间,AOF 后台子线程不进行刷盘操作# 相当于在这期间,临时把 appendfsync 设置为了 noneno-appendfsync-on-rewrite yes
开启这个配置项,在 AOF rewrite 期间,如果实例发生宕机,那么此时会丢失更多的数据,性能和数据安全性,你需要权衡后进行选择。
子进程正在执行 AOF rewrite,这个过程会占用大量的磁盘 IO 资源
如果占用磁盘资源的是其他应用程序,那就比较简单了,你需要定位到是哪个应用程序在大量写磁盘,然后把这个应用程序迁移到其他机器上执行就好了,避免对 Redis 产生影响。
有其他应用程序在执行大量的写文件操作,也会占用磁盘 IO 资源
appendfsync everysec 磁盘io高的问题
007开启AOF
很多时候,我们在部署服务时,为了提高服务性能,降低应用程序在多个 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 带来的性能问题。
当然,即便我们把 Redis 绑定在多个逻辑核心上,也只能在一定程度上缓解主线程、子进程、后台线程在 CPU 资源上的竞争。因为这些子进程、子线程还是会在这多个逻辑核心上进行切换,存在性能损耗。
不要让 Redis 进程只绑定在一个 CPU 逻辑核上,而是绑定在多个逻辑核心上,而且,绑定的多个逻辑核心最好是同一个物理核心,这样它们还可以共用 L1/L2 Cache。
Redis 在 6.0 版本已经推出了这个功能,我们可以通过以下配置,对主线程、后台线程、后台 RDB 进程、AOF rewrite 进程,绑定固定的 CPU 逻辑核心:
解决方案
一般来说,Redis 的性能已经足够优秀,除非你对 Redis 的性能有更加严苛的要求,否则不建议你绑定 CPU。
008绑定CPU
如果你发现 Redis 突然变得非常慢,每次的操作耗时都达到了几百毫秒甚至秒级,那此时你就需要检查 Redis 是否使用到了 Swap,在这种情况下 Redis 基本上已经无法提供高性能的服务了。
操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是 Swap。
当内存中的数据被换到磁盘上后,Redis 再访问这些数据时,就需要从磁盘上读取,访问磁盘的速度要比访问内存慢几百倍!
尤其是针对 Redis 这种对性能要求极高、性能极其敏感的数据库来说,这个操作延时是无法接受的。
# 先找到 Redis 的进程 ID$ ps -aux | grep redis-server# 查看 Redis Swap 使用情况$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'
每一行 Size 表示 Redis 所用的一块内存大小,Size 下面的 Swap 就表示这块 Size 大小的内存,有多少数据已经被换到磁盘上了,如果这两个值相等,说明这块内存的数据都已经完全被换到磁盘上了。
你可以通过以下方式来查看 Redis 进程是否使用到了 Swap:
如果只是少量数据被换到磁盘上,例如每一块 Swap 占对应 Size 的比例很小,那影响并不是很大。如果是几百兆甚至上 GB 的内存被换到了磁盘上,那么你就需要警惕了,这种情况 Redis 的性能肯定会急剧下降。
增加机器的内存,让 Redis 有足够的内存可以使用
整理内存空间,释放出足够的内存供 Redis 使用,然后释放 Redis 的 Swap,让 Redis 重新使用内存
预防的办法就是,你需要对 Redis 机器的内存和 Swap 使用情况进行监控,在内存不足或使用到 Swap 时报警出来,及时处理。
释放 Redis 的 Swap 过程通常要重启实例,为了避免重启实例对业务的影响,一般会先进行主从切换,然后释放旧主节点的 Swap,重启旧主节点实例,待从库数据同步完成后,再进行主从切换即可。
009使用Swap
Redis 的数据都存储在内存中,当我们的应用程序频繁修改 Redis 中的数据时,就有可能会导致 Redis 产生内存碎片
内存碎片会降低 Redis 的内存使用率,我们可以通过执行 INFO 命令,得到这个实例的内存碎片率:
# Memoryused_memory:5709194824used_memory_human:5.32Gused_memory_rss:8264855552used_memory_rss_human:7.70G...mem_fragmentation_ratio:1.45
mem_fragmentation_ratio = used_memory_rss / used_memory。
其中 used_memory 表示 Redis 存储数据的内存大小,而 used_memory_rss 表示操作系统实际分配给 Redis 进程的大小。
INFO
如果 mem_fragmentation_ratio > 1.5,说明内存碎片率已经超过了 50%,这时我们就需要采取一些措施来降低内存碎片了
如果你使用的是 Redis 4.0 以下版本,只能通过重启实例来解决
如果你使用的是 Redis 4.0 版本,它正好提供了自动碎片整理的功能,可以通过配置开启碎片自动整理
解决的方案
开启内存碎片整理,它也有可能会导致 Redis 性能下降
原因在于,Redis 的碎片整理工作是也在主线程中执行的,当其进行碎片整理时,必然会消耗 CPU 资源,产生更多的耗时,从而影响到客户端的请求。
当你需要开启这个功能时,最好提前测试评估它对 Redis 的影响。
# 开启自动内存碎片整理(总开关)activedefrag yes# 内存使用 100MB 以下,不进行碎片整理active-defrag-ignore-bytes 100mb# 内存碎片率超过 10%,开始碎片整理active-defrag-threshold-lower 10# 内存碎片率超过 100%,尽最大努力碎片整理active-defrag-threshold-upper 100# 内存碎片整理占用 CPU 资源最小百分比active-defrag-cycle-min 1# 内存碎片整理占用 CPU 资源最大百分比active-defrag-cycle-max 25# 碎片整理期间,对于 List/Set/Hash/ZSet 类型元素一次 Scan 的数量active-defrag-max-scan-fields 1000
Redis 碎片整理的参数配置如下:
你需要结合 Redis 机器的负载情况,以及应用程序可接受的延迟范围进行评估,合理调整碎片整理的参数,尽可能降低碎片整理期间对 Redis 的影响。
010碎片整理
网络带宽过载的情况下,服务器在 TCP 层和网络层就会出现数据包发送延迟、丢包等情况
Redis 的高性能,除了操作内存之外,就在于网络 IO 了,如果网络 IO 存在瓶颈,那么也会严重影响 Redis 的性能。
如果确实出现这种情况,你需要及时确认占满网络带宽 Redis 实例,如果属于正常的业务访问,那就需要及时扩容或迁移实例了,避免因为这个实例流量过大,影响这个机器的其他实例。
运维层面,你需要对 Redis 机器的各项指标增加监控,包括网络流量,在网络流量达到一定阈值时提前报警,及时确认和扩容。
011网络带宽过载
你的业务应用,应该使用长连接操作 Redis,避免频繁的短连接。频繁的短连接会导致 Redis 大量时间耗费在连接的建立和释放上,TCP 的三次握手和四次挥手同样也会增加访问延迟。
频繁短连接
要想提前预知 Redis 变慢的情况发生,必不可少的就是做好完善的监控。
监控其实就是对采集 Redis 的各项运行时指标,通常的做法是监控程序定时采集 Redis 的 INFO 信息,然后根据 INFO 信息中的状态数据做数据展示和报警。
在写监控脚本访问 Redis 时,尽量采用长连接的方式采集状态信息,避免频繁短连接。同时,你还要注意控制访问 Redis 的频率,避免影响到业务请求。
在使用一些开源的监控组件时,最好了解一下这些组件的实现原理,以及正确配置这些组件,防止出现监控组件发生 Bug,导致短时大量操作 Redis,影响 Redis 性能的情况发生。
我们当时就发生过,DBA 在使用一些开源组件时,因为配置和使用问题,导致监控程序频繁地与 Redis 建立和断开连接,导致 Redis 响应变慢。
运维监控
最后需要提醒你的是,你的 Redis 机器最好专项专用,只用来部署 Redis 实例,不要部署其他应用程序,尽量给 Redis 提供一个相对「安静」的环境,避免其它程序占用 CPU、内存、磁盘资源,导致分配给 Redis 的资源不足而受到影响。
其它程序争抢资源
012其他原因
CPU 相关:使用复杂度过高命令、数据的持久化,都与耗费过多的 CPU 资源有关内存相关:bigkey 内存的申请和释放、数据过期、数据淘汰、碎片整理、内存大页、内存写时复制都与内存息息相关磁盘相关:数据持久化、AOF 刷盘策略,也会受到磁盘的影响网络相关:短连接、实例流量过载、网络流量过载,也会降低 Redis 性能计算机系统:CPU 结构、内存分配,都属于最基础的计算机系统知识操作系统:写时复制、内存大页、Swap、CPU 绑定,都属于操作系统层面的知识
总结
https://mp.weixin.qq.com/s?__biz=MzIyOTYxNDI5OA==&mid=2247484679&idx=1&sn=3273e2c9083e8307c87d13a441a267d7&chksm=e8beb2b2dfc93ba4c28c95fdcb62eefc529d6a4ca2b4971ad0493319adbf8348b318224bd3d9&mpshare=1&scene=1&srcid=0623odH3Hjq2l1uz0ftZAp8o&sharer_sharetime=1624435906872&sharer_shareid=5b0dbe114e4781e0b7a66525c7c421a5&version=3.1.2.6203&platform=mac&st=26DF8A6452D711129508AE67C2F024D74438DB08DB39E0F347B7DDEA1235C1BBA71E7A204B750FA21AB054257B851B27121325B4BEE7BDC7B4C9334730C145DEF7D6D8F1CA79FBB0AE5FA40CF0FF24953AAFFEA426690307878F14F62D46E7D650ECE7EA0B718D946282F4A417E7B500AE1164A5F1B5E58861483706071BAA6F193A2E03439C2641F96D999B26439DB08926EF7753EC8F338A18B3260FF629A6337B6684CFEC51E43F7E824F90BB6AAE&vid=1688853573300456&cst=1A9C955F9D41AB94B5B4CB1348F043B7260A4A8C22C31E09BAA5DB1A1DAE0B966D2297A4205A990A4964A137A5A3E3C0&deviceid=2cefcfee-e5a7-40b7-8930-9817395986f5#rd
refer
性能考量
关于redis的性能,QPS 单机10W妥妥的,想要更高就要横向扩展为集群的方式。
性能
redis是基于内存的,内存的读写速度非常快;
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
redis是单线程的,省去了很多上下文切换线程的时间;
非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在IO上浪费一点时间。
redis使用多路复用技术,可以处理并发的连接。
性能高的问题
数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
SET:为一个key设置value,可以配合EX/PX参数指定key的有效期,通过NX/XX参数针对key是否存在的情况进行区别操作,时间复杂度O(1)GET:获取某个key对应的value,时间复杂度O(1)GETSET:为一个key设置value,并返回该key的原value,时间复杂度O(1)MSET:为多个key设置value,时间复杂度O(N)MSETNX:同MSET,如果指定的key中有任意一个已存在,则不进行任何操作,时间复杂度O(N)MGET:获取多个key对应的value,时间复杂度O(N)
INCR:将key对应的value值自增1,并返回自增后的值。只对可以转换为整型的String数据起作用。时间复杂度O(1)INCRBY:将key对应的value值自增指定的整型数值,并返回自增后的值。只对可以转换为整型的String数据起作用。时间复杂度O(1)DECR/DECRBY:同INCR/INCRBY,自增改为自减。
NCR/DECR系列命令的value,必须在[-2^63~2^63-1]范围内。
String
HSET:将key对应的Hash中的field设置为value。如果该Hash不存在,会自动创建一个。时间复杂度O(1)HGET:返回指定Hash中field字段的值,时间复杂度O(1)HMSET/HMGET:同HSET和HGET,可以批量操作同一个key下的多个field,时间复杂度:O(N),N为一次操作的field数量HSETNX:同HSET,但如field已经存在,HSETNX不会进行任何操作,时间复杂度O(1)HEXISTS:判断指定Hash中field是否存在,存在返回1,不存在返回0,时间复杂度O(1)HDEL:删除指定Hash中的field(1个或多个),时间复杂度:O(N),N为操作的field数量HINCRBY:同INCRBY命令,对指定Hash中的一个field进行INCRBY,时间复杂度O(1)
应谨慎使用的Hash相关命令:HGETALL:返回指定Hash中所有的field-value对。返回结果为数组,数组中field和value交替出现。时间复杂度O(N)HKEYS/HVALS:返回指定Hash中所有的field/value,时间复杂度O(N)
HASH
LPUSH:向指定List的左侧(即头部)插入1个或多个元素,返回插入后的List长度。时间复杂度O(N),N为插入元素的数量RPUSH:同LPUSH,向指定List的右侧(即尾部)插入1或多个元素LPOP:从指定List的左侧(即头部)移除一个元素并返回,时间复杂度O(1)RPOP:同LPOP,从指定List的右侧(即尾部)移除1个元素并返回LPUSHX/RPUSHX:与LPUSH/RPUSH类似,区别在于,LPUSHX/RPUSHX操作的key如果不存在,则不会进行任何操作LLEN:返回指定List的长度,时间复杂度O(1)LRANGE:返回指定List中指定范围的元素(双端包含,即LRANGEkey010会返回11个元素),时间复杂度O(N)。应尽可能控制一次获取的元素数量,一次获取过大范围的List元素会导致延迟,同时对长度不可预知的List,避免使用LRANGEkey0-1这样的完整遍历操作。
应谨慎使用的List相关命令:LINDEX:返回指定List指定index上的元素,如果index越界,返回nil。index数值是回环的,即-1代表List最后一个位置,-2代表List倒数第二个位置。时间复杂度O(N)LSET:将指定List指定index上的元素设置为value,如果index越界则返回错误,时间复杂度O(N),如果操作的是头/尾部的元素,则时间复杂度为O(1)LINSERT:向指定List中指定元素之前/之后插入一个新元素,并返回操作后的List长度。如果指定的元素不存在,返回-1。如果指定key不存在,不会进行任何操作,时间复杂度O(N)
List
SADD:向指定Set中添加1个或多个member,如果指定Set不存在,会自动创建一个。时间复杂度O(N),N为添加的member个数SREM:从指定Set中移除1个或多个member,时间复杂度O(N),N为移除的member个数SRANDMEMBER:从指定Set中随机返回1个或多个member,时间复杂度O(N),N为返回的member个数SPOP:从指定Set中随机移除并返回count个member,时间复杂度O(N),N为移除的member个数SCARD:返回指定Set中的member个数,时间复杂度O(1)SISMEMBER:判断指定的value是否存在于指定Set中,时间复杂度O(1)SMOVE:将指定member从一个Set移至另一个Set
慎用的Set相关命令:SMEMBERS:返回指定Hash中所有的member,时间复杂度O(N)SUNION/SUNIONSTORE:计算多个Set的并集并返回/存储至另一个Set中,时间复杂度O(N),N为参与计算的所有集合的总member数SINTER/SINTERSTORE:计算多个Set的交集并返回/存储至另一个Set中,时间复杂度O(N),N为参与计算的所有集合的总member数SDIFF/SDIFFSTORE:计算1个Set与1或多个Set的差集并返回/存储至另一个Set中,时间复杂度O(N),N为参与计算的所有集合的总member数
Set
ZADD:向指定SortedSet中添加1个或多个member,时间复杂度O(Mlog(N)),M为添加的member数量,N为SortedSet中的member数量ZREM:从指定SortedSet中删除1个或多个member,时间复杂度O(Mlog(N)),M为删除的member数量,N为SortedSet中的member数量ZCOUNT:返回指定SortedSet中指定score范围内的member数量,时间复杂度:O(log(N))ZCARD:返回指定SortedSet中的member数量,时间复杂度O(1)ZSCORE:返回指定SortedSet中指定member的score,时间复杂度O(1)ZRANK/ZREVRANK:返回指定member在SortedSet中的排名,ZRANK返回按升序排序的排名,ZREVRANK则返回按降序排序的排名。时间复杂度O(log(N))ZINCRBY:同INCRBY,对指定SortedSet中的指定member的score进行自增,时间复杂度O(log(N))
慎用的SortedSet相关命令:ZRANGE/ZREVRANGE:返回指定SortedSet中指定排名范围内的所有member,ZRANGE为按score升序排序,ZREVRANGE为按score降序排序,时间复杂度O(log(N)+M),M为本次返回的member数ZRANGEBYSCORE/ZREVRANGEBYSCORE:返回指定SortedSet中指定score范围内的所有member,返回结果以升序/降序排序,min和max可以指定为-inf和+inf,代表返回所有的member。时间复杂度O(log(N)+M)ZREMRANGEBYRANK/ZREMRANGEBYSCORE:移除SortedSet中指定排名范围/指定score范围内的所有member。时间复杂度O(log(N)+M)
SortedSet
数据操作
redis
0 条评论
回复 删除
下一页