Redis入门学习

​ Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串哈希表列表集合有序集合位图hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区

基础命令

redis-cli -h localhost -p 6379 # 客户端连接服务端

localhost:6379> keys * # 查看当前数据库所有key
1) "mykey"
2) "18:45"

localhost:6379> select 0 # 选择数据库index,默认config中redis一旦启动有16个库,不选择则自动选择0号库
OK

localhost:6379> DBSIZE # 查看当前数据库中有多少key
(integer) 2

localhost:6379> exists mykey # 查看当前数据库中是否存在该key
(integer) 1

localhost:6379> ttl mykey # 查看key的倒计时,-1代表永久
(integer) -1

localhost:6379> expire mykey 5 # 给key设置过期时间
(integer) 1

localhost:6379> ttl mykey # -2代表已过期
(integer) -2

localhost:6379> type mykey # 查看key的数据类型
none

localhost:6379> type 18:45 # 查看key的数据类型
string

localhost:6379> move 18:45 1 # 移动key去对应index的库

Redis基本数据结构

五大基本数据结构三大特殊类型
String(字符串)Geospatial(地理位置)
List(列表)Hyperloglog(基数统计)
Set(无序集合)Bitmap(位图)
ZSet(有序集合)
Hash(哈希)

String(字符串)

  • String可以存储字符串和数字!
  • 计数器。例如:浏览量
  • 统计多单位数量。例如:user的多个维度属性
############################################################################################
localhost:6379> set key1 va # 设置值
OK
localhost:6379> get key1 # 获取值
"va"
localhost:6379> append key1 "hello" # 字符串追加,如果不存在则相当于set,返回追加后的字符串长度
(integer) 7
localhost:6379> get key1
"vahello"
localhost:6379> strlen key1 # 获取key的长度
(integer) 7

############################################################################################
localhost:6379> set views 0
OK
localhost:6379> get views
"0"
localhost:6379> incr views # 自增+1
(integer) 1
localhost:6379> incr view # 自增一个不存在的key,默认从0变成1
(integer) 1
localhost:6379> get view
"1"
localhost:6379> INCRBY views 100 # 设置增加步长为100
(integer) 101
localhost:6379> DECRBY view 200 # 设置减少步长为100
(integer) -198
localhost:6379> DECRBY views 200
(integer) -99

############################################################################################

【截取字符串】

localhost:6379> set key1 wangzichen
OK
localhost:6379> get key1
"wangzichen"
localhost:6379> GETRANGE key1 0 -1
"wangzichen"
localhost:6379> GETRANGE key1 0 3
"wang"
localhost:6379> SETRANGE key1 3 shuaige # 从第N个索引位置改变字符串
(integer) 10
localhost:6379> get key1
"wanshuaige"

############################################################################################

【设置过期时间】

# 为指定的 key 设置值及其过期时间。如果 key 已经存在, SETEX 命令将会替换旧的值。
localhost:6379> setex key2 30 wangzichen
OK
localhost:6379> ttl key2
(integer) 28

# 如果不存在则设置成功,可以用作分布式锁
localhost:6379> setnx key3 wangzichen
(integer) 1
localhost:6379> setnx key3 wangzichen
(integer) 0

############################################################################################

【批量设置】

localhost:6379> mset k1 v1 k2 v2 k3 v3
OK
localhost:6379> keys *
1) "k1"
2) "k3"
3) "k2"
localhost:6379> get k3
"v3"
localhost:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"

 # 此处设置不成功,虽然k6不存在,但因为k1已经存在,导致k6也没有设置成功,msetnx是原子操作
localhost:6379> msetnx k1 v1 k6 v6
(integer) 0
localhost:6379> get k6
(nil)

############################################################################################

【保存对象】

localhost:6379> set user:1 {name:wangzichen,age:24}
OK
localhost:6379> get user:1
"{name:wangzichen,age:24}"

# user:{id}:{field}
# 这种方法虽然写法复杂了,但是获取值的时候不需要格式化了,视情况选择
localhost:6379> mset user:2:name wangzichen user:2:age 24 
OK
localhost:6379> get user:2
(nil)
localhost:6379> mget user:2:name user:2:age
1) "wangzichen"
2) "24"

############################################################################################

List(列表)

  • 具有方向性,可以用作队列、栈、阻塞队列
# LPUSH代表是对List的push操作,默认从左向右插入list,Rpush代表从右向左插入

localhost:6379> lpush list1 zhangsan
(integer) 1
localhost:6379> lpush list1 wangwu
(integer) 2
localhost:6379> lpush list1 lisi
(integer) 3

localhost:6379> lrange list1 0 -1 # lrange key 0 -1 查看list中所有元素
1) "lisi"
2) "wangwu"
3) "zhangsan"

localhost:6379> rpush list1 liuneng
(integer) 4
localhost:6379> lrange list1 0 -1
1) "lisi"
2) "wangwu"
3) "zhangsan"
4) "liuneng"

############################################################################################

localhost:6379> lpop list1 # 移除使用pop,方向同理
"lisi"
localhost:6379> lrange list1 0 4
1) "wangwu"
2) "zhangsan"
3) "liuneng"

############################################################################################

localhost:6379> lindex list1 1 # 获取list1中下标为1的元素值
"zhangsan"
localhost:6379> llen list1 # 获取list1的长度
(integer) 3

############################################################################################

localhost:6379> LRANGE list1 0 -1
1) "wangwu"
2) "zhangsan"
3) "liuneng"
localhost:6379> lpush list1 temp
(integer) 4
localhost:6379> lpush list1 wangwu
(integer) 5

# count>0从左向右移除,count<0从右向左移除,count=0移除全部
localhost:6379> lrem list1 -1 wangwu # lrem用于移除指定元素,使用count可以移除多个
(integer) 1
localhost:6379> LRANGE list1 0 -1
1) "wangwu"
2) "temp"
3) "zhangsan"
4) "liuneng"

############################################################################################
# Ltrim 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
localhost:6379> ltrim list1 1 2 
OK
localhost:6379> lrange list1 0 -1
1) "temp"
2) "zhangsan"

############################################################################################

localhost:6379> lpush mylist hello1
(integer) 1
localhost:6379> lpush mylist hello2
(integer) 2
localhost:6379> lpush mylist hello3
(integer) 3
localhost:6379> lpush mylist hello4
(integer) 4
localhost:6379> lpush mylist hello5
(integer) 5

localhost:6379> lset mylist 2 hello_mid # 更新某个位置上的值
OK
localhost:6379> lrange mylist 0 -1
1) "hello5"
2) "hello4"
3) "hello_mid"
4) "hello2"
5) "hello1"
localhost:6379> lset mylist 7 hello_mid # lset 如果list和index都不存在会报错
(error) ERR index out of range
localhost:6379> LINSERT mylist before 2 hello_last
(integer) -1

# 如果有多个相同的hello_mid,会插在从左向右找到的第一个值的前/后
localhost:6379> LINSERT mylist after hello_mid hello_after # 在list的某个元素前/后插入新元素
(integer) 6
localhost:6379> lrange mylist 0 -1
1) "hello5"
2) "hello4"
3) "hello_mid"
4) "hello_after"
5) "hello2"
6) "hello1"

小结:实际上内部结构是一个链表,before-Node-after,左右都可以插入值

  • 如果key不存在,插入则创建新的链表
  • 如果key存在,则新增内容
  • 如果移除了所有的值,变成了空链表,也代表不存在
  • 在两边插入或者改动值,效率最高;中间元素相对来说效率会低一点

Set(集合)

set中的值是不能重复的。

localhost:6379> sadd myset A # 添加元素,如果不存在则创建,添加成功返回1 ,添加失败返回0
(integer) 1
localhost:6379> sadd myset B
(integer) 1
localhost:6379> sadd myset C
(integer) 1
localhost:6379> sadd myset D
(integer) 1
localhost:6379> sadd myset E
(integer) 1
localhost:6379> smembers myset # 查看集合中的元素,集合是无序的
1) "B"
2) "A"
3) "D"
4) "C"
5) "E"
localhost:6379> sismember myset F # 判断F元素是否在集合中
(integer) 0
localhost:6379> sismember myset A
(integer) 1

############################################################################################

localhost:6379> SCARD myset # 查看集合中元素的数量
(integer) 5
localhost:6379> SREM myset A # 移除指定元素
(integer) 1
localhost:6379> SCARD myset 
(integer) 4

localhost:6379> SRANDMEMBER myset # 随机返回集合中一个元素
"C"
localhost:6379> SRANDMEMBER myset
"E"
localhost:6379> SRANDMEMBER myset 2 # 随机返回集合中指定个数的元素
1) "B"
2) "C"
localhost:6379> SRANDMEMBER myset 2
1) "B"
2) "D"

############################################################################################

127.0.0.1:6379> spop myset # 随机移除一个元素
"D"
127.0.0.1:6379> smembers myset
1) "B"
2) "C"
3) "E"
127.0.0.1:6379> spop myset
"E"
127.0.0.1:6379> SREM myset B # 移除集合中指定元素
(integer) 1
127.0.0.1:6379> SREM myset E
(integer) 0
127.0.0.1:6379> smembers myset
1) "C"

############################################################################################

127.0.0.1:6379> smove myset myset2 C # 将指定元素从一个集合移动到另一个集合
(integer) 1
127.0.0.1:6379> smembers myset
(empty array)
127.0.0.1:6379> smembers myset2
1) "C"

############################################################################################
【集合操作:交集、并集、差集】
127.0.0.1:6379> sadd myset a
(integer) 1
127.0.0.1:6379> sadd myset b
(integer) 1
127.0.0.1:6379> sadd myset c
(integer) 1
127.0.0.1:6379> sadd myset2 c
(integer) 1
127.0.0.1:6379> sadd myset2 d
(integer) 1
127.0.0.1:6379> sadd myset2 e
(integer) 1

# sdiff myset元素减去myset2元素
127.0.0.1:6379> sdiff myset myset2
1) "a"
2) "b"

# sinter 两个元素的交集
127.0.0.1:6379> SINTER myset myset2
1) "c"

# sunion 两个元素的并集
127.0.0.1:6379> sunion myset myset2
1) "b"
2) "c"
3) "e"
4) "a"
5) "d"

微博、微信的共同关注、共同好友功能基于集合可以实现

Hash(哈希)

  • key-map结构,value的值是一个map集合。
127.0.0.1:6379> hset myhash name wangzichen # 设置key-(key-value)的hash数据
(integer) 1

# 设置key-多个(key-value)的hash数据
127.0.0.1:6379> hmset myhash age 24 tall 180cm hobby basketball
OK

127.0.0.1:6379> hget myhash tall # 获取hash中key对应的value
"180cm"

127.0.0.1:6379> hmget myhash tall age hobby # 获取hash中多个key对应的value的数据
1) "180cm"
2) "24"
3) "basketball"

127.0.0.1:6379> hdel myhash name # 删除myhash中某一个key,key对应的value也随之消失
(integer) 1

127.0.0.1:6379> hgetall myhash # 获取myhash中所有数据
1) "name"
2) "wangzichen"
3) "age"
4) "24"
5) "tall"
6) "180cm"
7) "hobby"
8) "basketball"

127.0.0.1:6379> hlen myhash # 获取myhash的key数量
(integer) 2

############################################################################################

# 判断hash中key是否存在
127.0.0.1:6379> HEXISTS myhash hobby
(integer) 1
127.0.0.1:6379> HEXISTS myhash name
(integer) 0

127.0.0.1:6379> hkeys myhash # 获取hash中所有的key
1) "tall"
2) "hobby"
127.0.0.1:6379> HVALS myhash # 获取hash中所有的value
1) "180cm"
2) "basketball"

############################################################################################

【hash中只有HINCRBY命令】
127.0.0.1:6379> HINCRBY myhash tall 1
(error) ERR hash value is not an integer
127.0.0.1:6379> HINCR myhash tall 1
(error) ERR unknown command `HINCR`, with args beginning with: `myhash`, `tall`, `1`, 

127.0.0.1:6379> hsetnx myhash name wangzichen # 和字符串 setnx 作用相同
(integer) 1
127.0.0.1:6379> hsetnx myhash hobby basketball
(integer) 0

Hash更适合对象存储,方便变更,例如用户信息。

Zset(有序集合)

  • 给set中的key添加一个权值,方便对key排序
  • zrange参数代表排序的index,从start到stop;而zrevrange参数代表排序的index,从start到stop
  • zrangeByscore参数代表排序的score,从min到max;而zrevrangeByscore参数代表排序的score,从max到min。
# 1、2、3代表后面key的权值
127.0.0.1:6379> zadd myzset 1 one
(integer) 1
127.0.0.1:6379> zadd myzset 2 two
(integer) 1
127.0.0.1:6379> zadd myzset 3 three
(integer) 1

127.0.0.1:6379> ZRANGE myzset 0 -1
1) "one"
2) "two"
3) "three"

############################################################################################

127.0.0.1:6379> ZRANGEBYSCORE myzset 2 4 # 通过权值排序,范围是【2,4】
1) "two"
2) "three"
3) "four"
127.0.0.1:6379> ZRANGEBYSCORE myzset -inf +inf # 通过权值排序,范围是(负无穷,正无穷)
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"
127.0.0.1:6379> ZRANGEBYSCORE myzset -1 7
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"
127.0.0.1:6379> ZRANGEBYSCORE myzset 0 -1
(empty array)
127.0.0.1:6379> ZRANGEBYSCORE myzset -inf +inf withscores # 返回带有权值
 1) "one"
 2) "1"
 3) "two"
 4) "2"
 5) "three"
 6) "3"
 7) "four"
 8) "4"
 9) "five"
10) "5"

127.0.0.1:6379> zrevrangebyscore myzset 5 0 withscores
 1) "four"
 2) "5"
 3) "five1"
 4) "5"
 5) "five"
 6) "5"
 7) "three"
 8) "3"
 9) "two"
10) "2"
11) "one"
12) "1"
############################################################################################

127.0.0.1:6379> zrem myzset five # 移除有序集合的某个元素
(integer) 1
127.0.0.1:6379> ZCARD myzset # 返回有序集合中的元素数量
(integer) 4
127.0.0.1:6379> zcount myzset 1 3 # 获取指定区间的元素数量
(integer) 3

可以存储班级成绩表、工资表排序、消息权重存储、排行榜

Geospatial(地理位置)

  • 导入经纬度,计算距离,地理位置信息
  • 应用:附近的人,打车距离
相关命令
GEOADD
GEODIST
GEOHASH
GEOPOS
GEORADIUS
GEORADIUSBYMEMBER
# 添加城市经纬度数据  geoadd key 经度 纬度 名称
127.0.0.1:6379> GEOADD china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> GEOADD china:city 106.50 29.53 chongqing
(integer) 1
127.0.0.1:6379> GEOADD china:city 114.05 22.52 shenzhen
(integer) 1
127.0.0.1:6379> GEOADD china:city 120.16 30.24 hangzhou
(integer) 1
127.0.0.1:6379> geoadd china:city 108.96 34.26 xian
(integer) 1

# 获取城市的地理信息
127.0.0.1:6379> GEOPOS china:city xian
1) 1) "108.96000176668167114"
   2) "34.25999964418929977"

# 获取两个城市之间的距离
127.0.0.1:6379> GEODIST china:city xian chongqing
"575046.9885"
127.0.0.1:6379> GEODIST china:city xian shanghai
"1216930.7473"

# 以100,30为经纬度的点,半径1000km之内的城市
127.0.0.1:6379> GEORADIUS china:city 100.00 30.00 1000 km
1) "chongqing"
2) "xian"

# 带有参数的返回
127.0.0.1:6379> GEORADIUSBYMEMBER china:city xian 1000 km
1) "xian"
2) "chongqing"
3) "beijing"

127.0.0.1:6379> GEORADIUSBYMEMBER china:city xian 1000 km withcoord
1) 1) "xian"
   2) 1) "108.96000176668167114"
      2) "34.25999964418929977"
2) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
3) 1) "beijing"
   2) 1) "116.39999896287918091"
      2) "39.90000009167092543"

127.0.0.1:6379> GEORADIUSBYMEMBER china:city xian 1000 km withdist
1) 1) "xian"
   2) "0.0000"
2) 1) "chongqing"
   2) "575.0470"
3) 1) "beijing"
   2) "910.0565"
   
127.0.0.1:6379> GEORADIUSBYMEMBER china:city xian 1000 km withdist count 2
1) 1) "xian"
   2) "0.0000"
2) 1) "chongqing"
   2) "575.0470"
   
   ############################################################################################

# geospatial 底层使用的是Zset,所以Zset的命令适用于geospatial,并且geo没有提供的remove操作,Zset可以

127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> zrem china:city chongiqng
(integer) 0
127.0.0.1:6379> zrem china:city chongqing
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "xian"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "beijing"

Hyperloglog(基数)

什么是基数

A{1,3,5,7,9,7},B{1,3,5,7,8}

两个集合去重后,基数都是5

  • 一般用作UV的统计,只需要去重后的数量,不需要记录uid或pin信息
  • 如果使用set会造成内存浪费
  • Hyperloglog使用特定的算法和数据结构,2^64个元素只需要12KB的内存
# 合并之后只有{a,b,c,d,e,f}

127.0.0.1:6379> pfadd hyperkey a b c d e
(integer) 1
127.0.0.1:6379> pfcount hyperkey
(integer) 5
127.0.0.1:6379> pfadd hyperkey2 b c d e f
(integer) 1
127.0.0.1:6379> PFMERGE hyperkey3 hyperkey hyperkey2
OK
127.0.0.1:6379> pfcount hyperkey3
(integer) 6

Bitmap(位图)

使用0和1来表示事件是否发生。每一位只有两种状态

例如:用户打卡信息,7天每天打卡就用“1111111”表示,有几个1就代表打卡了几天。

这样一千万个用户365天的打卡信息就是10000000*365bit/8/1024/1024=435MB内存,占用内存非常小。

# setbit key offset value
# 设置每一天的打卡状态,第0、1、2、5天打卡了
# offset有最大限制,0~2^32之间

127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0

# 获取某一天的打卡情况
127.0.0.1:6379> getbit sign 0
(integer) 1

# bitmap也是使用string存储
127.0.0.1:6379> type sign
string

# 获取bitmap中为1的数量
127.0.0.1:6379> bitcount sign
(integer) 4

Redis事务

拓展:Mysql中,ACID代表 原子性、一致性、隔离性、持久性

原子性:事务一旦触发,就必须完成整个流程,如果中间一环出现问题,会回滚到最初的状态。比如银行转账,事务为【A转出,B转入】,但由于故障B没有转入,所以最后会让A的转出回滚,保证金额不变。

一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束触发器级联回滚等。

隔离性:数据库拥有,代表不同事务之间相互干扰的程度。隔离级别有【未提交读、提交读、可重复读、串行化】

持久性:事务对数据处理之后,对于数据的改变就是永久性的,即使系统故障也不会丢失。

  • **但是! Redis的事务不保证原子性,单条命令是原子性的 **
  • Redis事务中的命令会按照顺序执行

开启事务(multi)

命令入队(get/set)

提交(exec)

# 事务被成功执行
 
redis 127.0.0.1:6379> MULTI
OK
 
redis 127.0.0.1:6379> INCR user_id
QUEUED
 
redis 127.0.0.1:6379> INCR user_id
QUEUED
 
redis 127.0.0.1:6379> INCR user_id
QUEUED
 
redis 127.0.0.1:6379> PING
QUEUED
 
redis 127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
   ############################################################################################


# 如果出现问题可以取消事务

redis 127.0.0.1:6379> MULTI
OK
 
redis 127.0.0.1:6379> PING
QUEUED
 
redis 127.0.0.1:6379> SET greeting "hello"
QUEUED
 
redis 127.0.0.1:6379> DISCARD
OK

编译性异常(代码有问题,命令有错),事务中所有命令都不会被执行。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> setget k3
(error) ERR unknown command `setget`, with args beginning with: `k3`, 
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

运行时异常(类似于1除以0),如果事务队列中有命令在执行时抛出异常,那么其他命令可以正常执行。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1 
QUEUED
127.0.0.1:6379> incr k1 # 由于incr只能针对integer类型,所以运行的时候抛出了异常
QUEUED
127.0.0.1:6379> get k1 
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) "v1"

监控(watch)

悲观锁:在任何时候都认为会有冲突,所以在做操作前就给方法或者对象上锁,比较耗费性能,但是在冲突较多的情况下应该使用,比如synchronize。

乐观锁:认为大概率不会冲突,只要在更新数据的时候,判断在此期间是否有人修改过这个数据,如果不符合预期就等待,自旋。CAS就是乐观锁的一种实现方式。

  • 获取version
  • 更新的时候比较version
127.0.0.1:6379> watch k1 # 对 k1 添加监控
OK
127.0.0.1:6379> set k1 v1 #对 k1 进行修改
OK

127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v2
QUEUED
127.0.0.1:6379> get k1 
QUEUED
127.0.0.1:6379> exec # 此处执行发现 watch 的 k1 值已经变化,所以事务不会执行(所有语句都不会执行)
(nil)

# 执行一次exec之后,watch就失效了
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v2
QUEUED
127.0.0.1:6379> get k1 
QUEUED
127.0.0.1:6379> exec
1) OK
2) "v2"

使用Java代码实现redis的乐观锁,效果如下:

主线程不断循环获得watch,然后休眠4秒(给其他线程更改数据的时间),之后开启事务对key进行操作,检测到key对应的value被更改,继续循环获得watch。

副线程每隔3秒对key的值进行nextInt操作,随机赋值。

在这里插入图片描述

public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1",6379);
        //另开一个A线程随机更改key的值
        new Thread(()->{
            Jedis jedis2 = new Jedis("127.0.0.1",6379);
            while (true){
                jedis2.set("18:45",Integer.toString(new Random().nextInt(500)));
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"---> 设置了value");
            }

        },"A线程").start();
  			
				//由于try-catch,初始化需要提到外面来
        Transaction multi = null;
        List<Object> exec = null;
  
        //循环次数
        int loopCount = 0;
  
        try {
            do {
                System.out.println("此时key的值是: " + jedis.get("18:45"));
 								//添加监控
                jedis.watch("18:45");
                Thread.sleep(4000);
                System.out.println("自旋次数" + loopCount++);
								
                //开启事务
                multi = jedis.multi();
                multi.set("18:45", String.valueOf(3));
              	//如果事务执行失败,exec会返回null,否则是"[OK]"
                exec = multi.exec();

            } while (exec == null);

        } catch (Exception e) {
            if (multi != null){
                multi.discard();
            }
            e.printStackTrace();
        } finally {
            jedis.close();
        }

    }
  • 事务执行的结果是一个List,如果成功会返回[OK],如果有部分语句执行异常会返回[OK,异常信息]

  • multi.exec后,需要重新添加事务,需要重新对key做watch操作

  • ***redis事务的实现原理:***待补充

SpringBoot整合Redis

等我后面把spring看完再说。。。

Redis.conf配置详解

Units 单位

可以对存储单位进行配置

# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.

Include 包含

可以引入其他config文件来做配置,如果对每个服务器有公用配置模版,又有单个差异化配置,可以使用这个。

类似import

# Include one or more other config files here.  This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings.  Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include /path/to/local.conf
# include /path/to/other.conf

Modules 模块

加载so文件

Network 网络

bind 127.0.0.1 # 绑定的ip
port 6379 # 对外开放的端口
protected-mode yes # 保护模式 是都需要密码认证等
tcp等选项

GENERAL 通用

daemonize no # 以守护进程的方式运行,默认是不开启。开启后可以后台运行服务

pidfile /var/run/redis_6379.pid # 如果以后台方式运行,需要指定一个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 "" #日志的文件位置名

databases 16 # 数据库数量

REPLICATION 主从复制

replicaof <masterip> <masterport> # 如果配置了主机地址则会变成该主机的从机,搭建集群

masterauth <master-password> # 不论主机从机,都应该配置这个选项,如果集群中出现重新选举,改变主机时,从机连接主机会用到密码

SNAPSHOTTING 快照(RDB)

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""

save 900 1
save 300 10
save 60 10000


stop-writes-on-bgsave-error yes # 持久化如果出错后是否继续工作

rdbcompression yes # 是否压缩RDB文件,压缩会消耗CPU资源

rdbchecksum yes # 保存RDB文件时进行完整性检查

dbfilename dump.rdb # 持久化的文件名称

dir /usr/local/var/db/redis/ # 持久化的文件路径

SECURITY 安全

可以在这里设置连接密码

CLIENTS 客户端限制

maxclients 10000 # 最多连接10000个客户端

MEMORY MANAGEMENT 内存管理

maxmemory <bytes> # 最大内存容量

maxmemory-policy noeviction # 如果内存达到上限之后的处理策略

  1、volatile-lru:		只对设置了过期时间的key进行LRU(默认值) 
  2、allkeys-lru : 	删除lru算法的key   
  3、volatile-random:随机删除即将过期key   
  4、allkeys-random:	随机删除   
  5、volatile-ttl : 	删除即将过期的   
  6、noeviction : 		永不过期,返回错误

APPEND ONLY MODE AOF持久化

appendonly no # AOF默认不开启,使用RDB方式

appendfilename "appendonly.aof" # 持久化文件名字

# appendfsync always 
appendfsync everysec # 每秒执行一次同步,可能会丢失一秒的数据
# appendfsync no 	   
	 1、appendfsync no
	 当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。

  2、appendfsync everysec
  当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘。但是当这一 次的fsync调用时长超过1秒时。Redis会采取延迟fsync的策略,再等一秒钟。也就是在两秒后再进行fsync,这一次的fsync就不管会执行多长时间都会进行。这时候由于在fsync时文件描述符会被阻塞,所以当前的写操作就会阻塞。
  所以,结论就是:在绝大多数情况下,Redis会每隔一秒进行一次fsync。在最坏的情况下,两秒钟会进行一次fsync操作。这一操作在大多数数据库系统中被称为group commit,就是组合多次写操作的数据,一次性将日志写到磁盘。
  
  3、appednfsync always
  当设置appendfsync为always时,每一次写操作都会调用一次fsync,这时数据是最安全的,当然,由于每次都会执行fsync,所以其性能也会受到影响

Redis持久化

​ Redis的数据存储在在内存中,是断电即失的,所以要做持久化。数据从内存备份到磁盘中要有下面五个过程:

​ (1)客户端向服务端发送写操作(数据在客户端的内存中)。

​ (2)数据库服务端接收到写请求的数据(数据在服务端的内存中)。

​ (3)服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。

​ (4)操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。

​ (5)磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。

​ 这5个过程是在理想条件下一个正常的保存流程,但是在大多数情况下,我们的机器等等都会有各种各样的故障,这里划分了两种情况:

​ (1)Redis数据库发生故障,提供两种持久化方式(RDB和AOF),只要在上面的第三步执行完毕,那么就可以持久化保存,剩下的两步由操作系统替我们完成。

​ (2)操作系统发生故障,必须上面5步都完成才可以。

RDB(RedisDatabase)

  • Redis会单独fork(完全复制父进程的资源,拥有父进程的内存数据)一个子进程,进行bgsave(BackgroundSave),不会阻塞主线程响应客户端请求。

    # 客户端
    127.0.0.1:6379> bgsave
    Background saving started
    
    # 服务端 18240应该就是子进程号了
    11193:M 15 Mar 2021 19:16:10.859 * Background saving started by pid 18240
    18240:C 15 Mar 2021 19:16:10.861 * DB saved on disk
    11193:M 15 Mar 2021 19:16:10.913 * Background saving terminated with success
    
  • 父进程修改内存子进程不会反映出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

  • 类似于Copy-On-Write,缓冲区数据写入完成后才替换原来的dump.rdb文件。

  • 保存的是内存中二进制序列化数据,将redis中所有的数据备份到磁盘中。

bgsave


AOF(Append-Only-File)

  • AOF保存的是每一条写数据的命令,相当于命令日志,不断追加在aof文件后,根据日志可以恢复数据。

aof原理


  • 当AOF存储过大时,会进行重写(rewriteaof),根据redis中的数据,重新写一份类似set命令的日志文件。
  • 重写也是用的是fork子进程的方式,与RDB的bgsave相似。

aof重写


  • Redis默认不开启AOF,可以使用bgrewriteaof命令主动触发。
# 客户端
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started

# 服务端
11193:M 16 Mar 2021 10:57:52.755 * Background append only file rewriting started by pid 22807
11193:M 16 Mar 2021 10:57:52.781 * AOF rewrite child asks to stop sending diffs.
22807:C 16 Mar 2021 10:57:52.781 * Parent agreed to stop sending diffs. Finalizing AOF...
22807:C 16 Mar 2021 10:57:52.782 * Concatenating 0.00 MB of AOF diff received from parent.
22807:C 16 Mar 2021 10:57:52.782 * SYNC append only file rewrite performed
11193:M 16 Mar 2021 10:57:52.801 * Background AOF rewrite terminated with success
11193:M 16 Mar 2021 10:57:52.801 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
11193:M 16 Mar 2021 10:57:52.802 * Background AOF rewrite finished successfully
机制RDBAOF
文件体积
恢复速度
数据丢失子进程在生成快照的时候,如果数据更改并且宕机会丢失如果选择everysec,可能会丢失1秒之内的数据;如果手动rewriteaof,写入间隔修改数据并且宕机也会丢失
触发方式save [second] [changes]
bgsave
flushall命令
退出redis时
appendfsync [no/everysec/always]
bgrewriteaof
同步性能RDB文件紧凑,全量备份,适合用于进行备份和灾难恢复。如果是everysec方式,每秒会占用磁盘I/O
如果是always方式,性能较差数据完整性好

Redis订阅与发布

Redis发布订阅(pub/sub)是一种消费通信模式:发送者发送消息,订阅者接收消息。微信、微博的关注系统。

Redis客户端可以订阅任意数量的频道。

127.0.0.1:6379> SUBSCRIBE china blue # 订阅“china”和“blue”这;两个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "china"
3) (integer) 1
1) "subscribe"
2) "blue"
3) (integer) 2


# 另一个客户端给两个频道分别发送消息
127.0.0.1:6379> PUBLISH china "hello"
(integer) 1
127.0.0.1:6379> PUBLISH blue "hello"

# 订阅者打印接收到的消息
1) "message"
2) "china"
3) "hello"
1) "message"
2) "blue"
3) "hello"


实现原理

  • Redis的发布订阅模式维护了一个字典,所有chanel就是字典的key,字典的值是一个链表,每一个客户端就是链表的节点。
  • 当有客户端订阅了某个channel,就把自己添加到channel对应的字典key中。
  • 当有消息发布的时候,redis-server就会遍历该channel对应的所有链表节点。

pub/sub字典示例

与MQ消息的区别在于:如果客户端离线了,或者没有客户端订阅,那么在这个channel中发布的消息不会被记录,也不会被后来在关注订阅的客户端所消费

使用场景:

1.实时消息系统

2.实时聊天(频道当作聊天室,将信息显示给所有人)

3.订阅,关注系统

Redis集群环境搭建

主从复制,读写分离!

  • 将一台服务器(master)的数据复制到其他服务器(slave),前者称为主节点(master/leader),后者称为从节点(slave/follower)。
  • master以写操作为主,slave以读为主。
  • 数据备份:主机的数据在几个从机上复制了一份。
  • 负载均衡:减轻主节点的压力,提高redis服务器的并发处理能力
  • 故障恢复:主节点出问题后可以由其他节点继续提供服务(涉及到选举)
  • 高可用基石:是哨兵模式和集群能够实施的基础。

redis主从复制常见的一些坑- 吴正宇的个人空间- OSCHINA - 中文开源技术交流社区

环境配置

  • 只配置从机,不配置主机,因为服务器默认自己是主机
127.0.0.1:6379> info replication # 查看本机主从复制的信息
# Replication
role:master # 角色:主机
connected_slaves:0 # 无从机连接
master_replid:8b32f06a2ba02640823ad037de51c157af399438
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


# 拷贝三分配置文件,端口名作为后缀
dzsb-001996@DZ0400478 etc % cp redis.conf ./wconfig/redis79.config
dzsb-001996@DZ0400478 etc % cp redis.conf ./wconfig/redis80.config
dzsb-001996@DZ0400478 etc % cp redis.conf ./wconfig/redis81.config
dzsb-001996@DZ0400478 etc % cd wconfig 
dzsb-001996@DZ0400478 wconfig % ls
redis79.config	redis80.config	redis81.config
  • 分别配置每一个配置项,改变端口号改变pidfile的端口号守护模式开启改变日志文件的名称改变dump文件名称

  • 使用命令:

    redis-server …/etc/wconfig/redis79.config

    redis-server …/etc/wconfig/redis80.config

    redis-server …/etc/wconfig/redis81.config

    启动三个服务:

dzsb-001996@DZ0400478 bin % redis-server ../etc/wconfig/redis79.config
dzsb-001996@DZ0400478 bin % redis-server ../etc/wconfig/redis80.config
dzsb-001996@DZ0400478 bin % redis-server ../etc/wconfig/redis81.config

​ 启动后使用命令:ps -ef|grep redis 查看进程信息,确认有三个端口开放

dzsb-001996@DZ0400478 ~ % ps -ef|grep redis
  502 11193     1   0 三02下午 ??        12:16.76 redis-server *:6379
  502 75212     1   0  3:45下午 ??         0:00.52 redis-server 127.0.0.1:6379 
  502 76176     1   0  3:46下午 ??         0:00.27 redis-server 127.0.0.1:6380 
  502 76178     1   0  3:46下午 ??         0:00.25 redis-server 127.0.0.1:6381 
  502 76824 62231   0  3:47下午 ttys004    0:00.00 grep redis

主从配置

# 在80端口的客户端中,使用“slaveof [ip] [port]”命令配置从机
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
OK

# 从机查看信息
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:39779cefcf4f0ecf529aecf6b4bbc97972d92a3d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14

# 主机查看信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=252,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=252,lag=0
master_replid:39779cefcf4f0ecf529aecf6b4bbc97972d92a3d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:252
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:252

复制原理

  • 全量复制:当slave连接到master时,自动触发sync同步命令,master会启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕之后,传送整个数据文件到slave。
  • 增量复制:当slave连接到master后,master收集到的修改命令依次传给slave,完成同步。
  • 重新连接master,全量复制将被自动执行。

手动变更主机

  • 在没有哨兵模式的情况下,如果出现主机宕机的情况,可以使用“slaveof no one”命令把自己变成主机。

哨兵模式

  • 哨兵起到监督的作用,向每个服务器发送命令,如果收不到回复,则认为服务器已经宕机。例如哨兵2检测到master离线,此时称为主观离线。当其他哨兵也检测到master下线,并且数量达到一定值的时候,哨兵集群会进行一次投票(投票算法),选举出新的主机,此时故障的主机被称为客观下线

Redis哨兵模式讲解_星云随风倚码为诗-CSDN博客

配置哨兵

1.配置哨兵文件 sentinel.conf

# myredis是被监控的名称,数字1代表如果主机挂了,投票给谁成为主机
sentinel monitor myredis 127.0.0.1 6379 1 


# 启动哨兵
redis-sentinel ./etc/wconfig/sentinel.conf

37274:X 16 Mar 2021 16:55:23.228 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
37274:X 16 Mar 2021 16:55:23.229 # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=37274, just started
37274:X 16 Mar 2021 16:55:23.229 # Configuration loaded
37274:X 16 Mar 2021 16:55:23.230 * Increased maximum number of open files to 10032 (it was originally set to 2560).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.0.9 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 37274
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

37274:X 16 Mar 2021 16:55:23.233 # Sentinel ID is e5c6f2eaf1bc20373b862b0bd5894fb645320acb
37274:X 16 Mar 2021 16:55:23.233 # +monitor master myredis 127.0.0.1 6379 quorum 1
37274:X 16 Mar 2021 16:55:23.234 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
37274:X 16 Mar 2021 16:55:23.236 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379

2.将主机shutdown,哨兵自动选举6380为主机
在这里插入图片描述

3.此时6379的机器重新连接后,会变成slave,从属于6380机器

总结

优点:

1.哨兵集群,基于主从复制的模式,所有的主从配置的优点都具备。

2.主从可以快速自动切换,故障可以转移,实现系统高可用。

3.哨兵模式是主从模式的升级,手动到自动,更加健壮。

缺点:

1.Redis不好实现在线扩容,集群容量一旦达到上限,在线扩容十分麻烦。

2.哨兵模式的配置项实际有很多项。

# Example sentinel.conf  
  
# 哨兵sentinel实例运行的端口 默认26379  
port 26379  
  
# 哨兵sentinel的工作目录  
dir /tmp  
  
# 哨兵sentinel监控的redis主节点的 ip port   
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。  
# quorum 当这些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

缓存穿透、击穿、雪崩

缓存和数据库处理流程

img

缓存穿透

  • 缓存和数据库中都没有的数据,被恶意或者大量请求,会导致绕过缓存给数据库造成很大压力
  • 解决方法:布隆过滤器,设置key-null(缓存过期时间需要设置的稍微短一些)

缓存击穿

  • 缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

  • 解决方法:

    1.设置热点数据永不过期

    2.如果缓存中没有数据,设置独占锁,暂时阻塞其他线程,由一个线程去数据库中查询,并将数据更新到缓存中。

    img

缓存雪崩

  • 在某一个时间段,缓存集中过期失效或者Redis宕机!(比如6.18、双十一抢购,在12点准时上线一波抢购商品,设置过期时间一小时,等到1点整,仍在访问抢购商品的流量会瞬间访问数据库,出现缓存雪崩的现象)

  • 解决方法:

    1.设置key随机过期时间

    2.redis高可用:异地多活

    3.限流降级:停掉部分服务,限制一定流量

lua脚本

进阶还没看。。。

布隆过滤器

进阶还没看。。。

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值