网站建设柒首先金手指8,组织建设 湖南省直工会网站,即将倒闭的设计院,百度快照优化公司上篇文章#xff1a;Reids命令原理与应用2 - Redis网络层与优化#xff0c;pipeline#xff0c;发布订阅与事务-CSDN博客 个人代码仓库#xff1a;橘子真甜 (yzc-YZC) - Gitee.com橘子真甜 (yzc-YZC) - Gitee.com橘子真甜 (yzc-YZC) - Gitee.com 目录 一. Redis单线程与辅助…上篇文章Reids命令原理与应用2 - Redis网络层与优化pipeline发布订阅与事务-CSDN博客个人代码仓库橘子真甜 (yzc-YZC) - Gitee.com橘子真甜 (yzc-YZC) - Gitee.com橘子真甜 (yzc-YZC) - Gitee.com目录一. Redis单线程与辅助线程1.1 Redis主线程 redis-server1.2 辅助线程二 Redis线程设计原理2.1 单线程优点2.2 单线程缺陷2.3 Redis针对缺陷设计的辅助线程a IO密集型b cpu密集型三. Redis存储结结构3.1 Redis数据组织方式3.2 哈希字典3.3 哈希字典扩容缩容3.4 rehash渐进式哈希3.5 scan命令四. 总结4.1 Redis高效机制4.2 Redis优化一. Redis单线程与辅助线程redis是不是单线程redis对数据的操作是由主线程完成的不过为了提高redis效率redis还创建了很多辅助线程用于完成其他工作。这些线程不会影响数据库的安全我们可以通过调试的方式来查看redis创建的线程。sudo gdb /usr/local/bin/redis-server[yzcstudy 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_memory1 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 version7.4.7, bits64, commit00000000, modified1, pid517379, 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_waitGLIBC_2.3.2 () from /lib64/libpthread.so.0 3 Thread 0x7ffff57fe700 (LWP 517385) bio_aof 0x00007ffff74303fc in pthread_cond_waitGLIBC_2.3.2 () from /lib64/libpthread.so.0 4 Thread 0x7ffff4ffd700 (LWP 517386) bio_lazy_free 0x00007ffff74303fc in pthread_cond_waitGLIBC_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_waitGLIBC_2.3.2 () from /lib64/libpthread.so.0 9 Thread 0x7ffff23ff700 (LWP 517391) jemalloc_bg_thd 0x00007ffff74303fc in pthread_cond_waitGLIBC_2.3.2 () from /lib64/libpthread.so.0 (gdb)可以看到有很多个线程1.1 Redis主线程 redis-serverredis早期版本只有一个线程完成工作当前版本redis有很多线程。主线程负责监听网络连接命令处理数据读写操作lua脚本/事务处理。redis单线程指的是命令处理和数据操作在同一个线程1.2 辅助线程辅助线程作用bio_close_file异步关闭大文件bio_aof_async异步持久化刷盘bio_lazy_free异步惰性删除内存id_thd_numIO线程池负责网络数据的读写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密集型对于网络IOredis增加了网络IO线程池创建一个/多个IO线程负责网络IO。数据流程如下网络数据(监听数据除外) - 网络IO线程 - 主线程主线程进行命令处理并获取返回应答 - 网络IO线程将结果返回客户端这样就能不仅可以保证数据隔离性还能提高redis性能。对于磁盘IOredis有持久化不同的持久化的实现是不同的。aof持久化采用辅助线程方式进行异步刷盘rdb持久化采用fork子进程实现持久化b cpu密集型不同的数据量有不同的操作少量数据使用哈希表大量数据使用哈希表 跳表存储。当哈希表扩容时候主线程会进行渐进式哈希并有bio_lazy_free辅助线程帮助释放内存三. Redis存储结结构3.1 Redis数据组织方式redis通过一个哈希表哈希字典来组织所有数据。也就是通过一个哈希字典来管理所有的key, value键值对。不过这个value支持多种类型stringlistsetzsethash。redis有多个哈希字典默认15个通过 select 可以进行切换不同的字典。对于 value对象上篇文章详细说明了这里给出思维导图3.2 哈希字典查看源码可以看到哈希字典的各个字段。这里介绍一下各个字段的作用dictType *type;函数指针用于实现多态dictEntry **ht_table[2]; 两个哈希表long rehashidx; 重新哈希的下标int16_t pauserehash 是否需要暂停rehashsigned 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提供了多种不同的数据类型stringlistsetzsethash。用户针对不同的场景可以使用合适的数据类型来大大提高读写效率相同的数据类型根据数量大小也有不同的存储方案。4 Redis通过一个主线程和若干辅助线程实现只有少量锁和线程切换的开销。只有主线程才能读写数据保证数据的安全。5 Redis 通过 epoll reactor ET 高效网络模型来支持网络数据的读写对比MySQL的多线程和select效率明显更高4.2 Redis优化redis在长时间使用中针对自己的缺点也有很多优化。1 辅助线程实现网络数据读写通过IO线程池来进行网络数据的读写主线程只负责命令处理和数据操作。主线程的效率更高2 磁盘IOfree内存close 大文件交给其他线程/进程来处理。3 redis针对value的数据类型和数据量也在不断地优化。比如跳表/压缩列表/list等。。4 分治处理数据比如rehahs有定时rehahs也有增删查改时候rehash