上篇文章:Reids命令原理与应用2 - Redis网络层与优化,pipeline,发布订阅与事务-CSDN博客
个人代码仓库:橘子真甜 (yzc-YZC) - Gitee.com橘子真甜 (yzc-YZC) - Gitee.com橘子真甜 (yzc-YZC) - Gitee.com
目录
[一. Redis单线程与辅助线程](#一. Redis单线程与辅助线程)
[1.1 Redis主线程 redis-server](#1.1 Redis主线程 redis-server)
[1.2 辅助线程](#1.2 辅助线程)
[二 Redis线程设计原理](#二 Redis线程设计原理)
[2.1 单线程优点](#2.1 单线程优点)
[2.2 单线程缺陷](#2.2 单线程缺陷)
[2.3 Redis针对缺陷设计的辅助线程](#2.3 Redis针对缺陷设计的辅助线程)
[a IO密集型](#a IO密集型)
[b cpu密集型](#b cpu密集型)
[三. Redis存储结结构](#三. Redis存储结结构)
[3.1 Redis数据组织方式](#3.1 Redis数据组织方式)
[3.2 哈希字典](#3.2 哈希字典)
[3.3 哈希字典扩容缩容](#3.3 哈希字典扩容缩容)
[3.4 rehash渐进式哈希](#3.4 rehash渐进式哈希)
[3.5 scan命令](#3.5 scan命令)
[四. 总结](#四. 总结)
[4.1 Redis高效机制](#4.1 Redis高效机制)
[4.2 Redis优化](#4.2 Redis优化)
一. Redis单线程与辅助线程
redis是不是单线程?redis对数据的操作是由主线程完成的,不过为了提高redis效率,redis还创建了很多辅助线程用于完成其他工作。(这些线程不会影响数据库的安全)
我们可以通过调试的方式来查看redis创建的线程。
sudo gdb /usr/local/bin/redis-server
[yzc@study redis-data]$ sudo gdb /usr/local/bin/redis-server
GNU gdb (GDB) Red Hat Enterprise Linux 8.2-16.el8
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/local/bin/redis-server...done.
(gdb) run redis.conf
Starting program: /usr/local/bin/redis-server redis.conf
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-164.el8.x86_64
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
517379:C 03 Dec 2025 14:36:12.422 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
517379:C 03 Dec 2025 14:36:12.422 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
517379:C 03 Dec 2025 14:36:12.422 * Redis version=7.4.7, bits=64, commit=00000000, modified=1, pid=517379, just started
517379:C 03 Dec 2025 14:36:12.422 * Configuration loaded
517379:M 03 Dec 2025 14:36:12.423 * monotonic clock: POSIX clock_gettime
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis Community Edition
.-`` .-```. ```\/ _.,_ ''-._ 7.4.7 (00000000/1) 64 bit
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 517379
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
517379:M 03 Dec 2025 14:36:12.424 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
[New Thread 0x7ffff5fff700 (LWP 517384)]
[New Thread 0x7ffff57fe700 (LWP 517385)]
[New Thread 0x7ffff4ffd700 (LWP 517386)]
[New Thread 0x7ffff47fc700 (LWP 517387)]
[New Thread 0x7ffff3ffb700 (LWP 517388)]
[New Thread 0x7ffff37fa700 (LWP 517389)]
[New Thread 0x7ffff2ff9700 (LWP 517390)]
517379:M 03 Dec 2025 14:36:12.428 * Server initialized
517379:M 03 Dec 2025 14:36:12.428 * Loading RDB produced by version 7.4.7
517379:M 03 Dec 2025 14:36:12.428 * RDB age 54427 seconds
517379:M 03 Dec 2025 14:36:12.428 * RDB memory usage when created 2.43 Mb
517379:M 03 Dec 2025 14:36:12.428 * Done loading RDB, keys loaded: 14, keys expired: 0.
517379:M 03 Dec 2025 14:36:12.428 * DB loaded from disk: 0.000 seconds
517379:M 03 Dec 2025 14:36:12.428 * Ready to accept connections tcp
[New Thread 0x7ffff23ff700 (LWP 517391)]
^C
Thread 1 "redis-server" received signal SIGINT, Interrupt.
0x00007ffff715a0f7 in epoll_wait () from /lib64/libc.so.6
输入 ctrl C 让系统收到信号SIGINT从epoll_wait退出,进入可调试状态
然后输入thread info 查看所有的线程
[New Thread 0x7ffff23ff700 (LWP 517391)]
^C
Thread 1 "redis-server" received signal SIGINT, Interrupt.
0x00007ffff715a0f7 in epoll_wait () from /lib64/libc.so.6
(gdb) info threads
Id Target Id Frame
* 1 Thread 0x7ffff7fea1c0 (LWP 517379) "redis-server" 0x00007ffff715a0f7 in epoll_wait () from /lib64/libc.so.6
2 Thread 0x7ffff5fff700 (LWP 517384) "bio_close_file" 0x00007ffff74303fc in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
3 Thread 0x7ffff57fe700 (LWP 517385) "bio_aof" 0x00007ffff74303fc in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
4 Thread 0x7ffff4ffd700 (LWP 517386) "bio_lazy_free" 0x00007ffff74303fc in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
5 Thread 0x7ffff47fc700 (LWP 517387) "io_thd_1" 0x00007ffff743375d in __lll_lock_wait () from /lib64/libpthread.so.0
6 Thread 0x7ffff3ffb700 (LWP 517388) "io_thd_2" 0x00007ffff743375d in __lll_lock_wait () from /lib64/libpthread.so.0
7 Thread 0x7ffff37fa700 (LWP 517389) "io_thd_3" 0x00007ffff743375d in __lll_lock_wait () from /lib64/libpthread.so.0
8 Thread 0x7ffff2ff9700 (LWP 517390) "jemalloc_bg_thd" 0x00007ffff74303fc in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
9 Thread 0x7ffff23ff700 (LWP 517391) "jemalloc_bg_thd" 0x00007ffff74303fc in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
(gdb)
可以看到,有很多个线程
1.1 Redis主线程 redis-server
redis早期版本只有一个线程完成工作,当前版本redis有很多线程。主线程负责监听网络连接,命令处理,数据读写操作,lua脚本/事务处理。
redis单线程指的是:命令处理和数据操作在同一个线程
1.2 辅助线程
|---------------------|-----------------|
| 辅助线程 | 作用 |
| bio_close_file | 异步关闭大文件 |
| bio_aof_async | 异步持久化刷盘 |
| bio_lazy_free | 异步惰性删除内存 |
| id_thd_num | IO线程池,负责网络数据的读写 |
| jemalloc_bg_thd | 开源内存池 |
二 Redis线程设计原理
2.1 单线程优点
单线程优点,也就是为何不使用多线程。
1 单线程天然具有数据隔离性,只要程序运行就不会造成数据读写异常
2 没有线程切换的开销
3 不需要加锁,没有加锁/解锁/等待锁的开销
2.2 单线程缺陷
对于IO操作和CPU耗时任务的执行需要较长时间,这会严重影响redis性能
1 网络IO是比较耗时的,单线程效率较低
2 磁盘IO也是比较耗时的,单线程效率低
3 CPU密集型操作(比如一次性大量删除数据,移动数据),单线程效率低
2.3 Redis针对缺陷设计的辅助线程
a IO密集型
对于网络IO:redis增加了网络IO线程池,创建一个/多个IO线程负责网络IO。
数据流程如下:
网络数据(监听数据除外) -> 网络IO线程 -> 主线程,主线程进行命令处理,并获取返回应答 -> 网络IO线程,将结果返回客户端
这样就能不仅可以保证数据隔离性,还能提高redis性能。
对于磁盘IO:redis有持久化,不同的持久化的实现是不同的。
aof持久化:采用辅助线程方式进行异步刷盘
rdb持久化:采用fork子进程实现持久化
b cpu密集型
不同的数据量有不同的操作,少量数据使用哈希表,大量数据使用哈希表 + 跳表存储。当哈希表扩容时候主线程 会进行渐进式哈希 ,并有bio_lazy_free辅助线程帮助释放内存
三. Redis存储结结构
3.1 Redis数据组织方式
redis通过一个哈希表(哈希字典)来组织所有数据。也就是通过一个哈希字典来管理所有的<key, value>键值对。不过这个value支持多种类型,string,list,set,zset,hash。
redis有多个哈希字典(默认15个),通过 select 可以进行切换不同的字典。

对于 value对象,上篇文章详细说明了,这里给出思维导图

3.2 哈希字典
查看源码可以看到哈希字典的各个字段。

这里介绍一下各个字段的作用
dictType *type; 函数指针,用于实现多态
dictEntry **ht_table[2]; 两个哈希表
long rehashidx; 重新哈希的下标
int16_t pauserehash 是否需要暂停rehash
signed char ht_size_exp[2] 存储指数,快速计算表的size,同时对取模运算的优化。
redis哈希表size是 2^n,那么 hash(key) % size == hash(key) & (size - 1)。将%运算优化为位运算,效率提升很多。
3.3 哈希字典扩容缩容
负载因子:哈希表存储有效数据/哈希表表长度。
redis缩容扩容策略:
扩容:插入数据时候,负载因子 >= 1,就需要扩容,减少哈希冲突
缩容:删除数据时候,负载因子 <= 0.1 就需要缩容,减少内存浪费
redis扩容是翻倍扩容的,如果数据量较大的话直接拷贝大量数据会导致redis服务暂停。所以我们不可以直接去扩容大量数据,使用两个哈希表的目的就慢慢扩容以便redis不会突然性能降低
3.4 rehash渐进式哈希
hash扩容时候不可一次性直接复制全部数据,需要慢慢复制。此外,如果原有空间较大的化我们可以另起线程 bio_lazy_free来销毁大空间。
两个哈希表 和 rehashindex 以及 pauserehash 都是用于服务rehash的。
rehash原理:
redis每一次哈希时候不会移动所有数据,每一次只会移动一个桶数据(一条哈希链表)。
rehash时机:
1 每当增删查改时候都会判断是否能够rehash,如果可以就会进行一次rehash。在此期间,增删查改会遍历两张表
2 redis-server空闲的时候也会通过定时器进行rehash。默认是1ms,每次rehash 100个桶
3.5 scan命令
在rehash期间,我们使用keys * 获取所有的key会出现一些问题。如果直接遍历两张表,key的顺序是会发生变化的,并且keys * 是阻塞实现的,效率较低。
为了效率,reids利用数学关系实现了scan命令。通过游标方式来保证我们读取的key顺序不会出错。

如上图,如果一个数据的二进制末尾是00,那么扩容后一定在末尾二进制为00的桶中。依次迭代就能保证遍历所有的数据
scan目的:保证遍历期间存在的key一定被返回,但是有可能会遍历到重复的key。而且连续多次扩容/缩容会导致数据重复率上升,不过这种情况不常见。
在实际应用中,我们使用scan命令更多,因为效率较高。
四. 总结
redis使用单线程和辅助线程实现。redis为了提高性能,有哪些机制和优化?
4.1 Redis高效机制
1 Redis是内存数据库,读写效率比磁盘数据盘高效10w倍数。
2 Redis通过一张哈希字典来管理所有的<key, value> 。哈希增删查改效率是O(1),对比磁盘数据库的B+树,即便都在内存中读写,效率也是非常高的。合理的扩容以及缩容机以及通过渐进式rehash保证效率不会突然降低
3 Redis针对不同的value提供了多种不同的数据类型,string,list,set,zset,hash。用户针对不同的场景可以使用合适的数据类型来大大提高读写效率,相同的数据类型根据数量大小也有不同的存储方案。
4 Redis通过一个主线程和若干辅助线程实现,只有少量锁和线程切换的开销。只有主线程才能读写数据,保证数据的安全。
5 Redis 通过 epoll + reactor ET 高效网络模型来支持网络数据的读写,对比MySQL的多线程和select,效率明显更高
4.2 Redis优化
redis在长时间使用中,针对自己的缺点也有很多优化。
1 辅助线程实现网络数据读写,通过IO线程池来进行网络数据的读写,主线程只负责命令处理和数据操作。主线程的效率更高
2 磁盘IO,free内存,close 大文件交给其他线程/进程来处理。
3 redis针对value的数据类型和数据量也在不断地优化。比如跳表/压缩列表/list等。。
4 分治处理数据,比如rehahs有定时rehahs也有增删查改时候rehash