1.redis 有哪些数据类型
- 字符串(String):
Redis最基本的数据类型,一个键最大能存储512MB,可以包含任何数据。比如jpg图片或者序列化的对象,json。 - 列表(List):
简单的字符串列表,按照插入顺序排序 - 哈希表(Hash):
是一个string类型的field和value的映射表 - 集合(Set):
Redis的Set是string类型的无序集合。底层通过哈希表实现的 - 有序集(zset):
和set一样,不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。
2.Redis有哪几种持久化方式及对比
-
RDB(快照)
Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化。- Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代段和数据段。这时你可以将父子进程想像成一个连体婴儿,共享身体(内存)。
- 如果进程要对内存数据修改,这个时候就会使用操作系统的 COW 机制来进行数据段页面的分离。数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。
-
AOF
AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。内容是redis通讯协议(RESP )格式的命令文本存储.
当文件大小触碰到临界时,rewrite会被运行, 对 AOF 日志进行瘦身。其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中
优缺点是什么?
-
AOF文件比RDB更新频率高,优先使用AOF还原数据。
RDB 是间隔一段时间进行持久化,数据有点延迟 -
AOF比RDB更安全也更大
RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失 -
RDB性能比AOF好
性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能。RDB是内存快照,恢复速度肯定比指令速度快
3.缓存穿透,缓存雪崩
-
缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
解决方案:布隆过滤器,缓存空对象 -
缓存击穿: 是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。。
解决方案:限流降级,对某个key只允许一个线程查询数据和写缓存,其他线程等待 -
缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
解决方案:数据预热,就是在部署启动之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。为key设置不同的缓存失效时间。使redis高可用,不会造成所有key同时失效的情况。
4.怎么实现redis分布式锁
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。setnx相当于分布式的CAS,我们可以发展成重入锁,读写锁的。
5.Redis的过期键的删除策略
-
定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
-
惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
-
定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
6.redis数据淘汰策略
redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略
- volatile-lru:从设置了过期时间的数据集中,选择最近最久未使用的数据释放;
- allkeys-lru:从数据集中(包括设置过期时间以及未设置过期时间的数据集中),选择最近最久未使用的数据释放;
- volatile-random:从设置了过期时间的数据集中,随机选择一个数据进行释放;
- allkeys-random:从数据集中(包括了设置过期时间以及未设置过期时间)随机选择一个数据进行入释放;
- volatile-ttl:从设置了过期时间的数据集中,选择马上就要过期的数据进行释放操作;
- noeviction:不删除任意数据(但redis还会根据引用计数器进行释放),这时如果内存不够时,会直接返回错误。
7.redis高可用
- 采用Master/Slave主备的方式,使用AOF方式
- 增加Sentinel(哨兵),负责持续监控主从节点的健康,当主节点挂掉时,自动选择一个最优的从节点切换为主节点
- 客户端来连接集群时,会首先连接 sentinel,通过 sentinel 来查询主节点的地址
- 然后再去连接主节点进行数据交互。
- 当主节点发生故障时,客户端会重新向 sentinel 要地址,sentinel 会将最新的主节点地址告诉客户端。
如此应用程序将无需重启即可自动完成节点切换。Sentinel 会持续监控已经挂掉了主节点,待它恢复后,原先挂掉的主节点现在变成了从节点,从新的主节点那里建立复制关系。
8.hash一致性算法
先构造一个0到2^32的整数环,然后将服务器节点的Hash值,放在该环上(可以理解为将你的ip做hash,将ip的HashCode放在环上)。然后根据需要缓存的数据的Key,计算Key的HashCode,然后在环上,顺时针查找距离这个Key的Hash值最近的缓存服务器的节点,然后将Value,存储到该服务器节点上。
如果新增节点node5,计算的hash值趋于node2,node4之间,则 node2,node4之间的数据就有一部分需要迁移到node5,一方面对其他节点影响小,但是会导致数据会分配不均,其他节点数据压力依然很大。
解决方式:
-
引入虚拟节点,在现有节点上层在增加一层虚拟节点,
-
我们将每台缓存服务器,虚拟为一组虚拟缓存服务器,将虚拟服务器的Hash值,放置在Hash环上,Key在环上先找到虚拟服务器节点,再得到物理服务器的信息。
这样,一台服务器节点,被环中多个虚拟节点所代表,且多个节点均匀分配。很显然,每个物理节点对应的虚拟节点越多,各个物理节点之间的负载越均衡,新加入物理服务器对原有的物理服务器的影响越保持一致。
9.codis集群
Codis 将所有的 key 默认划分为 1024 个槽位(slot),它首先对客户端传过来的 key 进行 crc32 运算计算哈希值,再将 hash 后的整数值对 1024 这个整数进行取模得到一个余数,这个余数就是对应 key 的槽位。每个槽位都会唯一映射到后面的多个 Redis 实例之一,Codis 会在内存维护槽位和Redis 实例的映射关系.如果是Codis集群,则关系维护在ZK中。
如何扩容
- 通过ZK将一半的槽位划分到新的节点。
- Codis 通过 SLOTSSCAN 扫描出待迁移槽位的所有的 key,然后挨个迁移每个 key 到新的 Redis 节点。
- 当 Codis 接收到位于正在迁移槽位中的 key的请求后,会立即强制对当前的单个 key 进行迁移,迁移完成后,再将请求转发到新的 Redis 实例。
缺点
因为 Codis 中所有的 key分散在不同的 Redis 实例中,所以事务就不能再支持了