最近在看redis,花了点时间整理了一下学习内容,都是一些基础,想学的可以看下
本帖最后由 chengxuyuan01 于 2022-6-29 14:46 编辑# redis使用
## 安装部署Linux
系统:linux(centos)
下载地址:https://codeload.github.com/redis/redis/tar.gz/refs/tags/7.0.0
前提:
```shell
#redis 依赖于c++环境
yum install gcc-c++
#编译依赖
yum install make
make
# redis 默认安装路径 /usr/local/bin
make install
# redis 默认不是在后台运行的
# 修改启动方式为后台启动
# 修改conf文件中daemonize yes
# redis-server redis_conf/redis.conf 使用指定配置文件启动
# redis-cli -p 6379测试连接
# 连接中输入 shutdown 关闭redis-server
# redis-benchmark redis官方自带压力测试工具
#redis-benchmark -h localhost -p 6379 -c 100 -n 100000 本机并发100000条操作
```
## 基础知识
默认数据库数:16
查看库大小:dbsize
切换数据库:select index(数据库索引)
清空当前数据库:flushdb
清空所有数据库:flushall
单线程操作
> 为什么redis单线程响应速度还这么快?
redis将所有数据全部放在内存中的,对内存系统来说,如果不存在上下文切换,效率就是最高的(cpu多线程操作存在内容的上下文切换);
## 五种基本数据类型
```shell
#设置key
set key value
#获取key
get key
#移除 key 到指定数据库
move key inde
#key 是否存在
exists key
#查看所有key
keys *
#key 过期时间,单位s
expire key 时间
#查看 key 剩余时间
ttl key
# 查看 key 类型
type key
```
> string
```shell
# 追加字符串(key不存在为新增)
append key value
#获取字符串长度
strlen key
#自增
incr key
#自减
decr key
#自增一定比例
incrby key 自增量
#自减一定比例
decrby key 自减量
# 字符串截取,查看全部end为-1
getrange key start end
# 字符串替换,替换指定位置字符
setrange key start value
#设置过期时间
setex key time value
#如果不存在设置,如果存在key,设置无效
setnx key value
#批量设置值
mset k1 v1 k2 v2 k3 v3
#批量获取值
mget k1 k2 k3
# 如果不存在批量创建,原子性操作,只有成功和失败
msetnx k1 v1 k2 v2 k3 v3
#先get再set,如果不存在,返回null,如果存在,返回旧数据,填充新数据
getset key value
```
使用场景:
value 可以是字符串也可以是数字
- 计数器
- 浏览量
- 播放数
- 。。。。。。
> hash
Map集合,key-map时这是一个值为map结构!本质和String 没有太大区别,还是简单的key-value!
```shell
#存值
hset myhash field1 yanshaoxun
#取值
hget myhash field1
#多值设定
hmset myhash field1 a field2 b
#多值获取
hmget myhash field1 field2
#获取hash中全部的数据
hgetall myhash
#删除hash指定的键值,对应的值也会删除
hdel myhash field1
#获取hash表字段数量
hlen myhash
#判断hash某个key是否存在
hexists myhash field2
#只获取所有的key
hkeys myhash
#只获取所有的值
hvals myhash
#针对某个值自增
hincrby myhash field5 2
#针对某个值自减
#某个值不存在则创建
hsetnx myhash field3 aa
```
hash变更的数据user name age ,尤其时某用户信息之类的,经常变动的信息!hash更适合于对象的存取,String更适合于字符串的存取。
> list
使用范围:栈(先进后出)、队列(先进先出)
```shell
# 推送数据到列表头部
lpush key value
lpush key value2
#获取范围内的数据集合,end=-1 获取所有数据
lrange key start end
#推送数据到列表尾部
rpush key v1
rpush key v2
#移除头部数据
lpop key 移除量
#移除尾部数据
rpop key 移除量
#获取list指定下标shuj
lindex key index
#移除指定值
lrem key 移除数量 value
#通过下标截取指定长度
ltrim key start end
#移除列表最后一位到新数据列表
rpoplpush key1 key2
#更新列表指定下标数据,列表或列表下标数据必须存在,否则报错
lset key index value
#在集合某个元素前后插入对应数据
linsert key before|after value value value2
```
实际上相当于一个链表,before node after
队列:lpush rpop
栈:lpush lpop
> set
```shell
#新增
sadd key value
#查看集合所有数据
smembers key
#判断元素在set集合中是否存在
sismember key value
#查看set集合长度
scard key
#移除指定元素
srem key value
#获取set集合随机元素
srandmember key 随机的元素数
```
> zset(有序集合)
在set的基础上增加了一个值作为排序
```shell
# 添加多个值
zrange myzset 1 one 2 two 3 three
#排序,根据排序数据倒叙排列,-inf 负无穷 +inf 正无穷
zrangebyscore salary -inf +inf
#正序排列
zrevrange salary 0 -1
#排序并展示排序字段数据
zrangebyscore salary -inf +inf withscores
#排序并展示数据区间内的数据
zrangebyscore salary -inf 2500 withscores
#移除zset中的某个元素
zrem salary c
#获取zset中元素个数
zcard salary
#按区间计算区间内的元素的个数
zcount salary 500 1500
```
## 拓展类型
> geospatial 地理位置信息
手机定位,附近的人,打车距离计算
本质是zset集合
```shell
#geoadd
#有效经度为 -180 到 180 度。
#有效纬度为 -85.05112878 到 85.05112878 度
#将指定的地理空间项(经度、纬度、名称)添加到指定键。数据作为排序集存储到键中,以便可以使用GEOSEARCH命令查询项目。
#该命令采用标准格式 x,y 的参数,因此必须在纬度之前指定经度。可以索引的坐标有限制:非常靠近极点的区域不可索引
#注意:没有GEODEL命令,因为您可以使用它ZREM来删除元素。Geo 索引结构只是一个排序集
geoadd china:city 116.40 39.90 beijin
geoadd china:city 121.446 31.213 shanghai
geoadd china:city 106.549 29.581 chognqin
geoadd china:city 114.109 22.544 shenzhen
geoadd china:city 120.165 30.319 hangzhou
geoadd china:city 108.969 34.285 xian
#geohash
geohash china:city beijin shanghai shenzhen chognqin
#geodist
#GEODIST key member1 member2 [ M | KM | FT | MI]
#m为米。
#km换公里。
#mi数英里。
#ft换英尺。
#查看两经纬度之间的直线距离
geodist china:city beijin chognqin
#geopos
#查询城市经纬度数据
geopos china:city beijin
#georadius
#GEORADIUS key longitude latitude radius M | KM | FT | MI [ COUNT count ] [ ASC | DESC]
#查询经纬度范围内半径为某个大小的元素集合,且可以限制数量、排序、排序字段、距离排序
georadius china:city 110 30 500 kmwithcoord withdist count 1 desc
#georadiusbymember
#GEORADIUSBYMEMBER key member radius M | KM | FT | MI [ COUNT count ] [ ASC | DESC]
#指定中心点元素,对返回内的元素进行返回
georadiusbymember china:city beijin 1000 km withcoord
#georadiusbymember_ro
#georadius_ro
#geosearch
#geosearchstore
```
> hyperloglog (数据结构)
技术统计的算法:
**网页uv(同一个人访问一个网站多次,但还是算作一个人!)**
传统方式,使用set保存用户id,set集合元素不重复,但是会存在大量的用户id,浪费空间
```shell
#pfadd
#添加元素
pfadd mykey a b c d e f g h i j k l m n
#pfcount
#mykey中元素个数
pfcount mykey
#pfmerge
#集合元素求并集
pfmerge mykey3 mykey2 mykey
```
做页面访问人统计,存在0.81%的错误率
> bigmap 位图
查看中国感染疫情人数,统计网站登陆人
只有0和1两个状态的数据
操作二进制位进行记录
```shell
#统计一周的打卡情况
setbit sign 0 0
setbit sign 1 0
setbit sign 2 0
setbit sign 3 1
setbit sign 4 0
setbit sign 5 1
setbit sign 6 0
#查看某天是否有打卡
getbit sign 2
#统计打卡天数,默认全部
bitcount sign
```
# redis事务
## 事务
数据库事务:ACID
要么同时成功,要么同时失败,原子性!
> redis 事务
本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
一次性,顺序性,排他性
```shell
------队列 set set set 执行------
```
一次性,顺序性,排他性
**redis事务没有隔离级别概念**
所有的命令在事务中,没有被直接执行!,只有发起执行命令的时候才会执行
**redis单条命令是保存原子性的,但是事务不保证原子性**
- 开启事务(multi)
- 命令入队()
- 执行事务(exec)
锁:乐观锁,watch(监视器)
> 正常执行事务
```shell
#开启事务
multi
#命令入队
set k1 v1
set k2 v2
get k2
set k3 v3
#执行
exec
```
>放弃事务
一旦放弃事务,事务中的命令都不会执行
```shell
# 开启事务
multi
#命令入队
set k2 v2
set k4 v4
get k4
#放弃事务
discard
```
> 编译型异常(代码有问题!命令有误),事务中的所有命令不会被执行
```shell
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3 k3
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> get v4
QUEUED
127.0.0.1:6379(TX)> exec
#执行事务报错,所有命令都不会执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>
```
> 运行时异常,如果事务队列中存在语法性错误,那么执行命令的时候,其它命令是可以正常执行的,错误命令抛出异常
```shell
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> exec
#字符串不能自增,但是后续命令执行成功
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
```
## 监控
> watch
悲观锁:
- 认为什么操作都会出问题,所以无论什么操作都加锁!
乐观锁:
- 认为什么操作都不会出问题,所以什么操作都不会加锁!在更新数据的时候判断在此期间是否有人改动过数据(mysql:version)
- 获取version
- 更新的时候比较version
> redis 实现乐观锁
正常执行成功
```shell
OK
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> keys *
1) "o
127.0.0.1:6379> watch money #监视money 对象
OK
127.0.0.1:6379> multi # 事务正常结束,数据期间没有发生变动,此时正常执行成功
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
127.0.0.1:6379>
```
测试多线程修改value值,使用watch可以作为redis乐观锁操作!
```shell
#进程1
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby money 20
QUEUED
127.0.0.1:6379(TX)> exec#在执行事务之前使用进程2对money值进行修改,随后执行事务,会导致事务执行失败
(nil)
#如果发现事务执行失败,先执行unwatch解锁,再watch 获取最新值,再次监视,比对监视的值是否发生变化,如果没有发生变化,可以执行成功,如果变化了,那么执行失败(自旋锁)
#进程2
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set money 80
OK
```
# jedis
> redis官方推荐的java连接开发工具!使用Java操作redis的中间件!
> 连接测试
1. 导入依赖包
```xml
<!--导入jedis依赖包-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
```
2. 编码测试
```java
public static void main(String[] args) {
// new jedis 对象(ip,port)
Jedis jedis = new Jedis("127.0.0.1", 6379);
// redis可操作的所有指令
System.out.println(jedis.ping());;
}
//使用jedis操作redis,jedis封装的方法就是redis的指令,可参照上文关于redis类型操作指令
```
3. 事务测试
```java
public static void main(String[] args) {
// new jedis 对象(ip,port)
Jedis jedis = new Jedis("127.0.0.1", 6379);
JSONObject jsonobject = new JSONObject();
jedis.flushDB();
jsonobject.put("hello","world");
jsonobject.put("name","yanshaoxun");
//开启事务
Transaction multi = jedis.multi();
String result = jsonobject.toJSONString();
try {
multi.set("user1",result);
multi.set("user2",result);
int index = 1/0;
// 事务执行
multi.exec();
} catch (Exception e) {
// 事务放弃
multi.discard();
e.printStackTrace();
} finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
// 连接关闭
jedis.close();
}
}
```
# springboot整合
说明:springboot2.x后,原来使用的jedis被替换为了lettuce
jedis:底层采用直连,多个线程操作时不安全的,需要使用jedis pool连接池!类似于bio模式
lettuce:底层采用netty,实例在多个线程中进行共享,不存在线程不安全!可以减少线程数量,类似于nio模式
> 源码分析
```java
@AutoConfiguration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})//默认配置类
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)//我们可以自己定义一个redisTemplate来替换这个默认的
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//默认的redisTemplate没有过多
//两个泛型都是object类型,后续使用需要进行强制转换
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean//string是redis中最常使用的类型,所以单独提出来了一个bean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
```
1. 导入依赖
2. 导入配置
```properties
spring.redis.host=127.0.0.1
spring.redis.port=6379
```
3. 连接测试
```java
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
// 在企业开发中,不会直接通过原生方法进行操作,可以直接封装redisUtils作为操作工具类
// 通过封装不同的数据类型,用于对某个类型进行针对性操作,封装对象的方法就是redis中的命令
// 操作字符串
redisTemplate.opsForValue();
// 操作list
redisTemplate.opsForList();
// 操作set
redisTemplate.opsForSet();
// 操作hash
redisTemplate.opsForHash();
// 操作zset
redisTemplate.opsForZSet();
// 除了基本操作,常用的方法也可以直接通过redisTemplate操作,事务和基本的crud
redisTemplate.multi();
// 获取redis连接对象,进行操作redis数据库
RedisConnection co = redisTemplate.getConnectionFactory().getConnection();
co.flushDb();
}
}
```
> 保存中文字符出现乱码
源码分析
```java
//redistemplate本身存在序列化操作
@Nullable
private RedisSerializer<?> defaultSerializer;
@Nullable
private ClassLoader classLoader;
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (this.defaultSerializer == null) {
//默认的序列化使用的是jdk的序列化组件,在操作中文时会发生转译,我们可能会使用json进行转译,需要我们自定义redistemplate
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
if (this.enableDefaultSerializer) {
if (this.keySerializer == null) {
//匹配默认序列化类
this.keySerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.valueSerializer == null) {
this.valueSerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.hashKeySerializer == null) {
this.hashKeySerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.hashValueSerializer == null) {
this.hashValueSerializer = this.defaultSerializer;
defaultUsed = true;
}
}
if (this.enableDefaultSerializer && defaultUsed) {
Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");
}
if (this.scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor(this);
}
this.initialized = true;
}
```
> 自定义redisTemplate
**可以直接作为项目开发**
```java
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//配置具体的序列化方式
//json序列化配置
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(om);
//String 序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value和hash value使用jackson序列化
template.setValueSerializer(serializer);
template.setHashValueSerializer(serializer);
return template;
}
}
```
# redis配置文件
需要了解的常用配置
```shell
#单位
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#redis对大小写不敏感
# units are case insensitive so 1GB 1Gb 1gB are all the same.
#包含
#####################################includes##################################################
#可以整合多个配置文件作为配置源
# include /path/to/local.conf
# include /path/to/other.conf
# include /path/to/fragments/*.conf
#网络
#####################################network##################################################
#绑定ip
bind 127.0.0.1 -::1
#保护模式
protected-mode yes
#通用设置
#####################################general##################################################
#以守护进程的模式运行,默认为no
daemonize yes
#如果以守护进程运行,就需要指定pid文件
pidfile /var/run/redis_6379.pid
#日志文件
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
#日志文件名
logfile ""
#数据库数量,默认16
databases 16
#是否总是显示logo
always-show-logo no
#快照
#####################################snapshotting##################################################
#快照
#持久化,在规定的时间内,执行了多少次操作,会持久化到文件,.rdb,.aof
#redis是内存数据库,如果没有持久化,会出现断电既失问题
#如果3600秒内,如果至少有1key进行修改,将进行持久化操作
save 3600 1 300 100 60 10000
#持久化如果出错,是否需要继续工作,默认开启
stop-writes-on-bgsave-error yes
#是否压缩rdb文件,默认开启,会消耗一些cpu资源
rdbcompression yes
#保存rdb文件时进行文件校验
rdbchecksum yes
#rdb文件保存路径
dir ./
#主从复制
#####################################replication##################################################
#安全
#####################################replication##################################################
#密码,默认无密码
requirepass foobared
#client连接限制
#####################################client##################################################
#最大客户端连接数
maxclients 10000
#内存配置
############################## MEMORY MANAGEMENT ################################
#redis配置最大内存容量
maxmemory <bytes>
#内存到达上限后的处理测率
# maxmemory-policy noeviction
#noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。(默认值)
#allkeys-lru: 所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
#volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
#allkeys-random: 所有key通用; 随机删除一部分 key。
#volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
#volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。
#aop模式配置
############################## APPEND ONLY MODE ###############################
#默认不开启aof模式,默认使用rdb方式持久化,在大部分情况下,rdb足够使用
appendonly no
#持久化文件名
appendfilename "appendonly.aof"
持久化文件夹
appenddirname "appendonlydir"
#每次修改都会同步sync,消耗性能
# appendfsync always
#每秒执行一次sync,可能会丢失1s数据
appendfsync everysec
#不同步,这时操作系统自己同步数据,速度最快
# appendfsync no
```
> 密码修改
```shell
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass 123456 #设置redis密码
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> shutdown
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456 #使用密码登陆
OK
```
# redis持久化
工作原理参照:https://baijiahao.baidu.com/s?id=1682713097817867905&wfr=spider&for=pc
## rdb(redis database)
在主从复制中,rdb作为备用数据使用!
> 触发机制
1. save的规则满足下,会自动触发rdb规则
2. 执行flushdb命令,也会触发我们的rdb规则
3. 退出redis,也会产生rdb文件
备份自动生成一个dump.rdb文件
> 恢复
1. 只需要将rdb文件放在我们的redis启动目录就可以了,redis启动的时候会自动检查dump.rdb文件中的数据
- 执行 config get 获取redis启动目录
```shell
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"
```
> 优点和缺点!
**优点:**
1. 适合大规模的数据回复;
2. 对数据完整性要求不高;
**缺点:**
1. 需要一定的时间间隔进行操作,如果redis意外宕机,最后一次修改的数据就没有了;
2. fork子进程的时候,会占用一定的内存空间
## aof(append only file)
将我们的命令全部都记录下来,恢复的时候把命令全部重新执行一遍(只记录写操作)
aof保存的文件名为**appendonly.aof**
appendonly配置项改为yes就开启aof了
重启就可以生效了
如果aof文件存在错误,redis是无法启动的
需要使用redis-check-aof进行文件修复
```shell
#修复aof文件
redis-check-aof --fix appendonly.aof
```
如果文件正常了,重启就可以继续连接了
> 优点和缺点!
**优点:**
1. 每次修改都同步,会让文件的完整性更好;
2. 默认每秒同步一次,可能会丢失疫苗的数据;
3. 如果不开启aof,效率是最高的;
**缺点:**
1. 相对于数据文件来说,aof远远大于rdb,恢复的速度也比rdb慢;
2. 运行效率也要比rdb慢,所以redis默认使用rdb持久化;
> 重写规则
```shell
no-appendfsync-on-rewrite no
# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
```
如果aof文件大于配置文件中设置的大小,会新fork一个新的进程来将我们的文件进行重写
aof默认事无线追加的,会导致文件大小一直增加
## 混合持久化
重启 Redis 时,如果使用 RDB 来恢复内存状态,会丢失大量数据。而如果只使用 AOF 日志重放,那么效率又太过于低下。Redis 4.0 提供了混合持久化方案,将 RDB 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自 RDB 持久化开始到持久化结束这段时间发生的增量 AOF 日志,通常这部分日志很小。
# 订阅发布
参考:https://www.runoob.com/redis/redis-pub-sub.html
redis发布订阅(pub/sub)是一种消息通信模式,发送者发送消息,订阅者接收消息,微信、微博、关注提醒等环境下使用!
redis客户端可以订阅任意数量的频道
| 序号 | 命令及描述 |
| :--- | :----------------------------------------------------------- |
| 1 | ](https://www.runoob.com/redis/pub-sub-psubscribe.html) 订阅一个或多个符合给定模式的频道。 |
| 2 | ]](https://www.runoob.com/redis/pub-sub-pubsub.html) 查看订阅与发布系统状态。 |
| 3 | (https://www.runoob.com/redis/pub-sub-publish.html) 将信息发送到指定的频道。 |
| 4 | ]](https://www.runoob.com/redis/pub-sub-punsubscribe.html) 退订所有给定模式的频道。 |
| 5 | ](https://www.runoob.com/redis/pub-sub-subscribe.html) 订阅给定的一个或多个频道的信息。 |
| 6 | ]](https://www.runoob.com/redis/pub-sub-unsubscribe.html) 指退订给定的频道。 |
> 测试
订阅者
```shell
127.0.0.1:6379> subscribe yanshaoxun #订阅指定的频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "yanshaoxun"
3) (integer) 1
1) "message"
2) "yanshaoxun"
3) "aaa"
```
发布者
```shell
#向指定的频道发送数据
127.0.0.1:6379> publish yanshaoxun aaa
```
使用场景:
1. 实时消息系统;
2. 实时聊天(频道作为聊天室,消息全部回显);
3. 订阅,关注系统
如果场景稍微复杂我们会使用消息中间件mq......
# 集群
## 主从复制
> 概念
在数据库语境下,复制( replication)就是将数据从一个数据库复制到另一个数据库中。主从复制,是将数据库分为主节点和从节点,主节点源源不断地将数据复制给从节点,保证主从节点中存有相同的数据。有了主从复制,数据可以有多份副本,这带来了多种好处:
第—,提升数据库系统的请求处理能力。单个节点能够支撑的读流量有限,部多个节点,并构成主从关系,用主从复制保持主从节点数据一致,如此主从节点一起提供服务。
第二,提升整个系统的可用性。因为从节点中有主节点数据的副本,当主节点宕机后,可以立刻提升其中一个从节点为主节点,继续提供服务
> 必要性
(1)Redis虽然读写的速度都很快,单节点的Redis能够支撑QPS大概在5w左右,如果上千万的用户访问,Redis就承载不了,成为了[高并发](https://so.csdn.net/so/search?q=高并发&spm=1001.2101.3001.7020)的瓶颈。
(2)单节点的Redis不能保证高可用,当Redis因为某些原因意外宕机时,会导致缓存不可用
(3)CPU的利用率上,单台Redis实例只能利用单个核心,这单个核心在面临海量数据的存取和管理工作时压力会非常大
> 优点和缺点
优点:
1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2. 故障恢复:如果master宕掉了,使用哨兵模式,可以提升一个 slave 作为新的 master,进而实现故障转移,实现高可用
3. 负载均衡:可以轻易地实现横向扩展,实现读写分离,一个 master 用于写,多个 slave 用于分摊读的压力,从而实现高并发;
4. 高可用性基石:作为哨兵和集群模式实施的基石;
缺点:由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave服务器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重
**默认情况下,每台redis都是主节点**
> 环境配置
1. 只配置从库,不配置主库(redis 默认都是主节点);
2. 主从配置查看命令
```shell
#查看库信息
127.0.0.1:6379> info replication
# Replication
#默认主节点
role:master
#连接的从机数
connected_slaves:0
master_replid:d6380e8a7b11b2f3026962efd86cfd3708f533fd
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
```
3. 配置文件修改:
```shell
#复制配置文件,并对配置文件进行修改
root@kurento01:/usr/local/bin/redis_conf# cp redis.conf redis_79.conf
root@kurento01:/usr/local/bin/redis_conf# cp redis.conf redis_80.conf
root@kurento01:/usr/local/bin/redis_conf# cp redis.conf redis_81.conf
root@kurento01:/usr/local/bin/redis_conf# cp redis.conf redis_82.conf
#修改下列配置项为指定配置
port 6382
pidfile /var/run/redis_6382.pid
logfile "6382.log"
dbfilename dump_6382.rdb
#修改完启动各自redis服务
root@kurento01:/usr/local/bin# ps -ef| grep redis
root 67605 10 01:12 ? 00:00:00 redis-server 127.0.0.1:6379
root 67612 10 01:12 ? 00:00:00 redis-server 127.0.0.1:6380
root 67618 10 01:12 ? 00:00:00 redis-server 127.0.0.1:6381
root 67624 10 01:12 ? 00:00:00 redis-server 127.0.0.1:6382
#一主三从
#只需要配置从机向主机的连接
#配置从机连接6379端口主机
slaveof 127.0.0.1 6379
#配置后查看当前redis服务状态
127.0.0.1:6380> info replication
# Replication
role:slave#当前服务角色处于从机状态
master_host:127.0.0.1#主机ip
master_port:6379#主机端口
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_read_repl_offset:0
slave_repl_offset:0
master_link_down_since_seconds:-1
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:f69b524fbc400d2151288b14ad32ff7b259d6f34
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
#主机查看
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:3#从机连接数
slave0:ip=127.0.0.1,port=6380,state=online,offset=462,lag=1 # 从机连接状态
slave1:ip=127.0.0.1,port=6381,state=online,offset=462,lag=1
slave2:ip=127.0.0.1,port=6382,state=online,offset=462,lag=1
#使用命令配置,只是暂时的配置主从机状态
#只有在配置文件中的修改才是永久的
#修改配置文件中的主从配置项,永久配置
replicaof <masterip> <masterport>
```
> 细节
主机只能写,从机只能读!主机中的所有信息和数据都会自动被从机保存
```shell
#从机set测试
127.0.0.1:6380> set a test
(error) READONLY You can't write against a read only replica.
```
测试:
1. 主机断开连接,从机依旧是连接到主机的,但是只有读操作,没有写操作,如果主机重新连接,从机依旧可以直接获取到主机写入的数据!
2. 如果不是通过配置文件配置的主从,在断开连接后在启动服务,从机会默认变为主机,不进行数据主从备份,重新设置主从后,才能备份主机后续写入的数据;
> 主从复制原理
**全量复制流程:**
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份,具体步骤如下:
1. slave服务器连接到master服务器,便开始进行数据同步,发送psync命令(Redis2.8之前是sync命令)
2. master服务器收到psync命令之后,开始执行bgsave命令生成RDB快照文件并使用缓存区记录此后执行的所有写命令
```markdown
如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是每个连接都执行一次,然后再把这一份持久化的数据发送给多个并发连接的slave。
如果RDB复制时间超过60秒(repl-timeout),那么slave服务器就会认为复制失败,可以适当调节大这个参数
```
3. master服务器bgsave执行完之后,就会向所有Slava服务器发送快照文件,并在发送期间继续在缓冲区内记录被执行的写命令
```markdown
client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败
```
4. slave服务器收到RDB快照文件后,会将接收到的数据写入磁盘,然后清空所有旧数据,在从本地磁盘载入收到的快照到内存中,同时基于旧的数据版本对外提供服务。
5. master服务器发送完RDB快照文件之后,便开始向slave服务器发送缓冲区中的写命令
6. slave服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
7. 如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF
**增量复制流程**:
Redis的增量复制是指在初始化的全量复制并开始正常工作之后,master服务器将发生的写操作同步到slave服务器的过程,增量复制的过程主要是master服务器每执行一个写命令就会向slave服务器发送相同的写命令,slave服务器接收并执行收到的写命令。
**断点续传:**
当master-slave网络连接断掉后,slave重新连接master时,会触发全量复制,但是从2.8版本开始,slave与master能够在网络连接断开重连后,只从中断处继续进行复制,而不必重新同步,这就是所谓的断点续传。
```markdown
断电续传这个新特性使用psync命令,旧的实现中使用sync命令。Redis2.8版本可以检测出它所连接的服务器是否支持PSYNC命令,不支持的话使用SYNC命令。master服务器收到slave发送的psync命令后,会根据自身的情况做出对应的处理,可能是FULLRESYNC runid offset触发全量复制,也可能是CONTINUE触发增量复制
命令格式:psync runid offset
```
**工作原理:**
1. master服务器在内存缓冲区中给每个slave服务器都维护了一份同步备份日志(in-memory backlog),缓存最近一段时间的数据,默认大小1m,如果超过这个大小就会清理掉。
2. 同时,master 和 slave 服务器都维护了一个复制偏移量(replication offset)和 master线程ID(master run id),每个slave服务器在跟master服务器进行同步时都会携带master run id 和 最后一次同步的复制偏移量offset,通过offset可以知道主从之间的数据不一致的情况。
3. 当连接断开时,slave服务器会重新连接上master服务器,然后请求继续复制。假如主从服务器的两个master run id相同,并且指定的偏移量offset在同步备份日志中还有效,复制就会从上次中断的点开始继续。如果其中一个条件不满足,就会进行完全重新同步,因为主运行id不保存在磁盘中,如果从服务器重启的话就只能进行完全同步了。
```markdown
master服务器维护的offset是存储在backlog中,msater就是根据slave发送的offset来从backlog中获取数据的
```
4. 在部分同步过程中,master会将本地记录的同步备份日志中记录的指令依次发送给slave服务器从而达到数据一致。
>层层链路模式
上一个从节点依旧是下一个从节点的主节点,上一个从节点依旧作为主机的从节点
> 手动指定主节点
```shell
#在主机断开服务情况下,使用命令手动指定主机
127.0.0.1:6381> slaveof no one
OK
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:962a29b8995b1f4454a0c472411faf25e6b8a70a
master_replid2:e6f6cd9041d1f4bb3d486d7a775261419582da4d
master_repl_offset:3505
second_repl_offset:3506
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:421
repl_backlog_histlen:3085
#其它节点手动连接到最新主节点,如果这个时候主节点恢复了,依旧只能手动配置主节点
```
## 哨兵*
参考:https://blog.csdn.net/qq_39230153/article/details/121524461
自动选举master的模式
> 概述
主从切换技术:当主机宕机后,需要手动把一台从(slave)服务器切换为主服务器,这就需要人工干预,费时费力,还回造成一段时间内服务不可用,所以推荐哨兵架构(Sentinel)来解决这个问题。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
这里哨兵模式有两个作用:
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器
- 当哨兵监测到Redis主机宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他服务器,修改配置文件,让他们换主机
当一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此可以使用哨兵进行监控, 各个哨兵之间还会进行监控,这就形成了多哨兵模式。
哨兵集群:
存在哨兵1,2,3对服务进行监测,假设主服务器宕机,哨兵1先检测到结果,但是系统并不会马上进行failover过程,仅仅是哨兵1主观认为主服务器不可以用,这个现象称为主观下线,当后面的哨兵2,3也检测到主服务器不可用,并且数量达到一定时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作。
操作转移成功后。就会发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这一过程称为客观下线
哨兵除了监听服务是否正常外,每个哨兵之间也会互相监督
> 测试
1. 配置哨兵配置文件,sentinel.conf
```shell
#sentinel monitor 监控进程名称 host port 1 ,1代表如果主机宕机,会进行投票,判断哪台从机会变成主机
sentinel monitor shaobinRedis 127.0.0.1 6379 1
```
2. 启动哨兵
```shell
root@kurento01:/usr/local/bin# redis-sentinel redis_conf/sentinel.conf
67717:X 15 Jun 2022 02:34:44.797 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
67717:X 15 Jun 2022 02:34:44.797 # Redis version=7.0.0, bits=64, commit=00000000, modified=0, pid=67717, just started
67717:X 15 Jun 2022 02:34:44.797 # Configuration loaded
67717:X 15 Jun 2022 02:34:44.797 * Increased maximum number of open files to 10032 (it was originally set to 1024).
67717:X 15 Jun 2022 02:34:44.797 * monotonic clock: POSIX clock_gettime
_._
_.-``__ ''-._
_.-`` `.`_.''-._ Redis 7.0.0 (00000000/0) 64 bit
.-`` .-```.```\/ _.,_ ''-._
( ' , .-`| `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 67717
`-._ `-._`-./_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
67717:X 15 Jun 2022 02:34:44.798 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
67717:X 15 Jun 2022 02:34:44.807 * Sentinel new configuration saved on disk
67717:X 15 Jun 2022 02:34:44.807 # Sentinel ID is 84ab2e28aad8d42a2e0b61c59b7f1b79805b356e
67717:X 15 Jun 2022 02:34:44.807 # +monitor master shaobinRedis 127.0.0.1 6379 quorum 1
67717:X 15 Jun 2022 02:34:44.817 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:34:44.826 * Sentinel new configuration saved on disk
67717:X 15 Jun 2022 02:34:44.826 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:34:44.839 * Sentinel new configuration saved on disk
# 当哨兵监测到主节点服务无法连接,发送心跳包告知主节点服务不可用,在从机中随机投票选择新主机
#failover 故障转移
67717:X 15 Jun 2022 02:37:52.038 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:37:52.122 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:37:52.537 * Sentinel new configuration saved on disk
67717:X 15 Jun 2022 02:37:52.537 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:37:52.537 # +failover-state-reconf-slaves master shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:37:52.610 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:37:53.603 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:37:53.603 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:37:53.668 # +failover-end master shaobinRedis 127.0.0.1 6379
67717:X 15 Jun 2022 02:37:53.668 # +switch-master shaobinRedis 127.0.0.1 6379 127.0.0.1 6381
67717:X 15 Jun 2022 02:37:53.668 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ shaobinRedis 127.0.0.1 6381
67717:X 15 Jun 2022 02:37:53.668 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ shaobinRedis 127.0.0.1 6381
67717:X 15 Jun 2022 02:37:53.685 * Sentinel new configuration saved on disk
67717:X 15 Jun 2022 02:38:23.678 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ shaobinRedis 127.0.0.1 6381
#如果主机重新上线,发送心跳,原主机会归并到新主机下作为从机
67717:X 15 Jun 2022 02:41:53.168 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ shaobinRedis 127.0.0.1 6381
```
> 优点和缺点
优点:
1. 哨兵集群,基于主从复制模式,所有的主从配置优点都具有;
2. 主从可以切换,存在故障转移,系统可用性更好;
3. 哨兵模式是主从复制升级,手动到自动,更加健壮;
缺点:
1. redis不适合在线扩容,集群容量一旦达到上限,在线扩容会十分麻烦!
2. 实现哨兵模式的配置其实是很麻烦的,存在很多选择!
```shell
###普通配置
port 26379
# 保护模式关闭,这样其他服务起就可以访问此台redis
protected-mode no
# 哨兵模式是否后台启动,默认no,改为yes
daemonize yes
pidfile /var/run/redis-sentinel.pid
# log日志保存位置
logfile /usr/local/redis/sentinel/redis-sentinel.log
# 工作目录
dir /usr/local/redis/sentinel
###核心配置
# 核心配置。
# 第三个参数:哨兵名字,可自行修改。(若修改了,那后面涉及到的都得同步)
# 第四个参数:master主机ip地址
# 第五个参数:redis端口号
# 第六个参数:哨兵的数量。比如2表示,当至少有2个哨兵发现master的redis挂了,
# 那么就将此master标记为宕机节点。
# 这个时候就会进行故障的转移,将其中的一个从节点变为master
sentinel monitor mymaster 192.168.217.151 6379 2
# master中redis的密码
sentinel auth-pass mymaster 123456
# 哨兵从master节点宕机后,等待多少时间(毫秒),认定master不可用。
# 默认30s,这里为了测试,改成10s
sentinel down-after-milliseconds mymaster 10000
# 当替换主节点后,剩余从节点重新和新master做同步的并行数量,默认为 1
sentinel parallel-syncs mymaster 1
# 主备切换的时间,若在3分钟内没有切换成功,换另一个从节点切换
sentinel failover-timeout mymaster 180000
# 商业转载请联系作者获得授权,非商业转载请注明出处。
# For commercial use, please contact the author for authorization. For non-commercial use, please indicate the source.
# 协议(License):署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)
# 作者(Author):Mashiro
# 链接(URL):https://www.cnblogs.com/zxhbk/p/13074299.html
# 来源(Source):忘忧山的兰木
#SCRIPTS EXECUTION
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知 相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等), 将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
# 通知脚本
# 简单的来说,发生故障转移后就会发送邮件给管理员,我们可以自己编写,但是需要学习linux的shell编程才可以编写
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh # 一般都是由运维来配 置!
```
# 缓存穿透和雪崩(面试高频)
> 缓存穿透
**缓存穿透(查不到数据)**:用户想要查询一个数据,发现Redis内存数据库里没有,也就是缓存没有命中,于是向 持久层的数据库查询,发现也没有,于是本次查询失败,当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库,这会给持久层数据库造成很大的压力,出现缓存穿透。
解决方案:**布隆过滤器**
参考:https://blog.csdn.net/wang0112233/article/details/123665461
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的压力
但是:
1. 如果空值能被缓存起来,这就意味着缓存需要更多的空间存储更多的键
2. 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间的窗口不一致
优点:使用二进制组成的数组,内存占用率小,且插入和查询速度够快
缺点:随着数据增加,二进制数组中值的覆盖率增加,只能判断数据不存在,不能明确判定数据存在,且无法删除数据
> 缓存击穿
**缓存击穿(访问量过大)**:是指某一个key 非常热点,在不停的扛着大的并发,大并发集中对这个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。
当某个key过期的瞬间,就会有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大。
解决:
1. 设置热点数据永不过期
2. 加互斥锁:使用分布式锁,保证每一个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,只需等待(对分布锁要求高)
> 缓存雪崩
**缓存雪崩:**指在某一个时间段,缓存集中过期失效,Redis宕机
比如 :双十一零点,抢购,这波商品应该放在缓存区,假设缓存一小时,到了凌晨一点,商品缓存过期,而对于这批商品的访问,都跑到数据库中,对于数据库,产生压力峰。所有请求都会到达存储层,存储层的调用量增加,存储层狗带(缓存服务节点的宕机,对数据库服务器造成的压力不可预知)
解决:
1. redis高可用(多增加redis)
2. 限流降级(通过加锁来控制数据库写缓存的线程数量)
3. 数据预热(在正式部署之前,把可能的数据先访问一遍)
4. 设置随机失效keys 快乐的鸡蛋黄 发表于 2022-6-29 14:35
论坛发帖可以导入md格式的,可以修改排一下版好看一点,感谢分享
感谢告知,之前一直不知道 感谢分享 一起学习 有部分是跟着视频记录的,也有我自己的一些看法:lol 论坛发帖可以导入md格式的,可以修改排一下版好看一点,感谢分享 {:1_893:}{:1_893:}{:1_893:}{:按时发财 非常感谢,很实用 谢谢大佬分享 倒是很全,但是不细
页:
[1]
2