1、数据结构
string:int和sds,int针对数字存储,sds针对字符串、浮点,根据长度的不同,存在不同的HDR上
根据数据的大小不一样,实际上是声明不同的类型
因为要尽量节省内存空间,提升查询速度
list :默认压缩列表:支持不一样的数据类型,连续的内存空间,但不支持随机访问,毕竟文件大小变了
但假如元素较大的时候,就送双向链表
双向链表,通常的操作是向列表俩端添加元素或获得列表的某个片段,在列表两端的操作快
quickList是链表,节点是zipList 压缩
可用于阻塞队列,即消息队列,左边push,右边get
hash 字典: ziplist--->hashtable,类似java中的hashmap,但value只能是string
存储量较小的时候,也默认使用压缩list
set 集合:用的hashtable,value为空,O(1)
存储量较小的时候,也默认使用压缩list
无序
zset-有序集合:存储量较小的时候,也默认使用压缩list
根据score排序
基于链表的二分查找
跳表:类似于多级索引的链表,已空间换时间的思路。其插入、删除、查询都是O(logn)
但极端情况下,可能会退化成链表,需要平衡索引
skipList + hashtable,这个跳跃表类似currenthashmap,进行了分段处理,有路由,也类似:kafka的稀疏日志
2、原理分析
-
内存回收
-
过期时间设置
expire、setNx()
消极:get时,删除
积极:定时任务,定时轮训随机部分key删除
-
redis内存满了咋办
-
淘汰策略
-
最近最少使用 LRU
-
-
-
redis备份机制
--https://www.cnblogs.com/zhenghongxin/p/8669913.html
- RDB 快照
- 存储的数据,直接从内存中复制过来的
- 压缩
- 存储的数据,直接从内存中复制过来的
- AOF 日志
- 存储的是数据执行的日志,较大
- every second
- alway
- 文件会很大,例如100万次,累加记录
- 压缩
- 存储的是数据执行的日志,较大
分布式
- 1、哨兵模式(M-S 主备模式,只有master提供查询,slave只提供备份):高可用
- 发现其他
- 哨兵相互监控,通过队列订阅发现其他哨兵
- 2、 cluster模式(无中心化的分片集群)
发布、订阅
为啥单进程:
无需线程切换,使用的IO多路复用,异步非阻塞
同步阻塞 同步非阻塞 异步阻塞 异步非阻塞
同步:用户空间和内核空间的交互
阻塞:可以立即返回
- 1、内存
- 2、同步非阻塞的IO,多路复用
- 多个客户端(TCP)连接,多个socket或channel
- 复用服务端一个或多个线程
- 内核帮助应用程序监视IO,数据是否准备好,是否OK ,一旦某个任务OK ,立马通知客户连接
- 内核空间、用户空间
- 把多个连接放到队列里面
- 客户端无需一直阻塞,实现高性能
- 3、单线程,(线程创建、销毁、上下文切换、线程竞争)
- CPU不是瓶颈,内存才是
- redis6.0的多线程,其实是多线程的IO
- 针对的后台线程
- 但处理请求依然是单线程,
- 监听io状态的变成多线程
分片:HASH槽的概念
https://blog.csdn.net/tianyeshiye/article/details/79600014
面试:https://blog.csdn.net/ThinkWon/article/details/103522351
Redis的一些开发规范和建议(https://mp.weixin.qq.com/s/SGOyGGfA6GOzxwD5S91hLw)
1.冷热数据分离,不要将所有数据全部都放到Redis中
虽然Redis支持持久化,但是Redis的数据存储全部都是在内存中的,成本昂贵。建议根据业务只将高频热数据存储到Redis中【QPS大于5000】,对于低频冷数据可以使用MySQL/ElasticSearch/MongoDB等基于磁盘的存储方式,不仅节省内存成本,而且数据量小在操作时速度更快、效率更高!
2.不同的业务数据要分开存储
不要将不相关的业务数据都放到一个Redis实例中,建议新业务申请新的单独实例。因为Redis为单线程处理,独立存储会减少不同业务相互操作的影响,提高请求响应速度;同时也避免单个实例内存数据量膨胀过大,在出现异常情况时可以更快恢复服务! 在实际的使用过程中,redis最大的瓶颈一般是CPU,由于它是单线程作业所以很容易跑满一个逻辑CPU,可以使用redis代理或者是分布式方案来提升redis的CPU使用率。
3.存储的Key一定要设置超时时间
如果应用将Redis定位为缓存Cache使用,对于存放的Key一定要设置超时时间!因为若不设置,这些Key会一直占用内存不释放,造成极大的浪费,而且随着时间的推移会导致内存占用越来越大,直到达到服务器内存上限!另外Key的超时长短要根据业务综合评估,而不是越长越好!
4.对于必须要存储的大文本数据一定要压缩后存储
对于大文本【+超过500字节】写入到Redis时,一定要压缩后存储!大文本数据存入Redis,除了带来极大的内存占用外,在访问量高时,很容易就会将网卡流量占满,进而造成整个服务器上的所有服务不可用,并引发雪崩效应,造成各个系统瘫痪!
5.线上Redis禁止使用Keys正则匹配操作
Redis是单线程处理,在线上KEY数量较多时,操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求,而且在高QPS情况下会直接造成Redis服务崩溃!如果有类似需求,请使用scan命令代替!
6.可靠的消息队列服务
Redis List经常被用于消息队列服务。假设消费者程序在从队列中取出消息后立刻崩溃,但由于该消息已经被取出且没有被正常处理,那么可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态不一致等现象发生。
为了避免这种情况,Redis提供了RPOPLPUSH命令,消费者程序会原子性的从主消息队列中取出消息并将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。
7.谨慎全量操作Hash、Set等集合结构
在使用HASH结构存储对象属性时,开始只有有限的十几个field,往往使用HGETALL获取所有成员,效率也很高,但是随着业务发展,会将field扩张到上百个甚至几百个,此时还使用HGETALL会出现效率急剧下降、网卡频繁打满等问题【时间复杂度O(N)】,此时建议根据业务拆分为多个Hash结构;或者如果大部分都是获取所有属性的操作,可以将所有属性序列化为一个STRING类型存储!同样在使用SMEMBERS操作SET结构类型时也是相同的情况!
8.根据业务场景合理使用不同的数据结构类型
目前Redis支持的数据库结构类型较多:字符串(String),哈希(Hash),列表(List),集合(Set),有序集合(Sorted Set), Bitmap, HyperLogLog和地理空间索引(geospatial)等,需要根据业务场景选择合适的类型。
常见的如:String可以用作普通的K-V、计数类;Hash可以用作对象如商品、经纪人等,包含较多属性的信息;List可以用作消息队列、粉丝/关注列表等;Set可以用于推荐;Sorted Set可以用于排行榜等!
9.命名规范
虽然说Redis支持多个数据库(默认32个,可以配置更多),但是除了默认的0号库以外,其它的都需要通过一个额外请求才能使用。所以用前缀作为命名空间可能会更明智一点。
另外,在使用前缀作为命名空间区隔不同key的时候,最好在程序中使用全局配置来实现,直接在代码里写前缀的做法要严格避免,这样可维护性实在太差了。
如:系统名:业务名:业务数据:其他
但是注意,key的名称不要过长,尽量清晰明了,容易理解,需要自己衡量
10.线上禁止使用monitor命令
禁止生产环境使用monitor命令,monitor命令在高并发条件下,会存在内存暴增和影响Redis性能的隐患
11.禁止大string
核心集群禁用1mb的string大key(虽然redis支持512MB大小的string),如果1mb的key每秒重复写入10次,就会导致写入网络IO达10MB;
12.redis容量
单实例的内存大小不建议过大,建议在10~20GB以内。
redis实例包含的键个数建议控制在1kw内,单实例键个数过大,可能导致过期键的回收不及时。
13 可靠性
需要定时监控redis的健康情况:使用各种redis健康监控工具,实在不行可以定时返回redis 的 info信息。
客户端连接尽量使用连接池(长链接和自动重连)