1 Redis 介绍
-
什么是redis
- Redis是用C语言开发的一个开源的高性能键值对(key-value)内存数据库。
- 它提供五种数据类型来存储值:字符串类型(String)、散列类型(hash)、列表类型(list)、集合类型(set)、有序集合类型(zset)
- 它是一种NoSQL数据库
-
什么是NoSQL
-
NoSQL,即Not-Only SQL(不仅仅是SQL),泛指非关系型的数据库。
- 什么是关系型数据库?数据结构是一种有行有列的数据库
- NoSQL数据库是为了解决高并发、高可用、高可扩展、大数据存储问题而产生的数据库解决方案。
- NoSQL可以作为关系型数据库的良好补充,但是不能替代关系型数据库。
- Redis的应用场景
- 内存数据库(登录信息、购物车信息、用户浏览记录等)
- 缓存服务器(商品数据、广告数据等等)。(最多使用)
- 解决分布式集群架构中的session分离问题(session共享)。
- 任务队列。(秒杀、抢购、12306等等)
- 支持发布订阅的消息模式
- 应用排行榜。
- 网站访问统计。
- 数据过期处理(可以精确到毫秒)
- Redis单机版安装配置
4.1 Redis 下载
*官网地址 http://redis.io/
*中文官网地址 http://www.redis.cn/
*下载地址 http://download.redis.io/releases/
4.2 Redis安装
-
第一步:在VMware中安装CentOS(参考Linux教程中的安装虚拟机)
-
第二步:安装C语言需要的GCC环境 yum install gcc-c++
-
第三步:解压缩Redis源码压缩包 tar -zxf redis-3.2.9.tar.gz
-
第四步:编译Redis源码,进入redis-3.2.9目录,执行编译命令 make
-
第五步:安装Redis,需要通过PREFIX指定安装路径 make install PREFIX=/usr/local/redis
4.3 Redis 启动
1. 前台启动
启动命令:redis-server,直接运行bin/redis-server将以前端模式启动。
关闭命令:ctrl+c
启动缺点:客户端窗口关闭则redis-server程序结束,不推荐使用此方法
启动图例
- 后端启动(守护进程启动)
-
第一步:拷贝redis-3.2.9/redis.conf配置文件到Redis安装目录的bin目录
cp redis.conf /usr/local/redis/bin/ -
第二步:修改redis.conf,将daemonize由no改为yes
修改redis.conf中的61行代码
bind 127.0.0.1
改为
bind <redis实例所在机器的真实IP>,比如192.168.10.133 -
第三步:执行命令
./redis-server redis.conf
后端启动关闭:
./redis-cli shutdown
- 其他命令
- redis-server :启动redis服务
- redis-cli :进入redis命令客户端
- redis-benchmark: 性能测试的工具
- redis-check-aof : aof文件进行检查的工具
- redis-check-dump : rdb文件进行检查的工具
- redis-sentinel : 启动哨兵监控服务
4.4 Redis客户端
- 自带命令行客户端
-
命令格式
./redis-cli -h 127.0.0.1 -p 6379 -
修改redis配置文件(解决IP绑定问题)
bind 127.0.0.1 绑定的IP才能访问redis服务器,注释掉该配置
protected-mode yes 是否开启保护模式,由yes该为no
- 远程连接redis服务,需要关闭或者修改防火墙配置
- 第一步:编辑iptables
vim /etc/sysconfig/iptables - 第二步:重启防火墙
service iptables restart
5.多数据库支持
默认一共是16个数据库,每个数据库之间是相互隔离(但是可以使用flushall一次清空所有的库)。数据库的数量是在redis.conf中配置的。
切换数据库使用命令:
select 数据库编号(0-15)
-
Jedis 操作Redis的java客户端
添加jar包
-
单实例连接
注意事项:需要去设置redis服务器的防火墙策略(临时关闭、永久关闭、端口暴露)
-
连接池连接
-
Spring整合JedisPool
添加spring的jar包
配置spring配置文件applicationContext.xml连接池和单机配置:
测试代码
5.Redis数据类型
Redis中存储数据是通过key-value格式存储数据的,其中value可以定义五种数据类型:
- String(字符类型)
- Hash(散列类型)
- List(列表类型)
- Set(集合类型)
- SortedSet(有序集合类型,简称zset)
注意:在redis中的命令语句中,命令是忽略大小写的,而key是不忽略大小写的。
- String
赋值:SET key value
取值:GET key
数值的增减:INCR key/DECR key
数值的增减指定的数值::INCR key num/DECR key num
仅当不存在时赋值:setnx key value(实现分布式锁的功能)
向尾部追加值:APPEND key value
获取字符串的长度:STRLEN key
同时设置/获取多个键值:MSET key value [key value …]/MGET key [key …] - Hash类型
- 一次设置一个字段值
HSET key field value - 一次设置多个字段值
HMSET key field value [field value …] - 当字段不存在时赋值(如果字段存在,该命令不执行任何操作)
HSETNX key field value - 一次取一个字段的值
hget user username - 一次取多个字段的值
HMGET key field [field …] - 获取所有字段值
HGETALL key - 删除一个字段或多个字段,返回值为删除的字段的个数
HDEL key field [field …] - 增加数字
HINCRBY key field num - 判断字段是否存在
HEXISTS key field - 只获取字段名或字段值
HKEYS key/HVALS key - 获取字段数量
HLEN key - 获取所有字段(获得hash的所有信息,包括key和value)
hgetall key
- List类型
- ArrayList与LinkedList的区别
-
ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
-
LinkedList使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。
3.1 List介绍
Redis的列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。3.2 命令
- 向列表的左边添加元素
LPUSH key value [value …] - 向列表的右边添加元素
RPUSH key value [value …] - 查看列表(索引从零开始)
LRANGE key start stop - 从列表左边/右边移除值
LPOP key/RPOP key - 获取列表中元素的个数
LLEN key - 删除列表中指定个数的值(LREM命令会删除列表中前count个值为value的元素)
LREM key count value- 当count>0时, LREM会从列表左边开始删除。
- 当count<0时, LREM会从列表后边开始删除。
- 当count=0时, LREM删除所有值为value的元素。
- 获得/设置指定索引的元素值
LINDEX/LSET key index - 只保留列表指定片段
LTRIM key start stop - 向列表中插入元素(从左到右查找值为pivot的元素,然后根BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。)
LINSERT key BEFORE|AFTER pivot value - 将元素从一个列表转移到另一个列表中
RPOPLPUSH source destination
- 向列表的左边添加元素
4.Set类型(set类型即集合类型,其中的数据是不重复且没有顺序。)
- 增加/删除元素
SADD/SREM key member [member …] - 获得集合中的所有元素
SMEMBERS key - 判断元素是否在集合中
SISMEMBER key member - 集合的差集运算 A-B
SDIFF key [key …] - 属于A且属于B的元素构成的集合。
SINTER key [key …] - 属于A或者属于B的元素构成的集合
SUNION key [key …] - 获得集合中元素的个数
SCARD key - 从集合中弹出一个元素(SPOP命令会从集合中随机选择一个元素弹出 )
SPOP key
- Zset类型
有序集合(有序集合类型为集合中的每个元素都关联一个分数)
- 增加元素
ZADD key score member [score member …] - 获得排名在某个范围的元素列表(需要获得元素的分数的可以在命令尾部加上WITHSCORES参数)
ZRANGE key start stop [WITHSCORES] - 获取元素的分数
ZSCORE key member - 删除元素
ZREM key member [member …] - 获得指定分数范围的元素
ZRANGEBYSCORE key min max WITHSCORES - 增加某个元素的分数
ZINCRBY key num member - 获得集合中元素的数量
ZCARD key - 获得指定分数范围内的元素个数
ZCOUNT key min max - 按照排名范围删除元素
ZREMRANGEBYRANK key start stop - 按照分数范围删除元素
ZREMRANGEBYSCORE key min max - 获取元素的排名(从小到大)
ZRANK key member - 获取元素的排名(从大到小)
ZREVRANK key member
- 通用命令
- 返回满足给定pattern 的所有key
keys pattern - 删除key
DEL key - 确认一个key 是否存在
exists key - 到期后数据销毁(缓存)
EXPIRE key seconds - 查看key剩余于的生存时间
TTL key - 清除生存时间
PERSIST key - 重命名key
rename oldkey newkey - 显示指定key的数据类型
type key
6.Redis事务
- Redis事务介绍
- Redis的事务是通过MULTI,EXEC,DISCARD和WATCH这四个命令来完成的。
- Redis的单个命令都是原子性的,所以这里确保事务性的对象是命令集合。
- Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
- Redis不支持回滚操作
- 命令介绍
- MULTI(用于标记事务块的开始)
Redis会将后续的命令逐个放入队列中,然后使用EXEC命令原子化地执行这个命令序列。 - EXEC
在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态 - DISCARD
清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。 - WATCH(使用该命令可以实现redis的乐观锁。)
当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的状态。 - UNWATCH
清除所有先前为一个事务监控的键。
- 为什么redis不支持事务回滚?
- 大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的
- redis为了性能方面就忽略了事务回滚
7.Redis实现分布式锁
- 锁的处理
- 单应用中使用锁:单进程多线程
synchronize、Lock - 分布式应用中使用锁:多进程
-
分布式锁的实现方式
- 基于数据库的乐观锁实现分布式锁
- 基于zookeeper临时节点的分布式锁
- 基于redis的分布式锁
-
分布式锁的注意事项
- 互斥性:在任意时刻,只有一个客户端能持有锁(SETNX)
- 同一性:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
- 可重入性:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。(设置过期时间,防止一加锁进程崩溃,其它进程阻塞,出现死锁)
-
实现分布式锁
- 获取锁
- 释放锁
- 获取锁
8.Redis持久化方案
- RDB方式(默认的持久化方式)
-
RDB方式是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
-
Redis会在指定的情况下触发快照
- 符合自定义配置的快照规则
在redis.conf中设置自定义快照规则- save n m 每n秒钟至少有m个键被更改
- 配置dir指定rdb快照文件的位置
- 配置dbfilename指定rdb快照文件的名称
- 执行save或者bgsave命令
- 执行flushall命令
- 执行主从复制操作
- 符合自定义配置的快照规则
-
快照的实现原理
- redis使用fork函数复制一份当前进程的副本(子进程)
- 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数 据写入硬盘中的临时文件。
- 当子进程写入完所有数据后会用该临时文件替换旧的RDB文件,至此,一次快照操作完成。
-
注意事项
- redis在进行快照的过程中不会修改RDB文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候RDB文件都是完整的。
- 这就使得我们可以通过定时备份RDB文件来实现redis数据库的备份, RDB文件是经过压缩的二进制文件,占用的空间会小于内存中的数据,更加利于传输。
- 缺点
使用RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这个时候我们就需要根据具体的应用场景,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受范围。如果数据相对来说比较重要,希望将损失降到最小,则可以使用AOF方式进行持久化
- 优点
RDB可以最大化Redis的性能:父进程在保存RDB文件时唯一要做的就是fork出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无需执行任何磁盘I/O操作。同时这个也是一个缺点,如果数据集比较大的时候,fork可以能比较耗时,造成服务器在一段时间内停止处理客户端的请求;
- AOF方式
- 开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件,这一过程显然会降低Redis的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高AOF的性能。
- 默认情况下是关闭的(可以通过修改redis.conf配置文件中的appendonly参数开启 appendonly yes)
- AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数(dir ./)设置的。
- 默认的文件名是appendonly.aof,可以通过appendfilename参数(appendfilename appendonly.aof)修改:
- AOF重写原理
- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写
- 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。
- 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中, 会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
- AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松
- 参数说明
- auto-aof-rewrite-percentage 100 表示当前aof文件大小超过上一次aof 文件大小的百分之多少的时候会进行重写。如果之前没有重写过,以启动时aof文件大小为准
- auto-aof-rewrite-min-size 64mb 限制允许重写最小aof文件大小,也就是文件大小小于64mb的时候,不需要进行优化
-
同步磁盘数据
-
Redis每次更改数据的时候, aof机制都会将命令记录到aof文件,但是实际上由于操作系统的缓存机制,数据并没有实时写入到硬盘,而是进入硬盘缓存。再通过硬盘缓存机制去刷新到保存到文件
-
参数说明:
* appendfsync always 每次执行写入都会进行同步 , 这个是最安全但是是效率比较低的方式
* appendfsync everysec 每一秒执行
* appendfsync no 不主动进行同步操作,由操作系统去执行,这个是最快但是最不安全的方式
-
-
AOF文件损坏以后如何修复
redis-check-aof --fix readonly.aof -
如何选择RDB和AOF
- 一般来说,如果对数据的安全性要求非常高的话,应该同时使用两种持久化功能。
- 如果可以承受数分钟以内的数据丢失,那么可以只使用 RDB 持久化。
- 有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快 。
- 两种持久化策略可以同时使用,也可以使用其中一种。如果同时使用的话, 那么Redis重启时,会优先使用AOF文件来还原数据
9.Redis的主从复制
-
什么是主从复制
持久化保证了即使redis服务重启也不会丢失数据,因为redis服务重启后会将硬盘上持久化的数据恢复到内存中,但是当redis服务器的硬盘损坏了可能会导致数据丢失,不过通过redis的主从复制机制就可以避免这种单点故障,如下图
说明:
- 主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务。
- 主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上。
- 只有一个主redis,可以有多个从redis。
- 主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求
- 一个redis可以即是主又是从,如下图:
-
主从配置
- 主redis配置
无特殊配置 - 从redis配置
修改从服务器上的redis.conf文件
slaveof 192.168.101.3 6379
上边的配置说明当前【从服务器】对应的【主服务器】的IP是192.168.101.3,端口是6379
- 主redis配置
-
实现原理
-
Redis的主从同步,分为全量同步和增量同步。
-
只有从机第一次连接上主机是全量同步
-
断线重连有可能触发全量同步也有可能是增量同步(master判断runid是否一致)
-
除此之外的情况都是增量同步
- 全量同步
Redis的全量同步过程主要分三个阶段:- 同步快照阶段:Master创建并发送快照给Slave,Slave载入并解析快 照。Master同时将此阶段所产生的新的写命令存储到缓冲区。
- 同步写缓冲阶段:Master向Slave同步存储在缓冲区的写操作命令。
- 同步增量阶段:Master向Slave同步写操作命令。
- 全量同步
- 增量同步
- Redis增量同步主要指Slave完成初始化后开始正常工作时,Master发生的写操作同步到Slave的过程。
- 通常情况下,Master每执行一个写命令就会向Slave发送相同的写命令,然后Slave接收并执行。
10.Redis Sentinel哨兵机制
Redis主从复制的缺点:没有办法对master进行动态选举,需要使用Sentinel机制完成动态选举
- 简介
- Sentinel(哨兵)进程是用于监控redis集群中Master主服务器工作的状态
- 在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用(HA)
- 其已经被集成在redis2.6+的版本中,Redis的哨兵模式到了2.8版本之后就稳定了下来。
-
哨兵进程的作用
- 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
- 提醒(Notification): 当被监控的某个Redis节点出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作。
- 它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master;
- 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用现在的Master替换失效Master。
- Master和Slave服务器切换后,Master的redis.conf、Slave的redis.conf和sentinel.conf的配置文件的内容都会发生相应的改变,即,Master主服务器的redis.conf配置文件中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换。
-
哨兵进程的工作方式
- 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。
- 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)。
- 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态。
- 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)。
- 在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
- 当Master主服务器被 Sentinel(哨兵)进程标记为客观下(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
- 若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
-
启动哨兵服务
./redis-sentinel sentinel.conf
11.Redis 集群
redis cluster集群保证了高可用、高性能、高可扩展性。
-
redis-cluster架构图
-
架构细节
- 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
- 节点的fail是通过集群中超过半数的节点检测失效时才生效.
- 客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
- redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value
- Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点
- redis-cluster投票:容错
最小节点数:3台
- 节点失效判断:集群中所有master参与投票,如果半数以上master节点与其中一个master节点通信超过(cluster-node-timeout),认为该master节点挂掉.
- 集群失效判断:什么时候整个集群不可用(cluster_state:fail)?
- 如果集群任意master挂掉,且当前master没有slave,则集群进入fail状态。也可以理解成集群的[0-16383]slot映射不完全时进入fail状态。
- 如果集群超过半数以上master挂掉,无论是否有slave,集群进入fail状态。
-
安装Ruby环境
redis集群需要使用集群管理脚本redis-trib.rb,它的执行相应依赖ruby环境。- 第一步:安装ruby
yum install ruby
yum install rubygems - 第二步:安装ruby和redis的接口程序redis-3.2.2.gem
gem install redis -V 3.2.2 - 第三步:复制redis-3.2.9/src/redis-trib.rb文件到/usr/local/redis目录
cp redis-3.2.9/src/redis-trib.rb /usr/local/redis-cluster/ -r
- 第一步:安装ruby
-
安装Redis集群(RedisCluster)
12.Redis 消息模式
- 队列模式
使用list类型的lpush和rpop实现消息队列
注意事项:
- 消息接收方如果不知道队列中是否有消息,会一直发送rpop命令,如果这样的话,会每一次都建立一次连接,这样显然不好。
- 可以使用brpop命令,它如果从队列中取不出来数据,会一直阻塞,在一定范围内没有取出则返回null、
- 发布订阅模式(类似于主题模式)
- 订阅消息(subscribe)
- 发布消息(publish)
- Redis发布订阅命令
13.缓存穿透、缓存击穿、缓存失效
-
缓存数据的步骤
- 查询缓存,如果没有数据,则查询数据库
- 查询数据库,如果数据不为空,将结果写入缓存
-
缓存穿透(key对应的数据缓存里不存在,数据库里也不存在,空值不会写入缓存的.)
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。- 解决思路
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。(–存在问题,每次 请求不同的Key对应的数据都为空,依旧会穿透)
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。(布隆表达式)
- 解决思路
-
缓存雪崩
- 什么叫缓存雪崩?
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。 - 如何解决?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
3:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期(此点为补充)
- 缓存击穿
- 什么叫缓存击穿?
-
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
-
缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
-
如何解决?
使用redis的setnx互斥锁先进行判断,这样其他线程就处于等待状态,保证不会有大并发操作去操作数据库。
-
if(redis.sexnx()==1){
//查询数据库
//加入线程
}