文章目录
- 1. redis是单线程的吗?为什么这么快?
- 2. Redis 单线程如何处理那么多的并发客户端连接?
- 3. redis管道的作用
- 4. redis中Lua脚本的作用
- 5 单台redis可以抗多少并发?
1. redis是单线程的吗?为什么这么快?
答:redis并不是完全单线程。
redis6.0以前:
我们常说的redis单线程主要是指Redis的网络IO 和键值对读写是由一个线程来完成的,这也是redis对外提供的存储服务的主要流程。但 Redis 的其他功能,比如持久化、key过期异步删除、集群数据同步等,其实是由额外的线程执行的!
命令执行流程如下:
redis6.0以后:
redis6.0提供了多线程读写io,单最终执行命令的线程依然是单线程的,这样不仅避免了多线程的数据竞争关系,还使io读写更高效!!注意:多线程无法保证命令的执行顺序!!
可以通过如下参数配置多线程io-threads 4 // 4代表:一个main线程,负责io读写、命令执行;三个io线程,只负责io写
io-threads-do-read yes //开启这个得话,读写都是多线程,但命令执行都是单线程 命令读写流程如下:
至于redis单线程快的原因是:redis把所有的数据都存储在内存中,所有运算都是内存级别的运算,再者单线程避免了线程切换的性能损耗问题,所以redis是比较快的。正因为 Redis 是单线程,所以要小心使用 Redis 指令,对于那些耗时的指令(比如 keys *),一定要谨慎使用,一不小心就可能会导致 Redis 卡顿。
2. Redis 单线程如何处理那么多的并发客户端连接?
Redis的IO多路复用:redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
3. redis管道的作用
管道:客户端可以一次性发送多个请求命令而不用等待服务器的响应,待所有命令都发送完后再一次性读取服务的响应,这样可以极大的降低多条命令执行的网络传输开销!管道执行多条命令的网络开销实际上只相当于一次命令执行的网络开销。
注意:管道并不具备所有命令一起成功的含义,也就是说管道没有原子性!
管道示例:Pipeline pl = jedis.pipelined();
for (int i = 0; i < 10; i++) {
pl.incr("pipelineKey");
pl.set("zhuge" + i, "zhuge");
//模拟管道报错
// pl.setbit("zhuge", -1, true);
}
List<Object> results = pl.syncAndReturnAll();
System.out.println(results); 4. redis中Lua脚本的作用
Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。使用脚本的好处如下:
1、减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使用脚本,减少了网络往返时延。这点跟管道类似。
2、原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。管道不是原子的,不过redis的批量操作命令(类似mset)是原子的。
3、替代redis的事务功能:redis自带的事务功能很鸡肋,报错不支持回滚,而redis的lua脚本几乎实现了常规的事务功能,支持报错回滚操作,官方推荐如果要使用redis的事务功能可以用redis lua替代。
注意: 由于Lua脚本是一大批命令在redis中当做一条命令执行,占用着主线程,所以不要在Lua脚本中出现死循环和耗时的运算,否则redis会阻塞,将不接受其他的命令, 所以使用时要注意不能出现死循环、耗时的运算。redis是单进程、单线程执行脚本!
5 单台redis可以抗多少并发?
理论上单台可以抗10w并发,在实际生产中撑死七八万并发!在这种场景下就需要使用redis集群来应对高并发了!但集群不一定完美解决此问题,原因如下:
①:如果多线程操作的是多目标数据,可以使用集群部署,集群完美分摊压力!
②:如果多线程操作的是单目标数据,比如抢购某一件商品库存,由于这件商品的key经过hash运算分配的槽位还是在某一台机器上,所以集群无法分摊并发压力,这个场景的解决方案是:拆分商品的key,让其落在不同的机器上!这样其他机器就能分担压力了!
|