这里写目录标题
Redis
1、Redis 为什么这么快
- 因为它是内存数据库,所有操作都在内存上完成,内存的访问速度本身就很快。
- Redis 键值对是按一定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操作,所以说 Redis 能快速处理数据
- Redis 使用异步机制来执行一些并不关键的操作,不关键的操作一般就是不许要 Redis 返回数据结果的操作;像我们的删除操作;还有一些阻塞式的操作,比如:bigkey 删除,清空数据库都可以异步来执行
Redis 主线程在启动后,会创建三个子线程来负责执行删除操作、写AOF日志、文件的关闭;他会把接收到的操作封装成任务,然后放到一个任务队列里;拿着这个任务队列和子线程交互;比如说,我发出了一个删除操作,然后Redis会把这个操作封装成一个任务放到任务队列中,然后返回一个完成信息,但其实这个时候删除操作并没有完成,等到后台的子进程从任务队列中把这个任务取出来,才会开始执行删除操作;
补充(别读):
有两个阻塞是没有办法异步的:集合全量查询和聚合操作、从库加载 RDB 文件,有各自的技巧
- 集合全量查询和聚合操作:可以使用 SCAN 命令,分批读取数据,再在客户端进行聚合计算;
- 从库加载 RDB 文件:把主库的数据量大小控制在 2~4GB 左右,以保证 RDB 文件能以较快的速度加载。
2、Redis 是单线程的吗
Redis 的网络 IO 和键值对读写是由一个线程来完成的;
但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。
为什么用单线程?
一方面单线程的程序可以避免线程上下文切换带来的性能损耗;另一方面,单线程程序不存在临界资源,不会出现锁以及并发竞争的情况
为什么单线程能这么快?
- 基于内存操作;
- 定制化的数据结构,大部分命令的时间复杂度是O(1);
- 一方面单线程的程序可以避免线程上下文切换带来的性能损耗;另一方面,单线程程序不存在临界资源,不会出现锁以及并发竞争的情况;
- 采用了IO多路复用技术,可以高效的处理多个客户端的请求。
3、Redis 全局哈希
Redis 使用一个哈希表来保存所有键值对;每一个哈希桶位都保存了指向具体值的指针,不管值是 String,还是集合类型,哈希桶中的元素都是指向它们的指针;这样做的好处就是可以用 O(1) 的时间复杂度来快速查找到键值对
存在两个问题就是哈希表的冲突问题和 rehash 可能带来的操作阻塞
全局哈希表使用拉链法来解决哈希冲突;但是当哈希冲突剧烈的时候,哈希冲突链会过长,会影响我们查找效率,这个时候会有 rehash 操作,来增加数组的桶位,减少单个桶位的哈希冲突
Redis 的 rehash 操作是渐进式的,并且使用了两个全局哈希表来完成;当我们添加数据的时候,是往第一个哈希表添加,第二个哈希表只有当我们 rehash 的时候才会创建;rehash分为三步:
-
给第二个哈希表分配一个更大的空间
-
把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中;
第二步涉及大量的数据拷贝,如果一次性把哈希表 1 中的数据都迁移完,会造成 Redis 线程阻塞,无法服务其他请求;Redis的策略就是把一次性大量拷贝的开销,分摊到了多次处理请求的过程中
第二步拷贝数据时,Redis 仍然正常处理客户端请求,每处理一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝到哈希表 2 中;等处理下一个请求时,再顺带拷贝哈希表 1 中的下一个索引位