网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
I/O :网络 I/O
多路 :多个网络连接
复用:复用同一个线程。
IO多路复用其实就是一种同步 IO 模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出 cpu。
单线程模型
Redis是单线程模型的,而单线程避免了CPU不必要的上下文切换和竞争锁的消耗。也正因为是单线程,如果某个命令执行过长(如hgetall命令),会造成阻塞。
Redis是面向快速执行场景的数据库,所以要慎用如 smembers 和 lrange、hgetall 等命令。
Redis 6.0 引入了多线程提速,它的执行命令操作内存的仍然是个单线程。
- 虚拟内存机制
Redis直接自己构建了VM机制 ,不会像一般的系统会调用系统函数处理,会浪费一定的时间去移动和请求。
Redis的虚拟内存机制是啥呢?
虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过 VM 功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。
Redis支持的数据类型?
String(字符串)
格式: set key value、get key
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个键最大能存储512MB。
应用场景:共享session、分布式锁,计数器、限流。
内部编码有3种,int(8字节长整型)/embstr(小于等于39字节字符串)/raw(大于39个字节字符串)
C语言的字符串是char[]实现的,而Redis使用SDS(simple dynamic string) 封装,sds源码如下:
struct sdshdr{ unsigned int len; // 标记buf的长度 unsigned int free; //标记buf中未使用的元素个数 char buf[]; // 存放元素的坑 }
SDS 结构图如下:
Redis为什么选择SDS结构,而C语言原生的char[]不香吗?
举例其中一点,SDS中,O(1) 时间复杂度,就可以获取字符串长度;而 C 字符串,需要遍历整个字符串,时间复杂度为 O(n)
Hash(哈希)
格式: hset key field value 、hget key field
Redis hash 是一个键值 ( key=>value ) 对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
格式: lpush name value
在 key 对应 list 的头部添加字符串元素
格式: rpush name value
在 key 对应 list 的尾部添加字符串元素
格式: lrem name index
key 对应 list 中删除 count 个和 value 相同的元素
格式: llen name
返回 key 对应 list 的长度
Set(集合)
格式: sadd name value
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
zset (sorted set:有序集合)
格式: zadd name score value
zset 和 set 一样也是 string 类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
Redis为什么是单线程的?
- 代码更清晰,处理逻辑更简单;
- 不用考虑各种锁的问题,不存在加锁和释放锁的操作,没有因为可能出现死锁而导致的性能问题;
- 不存在多线程切换而消耗 CPU;
- 无法发挥多核 CPU 的优势,但可以采用多开几个 Redis 实例来完善;
Redis真的是单线程的吗?
Redis6.0 之前是单线程的,Redis6.0 之后开始支持多线程;
redis 内部使用了基于 epoll 的多路服用,也可以多部署几个 redis 服务器解决单线程的问题;
redis 主要的性能瓶颈是内存和网络;
内存好说,加内存条就行了,而网络才是大麻烦,所以 redis6 引入了多线程的概念,
redis6.0在网络 IO 处理方面引入了多线程,如网络数据的读写和协议解析等,需要注意的是,执行命令的核心模块还是单线程的。
Redis 持久化?
redis提供了两种持久化的方式,分别是 RDB(Redis DataBase)和 AOF(Append Only File)。
- RDB,简而言之,就是在不同的时间点,将 redis 存储的数据生成快照并存储到磁盘等介质上;
- AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次 redis 重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
RDB 持久化
是指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完操作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据。RDB触发机制主要有以下几种:
RDB 的优点
- 适合大规模的数据恢复场景,如备份,全量复制等
RDB缺点
- 没办法做到实时持久化/秒级持久化。
- 新老版本存在RDB格式兼容问题
在生成 RDB期间,Redis 可以同时处理写请求么?
可以的,Redis提供两个指令生成RDB,分别是 save 和 bgsave
- 如果是 save 指令,会阻塞,因为是主线程执行的。
- 如果是 bgsave 指令,是 fork 一个子进程来写入 RDB 文件的,快照持久化完全交给子进程来处理,父进程则可以继续处理客户端的请求。
AOF(append only file) 持久化
采用日志的形式来记录每个写操作,追加到文件中,重启时再重新执行AOF文件中的命令来恢复数据。它主要解决数据持久化的实时性问题。默认是不开启的。
AOF的工作流程如下
AOF的优点
数据的一致性和完整性更高
AOF的缺点
AOF记录的内容越多,文件越大,数据恢复变慢。
其实 RDB 和 AOF 两种方式也可以同时使用,在这种情况下,如果 redis 重启的话,则会优先采用 AOF 方式来进行数据恢复,这是因为 AOF 方式的数据恢复完整度更高。
如果你没有数据持久化的需求,也完全可以关闭 RDB 和 AOF 方式,这样的话,redis 将变成一个纯内存数据库。
缓存穿透/缓存雪崩?如何解决?
一个常见的缓存使用方式:读请求来了,先查下缓存,缓存有值命中,就直接返回;缓存没命中,就去查数据库,然后把数据库的值更新到缓存,再返回。
缓存穿透
一般的缓存系统,都是按照 key 去缓存查询,如果不存在对应的 value,就应该去后端系统查找(比如DB 数据库)。一些恶意的请求会故意查询不存在的 key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
缓存穿透一般都是这几种情况产生的:
业务不合理的设计,比如大多数用户都没开守护,但是你的每个请求都去缓存,查询某个userid查询有没有守护。
业务/运维/开发失误的操作,比如缓存和数据库的数据都被误删除了。
黑客非法请求攻击,比如黑客故意捏造大量非法请求,以读取不存在的业务数据。
如何解决?
- 如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。
- 对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该 key 对应的数据 insert 之后清理缓存。
- 对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过该Bitmap 过滤。
缓存雪崩
当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,会给后端系统带来很大的压力,导致系统崩溃。
如何解决?
- 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其它线程等待;
- 做二级缓存,A1 为原始缓存,A2 为拷贝缓存,A1 失效时,可以访问 A2,A1 缓存失效时间设置为短期,A2 设置为长期;
- 不同的 key,设置不同的过期时间,让缓存失效的时间尽量均匀;
缓存击穿
指热点 key 在某个时间点过期的时候,而恰好在这个时间点对这个 Key 有大量的并发请求过来,从而大量的请求打到 DB。
缓存击穿和缓存雪崩看着有点像,其实它两区别是,缓存雪奔是指数据库压力过大甚至 down 机,缓存击穿只是大量并发请求到了 DB 数据库层面。可以认为击穿是缓存雪崩的一个子集吧
如何解决?
- 使用互斥锁方案。缓存失效时,不是立即去加载 DB 数据,而是先使用某些带成功返回的原子操作命令,如(Redis的setnx)去操作,成功的时候,再去加载 DB 数据库数据和设置缓存。否则就去重试获取缓存。
- “永不过期”,是指没有设置过期时间,但是热点数据快要过期时,异步线程去更新和设置过期时间。
怎么保证缓存和数据库数据的一致性?
1、淘汰缓存
数据如果比较复杂,进行缓存的更新操作就会变得异常复杂,因此一般推荐选择淘汰缓存,而不是更新缓存。
2、选择先淘汰缓存,再更新数据库
假如先更新数据库,再淘汰缓存,如果淘汰缓存失败,那么后面的请求都会得到脏数据,直至缓存过期。
假如先淘汰缓存再更新数据库,如果更新数据库失败,只会产生一次缓存穿透,相比较而言,后者对业务则没有本质上的影响。
3、延时双删策略
如下场景:同时有一个请求 A 进行更新操作,另一个请求 B 进行查询操作。
- 请求 A 进行写操作,删除缓存
- 请求 B 查询发现缓存不存在
- 请求 B 去数据库查询得到旧值
- 请求 B 将旧值写入缓存
- 请求 A 将新值写入数据库
public void write(String key,Object data){
redisUtils.del(key);
db.update(data);
Thread.Sleep(100);
redisUtils.del(key);
}
这么做,可以将1秒内所造成的缓存脏数据,再次删除。这个时间设定可根据俄业务场景进行一个调节。
4、数据库读写分离的场景
两个请求,一个请求 A 进行更新操作,另一个请求B进行查询操作。
- 请求 A 进行写操作,删除缓存
- 请求 A 将数据写入数据库了,
- 请求 B 查询缓存发现,缓存没有值
- 请求 B 去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
- 请求 B 将旧值写入缓存
- 数据库完成主从同步,从库变为新值
依旧采用延时双删策略解决此问题。
Redis 有哪些架构模式
单机版
特点:
- 简单
问题:
- 内存容量有限
- 处理能力有限
- 无法高可用。
主从模式
Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。
只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步给 从服务器,从而一直保证主从服务器的数据相同。
特点:
- master/slave 角色
- master/slave 数据相同
- 降低 master 读压力在转交从库
问题:
- 无法保证高可用
- 没有解决 master 写的压力
哨兵模式
Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:
- 监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒(Notification):当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover):当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。
特点:
- 保证高可用
- 监控各个节点
- 自动故障迁移
缺点:
- 主从模式,切换需要时间丢数据
- 没有解决 master 写的压力
集群(代理型)
Twemproxy 是一个 Twitter 开源的一个 redis 和 memcache 快速/轻量级代理服务器;Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII 协议和 redis 协议。
特点:
- 多种 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
- 支持失败节点自动删除
- 后端 Sharding 分片逻辑对业务透明,业务方的读写方式和操作单个 Redis 一致
缺点:
- 增加了新的 proxy,需要维护其高可用。
- failover 逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预
集群(直连型)## 标题
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
is 一致
缺点:
- 增加了新的 proxy,需要维护其高可用。
- failover 逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预
集群(直连型)## 标题
[外链图片转存中…(img-dSQt5zal-1715530485107)]
[外链图片转存中…(img-dcNhobIt-1715530485107)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!