缓存现在几乎是所有中大型网站都在用的必杀技,合理的利用缓存不仅能够提升网站访问速度,还能大大降低数据库的压力。Redis提供了键过期功能,也提供了灵活的键淘汰策略,所以,现在Redis用在缓存的场合非常多。
很多网站都有排行榜应用的,如月度销量榜单、商品按时间的上新排行榜等。Redis提供的有序集合数据类构能实现各种复杂的排行榜应用。
什么是计数器,如电商网站商品的浏览量、视频网站视频的播放数等。为了保证数据实时效,每次浏览都得给+1,并发量高时如果每次都请求数据库操作无疑是种挑战和压力。Redis提供的incr命令来实现计数器功能,内存操作,性能非常好,非常适用于这些计数场景。
集群模式下,在应用不多的情况下一般使用容器自带的session复制功能就能满足,当应用增多相对复杂的系统中,一般都会搭建以Redis等内存数据库为中心的session服务,session不再由容器管理,而是由session服务及内存数据库管理。
在很多互联网公司中都使用了分布式技术,分布式技术带来的技术挑战是对同一个资源的并发访问,如全局ID、减库存、秒杀等场景,并发量不大的场景可以使用数据库的悲观锁、乐观锁来实现,但在并发量高的场合中,利用数据库锁来控制资源的并发访问是不太理想的,大大影响了数据库的性能。可以利用Redis的setnx功能来编写分布式的锁,如果设置返回1说明获取锁成功,否则获取锁失败,实际应用中要考虑的细节要更多。
Redis列表结构,LPUSH可以在列表头部插入一个内容ID作为关键字,LTRIM可用来限制列表的数量,这样列表永远为N个ID,无需查询最新的列表,直接根据ID去到对应的内容页即可。
消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。
1.秒杀(以前的经历)
假如此时我们查询到的商品库存为 1,这时候就会走 4 中上面的部分 (插入抢购信息并减少库存),由于并发量大的情况下,下一个请求在上一个还未执行减库操作就去查询了商品库存,这时候查询出来的库存量依然是 1。同样的,会走到 4 上面的步骤中去。然后上一个请求执行了减库操作,此时库存为 0,第二个请求再去减库时,就会把库存量设置为 - 1,这样就出现了超卖情况。
1.2.3 Redis解决卖超问题的实现 1.在秒杀前将商品的库存信息加入到 Redis 缓存list中。
$numberArr = range(1,100);
$redis->>lPush('goods:queue:5',...$numberArr);
2.当秒杀开始时,用户发送请求,每次去检测一下商品的队列是否为空,当非空时,则使用 lpop 减少一个长度,也就是减少一个库存量。这时候将秒杀的信息写入到缓存中去,给缓存信息配一个唯一的键,将该键返回给用户。(由于 lpop 是原子性的,即是大量并发来了,也是要在 Redis 内部进行排队执行的,假如在判断是否为空时,检测到是非空,进行 lpop 操作,由于队列是空,这时候去执行出队列也是返回错误的)。
持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
RDB持久化是将当前进程中的数据生成快照保存到硬盘,保存的文件后缀是rdb;当Redis重新启动时,可以读取快照文件恢复数据。
触发条件: 手动触发 bgsave命令 save: save 执行一个同步操作,以RDB文件的方式保存所有数据的快照。 bgsave: 执行一个异步操作,redis使用Linux系统的fock()生成一个子进程来将DB数据保存到磁盘,主进程继续提供服务以供客户端调用。如果操作成功,可以通过客户端命令LASTSAVE来检查操作结果。
自动 save m n的实现原理: m 秒内有至少有 n个键被改动,就会触发。Redis的save m n,是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。
AOF持久化,则是将Redis执行的每次写命令记录到单独的日志文件中。 开启: appendonly yes AOF持久化的三种策略:配置文件配置 Redis 多久才将数据记录到磁盘一次
因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF文件的体积也会变得越来越大。(100次INCR ,实际一个SET就可以搞定)Redis对AOF文件进行重建(rebuild)。执行 bgrewriteaof 命令, Redis 将生成一个新的AOF文件, 这个文件包含重建当前数据集所需的最少命令。
作用:
auto-aof-rewrite-min-size 64M 触发AOF文件执行重写的最小尺寸 auto-aof-rewrite-percentage 100 触发AOF文件执行重写的增长率(当前写入日志文件的大小超过上 一次rewrite之后的文件大小的百分之100时就是2倍时触发rewrite)
RDB和AOF的对比
为了使得集群在一部分节点下线或者无法与集群的大多数节点进行通讯的情况下, 仍然可以正常运作,Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品, 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)。简单的来说就是一个主节点master可以拥有一个甚至多个从节点的slave。 原理:从服务器都将向主服务器发送一个SYNC命令。接到 SYNC 命令的主服务器将开始执行 BGSAVE ,并在保存操作执行期间,将所有新执行的写入命令都保存到一个缓冲区里面。当 BGSAVE 执行完毕后,主服务器将执行保存操作所得的 .rdb 文件发送给从服务器,从服务器接收这个 .rdb 文件,并将文件中的数据载入到内存中。 其中主节点以写为主,从节点只能读不可写入。其中主节点写入的数据会同步到salve上,这样如果主节点出现故障,数据丢失,则可以通过salve进行恢复。
优点:主从模式很好的解决了数据备份问题,并且由于主从服务数据几乎是一致的,因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。 缺点:
本地Windows下测试步骤:
1.准备window for Redis分别命名为redis-6379,redis-6380
2.修改redis-6380下的redis.windows.conf
port 6380
# slaveof <masterip> <masterport>
slaveof 127.0.0.1 6379
启动server服务
port 6380 redis-server.exe redis.windows.conf
在port6379服务上set数据,在从服务上也能get到数据
* 主节点能读写,从节点只能读取不能写入
* 如果master设置了密码。需要配置:masterauth
为了解决Redis的主从复制的不支持高可用性能,Redis实现了哨兵机制解决方案。由一个或多个哨兵去监听任意多个主服务以及主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线的主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已经下线的从服务器,并且哨兵可以互相监视。
概念:
每个 Sentinel 以每秒钟一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个 PING 命令。
主观下线:指的是单个 Sentinel 实例对服务器做出的下线判断。
客观下线:指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断
优点
假如你 redis 只能存 5G 数据,可是你写了 10G,那会删 5G 的数据。怎么删的,这 个问题思考过么?还有,你的数据已经设置了过期时间,但是时间到了,内存占 用率还是比较高,有思考过原因么?
定时删除,用一个定时器来负责监视 key, 过期则自动删除。虽然内存及时释放, 但是十分消耗 CPU 资源。在大并发请求下,CPU 要将时间应用在处理请求,而不是删除 key, 因此没有采用这一策略。
定期删除,redis 默认每个100ms检查,是否有过期的 key, 有过期key则删除。需要说明的是,redis 不是每个100ms将所有的key检查一次,而是随机抽取进行检查 (如果每隔100ms, 全部key进行检查,redis 岂不是卡死)。因此,如果只采用 定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个 key 的时候,redis会检查一 下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
如果定期删除没删除 key。然后你也没即时去请求 key,也就是说惰性删除也没生效。这样,redis 的内存会越来越高。那么就应该采用内存淘汰机制。 在 redis.conf 中有一行配置 # maxmemory-policy volatile-lru 该配置就是配内存淘汰策略的noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
缓存雪崩: 由于原有缓存失效,新缓存未到期间 (例如:我们设置缓 存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库 CPU 和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。 解决方案: 1.考虑用加锁或者队列或者限流的方式的方式保证来保证不会有大量的线程对数据库一次性进行读写。 2.随机增加失效时间,比如 1-5 分钟,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
缓存穿透: 缓存穿透是指用户查询数据,在数据库没有,在缓存中没有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次 无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
** 缓存预热:** 缓存 预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求 的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据。
缓存更新:
如果您喜欢我的文章,请点击下面按钮随意打赏,您的支持是我最大的动力。
最新评论