Redis 理解
基于内存、可持久化的key-value非关系型数据库。
数据类型
①String、②List、③Hash、④Set、⑤ZSet。
持久化机制
RDB
每过一段时间执行一次写操作将内存数据写入磁盘,再在指定目录下生成一个rdb文件,写入成功后,替换之前的文件,用二进制进行压缩存储。
优点:
①:只会包含一个文件备份;
②:相比于AOP机制,效率高。
缺点:
①:出现宕机,数据可能会丢失。
AOF
每次写、删除操作会记录到日志文件中,查询不会记录。重启根据日志文件恢复。
优点:
①:有更高的数据安全性;
②:出现宕机,可采用redis-check-aof工具恢复,解决数据一致性问题;
③:格式清晰、易理解,能更好的完成数据重建。
缺点:
①:数据文件大;
②:运行效率低。
混合持久化
在redis4.0出现,将RDB文件内容和AOF文件内容存在一起,AOF日志不再是全量日志,只会记录从持久化开始->结束这段时间发生的增量AOF日志。
特点:
①:大量数据使用粗粒度(时间上)的rdb快照方式,性能高,恢复时间快。
②:增量数据使用细粒度(时间上)的AOF日志方式,尽量保证数据的不丢失。
混合持久化是最佳方式吗?
不一定。混合持久化是Redis 4.0才引入的特性,现在很多 公司可能都还在使用3.x版本。使用不了这一特性。
另外,可以使用下面这种方式。Master使用AOF,Slave使用RDB快照,master需要首先确保数据完整性,它作为数据备份的第一选择;slave提供只读服务或仅作为备机,它的主要目的就是快速响应客户端read请求或灾切换。
至于具体使用哪种持久化方式,就看大家根据场景选择。没有最好,只有最合适。
过期策略
定期删除
按一定时间随机扫描(随机扫描:因为redis中key是成千上万的,全部扫描效率低,给cpu带来很大的负载)已经过期的key,将过期key放到独立字典中,定期遍历该字典进行删除。
惰性删除
客户端在访问key时,检测该key的过期时间是否过期,过期直接删除,无返回结果。
淘汰策略
有了过期删除策略,为什么还要有淘汰策略?
因为相对定期删除、惰性删除都会遗留大量过期key没删除(场景:“定期删除”随机扫描,可能会有已过期key未扫描到;“惰性删除”客户端请求完后,key过期,后期未请求,过期key存在遗留)
①volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
②volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
③volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
④allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
⑤allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
⑥no-enviction(驱逐):禁止驱逐数据
分布式锁
分类
- Memcached:利用 Memcached 的 add 命令。此命令是原子性操作,只有在 key 不存在的情况下,才能 add 成功,也就意味着线程得到了锁。
- Redis:和 Memcached 的方式类似,利用 Redis 的 setnx 命令。此命令同样是原子性操作,只有在 key 不存在的情况下,才能 set 成功。
- Zookeeper:利用 Zookeeper 的顺序临时节点,来实现分布式锁和等待队列。Zookeeper 设计的初衷,就是为了实现分布式锁服务的。
- Chubby:Google 公司实现的粗粒度分布式锁服务,底层利用了 Paxos 一致性算法。
特性
①:一个方法在同一时间只能被一个机器的一个线程执行;
②:高可用的获取锁与释放锁;
③:高性能的获取锁与释放锁;
④:具备可重入特性;
⑤:具备锁失效机制,防止死锁;
⑥:具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
redis锁
-
通过setnx命令的排他性来达成锁的互斥。setnx命令的特性是,在插入一个key、value对时,当key不存在时,可以设置成功,返回1,当key已经存在时,会设置失败,返回0。
-
通过锁超时来达成避免死锁的问题。当在Redis中插入key成功后,即认为获取锁成功,当锁使用完成后,需要删除相应key,即释放锁。当删除失败时,就会出现死锁问题。这时候,可以通过在set时,设置超时时间来解决。如设置超时时间为10s等。
-
通过独立线程来完成锁续租。由于加入了锁超时机制,所以,有可能会发生任务还未完成,锁就被释放的问题。一些分布式锁的开源框架,会通过一个单独线程,来不断通过exprie命令重刷key的过期时间,来达成一个锁续租的目的。
-
通过value来配合解决锁重入问题。在分布式锁下,value可以是当前进程或线程的一个唯一id,当获取锁时,如果发现锁已经存在,可以根据value值来判断,是否可以获取锁,来达成一个锁重入的目的。
优点:①性能好;②实现方便
缓存击穿
大量请求同时访问同一个key,key刚好失效,同一时间所有请求就会穿过缓存,直接请求数据库,数据库无法扛着这么大的并发,直接挂了。
解决办法:①加锁、②设置永不过期
缓存雪崩
比击穿更严重,大量key失效,直接请求数据库,数据库无法扛着这么大的并发,直接挂了。
解决办法:①将过期时间错开、②搭集群、③服务降级策略
缓存穿透
存在大量请求访问,同时大量key也不存在,直接请求数据库,数据库无法扛着这么大的并发,直接挂了。
解决办法:①将数据库查询为空丢到缓存,设置短时间过期时间、②业务规则过滤请求,采用布隆过滤器初始化key标识,检测不存在key,过滤非法请求
补充
实现分布式锁
数据库锁
基于数据库唯一索引,创建一个锁表,根据方法名字段建立唯一索引,执行某个方法,往表中插入一条数据形成加锁步骤,业务处理完成后删除数据,达到释放锁的目的。
问题及解决办法:
①基于数据库,数据库的可用性和性能直接影响分布式锁的性能和可用性。所以需要数据库双击部署、数据同步、主备切换;
②不具备可重入特性。需要单独列记录锁机器和线程信息;
③无锁失效机制。因为可能出现数据插入成功后,服务宕机,对应数据没有删除,服务恢复后一直无法删除,需要创建定时任务处理失效数据;
④不具备阻塞锁特性。获取不到锁直接失败,需要优化逻辑,循环多次获取;
⑤维护复杂。ZooKeeper
为分布式应用提供一致性服务的开源组件,内部是一个分层的文件系统目录结构,规定同一个目录只能有一个唯一文件名。
①创建一个目录mylock;
②线程A获取锁就在mylock目录下创建临时顺序节点;
③获取mylock下所有子节点,再获取比自己小的兄弟节点,不存在,说明当前线程顺序号最小,获得锁;
④线程B获取所有节点,判断自己是否是最小节点,设置监听比自己次小的节点;
⑤线程A处理完,删除节点,线程B监听变更事件后,判断自己是不是最小的节点,如果是则获得锁。
优点:高可用、可重入、阻塞锁特性、可解决死锁问题。
缺点:需要频繁创建、删除节点,性能不如redis
上一篇: Vue结合axios的综合案例