为什么快?
纯内存访问
非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现,处理了epoll的连接、读写、关闭转化为事件,不在网络I/O上浪费过多时间
单线程避免了线程切换和竞态发生的消耗
多路复用:
select
有I/O流事件来时,无法知道是什么流发生只能通过轮询的方式找出数据
时间复杂度:O(n)
存储:数组 (有最大数限制)
最大连接数:单个进程所能打开的最大连接数有FD_SETSIZE定义,一般32位是3232 64位是3264
连接数过大时 :会因为连接数增加带来I/O效率问题
poll
和select 没有什么区别,但是他没有最大数限制
时间复杂度:O(n)
存储:链表(没有最大数限制)
最大连接数:没有限制
连接数过大时:会因为连接数增加带来I/O效率问题
epoll
可以理解为event epoll,所以不同于上面的轮询,epoll 会把I/O流事件通知我们所以时间复杂度降低到O(1)
时间复杂度:O(1)
连接数过大时: 因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。
总结:连接数少时 select,poll比epoll性能好因为epoll的callback也会造成消耗
最大并发连接
Redis 2.6 和更新版本中,这个限制是动态的:默认情况下它设置为10000个客户端,除非redis.conf中maxclient的值修改了
数据类型
Strings 字符串
Lists 列表,按插入顺序排序
Sets 无序集合
Hashes 哈希 字符串和字符串值之间的映射
Sort Sets 排序集
场景:排行榜存储
Bitmaps 位图
bitmaps 占用1位字符
用途:在特定场景使用bitmaps能大大节约内存使用
例如:1亿用户,有5000万活跃用户时 set集合和bitmaps内存占用量有明显差距
数据类型 | 单个占用空间 | 存储总用户量 | 内存量 |
集合类型 | 64位 | 50 000 000 | 64 x 50 000 000=400MB |
Bitmaps | 1位 | 100 000 000 | 1 x 100 000 000=12.5MB |
而如果用户量只有10万的时候则bitmaps的内存量就不如set
因为一亿用户bitmap还是要用12.5MB去存储 而集合类型则只用存储10万的用户量的空间
数据类型 | 单个占用空间 | 存储总用户量 | 内存量 |
集合类型 | 64位 | 100 000 | 64 x 100 000=800KB |
Bitmaps | 1位 | 100 000 000 | 1 x 100 000 000=12.5MB |
场景:用户活跃数据多时记录用户当天是否访问
HyperLogLogs
用途:能极大的减少内存的存储,但是存储的准确率不是百分之百 ,redis给出的数字是81%,这就可以做很多事情比如可以做一些不需要那么准确的事情 例如布隆过滤器
数据类型 | 1天 | 1个月 |
集合类型 | 80M | 2.4G |
Bitmaps | 15K | 450K |
Streams 流
Geospatial indexes 地理空间索引
内存回收策略
惰性删除
方法访问时将过期删除 例如:调用redis get命令时
定时任务删除
每10s执行定时任务回收过期key
used_memory>maxmemory触发内存驱逐策略(驱逐策略选择noeviction除外)
数据存储驱逐策略
- noeviction:达到内存限制时不保存新值。当数据库使用复制时,这适用于主数据库
- allkeys-lru:保留最近使用的密钥;删除最近最少使用 (LRU) 键
- allkeys-lfu : 保存常用键;删除最不常用 (LFU) 键
- volatile-lru:删除最近最少使用的键,
expire
字段设置为true
。 - volatile-lfu
expire
:删除字段设置为的最不常用键true
。 - allkeys-random:随机删除键为添加的新数据腾出空间。
- volatile-random:随机删除
expire
字段设置为 的键true
。 - volatile-ttl:删除
expire
字段设置为true
和最短剩余生存时间(TTL)值的键。
驱逐策略选择:
根据使用率来判断,请选择allkeys-lru策略
如果你缓存的对象使用不同的TTL值,则使用volatile-ttl策略
注:expire
为键设置值会消耗内存
持久化
RDB
当前进程数据生成快照保存到硬盘
持久化命令:
save:会阻塞redis服务器,生产不建议使用
bgsave:redis进程fork操作创建子线程 阻塞一般在fork阶段 这个时间很短,创建的子线程去做生成rdb文件(fork操作属于重量级所以无法频繁操作)
缺点:redis停止工作后丢失的数据会较多,因为RDB的保存点一般会每五分钟或更长时间创建一次 RDB 快照,所以就会丢失一部分最新的数据
RDB 需要经常 fork() 以便使用子进程在磁盘上持久化。如果数据集很大,fork() 可能会很耗时,并且如果数据集很大并且 CPU 性能不是很好,可能会导致 Redis 停止为客户端服务几毫秒甚至一秒钟。AOF 也需要 fork() 但频率较低
优点:适合灾难恢复,适合备份
场景:可以用做备份,例如:30 天内每天保存一个 RDB 快照。这使您可以在发生灾难时轻松恢复不同版本的数据集
Redis复制的部分同步
AOF
是通过将命令先写入AOF缓冲,AOF缓冲在异步写入AOF文件
优点:有不同的 fsync 策略(如下缓冲策略)
AOF 日志是一个仅附加日志,因此不会出现寻道问题,也不会在断电时出现损坏问题。即使由于某种原因(磁盘已满或其他原因)日志以写一半的命令结束,redis-check-aof 工具也能够轻松修复它
当 AOF 变得太大时,Redis 能够在后台自动重写 AOF
缺点:AOF 文件通常比相同数据集的等效 RDB 文件大
redis<7.0
如果在重写期间有对数据库的写入,AOF 可能会使用大量内存(这些被缓冲在内存中并在最后写入新的 AOF)。
重写期间到达的所有写入命令都会写入磁盘两次。
Redis 可以在重写结束时冻结写入并将这些写入命令同步到新的 AOF 文件
缓冲策略
always:命令写入AOF缓冲后,直接调用fsync命令将缓冲里面的数据放入AOF文件,fsync完成后线程返回
everysec:命令写入AOF缓冲后,调用系统write操作,write完成后返回,fsync同步文件操作由专门线程每秒调用一次 默认
no:命令写入AOF缓冲后调用write操作,不对aof文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期最长30秒(无法保证持久化)
建议:使用everysec 宕机情况下最多只会丢失一秒的数据
场景:实时持久化