吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3019|回复: 19
收起左侧

[Java 转载] 最近在看redis,花了点时间整理了一下学习内容,都是一些基础,想学的可以看下

   关闭 [复制链接]
chengxuyuan01 发表于 2022-6-29 14:13
本帖最后由 chengxuyuan01 于 2022-6-29 14:46 编辑

redis使用

安装部署Linux

系统:linux(centos)

下载地址:https://codeload.github.com/redis/redis/tar.gz/refs/tags/7.0.0

前提:

#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多线程操作存在内容的上下文切换);

五种基本数据类型

#设置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

# 追加字符串(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!

#存值
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

使用范围:栈(先进后出)、队列(先进先出)

# 推送数据到列表头部
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

#新增
sadd key value
#查看集合所有数据
smembers key
#判断元素在set集合中是否存在
sismember key value
#查看set集合长度
scard key
#移除指定元素
srem key value
#获取set集合随机元素
srandmember key 随机的元素数

zset(有序集合)

在set的基础上增加了一个值作为排序

# 添加多个值
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集合

#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 [WITHCOORD] [WITHDIST]  [WITHHASH] [ COUNT count [ANY]] [ ASC | DESC] [STORE key] [STOREDIST key]
#查询经纬度范围内半径为某个大小的元素集合,且可以限制数量、排序、排序字段、距离排序
georadius china:city 110 30 500 km  withcoord withdist count 1 desc

#georadiusbymember
#GEORADIUSBYMEMBER key member radius M | KM | FT | MI [WITHCOORD] [WITHDIST] [WITHHASH] [ COUNT count [ANY]] [ ASC | DESC] [STORE key] [STOREDIST key]
#指定中心点元素,对返回内的元素进行返回
georadiusbymember china:city beijin 1000 km withcoord

#georadiusbymember_ro

#georadius_ro

#geosearch

#geosearchstore

hyperloglog (数据结构)

技术统计的算法:

网页uv(同一个人访问一个网站多次,但还是算作一个人!)

传统方式,使用set保存用户id,set集合元素不重复,但是会存在大量的用户id,浪费空间

#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两个状态的数据

操作二进制位进行记录

#统计一周的打卡情况
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 事务

本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!

一次性,顺序性,排他性

------队列 set set set 执行------

一次性,顺序性,排他性

redis事务没有隔离级别概念

所有的命令在事务中,没有被直接执行!,只有发起执行命令的时候才会执行

redis单条命令是保存原子性的,但是事务不保证原子性

  • 开启事务(multi)
  • 命令入队()
  • 执行事务(exec)

锁:乐观锁,watch(监视器)

正常执行事务

#开启事务
multi 

#命令入队
set k1 v1

set k2 v2

get k2

set k3 v3

#执行
exec

放弃事务

一旦放弃事务,事务中的命令都不会执行

# 开启事务
multi

#命令入队
set k2 v2

set k4 v4

get k4

#放弃事务
discard

编译型异常(代码有问题!命令有误),事务中的所有命令不会被执行

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> 

运行时异常,如果事务队列中存在语法性错误,那么执行命令的时候,其它命令是可以正常执行的,错误命令抛出异常

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 实现乐观锁

正常执行成功

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乐观锁操作!

#进程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. 导入依赖包
<!--导入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>
  1. 编码测试
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类型操作指令
  1. 事务测试
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模式

源码分析

@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. 导入配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
  1. 连接测试
@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();
    }

}

保存中文字符出现乱码

源码分析

//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

可以直接作为项目开发

@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配置文件

需要了解的常用配置

#单位
# 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

密码修改

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启动目录
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进行文件修复

#修复aof文件
redis-check-aof --fix appendonly.aof

如果文件正常了,重启就可以继续连接了

优点和缺点!

优点:

  1. 每次修改都同步,会让文件的完整性更好;
  2. 默认每秒同步一次,可能会丢失疫苗的数据;
  3. 如果不开启aof,效率是最高的;

缺点:

  1. 相对于数据文件来说,aof远远大于rdb,恢复的速度也比rdb慢;
  2. 运行效率也要比rdb慢,所以redis默认使用rdb持久化;

重写规则

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 PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道。
2 PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。
3 PUBLISH channel message 将信息发送到指定的频道。
4 PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道。
5 SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息。
6 UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道。

测试

订阅者

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"

发布者

#向指定的频道发送数据
127.0.0.1:6379> publish yanshaoxun aaa

使用场景:

  1. 实时消息系统;
  2. 实时聊天(频道作为聊天室,消息全部回显);
  3. 订阅,关注系统

如果场景稍微复杂我们会使用消息中间件mq......

集群

主从复制

概念

在数据库语境下,复制( replication)就是将数据从一个数据库复制到另一个数据库中。主从复制,是将数据库分为主节点和从节点,主节点源源不断地将数据复制给从节点,保证主从节点中存有相同的数据。有了主从复制,数据可以有多份副本,这带来了多种好处:

第—,提升数据库系统的请求处理能力。单个节点能够支撑的读流量有限,部多个节点,并构成主从关系,用主从复制保持主从节点数据一致,如此主从节点一起提供服务。

第二,提升整个系统的可用性。因为从节点中有主节点数据的副本,当主节点宕机后,可以立刻提升其中一个从节点为主节点,继续提供服务

必要性

(1)Redis虽然读写的速度都很快,单节点的Redis能够支撑QPS大概在5w左右,如果上千万的用户访问,Redis就承载不了,成为了高并发的瓶颈。

(2)单节点的Redis不能保证高可用,当Redis因为某些原因意外宕机时,会导致缓存不可用

(3)CPU的利用率上,单台Redis实例只能利用单个核心,这单个核心在面临海量数据的存取和管理工作时压力会非常大

优点和缺点

优点:

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

  2. 故障恢复:如果master宕掉了,使用哨兵模式,可以提升一个 slave 作为新的 master,进而实现故障转移,实现高可用

  3. 负载均衡:可以轻易地实现横向扩展,实现读写分离,一个 master 用于写,多个 slave 用于分摊读的压力,从而实现高并发;

  4. 高可用性基石:作为哨兵和集群模式实施的基石;

缺点:由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave服务器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重

默认情况下,每台redis都是主节点

环境配置

  1. 只配置从库,不配置主库(redis 默认都是主节点);
  2. 主从配置查看命令
#查看库信息
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
  1. 配置文件修改:
#复制配置文件,并对配置文件进行修改
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      1  0 01:12 ?        00:00:00 redis-server 127.0.0.1:6379
root      67612      1  0 01:12 ?        00:00:00 redis-server 127.0.0.1:6380
root      67618      1  0 01:12 ?        00:00:00 redis-server 127.0.0.1:6381
root      67624      1  0 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>

细节

主机只能写,从机只能读!主机中的所有信息和数据都会自动被从机保存

#从机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快照文件并使用缓存区记录此后执行的所有写命令

    如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是每个连接都执行一次,然后再把这一份持久化的数据发送给多个并发连接的slave。
    如果RDB复制时间超过60秒(repl-timeout),那么slave服务器就会认为复制失败,可以适当调节大这个参数
  1. master服务器bgsave执行完之后,就会向所有Slava服务器发送快照文件,并在发送期间继续在缓冲区内记录被执行的写命令
    client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败
  1. slave服务器收到RDB快照文件后,会将接收到的数据写入磁盘,然后清空所有旧数据,在从本地磁盘载入收到的快照到内存中,同时基于旧的数据版本对外提供服务。

  2. master服务器发送完RDB快照文件之后,便开始向slave服务器发送缓冲区中的写命令

  3. slave服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

  4. 如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF

增量复制流程

Redis的增量复制是指在初始化的全量复制并开始正常工作之后,master服务器将发生的写操作同步到slave服务器的过程,增量复制的过程主要是master服务器每执行一个写命令就会向slave服务器发送相同的写命令,slave服务器接收并执行收到的写命令。

断点续传:

当master-slave网络连接断掉后,slave重新连接master时,会触发全量复制,但是从2.8版本开始,slave与master能够在网络连接断开重连后,只从中断处继续进行复制,而不必重新同步,这就是所谓的断点续传。

    断电续传这个新特性使用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不保存在磁盘中,如果从服务器重启的话就只能进行完全同步了。

    master服务器维护的offset是存储在backlog中,msater就是根据slave发送的offset来从backlog中获取数据的
  1. 在部分同步过程中,master会将本地记录的同步备份日志中记录的指令依次发送给slave服务器从而达到数据一致。

层层链路模式

上一个从节点依旧是下一个从节点的主节点,上一个从节点依旧作为主机的从节点

手动指定主节点

#在主机断开服务情况下,使用命令手动指定主机
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
#sentinel monitor 监控进程名称 host port 1 ,1代表如果主机宕机,会进行投票,判断哪台从机会变成主机
sentinel monitor shaobinRedis 127.0.0.1 6379 1                                          
  1. 启动哨兵
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. 实现哨兵模式的配置其实是很麻烦的,存在很多选择!
###普通配置

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

redis.zip

19.37 KB, 下载次数: 42, 下载积分: 吾爱币 -1 CB

具体md文件

免费评分

参与人数 8吾爱币 +8 热心值 +7 收起 理由
DueleElAmor + 1 + 1 谢谢@Thanks!
ghsfwy + 1 + 1 感谢分享,总结的很好啊
shusym + 1 + 1 用心讨论,共获提升!
Sunry0831 + 1 讲的特别好!支持
HELLOW0RD + 1 + 1 感谢大佬的笔记分享,看完后学到很多,蟹蟹
苏浩 + 1 + 1 热心回复!
塞北的雪 + 1 + 1 用心讨论,共获提升!
yc19951005 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| chengxuyuan01 发表于 2022-6-29 16:21
快乐的鸡蛋黄 发表于 2022-6-29 14:35
论坛发帖可以导入md格式的,可以修改排一下版好看一点,感谢分享

感谢告知,之前一直不知道
头像被屏蔽
洛枫 发表于 2022-6-29 14:17
yc19951005 发表于 2022-6-29 14:24
 楼主| chengxuyuan01 发表于 2022-6-29 14:25
有部分是跟着视频记录的,也有我自己的一些看法
快乐的鸡蛋黄 发表于 2022-6-29 14:35
论坛发帖可以导入md格式的,可以修改排一下版好看一点,感谢分享
tzcswl 发表于 2022-6-29 16:27
{:按时发财
苏浩 发表于 2022-6-29 17:31
非常感谢,很实用
13570648032 发表于 2022-6-29 20:06
谢谢大佬分享
52pojie12345 发表于 2022-7-6 09:53
倒是很全,但是不细
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-20 20:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表