引用
https://juejin.cn/post/6844903951502934030
https://blog.csdn.net/snakorse/article/details/78154402(些许过时)
https://blog.liexing.me/2019/12/28/from-ziplist-linkedlist-to-quicklist/
https://www.cnblogs.com/yangmingxianshen/p/8054094.html
https://blog.csdn.net/weixin_43935927/article/details/109498737
redis
- Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库,由C语言编写,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。
- 数据库使用磁盘,读写速度慢,redis使用内存,读写速度快
- redis是一种非关系行数据库(NoSQL),不用维护索引,在高IO环境下有天然优势
- 使用多路I/O复用模型,非阻塞IO;
- Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
redis编码方式
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
\#define OBJ_ENCODING_RAW 0 /* Raw representation */
\#define OBJ_ENCODING_INT 1 /* Encoded as integer */
\#define OBJ_ENCODING_HT 2 /* Encoded as hash table */
\#define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ // 已废弃
\#define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
\#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
\#define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
\#define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
\#define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
\#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
- embstr(embedded string) 嵌入式字符串:保存长度小于44字节的字符串(字符串与其对应的 redisObject 对象分配在 同一块连续的内存空间)
- raw 字符串:保存长度大于44字节的字符串(字符串与其redisObject 的 内存不再连续)
- int 整数:保存long 型的64位有符号整数 ( Redis 启动时会预先建立 10000 个分别存储 0~9999 的 redisObject 变量作为共享对象,set字符串的键值在 0~10000 之间的话,则可以直接指向共享对象)
- ziplist 压缩列表:链表(List),哈希(Hash),有序集合(Sorted Set)在成员较少,成员值较小的时候都会采用压缩列表(ZIPLIST)编码方式进行存储,Ziplist使用连续的内存数据块,其内存利用率很高,但增删改查效率较低
- ht(hashtable)哈希表:ht是Redis中存在最广泛的一种数据结构,集合对象()和有序结合对象()中都有使用,而且Redis所有的Key,Value都是存在db->dict这张字典中的。
- linkedlist 双链表:linkedlist在插入、删除元素时,元素数量、单个元素的长度对耗时影响小(耗时分布比较集中),但是空间利用率比较差,特别是数据规模较小时,空间利用率非常差
- quicklist 快速双链表:quicklist 每个节点的值都是使用ziplist 进行存储,quicklist结合了二者的优点,首先时间消耗上,数据规模对其影响小,其次是空间利用率,因为底层使用了ziplist,所以在小规模数据上空间表现也良好
- intset 整数集:当一个集合只包含整数值元素, 并且这个集合的元素数量不多时, Redis 就会使用整数集合作为集合键的底层实现
- skiplist 跳跃表:跳跃表以有序的方式在层次化的链表中保存元素,在一般情况下情况下效率和平衡树媲美, 比起平衡树来说,跳跃表的实现要简单直观得多
Redis数据类型
Redis内部使用一个redisObject对象来表示所有的key和value
String:字符串
- String 类型的值最大能存储 512MB
- String 编码可以是 int,raw 或者 embstr
SET key value
GET key
value
String使用环境
- 对象缓存(保存Json字符串等)
- 简单分布式锁
- 计数器
- Web集群Session共享
Hash:哈希
- Hash 可以存储 2^32 -1 个键值对(40多亿)
- Hash 编码可以是ziplist(512以下),hashtable
HSET myhash field1 "Hello" field2 "World"
HGET myhash field1
"hello"
Hash使用环境
- 对象缓存(较String 占用更少空间)
- 模拟购物车
List:列表
- List 可存储 2^32 - 1 个元素 (40多亿)
- List 编码可以是ziplist(512以下),linkedlist/quicklist
- 3.2后使用quicklist
右插入
rpush key value1 value2
返回 2 当前列表长度
lrange list 0 - 1
value1
value2
List 使用环境
- 实现栈,队列,阻塞队列
- 订阅(消息队列)
Set:集合
- Set 可存储 2^32 - 1 个元素 (40多亿)
- Set 编码可以是intset(512以下整数),hashtable
sadd key value
1(成功1,失败0)
sadd key value
0(重复)
smembers key
"value"
Set使用环境
- 抽奖(随机出队)
- 点赞(不可重复)
- 互相关注(多个set取交集)
- 多店商品列表(多个set取并集)
ZSet:有序集合
- ZSet 可存储 2^32 - 1 个元素 (40多亿)
- ZSet 编码可以是ziplist(128以下),skiplist
zadd key 1 a 3 b 2 c
3 (长度)
zrange key 0 10 withscores
a
1
c
2
b
3
ZSet使用环境
- 热搜(多天亦可)
- 排行榜(更新)
Redis 高级数据结构
HyperLogLog
- Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
- Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。
什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
HyperLogLog使用
//添加
pfadd key value1
1 (成功)
pfadd key value2
1
pfadd key value2
0 (重复)
// 返回估算值
pfcount key
2
// 合并两个HyperLogLog
pfadd key1 value1
1
pfadd key1 value3
1
pfmerge newkey key key1
OK
pfcount newkey
3
pfcount key
2
HyperLogLog原理
对于一个输入的字符串,首先得到64位的hash值,用前14位来定位桶的位置(共有 2^14 ,即16384个桶)。后面50位即为伯努利过程,每个桶有6bit,记录第一次出现1的位置count,如果count>oldcount,就用count替换oldcount。
Geo(3.2增加)
geoadd 用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。
Geo 使用
//添加
geoadd key 13.12 38.11 sh
1
geoadd key 15.08 37.50 ca
1
//获取
geopos key sh
13.12
38.11
//两点距离(单位:m,km,ft(英尺),mi(英里))
geodist key sh ca m
1666274.15
//获取范围内坐标
georadius key 13 38 2 km
georadiusbymember key sh 2 km
//获取位置hash
geohash key sh
pub/sub 发布订阅
发送者 (pub) 发送消息,订阅者 (sub) 接收消息
使用
//订阅
subscribe key
//发布
publish key value
1
//接收方
1) "message"
2) "key"
3) "value"
Redis 常见问题
缓存雪崩
原因
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
解决
随机过期时间
缓存击穿
原因
一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。
解决
- 设置永不过期
- 添加互斥锁,一个线程获取时,其他线程等待一段时间
缓存穿透
原因
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
解决
- 使用布隆过滤器,请求拦截
- 找不到值时,缓存空结果