1、为什么lua脚本结合redis命令可以实现原子性
Redis 提供了非常丰富的指令集,但是用户依然不满足,希望可以自定义扩充若干指令来完成一些特定领域的问题。Redis 为这样的用户场景提供了 lua 脚本支持,用户可以向服务器发送 lua 脚本来执行自定义动作,获取脚本的响应数据。Redis 服务器会单线程原子性执行 lua 脚本,保证 lua 脚本在处理的过程中不会被任意其它请求打断。
redis会为lua脚本执行创建伪客户端模拟客户端调用redis执行命令,伪客户端执行lua脚本是排他的,再加上redis是原子性的。
2、redis分布式锁会导致什么问题。
- 互斥性。在任意时刻,只有一个客户端能持有锁。
- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
- 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
- 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
业务逻辑执行太慢,比锁失效时间还长怎么办,redisson的WatchDog 会每10秒轮询,延长锁。
如果你很在乎高可用性,希望挂了一台 redis 完全不受影响,可以考虑 redlock。
3、redis六种淘汰key策略(默认时no-eviction)
- volatile-lru:在设置了过期时间的键空间中,移除最近最少使用的key
- allkeys-lru:移除最近最少使用的key
- volatile-random:在设置了过期时间的键空间中,随机移除一个key
- allkey-random:随机移除一个key
- volatile-ttl:在设置了过期时间的键空间中,移除将要过期的key
- no-eviction:在内存使用达到阈值的时候,所有引起申请内存的命令会报错
4、redis三种删除过期键策略
- 定时删除: 在设置键的过期时间的同时,创建一个定时器,让定时器执行对键的删除操作
- 惰性删除: 每次取的时候先判断 expires 对象里面的键是否已经过期,如果过期,则删除键,否则,返回该键
- 定期删除: 每隔一段时间,程序对数据库遍历检查一遍,然后删除过期的键
定时删除(并没有用到)
在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作;
定时删除操作对于内存来说是友好的,内存不需要操作,而是通过使用定时器,可以保证尽快的将过期键删除,但是对于CPU来说不是友好的,如果过期键比较多的话,起的定时器也会比较多,删除的这个操作会占用到CPU的资源;
惰性删除
放任键过期不管,但是每次从键空间中获取键是,都检查取得的键的过期时间,如果过期的话,删除即可;
惰性操作对于CPU来说是友好的,过期键只有在程序读取时判断是否过期才删除掉,而且也只会删除这一个过期键,但是对于内存来说是不友好的,如果多个键都已经过期了,而这些键又恰好没有被访问,那么这部分的内存就都不会被释放出来;
定期删除
每隔一段时间,程序就对数据库进行一次检查,删除掉过期键;
定期删除是上面两种方案的折中方案,每隔一段时间来删除过期键,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响,除此之外,还有效的减少内存的浪费;但是该策略的难点在于间隔时长,这个需要根据自身业务情况来进行设置;
目前,Redis采用的是惰性删除+定期删除的方案,通过配合使用,服务器可以很好的平衡 CPU 和内存。
5、Redis4.0新特性
包含几个重大改进:更好的复制(PSYNC2),线程DEL / FLUSH,混合RDB + AOF格式,活动内存碎片整理,内存使用和性能改进。
一、主从数据同步机制
PSYNC2: 新的一种主从复制同步机制。
PSYNC1:2.8~4.0之前版本的同步为PSYNC1
psync1因为网络中断或者阻塞导致主从中断,恢复后必须重新到主节点dump一份全量数据同步到从节点。psync2再中断恢复后只需要同步复制延迟的那部分数据。
二、命令优化
线程DEL / FLUSH 优化
Redis现在可以在不同的线程中删除后台的key而不会阻塞服务器。
三、慢日志记录客户端来源IP地址,这个小功能对于故障排查很有用处。
四、混合RDB + AOF格式
当我们开启了混合持久化时,启动redis依然优先加载aof文件,aof文件加载可能有两种情况如下:
- aof文件开头是rdb的格式,先加载 rdb内容再加载剩余的 aof。
- aof文件开头不是rdb的格式,直接以aof格式加载整个文件。
优点:既能快速备份又能避免大量数据丢失
缺点:RDB是压缩格式,AOF在读取它时可读性教差
五、内存使用和性能改进:
1、Redis现在使用更少的内存来存储相同数量的数据。
2、Redis现在可以对使用的内存进行碎片整理,并逐渐回收空间(这个功能依然是试用阶段,可以通过参数不开启即可)
6、redis有1000万个key,找出前缀为aaa的key的命令是什么?
- SCAN cursor [MATCH pattern] [COUNT count],指令指定返回条数
- SCAN 0 MATCH aaa* COUNT 5 表示从游标0开始查询aaa开头的key,每次返回5条,但是这个5条不一定,只是给Redis打了个招呼,具体返回数量看Redis心情。
7、本地redis客户端连接远程服务器命令
redis-cli -h host -p port -a password
8、为什么Redis选择使用跳表而不是红黑树来实现有序集合?
Redis 中的有序集合(zset) 支持的操作:
- 插入一个元素
- 删除一个元素
- 查找一个元素
- 有序输出所有元素
- 按照范围区间查找元素(比如查找值在 [100, 356] 之间的数据)
其中,前四个操作红黑树也可以完成,且时间复杂度跟跳表是一样的。但是,按照区间来查找数据这个操作,红黑树的效率没有跳表高。按照区间查找数据时,跳表可以做到 O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了,非常高效。
9、Redis Sentinel 与 Redis Cluster区别和各自适用场景
Redis Sentinel
Redis-Sentinel(哨兵模式)是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自懂切换。它的主要功能有以下几点:
- 不时地监控redis是否按照预期良好地运行;
- 如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);
- 能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址。
Redis Cluster
- Redis Cluster是Redis的分布式解决方案,在Redis 3.0版本正式推出的,有效解决了Redis分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用Cluster架构达到负载均衡的目的。分布式集群首要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整个数据的一个子集。
- Redis Cluster采用哈希分区规则中的虚拟槽分区。虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有的数据映射到一个固定范围内的整数集合,整数定义为槽(slot)。Redis Cluster槽的范围是0 ~ 16383。槽是集群内数据管理和迁移的基本单位。采用大范围的槽的主要目的是为了方便数据的拆分和集群的扩展,每个节点负责一定数量的槽。
- Redis Cluster采用虚拟槽分区,所有的键根据哈希函数映射到0 ~ 16383,计算公式:slot = CRC16(key)&16383。每一个实节点负责维护一部分槽以及槽所映射的键值数据。下图展现一个五个节点构成的集群,每个节点平均大约负责3276个槽,以及通过计算公式映射到对应节点的对应槽的过程。
10、redis的内存优化
1、redisObject对象
2、缩减键值对象
3、共享对象池
4、字符串优化
5、编码优化
6、控制key的数量
https://www.cnblogs.com/williamjie/p/11288062.html
11、热key问题,如何发现,业内方案
热Key问题
- 所谓热key问题就是,突然有几十万的请求去访问redis上的某个特定key。那么,这样会造成流量过于集中,达到物理网卡上限,从而导致这台redis的服务器宕机。
怎么发现热key
方法一:凭借业务经验,进行预估哪些是热key
其实这个方法还是挺有可行性的。比如某商品在做秒杀,那这个商品的key就可以判断出是热key。缺点很明显,并非所有业务都能预估出哪些key是热key。
方法二:在客户端进行收集
这个方式就是在操作redis之前,加入一行代码进行数据统计。那么这个数据统计的方式有很多种,也可以是给外部的通讯系统发送一个通知信息。缺点就是对客户端代码造成入侵。
方法三:在Proxy层做收集
有些集群架构是下面这样的,Proxy可以是Twemproxy,是统一的入口。可以在Proxy层做收集上报,但是缺点很明显,并非所有的redis集群架构都有proxy。
方法四:用redis自带命令
(1)monitor命令,该命令可以实时抓取出redis服务器接收到的命令,然后写代码统计出热key是啥。当然,也有现成的分析工具可以给你使用,比如redis-faina。但是该命令在高并发的条件下,有内存增暴增的隐患,还会降低redis的性能。
(2)hotkeys参数,redis 4.0.3提供了redis-cli的热点key发现功能,执行redis-cli时加上–hotkeys选项即可。但是该参数在执行的时候,如果key比较多,执行起来比较慢。
方法五:自己抓包评估
Redis客户端使用TCP协议与服务端进行交互,通信协议采用的是RESP。自己写程序监听端口,按照RESP协议规则解析数据,进行分析。缺点就是开发成本高,维护困难,有丢包可能性。
(1)利用二级缓存
比如利用ehcache,或者一个HashMap都可以。在你发现热key以后,把热key加载到系统的JVM中。
针对这种热key请求,会直接从jvm中取,而不会走到redis层。
假设此时有十万个针对同一个key的请求过来,如果没有本地缓存,这十万个请求就直接怼到同一台redis上了。
现在假设,你的应用层有50台机器,OK,你也有jvm缓存了。这十万个请求平均分散开来,每个机器有2000个请求,会从JVM中取到value值,然后返回数据。避免了十万个请求怼到同一台redis上的情形。
(2)备份热key
这个方案也很简单。不要让key走到同一台redis上不就行了。我们把这个key,在多个redis上都存一份不就好了。接下来,有热key请求进来的时候,我们就在有备份的redis上随机选取一台,进行访问取值,返回数据。
(1)监控热key
在监控热key方面,有赞用的是方式二:在客户端进行收集。
在《有赞透明多级缓存解决方案(TMC)》中有一句话提到
TMC 对原生jedis包的JedisPool和Jedis类做了改造,在JedisPool初始化过程中集成TMC“热点发现”+“本地缓存”功能Hermes-SDK包的初始化逻辑。
也就说人家改写了jedis原生的jar包,加入了Hermes-SDK包。
那Hermes-SDK包用来干嘛?
OK,就是做热点发现和本地缓存。
从监控的角度看,该包对于Jedis-Client的每次key值访问请求,Hermes-SDK 都会通过其通信模块将key访问事件异步上报给Hermes服务端集群,以便其根据上报数据进行“热点探测”。
当然,这只是其中一种方式,有的公司在监控方面用的是方式五:自己抓包评估。
具体是这么做的,先利用flink搭建一套流式计算系统。然后自己写一个抓包程序抓redis监听端口的数据,抓到数据后往kafka里丢。
接下来,流式计算系统消费kafka里的数据,进行数据统计即可,也能达到监控热key的目的。
(2)通知系统做处理
在这个角度,有赞用的是上面的解决方案一:利用二级缓存进行处理。
有赞在监控到热key后,Hermes服务端集群会通过各种手段通知各业务系统里的Hermes-SDK,告诉他们:"老弟,这个key是热key,记得做本地缓存。"
于是Hermes-SDK就会将该key缓存在本地,对于后面的请求。Hermes-SDK发现这个是一个热key,直接从本地中拿,而不会去访问集群。
除了这种通知方式以外。我们也可以这么做,比如你的流式计算系统监控到热key了,往zookeeper里头的某个节点里写。然后你的业务系统监听该节点,发现节点数据变化了,就代表发现热key。最后往本地缓存里写,也是可以的。
12、思维导图
数据结构与对象
单机数据库
多机数据库
独立功能
13、Gossip是什么
Gossip协议是一个通信协议,一种传播消息的方式,灵感来自于:瘟疫、社交网络等。使用Gossip协议的有:Redis Cluster、Consul、Apache Cassandra等。
Gossip协议基本思想就是:一个节点想要分享一些信息给网络中的其他的一些节点。于是,它周期性的随机选择一些节点,并把信息传递给这些节点。这些收到信息的节点接下来会做同样的事情,即把这些信息传递给其他一些随机选择的节点。一般而言,信息会周期性的传递给N个目标节点,而不只是一个。这个N被称为fanout(这个单词的本意是扇出)
Gossip协议的主要用途就是信息传播和扩散:即把一些发生的事件传播到全世界。它们也被用于数据库复制,信息扩散,集群成员身份确认,故障探测等
特点:
1、可扩展性:即使某条消息传播过程中丢失,它也不需要做任何补偿措施
2、失败容错:因为一个节点会多次分享某个需要传播的信息,即使不能连通某个节点,其他被感染的节点也会尝试向这个节点传播信息。
3、健壮性:没有任何扮演特殊角色的节点(比如leader等)。任何一个节点无论什么时候下线或者加入,并不会破坏整个系统的服务质量。
14、Redis中的11大优秀设计
15、Redis RDB和AOF的优缺点对比以及如何选择
RDB的优点
- RDB文件是紧凑的二进制文件,比较适合做冷备,全量复制的场景。
- 相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复Redis进程,更加快速;
- RDB对Redis对外提供的读写服务,影响非常小,可以让Redis保持高性能,因为Redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可;
- RDB使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了Redis的高性能 ;
RDB的缺点
- 如果想要在Redis故障时,尽可能少的丢失数据,那么RDB没有AOF好。
- RDB每次在fork子进程来执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒;
- RDB无法实现实时或者秒级持久化
AOF的优点
- AOF可以更好的保护数据不丢失(每隔1秒,后台线程执行一次fsync操作)
- AOF日志文件以append-only模式写入,写入性能比较高
- AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
- 适合做灾难性的误删除紧急恢复
AOF的缺点
- 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大,恢复速度慢;
- AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的;
RDB和AOF到底该如何选择
重启Redis时,我们很少使用rdb来恢复内存状态,因为会丢失大量数据。我们通常使用AOF日志重写,
但是AOF重写性能相对rdb来说要慢很多,这样在Redis实例很大的情况下,启动需要花费很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。
AOF在进行文件重写时将重写这一刻之前的内存rdb快照文件的内容和增量的AOF修改内存数据的命令日志文件存在一起,都写入新的aof文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,原子的覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
混合持久化文件结构:
16、为什么恢复的时候RDB比AOF快?
- AOF,存放的指令日志,做数据恢复的时候,其实是要回放和执行所有的指令日志,来恢复出来内存中的所有数据的;
- RDB,就是一份数据文件,恢复的时候,直接加载到内存中即可;
17、怎么设置redis失效时间、怎么设置永久有效?
设置redis失效时间
- expire <KEY> <TTL> : 将键的生存时间设为 ttl 秒
- pExpire <KEY> <TTL> :将键的生存时间设为 ttl 毫秒
- expireAt <KEY> <timestamp> :将键的过期时间设为 timestamp 所指定的秒数时间戳
- pExpireAt <KEY> <timestamp>: 将键的过期时间设为 timestamp 所指定的毫秒数时间戳。
**redis设置了数据永不过期 **
- persist key持久化key,不设置失效时间,并用noeviction:默认回收策略,不淘汰,如果内存已满,添加数据是报错。
18、分布式锁的使用场景
使用分布式锁的目的,无外乎就是保证同一时间只有一个客户端可以对共享资源进行操作。
订单系统和库存系统部署两台机器
1、检查redis库存
2、更新redis库存数量
3、锁定库存MySQL数据库
保证1、2、3是原子性。防止库存超卖
19、redis的主从复制怎么做的?
主从复制主要通过旧版同步和命令传播来实现。
- 旧版同步是从服务器主动发起SYNC命令,主服务器生成RDB文件,并且使用缓存区记录下接下来的命令,从服务器操作完成,主服务器将缓存区中的命令给从节点执行;
- 命令传播是主服务器会将自己的写命令发送给从服务器执行,保持主从一致。
20、缓存穿透、缓存击穿和缓存雪崩
1、缓存穿透
访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉。
解决方案
- 采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤;
- 拦截器,id<=0的直接拦截。
- 从cache和db都取不到,可以将key-value写为key-null,设置较短过期时间,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
2、缓存击穿
一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。
解决方案
- 设置热点数据永远不过期。
- 加互斥锁。
3、缓存雪崩
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
解决方案
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
- 设置热点数据永远不过期。
21、Redis事务和MySQL事务有什么区别?
22、如何使用redis来保证幂等性
可以利用redis的setnx函数对请求入参的某个唯一的字段(如orderId)做幂等拦截,防止重复处理请求。
//幂等校验
if(!jedisTemplate.setnx(RedisConstants.RED_PACKET_RECV + getRedPacketDto.getOrderId() + getRedPacketDto.getRedPacketId(), "0.00")){
//业务代码
}
业务代码执行成功删除key。