Redis 入门 【vaynexiao】

基础

c语言编写,内存存储,key-value保存,没有表
一秒写8w次,读11w次
NoSQL = Not Only SQL
端口 6379
默认16个数据库,默认使用第0个
启动时,先启动服务端,再启动客户端;
可视化客户端RedisDestopManager

安装

Windows
zip:https://github.com/MicrosoftArchive/redis/releases(停更很久了,因为主推linux使用)
cmd到文件夹路径启动:redis-server redis.windows.conf
安装服务:redis-server --service-install redis.windows.conf
卸载服务:redis-server --service-uninstall
开启服务:redis-server --service-start
停止服务:redis-server --service-stop

Linux

download:http://download.redis.io/releases/
tar -zxvf xxx.tar.gz
yum install gcc-c++
make  # 耗时大约5-10分钟
make install # 默认安装路径在/usr/local/bin

# 建议复制config文件到指定目录,后期使用这个新的config文件
cp /root/install/redis-6.0.3/redis.conf /usr/local/bin/redisconfig/

vim redis.conf 
daemonize yes # 改为后台启动方式
redis-server redisconfig/redis.conf  # 启动服务,不指定配置文件默认使用redis目录下的redis.conf
redis-cli  -p 6379 # 客户端连接,默认 -h localhost -p 6379

ps -ef|grep redis   # 查看redis进程是否开启,一个服务端,一个客户端
root     21736  2965  0 02:47 ?        00:01:16 redis-server 127.0.0.1:6379
root     28975 26023  0 19:30 pts/3    00:00:00 redis-cli -p 6379
root     29140 24347  0 19:30 pts/1    00:00:00 grep --color=auto redis

压力测试

src/redis-benchmark  -h localhost -p 6379 -c 100 -n 100000
====== SET ======  # 这里只是截取了set,还有get 等很多命令
  100000 requests completed in 2.01 seconds  # 对100000个请求进行写入测试
  100 parallel clients # 100个并发客户端
  3 bytes payload # 每次写入3字节
  keep alive: 1 # 只有一台服务器来处理这些请求,单击性能
  host configuration "save": 900 1 300 10 60 10000
  host configuration "appendonly": no
  multi-thread: no
23.81% <= 1 milliseconds
91.56% <= 2 milliseconds
95.31% <= 3 milliseconds
97.77% <= 4 milliseconds
98.51% <= 5 milliseconds  # 5秒后处理了98.51%
100.00% <= 20 milliseconds # 20秒后处理了100%
49751.24 requests per second # 每秒处理49751.24个请求

基本命令

select index # 切换数据库 0-15
dbsize # 查看当前数据库大小
keys * # 查看所有key
keys s* # 查看所有以s开头的key
del k
flushdb 
flushall
exists k # 是否存在
move k index # 移动到 指定数据库 中
rename oldk newk 
renamenx oldk newk # 当newk不存在时才会执行
ttl key  # 查看过期时间 单位秒 -1永远不过期 -2已过期
persist k # 设置k永不过期
expire key 10 # 设置过期时间,单位s
expireat k # 设置过期时间,单位时间戳
type key # 查看类型
randomkey # 随机返回一个k

Redis 为什么单线程还那么快?
单线程,基于内存操作,性能瓶颈依据机器内存和网络宽带
误区1:高性能不一定是多线程
误区2:多线程(CPU上下文切换)一定比单线程快
核心:redis是将所有的数据全部放在内存中,所以说使用单线程去操作效率已经是最高,
多线程会多次上下文切换,很耗时;对于内存系统来说,没有上下文切换就是理论最快,内存情况下这样就是最佳方案!

数据类型

http://doc.redisfans.com/index.html【在线手册】

String 字符串

set k v   
set k v ex 20 # set同时过期时间20s,默认s
get k
setex k 30 v # 新建key,同时设置过期时间,单位s
setnx k v #  当k不存在时set
getset k v # 没有k,新建且赋值v 返回错误信息:(nil),有k,获取v

ttl k # 查看剩余过期时间 -2表示已过期
mset k1 v1 k2 v2 # 同时设置多个key
msetnx k1 v1 k2 v2 # k不存在时候执行;原子操作,所有k必须都不存在,才能顺利执行
mget k1 k2
append k # 给key的value追加str,key不存在,会创建key
strlen k # 查看length

incr k # 自增1,只针对数字
incrby k 10 # 增加10, key不存在时先被初始化为0,然后再执行
decr key # 月incr相反
decrby key 7

getrange k start end #  index从0开始 (包括 start 和 end 在内)
getrange k 1 -1 # -1表示截取到尾部
setrange k index  xxxxx # index从0开始,从index开始替换五个字符

应用场景:

mset user:1 {
    
    name:zhangan,age:24}
上面的name age都在一个key里面,下面的写法优势是可以设置/更新部分字段
mset user:1:name zhangsan user:1:age 24
mget user:1:name user:1:age

setnx分布式锁
优势:因为是setnx所以第一个才能设置成功,达到锁的目的

incr article:readcount:{
    
    } 文章阅读数+1、点赞统计,类似计数器

List 列表

  • 实际上是一个链表,有节点概念,left right before after 都可以操作
  • key不存在,会创建新的链表
  • 移除了所有值,空链表,便会消失
  • 两侧操作效率高,中间操作相对慢一点
  • 变相可实现消息 (消息队列 lpush rpop)(栈 lpush lpop)
  • 命令全部L开头 先进先出 value可重复,有序
lpush k v1 v2 v3 v4 v5
lrange k start end # 截取一部分,index从0开始,-1表示尾部,-2表示倒数第二个
lpushx k v1 v2 # key存在时插入
rpush k v
rpushx k v # k不存在,push无效

lpop k # 得到一个元素且移除该元素
rpop k
blpop [k1, k2, k3...]  timeout  # k1 k2 k3 按顺序查找,返回匹配的第一个k和v,等待时间timeout毫秒 ???

lindex k index # 根据index获取指定元素,也可以-1 -2倒叙获取
llen k # 获取个数

lrem k count v # 从左边开始移除count个匹配的value
lrem k -2 morning    # 移除从表尾到表头,共计-2的绝对值个 morning
lrem k 0 hello      # 移除表中所有 hello,0表示所有

ltrim key start end # 裁掉范围之外元素,含头不含尾
rpoplpush source destination # 剪切一个元素到目标,右弹左入
lset key index value # key不存在会报错,更新指定index的value
linsert k before/after targetValue myValue #在指定value前/后插入 value

Set 无序集合

不重复。随机获取

sadd k v1 v2 v3 v4 v5
smembers k # 查看时顺序并不是按照插入顺序,但每次查出后的顺序是固定的
sismember k v # 查看v在不在k中
srem k v # 移除指定的value
scard k # 统计元素个数
spop k # 弹出一个 且 移除
smove source destination v # 从源key剪切一个value存入目标key
srangemember k # 随机获取一个元素,并不移除该元素
srangemember k count # 随机获取count个元素,并不移除该元素

sdiff k1 k2 k3 k4 # 以k1为基准,向右逐个比较,取出差集
sdiffstore destination k1 k2 k3 # 取出差集,存入新的key中
sinter k1 k2 k3 # 交集
sinterstore destination k1 k2 k3 # 取出交集,存入新的key中
sunion k1 k2 k3 # 并集
sunionstore destination k1 k2 k3 # 取出并集,存入新的key中

抽奖
sadd k zs lisi wangwu # 将用户全部sadd进去,
smembers k # smembers查看所有用户
spop key # spop抽出一个幸运用户,该用户会移除该队伍,避免重复中奖
srangemember k 5 # 随机抽5名用户

点赞
sadd like:消息ID zhangsan # 张三点赞了,加入该消息的like列表
srem like:消息ID zhangsan # 张三取消点赞
sismember like:消息ID zhangsan # 检查张三是否给该消息点过赞
smenbers like:消息ID # 获取全部点赞的人
scard like:消息ID # 获取点赞总数

关注模型
sinter Mata Faker  # Mata和Faker的共同关注
sismember jay G.E.M # 我关注的G.E.M是否也关注jay (我关注的人也关注他)
sdiff k1 k2 # 差集 (你认识的,我不认识的 = 可能认识的人)

Zset 有序集合

比set多了一个属性score,根据score排序,默认从小到大

zadd k score1 v1 # key没有则创建
zadd k score1 v1 score2 v2

zrange k 0 -1 [withscores]
zrevrange k 0 -1 # 反排序
zrevrangebyscore k max min # 按分数从大到小获取

zrank k v # 获取v在k中index
zrevrank k v # 获取v的index

zscore k v  # 获取分数
zincrby k step v # 给v增加step,只针对数字

zadd sal 5000 zhangsan
zadd sal 7800 xiaohong
zadd sal 9000 bob

zrangebyscore k min max
zrangebyscore k -inf +inf
zrangebyscore k -inf +inf withscores
zrangebyscore k -inf 2500 withscores # 包括2500

zrem k v
zremrangebyscore k min max 
zremrangebyrank k start end 

zcard k
zcount k min max


zunionstore newKey count k1 k2 k3 
# 将三个k组合得到交集放入newkey,count是期望组合的key的数量
# 返回的newkey为空时,会删除newkey

积分排行榜
zadd hotnews:202010001 点击数 新闻标题 # 新建一则新闻   key=hotnews:202010001   score=点击数    v=新闻标题
zincrby hotnews:202010001 1 新闻标题  # 点击次数+1
zrevrange hotnews:202010001 0 9 withscores # 获取热点新闻前十名 做排行
zunionstore newKey count k1 k2 k3 # 三天三个knewkey,选出交集,newkey就是三天全部新闻 再使用zrevrange newkey 0 4 withscore 得到前5

Hash 哈希

hset k filed1 value1 # 一个key,可以存多个字段,都有对应的value
hsetnx f k v # 不存在就执行
hmset f k1 v1 k2 v2
hget f k1
hmget f k1 k2
hgetall k # 取出key的全部filed value
hkeys f # 取出key的全部filed
hvals f# 取出key的全部value

hdel f k1
hlen f # key中filed的总数
hexits f k1

hincrby f k num # 增加num
hincrby f k -1 # 减少1

存储user对象
hset user:1 name zhangsan
hmset user {
    
    userid}:name zs {
    
    userid}:age 24
hmget user 9527:name 9527:age
hmset userid name zs age 24
hmget userid name age

购物车
用户id:9527      商品id:10087    商品总数:value
hset 9527 10087 1 # 给用户 9527 添加 1 件商品 10087
hset 9529 10088 1
hset 9628 10089 1
hincrby 9527 10087 10 # 给10087商品数量增加10
hlen 9527 # 查看商品总数
hdel 9527 10087 #删除用户的10087商品
hgetall 9527 # 获取全部商品

同类数据整合归类,方便管理
相比string消耗内存与cpu更小,更节省空间

过期时间只能用在key上,不能用再filed
Redis集群架构下不适合大规模使用(大量用户的数据不能全放在一个user下)

geospatial 地理位置

有效经度 -180 180

有效纬度 -85.05112878 85.05112878

geoadd china:city 116.40 39.90 beijing
geoadd china:city 121.47 31.23 shanghai
geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen
geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian

geopos china:city beijing
geopos china:city beijing chongqin

geodist china:city beijing shanghai km  # m km mi ft

georadius china:city 110 30 1000 km # 以110 30未中心寻找1000km的城市
georadius china:city 110 30 1000 km withdist # 以110 30为中心寻找1000km的城市,且显示距离
georadius china:city 110 30 1000 km withcoord # 以110 30为中心寻找1000km的城市,显示经纬信息
georadius china:city 110 30 1000 km withdist withcoord count 1 # 只显示1条

georadiusbymember china:city beijing 1000 km # 与georadius类似,不过以现有值作为基准,此处为“beijing”

geohash china:city beijing chongqing # 将多个元素转为字符串,如“wx4fbxxfke0”

geo底层实现就是用的zset,所以可以使用zset命令操作
zrange china:city 0 -1 # 查看全部元素
zrem china:city beijing # 删除指定元素

Hyperloglog 基数

统计计数而已,可以接受误差

pfadd k a b c d e f g
pfcount k
pfmerge targetKey k1 k2

Bitmap

位存储,只关注状态,比如登陆和未登录,关注和未关注,打卡和未打卡, 只能存0或者1

setbit kaoqin 0 1
setbit kaoqin 1 0
setbit kaoqin 2 1
setbit kaoqin 3 0
setbit kaoqin 4 1

getbit kaoqin 2
bitcount kaoqin # 统计key=kaoqin中 value=1 的总数

事务

multi
sql1
sql2
...
sql3
exec
discard

redis事务没有原子性,单条命令才有
编译时错误,exec时全部sql不执行,直接报错
运行时错误,会跳过该sql,其他sql照样执行,换句话说redis没有事务原子性

watch
unwatch
exec时会检查监视对象有无变化,有则执行失败

jedis

官方推荐的连接方式

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.2.0</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.62</version>
    </dependency>
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        System.out.println(jedis.ping()); // pong
        jedis.set("test1","aaa");
        jedis.set("test2","bbb");
        Set<String> keys = jedis.keys("*");
        long del = jedis.del("test2");
        System.out.println(del);
        
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("age",24);
        jsonObject.put("sex","female");
        jsonObject.put("name","Faker");

        Transaction multi = jedis.multi();
        String str = jsonObject.toJSONString();

        try {
    
    
            multi.set("user1", str);
            multi.set("user2", str);
            multi.exec();
        } catch (Exception e){
    
    
            multi.discard();
            e.printStackTrace();
        } finally {
    
    
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();
        }

持久化

rdb

指定的时间间隔内将内存中数据集快照写入磁盘
dump.rdb
file dump.db # data数据类型文件  二进制

触发机制:
	1,正常关闭数据库时
	2,flushall
	3,save 900 1 等等(900s内有一个变化)
优点:
	1,效率高
	2,适合大规模数据恢复
	3,对数据的完整性要求不高
缺点:
	1,处于时间间隔中的数据在意外宕机后会丢失
	2,fork进程时会占用一定的内存空间

aof

记录每一句命令,然后执行命令达到恢复数据的目的

appendonly no # 默认不开启aof模式,大部分情况下rdb已经够用
appendfilename "appendonly.aof"

appendfsync always # 每次修改都会sync,消耗性能
appendfsync everysec # 每一秒sync一次,最多丢失1s的数据
appendfsync no # 不执行sync,系统会自动同步,这种速度最快 ???

优点:
	数据文件相对比较大,aof远大于rdb,恢复数据速度也较慢,因为记录的是每一条命令
	运行效率也比rdb慢,所以默认时rdb持久化方式

发布/订阅

客户端a订阅频道,客户端b向频道发送消息,a即可自动收到消息,频道名称可自定义

客户端1:
SUBSCRIBE channel1 # 表示订阅自定义频道channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1

客户端2:
PUBLISH channel1 "hello world" # 往channel1频道发送消息
(integer) 1
127.0.0.1:6379> PUBLISH channel1 "22222"
(integer) 1
发送之后,客户端1自动接收:
1) "message"
2) "kuang"
3) "hello world"
1) "message"
2) "kuang"
3) "22222"
使用场景:
	1,实时消息系统,比如订阅某up主后会立刻受到up主设置的问候消息
	2,实时聊天系统,订阅,关注场景
稍微复杂的场景要使用消息队列MQ

在这里插入图片描述

主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点
(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
Master以写为主,Slave 以读为主。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

主从复制作用:

  • 数据的热备份:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障转移:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 高可用:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是
    Redis高可用的基础。

Redis数量推荐
1、从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
2、从容量上,单个Redis服务器内存容量有限,大型项目不满足。

对于这种场景,我们可以使如下这种架构:
(一般至少3个,一主二从)
在这里插入图片描述

  • 多配置几个服务器分散压力;服务器有好几个,那么客户端就得好几个;
  • 主服务器负责写入数据,其他服务器作为从服务器,同步master的数据,负责读取数据;这就是:读写分离;
  • 80%都是读操作;
  • 主服务器写入数据后,从服务器salveof master后,会自动同步master的数据,包括master的历史数据

环境搭建

复制三个conf文件
cp redis.conf redis6379.conf
cp redis.conf redis6380.conf
cp redis.conf redis6381.conf
然后逐个修改3个文件的内容:
	port 改为6379
	daemonize yes (是否可后台运行)
	pidfile redis_6379.pid
	logfile "6379.log"
	dbfilename dump6379.rdb
ps -ef | grep redis # 3个服务都启动后,要能看到3个server
 
配置从机(关联主机)(不配置主从关系前,默认都是主节点)
slaveof 主库ip 主库端口 # 配置主从
info replication # 查看信息,包括角色是不是master,连接的从机有几个
 
主机可以写入,从机只能读(读写分离)
slaveof  ip 6379
命令来配置所属主服务器时手动操作,一次性的,需要永久生效,就配置conf文件,但后期如果修改的话就得改文件


层层链路(自己起的名字)
上一个Slave可以是下一个Slave的Master,Slave同样可以接收其他slaves的连接和同步请求,
那么该slave作为了链条中下一个slave的Master,如此可以有效减轻Master的写压力。
如果slave中途变更转向,会清除之前的数据,重新建立最新的。


测试:主机挂了,查看从机信息无影响,主机恢复,再次查看信息又恢复正常

全量复制:从机salveof master时,会连同历史数据全部发送至从机
增量复制:只同步新增的数据

在这里插入图片描述
b机器既是主机,也是从机,info查询显示实际上是slave,也不能写入数据

在这里插入图片描述

当Master挂掉后,Slave可键入命令 slaveof no one使当前从机独立,自然就成了Master;
但如果不修改从机的master,主机回来后依然可以恢复master的身份进行主从复制。

sync原理:
1、Slave启动成功连接到master后会发送一个sync命令;
2、Master接到命令启动后的存盘进程,同时收集所有接收到的用于修改数据集命令,
在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步;
3、全量复制:而slave服务在数据库文件数据后,将其存盘并加载到内存中;
4、增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步;
5、但是只要是重新连接master,一次完全同步(全量复制)将被自动执行。

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

哨兵模式

手动版的升级版,即自动版解决主从关系

就是将主从复制功能配置为自动化,哨兵机制是一个独立进程,独立运行,所以要新开启一个服务,专门启动哨兵,通过发送命令等待Redis服务器的响应,从而达到监控的目的。

哨兵模式是一种特殊模式,首先Redis提供了哨兵命令,哨兵是一个独立的进程,它会独立运行。其原理是哨兵通过发送命令等待Redis服务器响应,从而监控运行的多个Redis实例。

哨兵的作用:
1.通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
2.当哨兵检测到master宕机,会自动从slave中选举一个为master,然后通过发送订阅模式通知其他的从服务器,修改配置文件,让他们切换主机。

一个哨兵进程对Redis服务器进行监控,可能会出现问题,所以我们使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多个哨兵模式

故障切换的过程:
假设主服务器宕机,哨兵1先检测到这个结果,系统不会马上进行failover过程,仅仅时哨兵1直观认为主服务器不可用,这个现象叫主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程成为客观下线。(主观下线的数量到达一定后,才会客观下线)

反客为主的自动版,能够后台监控Master库是否故障,如果故障了根据投票数自动将slave库转换为主库。
一组sentinel能同时监控多个Master。
使用步骤:
1、在Master对应redis.conf同目录下新建sentinel.conf文件,名字绝对不能错;
2、配置哨兵,在sentinel.conf文件中填入内容:
sentinel monitor 被监控数据库名字(自己起名字) ip port 1
说明:上面最后一个数字1,表示主机挂掉后slave投票看让谁接替成为主机,得票数多少后成为主机。
3、启动哨兵模式:
命令键入:redis-sentinel /myredis/sentinel.conf
注:上述sentinel.conf路径按各自实际情况配置

哨兵配置说明

# Example sentinel.conf
# 哨兵sentinel实例运行的端口 默认26379
port 26379
# 哨兵sentinel的工作目录
dir /tmp
# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2

# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都
要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同
步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的
master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。 
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超
时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
# SCRIPTS EXECUTION
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮
件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执
行。
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等
等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常
运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果
sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执
行的,否则sentinel无法正常启动成功。
#通知脚本
# 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缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一
些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据
的一致性要求很高,那么就不能使用缓存。
另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也都有比较流行的解决方案。

穿透:(查不到数据)

程序在处理缓存时,一般是先从缓存查询,如果缓存没有这个key获取为null,则会从DB中查询,并设置到缓存中去。
按这种做法,那查询一个一定不存在的数据值,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
在这里插入图片描述
在这里插入图片描述
但是这种方法会存在两个问题:
1、如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多
的空值的键;
2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于
需要保持一致性的业务会有影响。

击穿(查询量太大)

一个key非常热点,在不停的扛着大并发,大并发集中访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,并且回写缓存,会导使数据库瞬间压力过大。

解决方案:

  1. 加互斥锁:通过synchronized+双重检查机制:某个key只让一个线程查询,阻塞其它线程(缺点: 会阻塞其它线程)
  2. 设置value永不过期(是最可靠的,最安全的但是占空间,内存消耗大,并且不能保持数据最新 这个需要根据具体的业务逻辑来做 )

雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效

雪崩就是指缓存中大批量热点数据过期后系统涌入大量查询请求,因为大部分数据在Redis层已经失效,请求渗透到数据库层,大批量请求引起数据库宕机。

解决办法:
1,将缓存失效时间分散开,比如每个key的过期时间是随机,防止同一时间大量数据过期现象发生,这样不会出现同一时间全部请求都落在数据库层,如果缓存数据库是分布式部署,将热点数据均匀分布在不同Redis和数据库中,有效分担压力,别一个人扛。
2,简单粗暴,让Redis数据永不过期(如果业务准许,比如不用更新的名单类)。当然,如果业务数据准许的情况下可以,比如中奖名单用户,每期用户开奖后,名单不可能会变了,无需更新。

产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商
品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都
过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波
峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
在这里插入图片描述
其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。

3种解决方案
redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续
工作,其实就是搭建的集群。
限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对
某个key只允许一个线程查询数据和写缓存,其他线程等待。
数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数
据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让
缓存失效的时间点尽量均匀。

区别

穿透是单个key在缓存中不存在,从而进入数据库中查询,此时DB产生压力;
击穿是指热点key的大量查询,此时DB压力巨大,面临随时宕机可能;
雪崩是大量的热点key全部涌入服务器,此时DB已经扛不住而宕机,即使重启也会再次宕机。

猜你喜欢

转载自blog.csdn.net/vayne_xiao/article/details/106434722