缓存数据库Redis之四:单线程下的一些事
Posted 云来云去-起飞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了缓存数据库Redis之四:单线程下的一些事相关的知识,希望对你有一定的参考价值。
目录
一、Redis 6版本之前的单线程模型
1.1.模型版本描述
Redis单线程是指获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理,这个主线程就是我们平时说的"单线程",当然其他清理脏数据、无用连接的释放、LRU淘汰策略等等也是有其他线程在处理的,因此本质上也是多线程的,那么今天我们主要去研究socket 读到socket写的单线程。
1.2.为何不是多线程
- 通常瓶颈不在 CPU,而是在内存和网络IO;
- 多线程会带来线程不安全的情况;
- 多线程可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗;
- 单线程降低了Redis内部实现复杂度;
- hash的惰性rehash,lpush等线程不安全的命令可以无锁执行;
二、Redis 6 引入多线程性
既然单进程的有如此多的好处,为什么还要引入多线程性?
为了解决这个问题, Redis6.0 引入的 “多线程” 机制就是对于该瓶颈的优化。核心思路是,将主线程的 IO 读写任务拆分出来给一组独立的线程执行,使得多个 socket 的读写可以并行化。
Redis 抽象了一套 AE 事件模型,将 IO 事件和时间事件融入一起,同时借助多路复用机制(linux上用epoll) 的回调特性,使得 IO 读写都是非阻塞的,实现高性能的网络处理能力。加上 Redis 基于内存的数据处理,这就是 “单线程,但却高性能” 的核心原因。
以上的图是官方的一张IO复用的模型图,大家可以看到,在下半部分,当 socket 中有数据时,Redis 会通过系统调用将数据从内核态拷贝到用户态,供 Redis 解析用。这个拷贝过程是阻塞的,术语称作 “同步 IO”,数据量越大拷贝的延迟越高,时间消耗也越大,糟糕的是这些操作都是单线程处理的。
三、多线程的解析及建议
3.1.建议
官方建议:至少4核的机器才开启IO多线程,并且除非真的遇到了性能瓶颈,否则不建议开启此配置 ,且配置的线程数少于机器总线程数,如果有4核建议开启2,3个线程,如果有8核建议开6线程。 线程并不是越多越好,多于8个线程意义不大。
3.2.测试命令
./redis-benchmark -h localhost -p 6380 --user default -a zzz123123 -t set,get -n 1000000 -r 1000000 --threads 3 -d 128 -c 200 -q
3.3.多线程解析
刚才提到IO多线程只是在网络数据的读写上是多线程了,具体流程如下:
流程:
- 主线程获取 socket 放入等待列表
- 将 socket 分配给各个 IO 线程(并不会等列表满)
- 主线程阻塞等待 IO 线程读取 socket 完毕
- 主线程以单线程执行命令 (如果命令没有接收完毕,会等 IO 下次继续)
- 主线程阻塞等待 IO 线程将数据回写 socket 完毕(一次没写完,会等下次再写)
- 解除绑定,清空等待队列
注:
- IO 线程要么同时在读 socket,要么同时在写,不会同时读或写;
- IO 线程只负责读写 socket 解析命令,不负责执行命令,由主线程串行执行命令;
- IO 线程数可配置,默认为 1;
- 上面的过程是完全无锁的,因为在 IO 线程处理的时主线程会等待全部的 IO 线程完成,所以不会出现 data race 的场景。
四、Redis6.0与Memcached多线程模型对比
- 相同点:都采用了 master线程-worker 线程的模型
- 不同点:Memcached 执行主逻辑也是在 worker 线程里,模型更加简单,实现了真正的线程隔离,符合我们对线程隔离的常规理解。而 Redis 把处理逻辑交还给 master 线程,虽然一定程度上增加了模型复杂度,但也解决了线程并发安全等问题。
以上是关于缓存数据库Redis之四:单线程下的一些事的主要内容,如果未能解决你的问题,请参考以下文章