程序猿必备的Redis常见功能知识点,这些你都会吗?

源于蚂蚁课堂的学习,点击这里查看(老余很给力)    

基础知识 

线程安全

由于单线程,故Redis天然规避线程安全问题。那么,为什么单线程还能这么高效呢?
这得益于其底层进行io操作时,采用了NIO的多路复用原则。(如有读者感兴趣,可以研究一下nio多路复用原理即可)

Redis官方是没有windows版本的,因为Redis底层做io操作是基于linux的epoll来进行NIO的io多路复用。
其主要通过socket收到消息后进行主动调用回调,去提升io轮询效率。而Windows没有epoll,只能通过selector去不停轮询。

持久化机制

RDB

Redis默认采用rdb方式实现数据的持久化
即以快照的形式将数据持久化到磁盘,是一个二进制的文件dump.rdb

rdb方式会将Redis中的数据进行全量的备份,即覆盖式更新,默认备份间隔时间是900s。
所以如果Redis出现故障无法备份数据时,如果采用rdb方式持久化数据,会导致这900s内数据的丢失。
但其优点是消耗服务器内存少

==================redis.conf==================
# rdb方式备份数据的文件名称
dbfilename dump.rdb

# 在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照
save 900 1

# 在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 300 10

# 在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
save 60 10000

AOF  

AOF是基于数据日志操作实现的持久化,故属于增量备份
在Redis中,aof有三种模式

# 每次有数据修改发生时都会写入AOF文件,能够保证数据不丢失,但是效率非常低
appendfsync always

# 每秒钟同步一次,可能会丢失1s内的数据,但是效率非常高, 这也是推荐使用的方式
appendfsync everysec

# 从不同步。高效但是数据不会被持久化。
appendfsync no

==================redis.conf================== 
# 开始aof持久化,默认采用everysec
appendonly yes

 与MySQL保持一致性

Redis作为缓存,是需要和数据库保持一致的,当然这里的一致不是强一致,而是最终一致。

常见的解决方法有以下
1.通过mq去监听MySQL的binlog文件,将其变化的结果以消息队列的方式传输至redis
2.有种比较low的方式,就是手动清空redis,让其访问数据库,然后同步至Redis,这种方式风险高,容易雪崩
3.可采用阿里的canal去实现数据同步

淘汰策略

Redis中的内存并非无止境,故当其内容满了再去存放数据会出问题,所以其内部维护了6种淘汰策略

noeviction:不淘汰,当内存满了,直接报错
allkeys-lru:指定了有效期的key中,淘汰不经常使用的key
volatile-lru:指定了有效期的key中,随机淘汰
allkeys-random:所有key中,淘汰不经常使用的key
volatile-random:所有key中,随机淘汰
volatile-ttl:指定了有效期的key中,淘汰具有更早过期时间的key


==================redis.conf==================
# 标识Redis内存大小的阈值,超过则会进行淘汰策略
maxmemory <bytes>

# 指明Redis要使用的淘汰策略为哪一种
maxmemory-policy 淘汰策略名称

key的自动过期

当我们的key失效时,可以执行我们的客户端回调监听的方法

==================redis.conf==================
notify-keyspace-events "Ex"

SpringBoot整合key失效监听

@Configuration
public class RedisListenerConfig {
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}



@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }


    /**
     * Redis失效事件 key
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiraKey = message.toString();
        // 拿到失效的key做一些业务处理
       
    }
}

事务 

multi 开启事务
exec 提交事务
discard 取消提交事务
watch keyName 可以监听一个或者多个key,提交事务前判断key是否变化,进而进行事务提交或取消,通过版本实现乐观锁

ps:Redis官方是没有提供回滚方法, 只提供了取消事务。
multi只保证了事务的原子性,但没有保证其隔离性,故一般配合watch使用

分布式锁

分布式锁的大体实现思路是相同的,即多个请求去创建同一个节点,谁成功谁就拿到锁。
Redis也是一样的,它维护了一个setnx操作,即带返回结果的设置key,1表示设置成功,0表示失败

所以当setnx返回1时,执行业务操作,超时则回滚数据,正常执行结束后删除此key,这样其他请求继续抢夺锁。

 进阶知识 

 主从复制

单个节点的Redis可用性不高,即当其宕机时,对外Redis不可用,故采用集群的方式保证高可用。
主从模式遵循一主多从,读写分离(主写从读)。

==================redis.conf==================
# replicaof <masterip> <masterport>
slaveof 主节点的IP 主节点的端口
masterauth 主节点的密码


从节点启动时读取其配置,根据主节点地址信息与其建立socket长连接,定期保持数据同步的通讯。
在一主多从模式中,如果一个主节点对应的从节点特别多时,主节点的同步压力非常大,所以往往采用树状结构进行集群。
即: master-->s0-->s2
                -->s3
           -->s1-->s4
           
从节点首次同步采用读取rdb方式全量备份数据,当有更新key操作时,会采用增量方式同步

在客户端连接中通过info replication可以获取当前Redis节点集群的信息

哨兵机制

主从模式存在很大的弊端,即当主节点宕机,会使得集群对外不可写,需要人工维护。
故Redis采用哨兵机制很好的解决这个问题。

哨兵是一个独立的进程,其主要用于监听master节点(定时向master发送ping的命令)的活跃状态。
一般来说,一个Redis对应一个哨兵,多个哨兵都是监听集群中同一个master节点,当哨兵启动时,会向消息队列中订阅
关于master的通道channel,然后将自身的地址作为消息投放到消息通道中,然后作为消费者等待消费,其它哨兵也做此
操作,这样一来,哨兵之间可以得到彼此的信息,然后建立socket长连接进行通讯。
当有哨兵发现master宕机后,会发送消息至channel,通知所有哨兵去访问master,超过阈值(可配置)数量的哨兵得
到master宕机结果的话,就会认为master宕机,然后进行选举。
之前的master启动后,发现集群已经有了master,那么他就会变成slave的从。
通过这种机制,实现Redis的故障转移


哨兵通过监听master,执行info replication获取master下对应从节点信息,然后向下递归执行info replication
从而获取整个集群的节点信息,进行选举


==================sentinel.conf==================
sentinel monitor mymaster master的IP master的端口 哨兵阈值(认为master宕机的阈值)
sentinel auth-pass mymaster master的密码

# sentinel心跳检测主3秒内无响应,视为挂掉,开始切换其他从为主
sentinel down-after-milliseconds mymaster 3000

# 每次最多可以有1个从同步主。一个从同步结束,另一个从开始同步
sentinel parallel-syncs mymaster 1

# 主从切换超时时间
sentinel failover-timeout mymaster 18000


#启动哨兵
./redis-sentinel sentinel.conf的路径

安全控制

缓存穿透 

大量Redis不存在的key访问,导致Redis无法命中,感觉像是穿过了缓存,直接访问数据库,造成数据库访问压力。

解决方法:
    api限流
    接口限制
    用户授权
    IP检查
    网关黑白名单
    布隆过滤器

缓存击穿

在高并发的情况下,热点key失效,导致大量请求访问到了数据库,如同某个点被击穿一样。

解决方法:
    分布式锁
    软过期

缓存雪崩 

Redis持久化文件丢失,使得Redis启动时进行数据预热,大量的查询涌向数据库,导致数据库无法承受而宕机。
这样一来,缓存拿不到数据,数据库又起不来,形成雪崩效应

Cluster集群

传统的主从模式有很大弊端,即:
    中心化 只有一个主在写,宕机需要选举,期间对外不可用
    数据全量同步 占用资源,内容冗余

故Redis推出分片化管理数据
    其原理是内部维护一个hash槽,默认槽的个数是16384个,然后支持多个master节点,每个master可以支持各自的主从。
    
    根据槽的长度取模master节点数,将槽的下标进行均匀分配,即每个master都会有属于自己的槽区间
    通过对key进行crc16算法计算及其然后对16384取模获取此key对应的槽下标
    根据槽下标寻找相应的master节点,然后将其存放至对应的槽位,一个槽位类似于一张表,即可存放多个key
    
    其原理近似MySQL的分库分表和hashMap
    

RedisCluster集群模式环境搭建

mkdir rediscluster
cd rediscluster/
mkdir redis7000
mkdir redis7001
mkdir redis7002
mkdir redis7003
mkdir redis7004
mkdir redis7005


每个配置文件内容(以7005为例)
# 后台启动
daemonize yes 
# 允许外部访问
protected-mode no 
# 修改端口号,从7000到7005
port 7005 
# 开启cluster,去掉注释
cluster-enabled yes 
# 自动生成
cluster-config-file 7000nodes.conf
# 节点通信时间 
cluster-node-timeout 15000 
logfile   /usr/rediscluster/redis7005/redis.log


启动我们的redis

/usr/redis/bin/redis-server /usr/rediscluster/redis7000/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7001/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7002/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7003/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7004/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7005/redis.conf


连接任意一个redis
/usr/redis/bin/redis-cli -h 192.168.212.163 -p 7000

(error) CLUSTERDOWN Hash slot not served  说明没有分配hash槽

# 分配hash槽
终端执行
/usr/redis/bin/redis-cli --cluster create  192.168.212.163:7000  192.168.212.163:7001  192.168.212.163:7002  192.168.212.163:7003  192.168.212.163:7004  192.168.212.163:7005  --cluster-replicas 1
(建议最好使用服务器的ip地址搭建)

# -c 标识当key的槽下标不在此Redis时,会进行重定向
/usr/redis/bin/redis-cli -h 192.168.212.163 -p 7000 –c

动态扩容

扩容节点
/usr/redis/bin/redis-server /usr/rediscluster/redis7006/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7007/redis.conf


新增一个主节点 为7006
/usr/redis/bin/redis-cli --cluster add-node 192.168.212.163:7006   192.168.212.163:7000

新增一个从节点 为7007
/usr/redis/bin/redis-cli --cluster add-node 192.168.212.163:7007   192.168.212.163:7000  --cluster-salve  --cluster-master-id     5d94171eb34ed4396bf5b9db8efaab4d96d0cf10(7006的ID)


新增的7006 是没有任何槽位 需分配Redis槽位扩容
cluster slots 
/usr/redis/bin/redis-cli --cluster reshard  192.168.212.163:7000

动态缩容

/usr/redis/bin/redis-cli --cluster  reshard  192.168.212.163:7000  --cluster-from   5d94171eb34ed4396bf5b9db8efaab4d96d0cf10  --cluster-to 511058958a3b80dd600e060c2500050c6c5a02ab  --cluster-slots 

猜你喜欢

转载自blog.csdn.net/yxh13521338301/article/details/106809617