Redis知识点整理

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis基础

1、Redis的特性:

1)快:

  • 基于内存,操作不需要与磁盘交互
  • 命令执行单线程
  • 多路复用
  • k-v 底层也有很多数据结构支持 比如跳跃表(空间换时间)

2)高可用

  • 持久化
  • 主从、集群

3)支持很多种数据类型

4)支持多种语言客户端


2、Redis的五种数据结构:

1)string(底层结构:sds):

image.png

适用场景:

1.session过期机制、缓存

2.分布式锁:Redis单线程

3.分布式ID:incr

2)hash(底层结构:哈希表):

image.png

适用场景:

1、重入锁

2、购物车

3)list(底层结构:linkedlist或者ziplist(3.0以前);quicklist(3.2及以后)):

linkedlist:

image.png

ziplist:


适用场景:

1、消息队列(有丢失数据的风险)

2、浏览记录

4)set:

补充:如果元素都是整数,底层使用的是整数集合

使用场景:

1、抽奖

5)zset(底层结构:skiplist(跳表))

使用场景:

1、定时任务

2、排行榜

3、Redis面试题

1、有一个100M的数据,可以放到redis中吗?

可以放,但是不建议。数据量太大,查询会有io开销,因为Redis查询时单线程的,会阻塞到其它查询命令。


2、Redis的过期策略

主动删除:定时删除(内存友好、CPU不友好)、定期删除(折中方案)

主动删除:被动过期

1)被动过期(即拿的时候发现过期):对内存不友好,对CPU友好

2)定期删除:

1、只需要找到设置了过期时间的key

2、也不是一次性全部拿到设置过期时间的,根据hash桶的维度,拿到20(可配)个值为止:

假如第一个hash桶是30,拿30

假如第一个hash桶是18,第二个hash桶是100,拿118

3、删掉拿到的值里面过期的数据,如果删除比例超过10%,继续执行2,3步

4、最多只会拿16次

补充:多久执行一次?

1秒执行10次,也就是100ms执行一次


3、Redis的淘汰策略(正常的数据进行清理,内存满了)

wecom-temp-20fe8080d73ee760289b319caef3aea9.png

1)LRU(Least Recently Used):最近最少使用,操作时间离当前越远的约容易淘汰

1.volatile(设置了过期时间的)

2.allkeys(所有key)


1、键值对lru时钟值的初始化:

Redis结构都是key - value,其中value结构:

typedefstructredisObject {
unsignedtype:4; // 类型:string、list。。。unsignedencoding:4; // 底层结构:quicklist、ziplist。。。unsignedlru:LRU_BITS; /* LRU time (relative to global lru_clock) or* LFU data (least significant 8 bits frequency* and most significant 16 bits access time). */// lru:代表这个redisObject被操作的// 时间(秒单位)的最后24bitintrefcount;
void*ptr;  // 指向真正数据地址的指针} robj;

创建RedisObject时,会给这个对象加上lru属性

参数lru解析:

maxmemory-policy:

为lru时:

被操作的时间(秒单位)的最后24bit

lfu:

前16 bits access time:被操作的时间(分单位)的最后16bit

后8 bits frequency:操作次数的8bit

当后续这个对象被访问时,lru属性就会被更新


2、lru算法何时执行?

每次redis-service执行操作命令时:不超时并且没有载入数据时执行


3、lru的具体执行:

1)判断当前内存使用情况

配置文件设置了maxmemory,判断内存使用是否大于maxmemory,当大于时会while循环进行淘汰策略


2)更新待淘汰的候选键值对集合

从哈希表中随机取出一定数量(默认是5个)的key,注意如果maxmemory_policy为allkeys_lru则取全局key,否则取设置过期时间的key


3)选择被淘汰的键值对并删除:找到符合淘汰的key进行删除,每次删除数组中最久未被操作过的key

#define LRU_BITS 24#define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */unsignedlonglongestimateObjectIdleTime(robj*o) {
unsignedlonglonglruclock=LRU_CLOCK(); // 当前时间的最后24位bit/*如果当前时间大于等于redisObject.lru,返回当前时间减去lru的时间差值如果小于,返回当前时间加上24bit减去lru的时间差值*/if (lruclock>=o->lru) {
return (lruclock-o->lru) *LRU_CLOCK_RESOLUTION;
    } else {
return (lruclock+ (LRU_CLOCK_MAX-o->lru)) *LRU_CLOCK_RESOLUTION;
    }
}

总结:每个RedisObjective-C(redis对象)会存储lru(保存最近操作的时间),redis进行淘汰时,会每次取5个,将操作时间越久的淘汰了。


2)LFU(least frequently used):最不频繁使用

1.volatile(设置了过期时间的)

2.allkeys(所有key)

1、键值对的初始化:image.pnglru变量存储:

前16 bits access time:被操作的时间(分单位)的最后16bit

后8 bits frequency:操作次数的8bit


2、key更新操作

第一步,根据距离上次访问的时长,衰减访问次数

/* Update LFU when an object is accessed.* Firstly, decrement the counter if the decrement time is reached.* Then logarithmically increment the counter, and update the access time. */voidupdateLFU(robj*val) {
// 首先,递减计数器unsignedlongcounter=LFUDecrAndReturn(val);
// 然后以logN级别递增计数器,并更新访问次数。counter=LFULogIncr(counter);
val->lru= (LFUGetTimeInMinutes()<<8) |counter;
}

第二步,根据当前访问更新访问次数。

unsignedlongLFUTimeElapsed(unsignedlongldt) {
unsignedlongnow=LFUGetTimeInMinutes();
if (now>=ldt) returnnow-ldt;
return65535-ldt+now;
}  // 返回多久没有访问过unsignedlongLFUDecrAndReturn(robj*o) {
unsignedlongldt=o->lru>>8;
unsignedlongcounter=o->lru&255;
unsignedlongnum_periods=server.lfu_decay_time?LFUTimeElapsed(ldt) /server.lfu_decay_time : 0;
if (num_periods)
counter= (num_periods>counter) ?0 : counter-num_periods;
returncounter;
} // 原本次数约大越不容易被更新


3、lfu淘汰流程:

1)第一步,调用 getMaxmemoryState 函数计算待释放的内存空间;


2)第二步,调用 evictionPoolPopulate 函数随机采样键值对,并插入到待淘汰集合 EvictionPoolLRU 中;

具体来说,在实现 LRU 算法时,待淘汰候选键值对集合 EvictionPoolLRU 中的每个元素,都使用成员变量 idle 来记录它距离上次访问的空闲时间。

而当实现 LFU 算法时,因为 LFU 算法会对访问次数进行衰减和按概率增加,所以,它是使用访问次数来近似表示访问频率的。相应的,LFU 算法其实是用 255 减去键值对的访问次数,这样来计算 EvictionPoolLRU 数组中每个元素的 idle 变量值的。而且,在计算 idle 变量值前,LFU 算法还会调用 LFUDecrAndReturn 函数,衰减一次键值对的访问次数,以便能更加准确地反映实际选择待淘汰数据时,数据的访问频率。

if (server.maxmemory_policy&MAXMEMORY_FLAG_LRU) {
idle=estimateObjectIdleTime(o);
 } elseif (server.maxmemory_policy&MAXMEMORY_FLAG_LFU) {
idle=255-LFUDecrAndReturn(o);
}


3)第三步,遍历待淘汰集合 EvictionPoolLRU,选择实际被淘汰数据,并删除。


3)random:随机

4)ttl:最接近过期

5)novication(默认的):不能写,能读


4、redis持久化

1)rdb(redis database):默认的持久化方式,快照(将某个时间点的redis信息保存到jump.db二进制文件中),保存数据库键值对

1、触发机制:

1)自动触发

save9001// 900s内至少有1个key被修改、添加save30010save6010000

2)关闭redis时触发

3)flushall

4)手动

  • save 阻塞操作
  • bgsave 后台非阻塞操作


缺点:可靠性很低,会丢失很多数据

有点:恢复快


2)aof(Append Only File),保存Redis执行的写命令

默认是关闭的,开启之后,默认会用aof去备份,rdb还是会同时存在

redis提供了跟aof文件交互的配置

#define AOF_FSYNC_NO 0 //交给操作系统同步#define AOF_FSYNC_ALWAYS 1 //每次都会交互#define AOF_FSYNC_EVERYSEC 2 // 每秒同步一次,优先保证性能

优点:数据可靠性更强

缺点:性能没有rdb快


3)重写机制

  • 4.0版本之前:比较指令
lpushtestalpushtestbcdlpop----------重写机制lpushtestabc

缺点:效率低


  • 4.0以后:RDB和AOF混合模式:重写的时候,会将aof文件压缩成rdb(二进制)格式的文件,后面又是命令的形式


  • 触发重写机制
createIntConfig(
"auto-aof-rewrite-percentage", NULL, MODIFIABLE_CONFIG, 
0, INT_MAX, server.aof_rewrite_perc, 
100, INTEGER_CONFIG, NULL, NULL) //下一次触发的条件是:aof文件的大小跟上次压缩过之后的大小进行比较,如果超过了一倍了,就会进行重写createOffTConfig(
"auto-aof-rewrite-min-size", NULL, MODIFIABLE_CONFIG, 
0, LLONG_MAX, server.aof_rewrite_min_size, 
64*1024*1024, MEMORY_CONFIG, NULL, NULL)//aof文件必须打到64MB才能重写


5、主从复制

1)主从复制的过程

  1. 建立连接:主从互相保存对方的IP、端口、偏移量等信息
  2. 全量同步:从库发送指令到主库,主收到后,通过BGSAVE备份RDB文件发送给从库,从库删除自己的数据,将RDB数据同步过来
  3. 增量同步:全量数据同步过程中,主的增量数据会先放到缓存中,等从库同步完全量数据后,主库会把缓存和后面的增量命令同步给从库


2)主从数据丢失的场景?

1、自动切换:哨兵、cluster

  1. 主库每次的命令都会给到从库,假如没给到从库就挂了,这部分数据只在主库有
  2. 这个时候会重新去从节点找一个从库顶替主库,产生了新的主节点
  3. 如果挂了的旧主节点重启了,旧主节点就会变成新的从节点
  4. 数据同步,旧主节点的数据被删除,这部分数据丢失

2、脑裂:哨兵和主节点失去连接,这个时候可能会出现双节点,也会出现数据丢失


6、cluster集群

最低配置:3主3从

分片思想:保证高可用

分片的一般实现:

1、取模,缺点:每次改动要处理所有数据

2、一致性hash:会导致数据不均匀问题

wecom-temp-2e6f36bb488a2efee4ff405485d91ace.png

redis的解决方法:使用虚拟槽(0-16383个虚拟槽),解决数据不均匀问题。

虚拟节点跟真实节点有对应关系,例如:

节点1: 0-5460的虚拟槽

节点2: 5461-10922的虚拟槽

节点3: 10923-16383的虚拟槽

操作key时,根据key取模,决定放在哪个节点上


7、redission分布式锁

lua + hash(可重入):看门狗

特点:

1、防止死锁

2、防止业务没有执行完,锁释放。你去设置时间的话,就没有看门狗了,也就是等于-1的时候才有看门狗


8、场景分析

缓存雪崩、缓存穿透、缓存击穿

缓存雪崩:redis大量的热点数据同时过期,所有请求打到DB,解决:过期时间不一致

缓存穿透:一条数据过期,但是过期的时候发生大量的并发,并发请求打到DB,解决:加锁

缓存穿透:redis没有,DB没有,但是前端大量的请求过来,会打到DB。解决:布隆过滤器。布隆过滤器:bitmap+hash  没法判断一定存在,但是能判断一定不存在。存在一定的误判率;减少误判率:1、多次hash;2、扩容值:会带来性能消耗


9、Redis跟DB的数据一致性问题

(1)造成原因:在多并发的场景下,操作不是原子性的。例如:

线程A请求数据,没有缓存,访问DB,更新缓存

线程A没有同步完,线程B将DB改成2,删除缓存

线程A继续同步,缓存的数据是1,数据不一致

(2)解决:延迟双删(并不是最好的办法),最好的办法是别管,业务场景下的话就舍弃一个,Redis和DB只能一个为主

10、面试八股文

https://zhuanlan.zhihu.com/p/419224864

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
运维 NoSQL 测试技术
从一个事故中理解Redis(几乎)所有知识点
作者从一个事故中总结了Redis(几乎)所有的知识点,供大家学习。
|
6月前
|
缓存 NoSQL 定位技术
深入探索Redis:面试中必须掌握的关键知识点
深入探索Redis:面试中必须掌握的关键知识点
|
存储 NoSQL 测试技术
关于redis涉及的知识点,C语言如何操作redis
关于redis涉及的知识点,C语言如何操作redis
|
存储 SpringCloudAlibaba 运维
Redis高级知识点总结
在 Redis 6.0 中,非常受关注的第一个新特性就是多线程。这是因为,Redis 一直被大家熟知的就是它的单线程架构,虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF 重写),但是,**从网络 IO 处理到实际的读写命令处理,都是由单个线程完成的**。随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 IO 的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度
259 0
Redis高级知识点总结
|
存储 缓存 NoSQL
redis知识点
redis 知识点
83 0
|
缓存 监控 NoSQL
【Redis】Redis知识点阶段性总结 2
【Redis】Redis知识点阶段性总结
59 0
|
NoSQL Linux Redis
【Redis】Redis知识点阶段性总结 1
【Redis】Redis知识点阶段性总结
95 0
|
存储 缓存 监控
全新Redis6全部知识点,零基础入门3
全新Redis6全部知识点,零基础入门
12226 1
|
存储 缓存 NoSQL
全新Redis6全部知识点,零基础入门2
全新Redis6全部知识点,零基础入门
|
存储 缓存 JSON
全新Redis6全部知识点,零基础入门1
全新Redis6全部知识点,零基础入门
下一篇
无影云桌面