1.简单介绍一下Redis
Redis是一个使用C语言开发的数据库,不过与传统的数据库不同的是Reids的数据库是存在内存中的,也就是它是内存数据库,所以读写速度非常快,因此Redis被广泛应用于缓存方向。
Redis除了做缓存之外,Redis也经常用来做分布式锁,甚至是消息队列。
Redis提供了多种数据类型来支持不同的业务场景。Redis还支持事务、持久化、Lua脚本、多种集群方案。
优点:
读写性能极高
支持数据持久化
支持事务
数据结构丰富
支持主从复制
丰富的特性
缺点:
数据库容量收到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
2.缓存数据的处理流程
如果用户请求的数据在缓存中就直接返回
缓存中不存在的话就看数据中是否存在
数据库中不存在的话就更新缓存中的数据
数据库中不存在的话就返回空数据。
3.Rsdis常见的数据库
A.字符串类型 String
String数据结构是简单的key-value类型,可以存储任何类型的数据,包括二进制数据,序列化后的数据,JSON化后的数据,甚至是一张图片。最大为512M
常用命令:set、get、strlen、exists、dect、incr、setex
应用场景:计数器:记录播放量,记录访问数(Redis记总数,MySQL记录日志)、登录验证码
B.列表类型 list
是简单的字符串列表,按照插入顺序排序,元素可以重复。可以添加一个元素到列表的头部或者尾部,底层是个链表结构。实现的是双向链表,既可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
常用命令:rpush、lpop、lpush、rpop、lrange、llen
应用场景:消息队列,慢查询。
C. 集合类型 set
是String类型的无序无重复集合
常用命令:sadd、spop、smembers、scard
应用场景:需要存放的数据不能重复,以及需要获取多个数据源交集和并集等场景
D. 哈希类型hash
hash是一个string类型的field和value的映射表,hash特别适合于存储对象
常用命令:hset、hget、hgetall
应用场景:系统中对象的存储
E. 有序集合类型zset(sorted set)
有序集合zset和set集合一样也是string类型元素的集合,且不允许重复的成员。不同的是zset每个元素都会关联一个分数(分数可以重复),redis通过分数来为集合中的成员进行从小到大的排序。
常用命令:zadd、zscore、zcard、zrange
应用场景:需要对数据根据某个权重进行排序的场景。
4.Redis单线程模型
redis中的文件事件处理器,文件事件处理器是单线程模式运行的。
文件事件处理器的四个部分:
多个socket(客户端连接)
IO多路复用程序(支持多个客户端连接的关键)
文件事件分派器(将socket关联到相应的事件处理器)
事件处理器(连接 应答处理器、命令请求处理器、命令恢复处理器)
单线程为什么执行那么快
纯内存操作
避免了不必要的上下文切换
避免了多线程之间的竞争和切换而消耗CPU,避免了各种锁操作
采用了非阻塞I/O多路复用技术:这个技术让redis不需要额外创建多余的线程来监听客户端的大量连接,降低了资源消耗。
5. Redis6.0之前,为什么不使用多线程
单线程编程容易并且更容易维护
Redis的性能瓶颈不在CPU,主要在内存和网络;
多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能
6.0后为何引入了多线程?
Redis引入多线程主要是为了提高IO读写性能,这个也算是Redis中的一个性能瓶颈。
Redis6.0多线程的实现机制?
主线程负责接收建立连接请求,获取socket放入全局等待读处理队列
主线程处理完读事件后,通过RR(Round Robin)将这些连接分配给这些IO线程。
主线程阻塞等待IO线程读取socket完毕
主线程通过单线程的方式执行请求命令,请求数据读取并解析完成,但并不执行
主线程阻塞等待IO线程将数据回写Socket完毕。
6.缓存
6.1缓存淘汰
先进先出(FIFO)
近期较少使用(LFU)
最长时间未被使用哪个(LRU)
6.2 key过期删除策略
惰性删除:当使用的时候,如果过期就删除,如果没过期就使用
定时删除:key过期后设置一个定时器
定期删除:一段时间对key进行随机检查,删除。控制删除的时长和频率。
6.3 缓存穿透
当获取一个数据但是缓存中没有,数据库中也没有,多次查询使数据库宕机
解决方案;
布隆过滤器:将mysql中的key加入布隆过滤器,查询之前,查看数据库是否有这个key
在redis存在不存在的key设置value为null
6.4 缓存击穿
当获取一个数据但是缓存中没有(过期时间到,宕机),数据库中有,多次查询使数据库宕机
解决:redis集群。redis数据库中,添加缓存层或限流
6.5 缓存雪崩
同一时刻,大量缓存失效,直接请求数据库
解决:
设置不同失效时间
双层缓存
定期更新
增加过期标志
6.6 缓存阻塞
数据结构不合理
CPU饱和
持久化阻塞
6.7 热点key缓存倾斜
使用本地缓存
利用分片算法,对key加前缀,把一个热点key分散成多个key
7. 持久化
Redis的一种持久化方式叫快照(snapshotting RDB),另一种方式只追加文件(append-only file AOF).
快照持久化(RDB):定期的全盘缓存
在指定的时间间隔内将内存中的数据集快照写入磁盘,它恢复时是将快照文件直接读到内存里。
优势:适合大规模的数据恢复;对数据完整性和一致性要求不高
劣势:在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
AOF持久化:日志文件; 触发机制,每次记录
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以写文件,Redis启动之初会读取该文件重新构建数据。
避免文件越来越大,新增了重写机制,当AOF文件的大小超过所设定的阈值的时候,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。
优势:同步持久化;每秒同步;不同步
劣势:相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb
aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
两者的比较:
AOF文件比RDB更新效率高
优先使用AOF
AOF更安全
RDB比AOF性能好
7.1Redis持久化数据和缓存怎么做扩容?
如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩写。
如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化,否则的话必须使用可以在运行时进行数据再平衡一套系统
8.Redis事务
Redis可以通过MULTI . EXEC , DISCARD, WATCH等命令来实现事务(transcation)功能
Multi开启事务
Exec执行事务代码块
Discard取消事务
Watch监制一个或多个key,如果事务执行前key被改动,事务将打断
8.1 redis事务的实现特征
所有的命令都将被串行化的顺序执行,事务执行期间,Redis不会再为其他客户提供要求
Redis事务中如果有一条命令执行失败,之后的命令仍然会继续执行
在事务开始之前,如果客户端和服务器之间出现通信故障并导致网络断开,之后的所有待执行语句都不会执行。如果exec之后网络中断后,事务中的所有命令都会被服务器执行。
当使用Append-Only模式的时候,Redis通常会调用write全部写入磁盘。当写入断电的时候,会发生数据不一致的信息,使用redis-check-aof对数据进行回滚
9.Redis集群
官方cluster集群3主3从
客户端只需要连接其中的任何一个节点
使用哈希槽(hash slot) 的方式来分配16384个slot,当新节点加入,取各个槽的前部门分给新节点。
9.1 容错性
投票:集群中所有的master节点投票,半数以上的master挂掉集群不可用
节点分配:新增节点时,从每个节点的前半部分分配给新的节点
9.2 主从复制
从机连通会给主机发送sync指令
主机进行存盘操作,发送RDB文件给从机
从机收到后,进行全盘加载
每次主机写操作,都会立刻发送给从机,从机执行相同的命令
9.3 性能优化
主机不进行持久化,从机AOF备份数据
主从位于同一局域网
避免在压力大的主库上增加从库
使用链式结构增加从机
9.3 集群不可用
主机挂掉,当前主机没有从机
半数以上的主机挂掉。
分布式问题
10.什么是分布式锁?为什么用分布式锁?
锁在程序中的作用就是同步工具,保证共享资源在同一时刻只能被一个线程访问,Java中的锁我们很熟悉,像synchronized、lock都是常见的,但是Java的锁只能保证单机的时候有效,分布式集群环境就无能无力了,这个时候我们就需要用到分布式锁。
分布式锁,就是分布式项目开发中用到的锁,可以用来控制分布式系统之间同步访问共享资源
需要满足的特点:
互斥性:在任何时刻,对于同一数据,只有一台应用可以获取到分布式锁
高可用性:在分布式场景下,一小部分服务器宕机不影响正常使用,这种情况就需要将提供分布式锁的服务以集群的方式部署
防止锁超时:如果客户端没有主动释放锁,服务器会在一段时间之后自动释放锁,防止客户端宕机或者网络不可达时产生死锁
独占性:加锁解锁必须由同一台服务器进行,也就是锁的持有者才可以释放锁。
11.常见的分布式锁有哪些解决方案?
三种解决方案:关系型数据库、Redis、ZooKeeper
12.Redis实现分布式锁
分布式锁的三大核心要素:加锁、解锁、锁超时
13.了解RedLock吗?
Redlock是一种算法,Redlock也就是Redis Distributed Lock,可以实现多节点的分布式锁
这种方法的特性:
互斥访问:即永远只有一个client能拿到锁
避免死锁:最终client都可能拿到锁,不会出现死锁的情况,即使锁定资源的服务崩溃或者分区,仍然能释放锁
容错性:只要大部分Redis节点存活(一半以上),就可以正常提供服务。
14.Redis如何做内存优化?
控制key的数量:当时用Redis存储大量数据的时候,通常会存在大量键,过多的键同样会消耗大量内存。
缩减键值对象:降低Redis内存使用最直接的方式就是缩减key和value的长度
编码优化:Redis对外提供了string,list,hash,zet,set等类型,但是Redis内部针对不同类型存在编码的概念,所谓编码就是具体使用哪种底层数据结果来实现。编码不同将直接影响数据的内存占用和读写效率
15.如果现在有个读超高并发的系统,用Redis来抗住大部分读请求,你会怎么设计?
如果是读高并发的话,先看并发的数量级是多少,因为Redis单机的读QPS在万级,每秒几万没问题,使用一主多从+哨兵集群的缓存架构来承载每秒10W的读并发,主从复制,读写分离,
使用哨兵集群主要是提高缓存架构的可用性,解决单点故障问题。主库负责写,多个从库负责读,支持水平扩容,根据读请求的QPS来决定加多少个Redis从实例。如果读并发继续增加的话,只需要增加Redis实例就行了。
如果需要缓存1T+的数据,选择Redis cluster模式,每个主节点存一部分数据,假设一个master存32G,那只需要n*32G>=1T,n个这样的master节点就可以支持1T+的海量数据的存储。