本文用 1.5 W字介绍 Redis 缓存数据的基础知识,包括基础知识、基础操作、数据类型、通用指令、Jedis、缓存预热、缓存雪崩、缓存击穿、缓存穿透、事务、哨兵、主从复制等。
历史发布过 Java技术栈相关学习手册链接如下,可以先收藏,随后在看:
1. ElasticSearch 学习手册
下载与安装
https://github.com/microsoftarchive/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.zip
下载完成后解压即可。执行解压后文件夹中的 redis-server.exe 即可启动 redis:
出现下图则启动成功:
端口号默认为6379。
基本操作
既然是数据库,那就少不了与数据打交道,我们先来看看如何通过redis进行数据的保存和读取。
首先是保存数据:
set key value
redis中的数据都是以键值对进行存储的,所以在保存数据的时候只需要指定数据的键名和键值即可,读取的时候通过键名获取:
get key
若是指定的key不存在,则会返回空(nil)。
redis也提供了帮助命令,用于查询一些指令的用法:
help 指令名
比如想知道set指令的作用:
help set
得到结果:
SET key value [EX seconds] [PX milliseconds] [NX|XX]
summary: Set the string value of a key
since: 1.0.0
group: string
数据类型
redis有五种常用的数据类型:
-
string
-
hash
-
list
-
set
-
sorted_set
下面分别介绍这五种类型。
string
string是redis中最简单的数据类型,也是最常用的数据类型,比如:
set name zhangsan
我们还能一次性保存多个数据:
mset name lisi age 30 gender 1
这样我们就同时设置了三个键值,当然也可以一次性取出多个数据:
mget name age gender
还可以获取字符串长度,比如:
strlen name
需要注意的是当set保存的数据,其键名已经存在的情况下,新的值会覆盖旧的值,redis提供了一种追加方式,以适应更灵活的场景:
append name abc
此时会在key为name字符串后拼接上 abc
,若name不存在,则创建新的数据。
若是数据的键值为数字,则redis会提供一些数字特有的功能,但它本质上还是字符串:
set num 1
比如自增操作:
incr num
结果如下:
127.0.0.1:6379> set num 1
OK
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> incr num
(integer) 3
127.0.0.1:6379> incr num
(integer) 4
自减操作:
decr key
结果如下:
127.0.0.1:6379> decr num
(integer) 3
127.0.0.1:6379> decr num
(integer) 2
127.0.0.1:6379> decr num
(integer) 1
默认自增自减操作为加1或减1,若是想设置步长,则:
incrby num 2
结果如下:
127.0.0.1:6379> incrby num 2
(integer) 3
127.0.0.1:6379> incrby num 2
(integer) 5
127.0.0.1:6379> incrby num 2
(integer) 7
自减也是如此:
decrby num 2
步长还支持设置为小数,但incrby是不支持的,我们需要使用incrbyfloat:
incrbyfloat num 0.5
作为缓存数据库,redis的数据都是存储在内存中的,内存十分宝贵,所以我们不应该让一些垃圾数据还残留在redis中浪费资源,为此,我们需要为数据设置它的时效,即:时间一到,就删除对应的数据信息。
redis有两种方式设置数据的时效:
setex key seconds value
psetex key milliseconds value
这两种方式的区别在于时间单位不同,第一种设置的是秒,第二种设置的是毫秒,比如:
setex number 10 5
则5秒后,number数据将会被删除。
最后是对数据的删除操作:
del name
指定需要删除的键名,则执行该操作后,对应的数据会从redis中删除。
hash
对于一个商城的秒杀业务,其访问量无疑是巨大的,为此,我们应该将一些固定不变的信息提前抽取到redis中,而不是在用户进行秒杀活动的时候再去数据库查询。比如商品的名称、商品的描述、商品的秒杀价格等等,这些都是在活动即将开始之前就确定好并且不会改变的,我们将其存放到redis中:
set product:seckill:id:001:name 榨汁机
set product:seckill:id:001:name 美的榨汁机
set product:seckill:id:001:price 120
这是一种较为规范的存储方式,以表名加业务场景加属性名作为数据的键,我们也可以将所有数据封装成一段json数据进行存储:
set product:seckill:id:001 {id:001,name:榨汁机,desc:美的榨汁机,price:120}
然而对于json字符串方式的存储,其弊端是非常明显的,因为若是需要修改商品中的数据,则修改操作就会变得非常麻烦,为此,我们可以使用hash类型来存储。
hash类型不同于string,string的特点是一个键对应一个字符串,而hash一个键会对应一组数据,而这组数组也是以键值对的形式进行存储的,对于hash的保存数据和读取数据,只需要在 set
指令的基础上加一个 h
即可:
hset user name zhangsan
hset user age 20
其中 user
为整个hash类型数据的键名,而 name zhangsan
和 age 20
均为hash中的数据,这两个数据也是键值对的形式。需要注意的是,hset在保存数据时需要一个属性一个属性地进行保存,而不能这样做:
hset user name zhangsan age 20
若是想同时设置多个属性,需要使用 hmset
指令:
hmset user name zhangsan age 20
通过 hgetall
指令可以将属性一次性读取出来:
hgetall user
结果如下:
127.0.0.1:6379> hgetall user
1) "name"
2) "zhangsan"
3) "age"
4) "20"
当然也可以通过 hmget
指令获取所有的属性:
hmget user name age
这种方式相比 hgetall
更加地灵活,也可以读取指定的一个属性:
hget user name
hget user age
删除属性也是如此:
hdel user name
hdel user age
通过 hlen
可以获取hash中属性的数量:
hlen user
通过 hexists
指令可以判断hash中是否存在指定的属性:
hexists user name
存在返回1,不存在返回0。
基于hash的特殊结构,redis也提供了hash的特有功能,比如获取hash中的所有属性名和属性值:
hkeys user
hvals user
设置hash中的指定属性进行自增:
hincrby user age 1
hincrbyfloat user age 0.5
hash类型是不支持 hdecrby
和 hdecrbyfloat
的,所以若是想实现自减,可以这么做:
hincrby user age -1
list
list类型适用于存储多个数据,而且能够保持这些数据间的某些顺序。
list类型保持数据方式如下:
lpush key value1 [value2] ......
rpush key value1 [value2] ......
其中 lpush
表示从左侧添加, rpush
表示从右侧添加,比如:
lpush nums 1 2 3 4 5
使用 lrange
指令可以读取数据:
lrange nums 0 4
其中 0 4
表示需要读取的索引范围,结果如下:
127.0.0.1:6379> lrange nums 0 4
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
这说明 lpush
都是从左侧加入,也就是从序列的前面加入数据的,那么相应的:
rpush nums 1 2 3 4 5
rpush
就是从右侧加入,即:从序列的后面加入数据的,其读取顺序应为 1 2 3 4 5
,结果如下:
127.0.0.1:6379> lrange nums 0 4
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
list类型读取数据的方式如下:
lpop key
rpop key
分别对应从左边获取和从右边获取,若是保存了这样一个数据:
rpush nums 1 2 3 4 5
则 lpop
的效果如下:
127.0.0.1:6379> lpop nums
"1"
左边的第一个数据 1
就成功被获取了,注意了,通过 lpop
和 rpop
指令获取数据之后,该数据会从list中移除,我们再来试试 rpop
从右边获取,按道理获取到的应该就是 5
:
127.0.0.1:6379> rpop nums
"5"
最后看看list中的数据发生了什么变化:
127.0.0.1:6379> lrange nums 0 4
1) "2"
2) "3"
3) "4"
有时候我们需要移除中间的某个数据,那么 lpop
和 rpop
肯定是无能为力了,为此,我们需要使用 lrem
指令:
lrem nums 1 2
它表示从nums中移除一个数据 2
,因为list是允许数据重复出现的,所以需要指定移除的数据数量。
我们还能通过 lindex
指令获取指定索引上的数据:
lindex nums 0
通过llen
指令获取list的长度,即:数据的个数:
llen nums
list类型也有其特有的功能,它可以在规定时间内获取并移除数据,实现如下:
blpop key1 [key2] timeout
brpop key1 [key2] timeout
比如:
blpop nums nums2 num3 30
它表示从nums、nums2、nums3的左边获取数据,在30秒之内,若是30秒过后获取不到数据,则输出nil;若是在某个时间内获取到了数据,则直接结束,输出内容。
set
set类型同样提供大量数据的存储,但set的优势在于查询速度更快,list类型底层实质上是一个双向链表,我们都知道,链表的查询效率是比较低的,所以若是出于性能的考虑,set绝对是更胜一筹。
set类型保持数据的方式如下:
sadd key member1 [member2]
比如:
sadd nums 1 2 3
使用 smembers
指令可以获取到set中的所有数据:
smember nums
删除数据:
srem nums 1 2
表示删除nums中的数据 1
和 2
。
获取set的长度:
scard nums
判断set中是否包含指定的数据:
sismember nums 1
set类型比较特别的地方在于它能够进行随机操作,比如随机获取set中指定数量的数据:
srandmember nums 5
它表示从nums中随机获取5个数据,这5个数据获取后并不会消失,仍然存在nums中,相较于接下来的这种方式:
spop nums 5
它表示从nums中随机获取5个数据,但这些数据都会从nums中移除掉。
sorted_set
sorted_set类型支持存储大量的数据,同时还提供这些数据按某种方式进行排序的功能。
其保存数据的方式如下:
zadd key score1 member1 [score2 member2]
比如:
zadd scores 95 chinese 98 math 85 english
通过 zrange
指令获取全部数据:
zrange scores 0 -1
携带 withscores
还能够将数据的分数输出:
zrange scores 0 -1 withscores
结果如下:
127.0.0.1:6379> zrange scores 0 -1 withscores
1) "english"
2) "85"
3) "chinese"
4) "95"
5) "math"
6) "98"
通过 zrevrange
指令可以以逆序的方式获取数据:
zrevrange scores 0 -1 withscores
删除数据:
zrem scores chinese math
这里需要注意一点,在保存数据的时候每个数据前都跟着一个数字,比如:95 chinese
,这个95其实是一个分数,它并不具有特殊的含义,通过该分数,使得sorted_set类型具有一些特殊的功能。
首先准备一些数据:
zadd scores 100 zhangsan 90 lisi 95 wangwu
比如查找分数在95以下的数据:
zrangebyscore scores 0 95
其中scores是key, 0 95
是查找范围,结果如下:
127.0.0.1:6379> zrangebyscore scores 0 95
1) "lisi"
2) "wangwu"
它还能够通过 limit
来限定查询的结果,比如查询分数在100以下的前两个数据:
zrangebyscore scores 0 100 limit 0 2
按条件删除数据:
zremrangebyscore scores 0 95
表示删除95分以下的数据,它也支持按照索引删除数据,比如:
zremrangebyrank scores 0 2
它表示删除索引0到索引2的数据。
通用指令
对于各个数据类型,redis提供了各自的指令来操作,而对于所有的数据类型,它们都有着一些通用的指令用来控制,一起来看看吧。
首先是删除指定的key:
del key
判断指定的key是否存在:
exists key
获取key的类型:
type key
key的时效性操作在前面已经简单接触了一下,现在来仔细了解了解,首先是为key设置有效时间:
expire key seconds # 设置有效时间,单位:秒
pexpire key milliseconds # 设置有效时间,单位:毫秒
expireat key timestamp # 设置时间戳,单位:秒
pexpireat key milliseconds-timestamp # 设置时间戳,单位:毫秒
获取key的剩余有效时间:
ttl key # 返回有效时间,单位:秒
pttl key # 返回有效时间,单位:毫秒
需要注意的是,这两个指令都能够返回key的剩余有效时间,所以若是key不存在,则返回-2;若是key存在但未设置有效时间,则返回-1;否则返回key的剩余有效时间。
将key从时效性切换为永久性:
persist key
redis提供了一些常用的查询指令帮助我们了解key的信息,比如查询指定条件的key:
keys pattern
其中pattern是匹配模式,若是指定为 *
则查询所有key:
keys *
它提供了三种匹配模式:
-
*:匹配任意数量的任意符号
-
?:匹配一个任意符号
-
[]:匹配一个指定符号
为key修改名字:
rename key newkey
renamenx key newkey
需要注意 rename
指令将当前key修改为已经存在的key时,该key的值会被覆盖,而 renamenx
会报错,所以 renamenx
能够避免覆盖的情况发生。
对所有key排序:
sort
随着数据量的逐渐增大,key极易出现重复、出错的情况,大量的数据混杂在一起也很难分别处理,为此,redis提供了数据库的概念。redis为每个服务提供了16个数据库,编号为0~15,每个数据库之间的数据是相互独立的。
切换数据库:
select index
默认使用的是0号数据库,若是想切换至3号,则:
select 3
数据移动:
move key db
将当前数据库指定的key移动到指定的数据库,比如将 name
移动到3号数据库(移动之后,原数据库的key就不存在了):
move name 3
数据清除:
dbsize # 返回当前数据库的key数量
flushdb # 清空当前数据库的key
flushall # 清空所有数据库的key
Jedis
在项目开发中,我们需要使用Java来帮助我们操作redis,所以来了解一下Java操作redis的工具——Jedis。
引入依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
首先来测试一下能够连接成功:
@Test
public void testJedis(){
// 连接redis
Jedis jedis = new Jedis("127.0.0.1", 6379);
System.out.println(jedis.ping());
}
输出结果:
PONG
输出 PONG
则说明连接成功了,那么jedis该如何操作redis呢?
操作方法与在redis中的操作一模一样,所以我们可以直接调用同名的方法即可,比如保存一个string类型的数据:
@Test
public void testJedis(){
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.set("name","zhangsan");
String name = jedis.get("name");
System.out.println(name);
}
若是保存list数据,则调用 lpush
或 rpush
方法:
@Test
public void testJedis(){
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.lpush("nums","1","2","3","4","5");
// 获取所有数据
List<String> nums = jedis.lrange("nums",0,-1);
for (String num : nums) {
System.out.println(num);
}
}
若是保存hash数据,则调用 hset
方法:
@Test
public void testJedis() {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.hset("user", "name", "zhangsan");
jedis.hset("user", "age", "20");
String name = jedis.hget("user", "name");
String age = jedis.hget("user", "age");
System.out.println(name + ":" + age);
}
因为需要频繁操作jedis,所以我们可以为其编写一个简单的工具类:
package com.wwj.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
public class JedisUtil {
private static JedisPool jedisPool = null;
private static final String host;
private static final Integer port;
private static final Integer maxTotal;
private static final Integer maxIdle;
/**
* 加载连接池,只执行一次
*/
static {
// 加載配置文件
ResourceBundle bundle = ResourceBundle.getBundle("redis");
host = bundle.getString("redis.host");
port = Integer.parseInt(bundle.getString("redis.port"));
maxTotal = Integer.parseInt(bundle.getString("redis.maxTotal"));
maxIdle = Integer.parseInt(bundle.getString("redis.maxIdle"));
// 配置连接池信息
JedisPoolConfig config = new JedisPoolConfig();
// 设置最大连接数
config.setMaxTotal(maxTotal);
// 设置活动连接数
config.setMaxIdle(maxIdle);
jedisPool = new JedisPool(config, host, port);
}
/**
* 获取Jedis连接
*
* @return
*/
public static Jedis getJedis() {
return jedisPool.getResource();
}
}
配置文件:
redis.host=127.0.0.1
redis.port=6379
redis.maxTotal=30
redis.maxIdle=10
Linux下的Redis
现在我们已经对redis有了一个大致的认识,下面我们就在linux环境下来看看redis的一些更加高级的操作,首先下载redis的压缩包:
wget http://download.redis.io/releases/redis-4.0.0.tar.gz
解压一下:
tar -zxvf redis-4.0.0.tar.gz
进入解压后的目录,然后进行编译安装:
cd redis-4.0.0
make install
首先将redis中的配置文件复制一份:
cp redis.conf redis-6379.conf
并修改配置文件:
daemonize yes # 以守护进程的方式启动
logfile "6379.log" # 指定日志文件名
dir /opt/redis-4.0.0/logs # 指定日志的存放位置
此时以该配置文件启动redis:
redis-server redis-6379.conf
我们可以查看日志来判断redis是否成功启动了:
cd /opt/redis-4.0.0/logs
cat 6379.log
日志内容:
8454:C 15 Mar 02:57:25.275 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
8454:C 15 Mar 02:57:25.276 # Redis version=4.0.0, bits=64, commit=00000000, modified=0, pid=8454, just started
8454:C 15 Mar 02:57:25.276 # Configuration loaded
8455:M 15 Mar 02:57:25.279 * Increased maximum number of open files to 10032 (it was originally set to 1024).
8455:M 15 Mar 02:57:25.281 # Creating Server TCP listening socket 127.0.0.1:6379: bind: Address already in use
通过日志我们发现,6379端口好像被占用了,我们只需查看哪个应用占用了6379端口,并将其kill掉然后重新启动redis即可。
通过配置文件启动的方式可以非常方便地实现多个redis服务的启动,现在复制一个刚才的配置文件:
cp redis-6379.conf redis-6380.conf
然后修改redis-6380.conf:
port 6380
把端口修改为6380,然后启动该redis服务:
redis-server redis-6380.conf
logfile "6380.log"
同样检查日志即可判断redis服务是否成功启动。
服务启动完成后,若是想连接redis进行操作,则:
redis-cli -p 6379
若是不指定端口号,则默认使用6379端口进行连接。
持久化
RDB
在前面我们已经设置了 dir
的配置值为 /opt/redis-4.0.0/logs
,所以redis生成的rdb文件也会在该目录出现,我们使用客户端连接上一个redis服务:
redis-cli -p 6380
然后设置几个数据:
set name zhangsan
set age 20
执行 save
指令即可进行持久化,此时在logs目录下即可看到持久化文件:
[root@centos-7 logs]# ls
6379.log 6380.log dump.rdb
该文件的相关信息可以在配置文件中进行设置,比如:
dbfilename dump.rdb # 持久化文件名,默认为dump.rdb
dir ./ # 文件存放位置,默认为当前目录
rdbcompression yes # 设置存储至本地时是否压缩数据,默认为yes,采用LZF压缩
rdbchecksum yes # 设置是否进行RDB文件格式校验,默认为yes
修改一下6380端口的配置文件:
dbfilename dump-6380.rdb
bgsave
,该指令的执行将会在后台进行,当我们执行该指令后,redis会调用fork函数生成一个子进程,然后在子进程中创建RDB文件。bgsave指令也有一个可配置项:
stop-writes-on-bgsave-error yes
bgsave
指令能够备份数据,然而手动执行备份指令并不好,为此,可以让redis自动为我们进行备份,还可以设置备份的频率和触发条件。需要在配置文件中进行配置:save 10 2
它表示在10秒的时间内若是有2个key及以上的数据发生了变化,无论是添加、修改还是删除,只要发生了变化,那么就会触发自动备份。
AOF
-
always:每次写入操作均同步到AOF文件中,数据零误差,性能较低
-
everysec:每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能较高,但在系统突然宕机的情况下会丢失1秒的数据
-
no:由操作系统控制每次同步到AOF文件的周期,整体过程不可控
appendonly yes # 开启AOF
appendfsync always # 指定策略为每次都同步
logs
目录:[root@centos-7 logs]# ll
总用量 28
-rw-r--r--. 1 root root 4650 3月 15 03:35 6379.log
-rw-r--r--. 1 root root 14704 3月 15 19:41 6380.log
-rw-r--r--. 1 root root 0 3月 15 19:41 appendonly.aof
-rw-r--r--. 1 root root 184 3月 15 19:31 dump-6380.rdb
appendonly.aof
文件,这就是AOF的持久化文件,接下来我们连接客户端,并写入一个数据后,再观察该文件:[root@centos-7 logs]# ll
总用量 32
-rw-r--r--. 1 root root 4650 3月 15 03:35 6379.log
-rw-r--r--. 1 root root 14704 3月 15 19:41 6380.log
-rw-r--r--. 1 root root 60 3月 15 19:42 appendonly.aof
-rw-r--r--. 1 root root 184 3月 15 19:31 dump-6380.rdb
appendfilename appendonly-6380.aof
随着指令不断写入AOF,文件也会越来越大,但有时候会出现这样一种情况:
set name zs
set name ls
set name ww
set
指令,但事实上,它的效果等价于一条指令:set name ww
,因为后面的数据覆盖了前面的数据,那么如果AOF记录了这三条指令,很显然就造成了资源的浪费。为此,redis提供了AOF重写机制,它能够将对同一个数据的若干条指令转换为最终结果数据对应的指令记录,这样便极大地压缩了AOF文件的体积。bgrewriteaof
事务
比如这样的一个现象:
127.0.0.1:6380> set name zhangsan
OK
127.0.0.1:6380> get name
"lisi"
127.0.0.1:6380> multi
OK
127.0.0.1:6380> set name zhangsan
QUEUED
127.0.0.1:6380> get name
QUEUED
127.0.0.1:6380> exec
1) OK
2) "zhangsan"
multi
指令开启事务,然后添加数据,在获取数据之前同样另一个客户端修改了数据,最后执行exec
指令提交事务,redis便会输出在这次事务中所有指令的结果,可以看到数据并没有被别的客户端修改。discard
指令来取消此时事务,取消之后事务中的所有指令操作都将失效。而且在事务过程中,执行了语法错误的指令,比如set
打成了sat
,redis会自动帮助我们取消事务,又比如对string类型的数据执行incr
操作,redis会自动执行事务中正确的操作指令,并取消执行那些错误的指令。
锁
watch
指令可以监控某个数据,当数据发生变化时取消所有操作,比如:set name zs
watch name
multi
set age 20
exec
127.0.0.1:6380> set name zs
OK
127.0.0.1:6380> watch name
OK
127.0.0.1:6380> multi
OK
127.0.0.1:6380> get name
QUEUED
127.0.0.1:6380> set age 20
QUEUED
127.0.0.1:6380> exec
(nil)
127.0.0.1:6380> keys *
1) "name"
unwatch
指令可以取消所有数据的监控。
分布式锁
setnx
指令设置一个分布式锁:setnx lock-key value
比如这样的一组指令:
127.0.0.1:6380> setnx lock-num true
(integer) 1
127.0.0.1:6380> incrby num -1
(integer) 9
127.0.0.1:6380> del lock-num
(integer) 1
127.0.0.1:6380> setnx lock-num true
(integer) 0
死锁
expire
指令为分布式锁设置一个有效时间,当有效时间过后redis便会自动删除该锁,这样就解决了死锁问题:expire lock-num 60
当60秒时间过后,锁会被自动删除。
服务器配置
配置项 | 说明 |
---|---|
daemonize yes | no |
bind 127.0.0.1 | 绑定主机地址 |
port 6379 | 设置服务器端口号 |
databases 16 | 设置数据库数量 |
loglevel debug | verbose |
logfile 端口号.log | 设置日志文件名 |
maxclients 0 | 设置同一时间最大客户端连接数,默认无限制,当客户端连接达到上限时,redis会关闭新的连接 |
timeout 300 | 客户端闲置等待最大时长,达到最大值后关闭连接,如需关闭该功能, 设置为0 |
include /path/server-端口号.conf | 导入并加载指定配置文件信息,用于快速创建redis公共配置较多的redis实例配置文件,便于维护 |
主从复制
redis-cli -p 6381
slaveof 127.0.0.1 6380
这里表示使用6381端口连接6380端口,作为它的从机,此时我们再来到master窗口,连接客户端:
redis-cli -p 6380
set name zs
会发现,slave窗口中6381端口的redis也能够获取到该数据,此时证明主从搭建好了。
我们也可以在启动redis服务的时候就进行连接:
redis-server redis-6381.conf --slaveof 127.0.0.1 6380
redis推荐使用配置文件的方式搭建主从结构,修改redis-6381.conf:
slaveof 127.0.0.1 6380
此时6381就成了6380的从机了。
哨兵
redis-sentinel sentinel.conf
哨兵的客户端链接方式:
redis-cli -p 26379
需要注意的是哨兵客户端不支持数据操作,它只作监控用途。
企业级解决方案
下面介绍一些企业中常用的redis解决方案,这也是一些岗位面试的重点。
缓存预热
缓存雪崩
-
构建多级缓存的架构
-
优化数据库 耗时的业务
-
监控redis服务性能指标
-
对服务进行限流、降级
缓存击穿
缓存穿透
-
增加校验逻辑,如检验id是否合法
-
当数据从缓存和数据库中都未获取到时,将key的value值写为null,这样可以防止攻击者反复使用一个id进行攻击
-END-
如果看到这里,说明你喜欢这篇文章,请 转发、点赞。微信搜索「web_resource」,关注后回复「进群」或者扫描下方二维码即可进入无广告交流群。
↓扫描二维码进群↓
推
荐
阅
读
1. 一份 Spring Boot 项目搭建模板
2.
Spring Boot 实现应用监控和报警
3. Nginx 从入门到实战
4. 一键式搭建分布式文件服务器
5. 团队开发中 Git 最佳实践
喜欢文章,点个
在看
本文分享自微信公众号 - Java后端(web_resource)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。