文章目录
前面我们已经认识了redis是什么与redis的五大数据类型以及相对应的指令,后面我们将继续学习redis.
三种特殊数据类型
redis中除了五大数据类型之外,还有三种特殊数据类型。
Geospatial 地理位置
朋友的定位,附近的人,打车距离计算?
Redis GEO
主要用于存储地理位置信息,并对存储的信息进行操作。
Redis 的 Geo
在Redis3.2 版本就推出了! 这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
可以查询一些测试数据:只有6个命令
Redis GEO 操作方法有:
geoadd
:添加地理位置的坐标。可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。
语法格式如下:
GEOADD key longitude latitude member [longitude latitude member ...]
geopos
:获取地理位置的坐标。从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
geopos 语法格式如下:
GEOPOS key member [member ...]
geodist
:计算两个位置之间的距离。返回两个给定位置之间的距离。
geodist 语法格式如下:
GEODIST key member1 member2 [m|km|ft|mi]
其中member1
member2
为两个地理位置。
最后一个距离单位参数说明:m
:米,默认单位。km
:千米。mi
:英里。ft
:英尺。
georadius
:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。georadiusbymember
:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
语法格式:
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
geohash
:返回一个或多个位置对象的 geohash 值。保存地理位置的坐标
geohash 语法格式如下:
GEOHASH key member [member ...]
详细分析GEO指令
getadd
# getadd 添加地理位置
# 规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
# 有效的经度从-180度到180度。
# 有效的纬度从-85.05112878度到85.05112878度。
# 当坐标位置超出上述指定范围时,该命令将会返回一个错误。
# 127.0.0.1:6379> geoadd china:city 39.90 116.40 beijin
(error) ERR invalid longitude,latitude pair 39.900000,116.400000
# 参数 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 chongqi 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
getpos
获得当前定位:一定是一个坐标值!
127.0.0.1:6379> GEOPOS china:city beijing # 获取指定的城市的经度和纬度!
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> GEOPOS china:city beijing chongqi
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
GEODIST
127.0.0.1:6379> GEODIST china:city beijing shanghai km # 查看上海到北京的直线距离
"1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqi km # 查看重庆到北京的直线距离
"1464.0708"
georadius 以给定的经纬度为中心, 找出某一半径内的元素
我附近的人? (获得所有附近的人的地址,定位!)通过半径来查询!
获得指定数量的人,200
所有数据应该都录入:china:city ,才会让结果更加请求
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 以110,30 这个经纬度为中心,寻
找方圆1000km内的城市
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
1) "chongqi"
2) "xian"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist # 显示到中间距离的位置
1) 1) "chongqi"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord # 显示他人的定位信息
1) 1) "chongqi"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 1 #
筛选出指定的结果!
1) 1) "chongqi"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 2
1) 1) "chongqi"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) "483.8340"
3) 1) "108.96000176668167114"
2) "34.25999964418929977"
GEORADIUSBYMEMBER
# 找出位于指定元素周围的其他元素!
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 400 km
1) "hangzhou"
2) "shanghai"
GEOHASH 命令 - 返回一个或多个位置元素的 Geohash 表示
该命令将返回11个字符的Geohash字符串!
# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近!
127.0.0.1:6379> geohash china:city beijing chongqi
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo!
127.0.0.1:6379> ZRANGE china:city 0 -1 # 查看地图中全部的元素
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> zrem china:city beijing # 移除指定元素!
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
Hyperloglog
简介
- Redis 2.8.9 版本就更新了 Hyperloglog 数据结构!
- Redis Hyperloglog 基数统计的算法!
- 优点:占用的内存是固定,2^64 不同的元素的技术,只需要废 12KB内存!如果要从内存角度来比较的话 Hyperloglog 首选!
网页的 UV (一个人访问一个网站多次,但是还是算作一个人!)
Redis HyperLogLog 命令
指令 | 描述 |
---|---|
PFADD key element [element …] | 添加指定元素到 HyperLogLog 中。 |
PFCOUNT key [key …] | 返回给定 HyperLogLog 的基数估算值。 |
PFMERGE destkey sourcekey [sourcekey …] | 将多个 HyperLogLog 合并为一个 HyperLogLog |
测试使用
127.0.0.1:6379> PFadd mykey a b c d e f g h i j # 创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> PFCOUNT mykey # 统计 mykey 元素的基数数量
(integer) 10
127.0.0.1:6379> PFadd mykey2 i j z x c v b n m # 创建第二组元素 mykey2
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并两组 mykey mykey2 => mykey3 并集
OK
127.0.0.1:6379> PFCOUNT mykey3 # 看并集的数量!
(integer) 15
- 如果允许容错,那么一定可以使用 Hyperloglog !
- 如果不允许容错,就使用 set 或者自己的数据类型即可!
Bitmap
位存储
常用在统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,365打卡! 两个状态的,都可以使用Bitmaps
!
Bitmap
位图,数据结构! 都是操作二进制位来进行记录,就只有0 和 1 两个状态!
案例
使用bitmap 来记录 周一到周日的打卡!
周一:1 周二:0 周三:0 周四:1 …
查看某一天是否有打卡!
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
统计操作,统计 打卡的天数!
127.0.0.1:6379> bitcount sign # 统计这周的打卡记录,就可以看到是否有全勤!
(integer) 3
全局变量保护
为了防止不必要的数据泄漏进 Lua
环境, Redis
脚本不允许创建全局变量。如果一个脚本需要在多次执行之间维持某种状态,它应该使用 Redis key 来进行状态保存。
避免引入全局变量的一个诀窍是:将脚本中用到的所有变量都使用 local 关键字定义为局部变量。
事务
- Redis 事务本质:一组命令的集合! 一个事务中的所有命令都会被序列化,在事务执行过程的中,会按
照顺序执行! - 一次性、顺序性、排他性!执行一些列的命令!
------ 队列 set set set 执行------
- Redis事务没有没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec
Redis单条命令式保存原子性的,但是事务不保证原子性! - redis的事务:
开启事务(multi
)
命令入队(…)
执行事务(exec
)
正常执行事务!
127.0.0.1:6379> multi # 开启事务
OK
# 命令入队
127.0.0.1:6379> set k1 v1
bilibili:狂神说Java
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) OK
3) "v2"
4) 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> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD # 取消事务
OK
127.0.0.1:6379> get k4 # 事务队列中命令都不会被执行!
(nil)
编译型异常(代码有问题! 命令有错!) ,事务中所有的命令都不会被执行!
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> set k3 v3
QUEUED
127.0.0.1:6379> getset k3 # 错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec # 执行事务报错!
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 # 所有的命令都不会被执行!
(nil)
运行时异常(1/0), 如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行
的,错误命令抛出异常!
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 # 会执行的时候失败!
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> 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"
127.0.0.1:6379> get k3
"v3"
监控(Watch)
悲观锁:
- 很悲观,认为什么时候都会出问题,无论做什么都会加锁!
乐观锁:
- 很乐观,认为什么时候都不会出问题,所以不会上锁! 更新数据的时候去判断一下,在此期间是否有人修改过这个数据
- 获取version
- 更新的时候比较 version
Redis测监视测试
正常执行成功!
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 监视 money 对象
OK
127.0.0.1:6379> multi # 事务正常结束,数据期间没有发生变动,这个时候就正常执行成功!
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
测试多线程修改值 , 使用watch 可以当做redis的乐观锁操作!
127.0.0.1:6379> watch money # 监视 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec # 执行之前,另外一个线程,修改了我们的值,这个时候,就会导致事务执行失
败!
(nil)
如果修改失败,获取最新的值就好
Jedis
Jedis,是 Redis 官方推荐的 java连接开发工具! 使用Java 操作Redis 中间件!如果你要使用java操作redis,那么一定要对Jedis 十分的熟悉!
测试使用
1、导入对应的依赖
<!--导入jedis的包-->
<dependencies>
<!-- https://mvnrepository.com/artifact/redis.clients/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.62</version>
</dependency>
</dependencies>
2、编码测试:
- 连接数据库
- 操作命令
- 断开连接!
public class TestPing {
public static void main(String[] args) {
// 1、 new Jedis 对象即可
Jedis jedis = new Jedis("127.0.0.1",6379);
// jedis 所有的命令就是我们之前学习的所有指令!所以之前的指令学习很重要!
System.out.println(jedis.ping());
}
}
事务
public class TestTX {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","kuangshen");
// 开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
//jedis.watch(result)
try {
multi.set("user1",result);
multi.set("user2",result);
int i = 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整合Redis
Redis stream
-
Redis Stream 是 Redis 5.0 版本新增加的数据结构。
-
Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
-
简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。
-
Redis Stream
提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。 -
结构是一个消息链表,将所有加入的消息都串起来,每个消息都有一个唯一的 ID 和对应的内容。
Consumer Group
:消费组,使用 XGROUP CREATE 命令创建,一个消费组有多个消费者(Consumer)。
last_delivered_id
:游标,每个消费组会有个游标 last_delivered_id,任意一个消费者读取了消息都会使游标 last_delivered_id 往前移动。
pending_ids
:消费者(Consumer)的状态变量,作用是维护消费者的未确认的 id。 pending_ids 记录了当前已经被客户端读取的消息,但是还没有 ack (Acknowledge character:确认字符)。 -
每个
Stream
都有唯一的名称,它就是 Redis 的 key,在我们首次使用xadd
指令追加消息时自动创建。
消息队列相关命令:
XADD
- 添加消息到末尾,如果指定的队列不存在,则创建一个队列,XADD 语法格式:
XADD key ID field value [field value ...]
# key :队列名称,如果不存在就创建
# ID :消息 id,我们使用 * 表示由 redis 生成,可以自定义,但是要自己保证递增性。
# field value : 记录。
实例:
redis> XADD mystream * name Sara surname OConnor
"1601372323627-0"
redis> XADD mystream * field1 value1 field2 value2 field3 value3
"1601372323627-1"
redis> XLEN mystream
(integer) 2
redis> XRANGE mystream - +
1) 1) "1601372323627-0"
2) 1) "name"
2) "Sara"
3) "surname"
4) "OConnor"
2) 1) "1601372323627-1"
2) 1) "field1"
2) "value1"
3) "field2"
4) "value2"
5) "field3"
6) "value3"
redis>
XTRIM
- 对流进行修剪,限制长度
XTRIM key MAXLEN [~] count
# key :队列名称
# MAXLEN :长度
# count :数量
实例:
127.0.0.1:6379> XADD mystream * field1 A field2 B field3 C field4 D
"1601372434568-0"
127.0.0.1:6379> XTRIM mystream MAXLEN 2
(integer) 0
127.0.0.1:6379> XRANGE mystream - +
1) 1) "1601372434568-0"
2) 1) "field1"
2) "A"
3) "field2"
4) "B"
5) "field3"
6) "C"
7) "field4"
8) "D"
127.0.0.1:6379>
redis>
XDEL
- 删除消息
XDEL key ID [ID ...]
# key:队列名称
# ID :消息 ID
实例:
> XADD mystream * a 1
1538561698944-0
> XADD mystream * b 2
1538561700640-0
> XADD mystream * c 3
1538561701744-0
> XDEL mystream 1538561700640-0
(integer) 1
127.0.0.1:6379> XRANGE mystream - +
1) 1) 1538561698944-0
2) 1) "a"
2) "1"
2) 1) 1538561701744-0
2) 1) "c"
2) "3"
XLEN
- 获取流包含的元素数量,即消息长度
XLEN key
# key:队列名称
实例:
redis> XADD mystream * item 1
"1601372563177-0"
redis> XADD mystream * item 2
"1601372563178-0"
redis> XADD mystream * item 3
"1601372563178-1"
redis> XLEN mystream
(integer) 3
redis>
XRANGE
- 获取消息列表,会自动过滤已经删除的消息
XRANGE key start end [COUNT count]
# key :队列名
# start :开始值, - 表示最小值
# end :结束值, + 表示最大值
# count :数量
实例:
redis> XADD writers * name Virginia surname Woolf
"1601372577811-0"
redis> XADD writers * name Jane surname Austen
"1601372577811-1"
redis> XADD writers * name Toni surname Morrison
"1601372577811-2"
redis> XADD writers * name Agatha surname Christie
"1601372577812-0"
redis> XADD writers * name Ngozi surname Adichie
"1601372577812-1"
redis> XLEN writers
(integer) 5
redis> XRANGE writers - + COUNT 2
1) 1) "1601372577811-0"
2) 1) "name"
2) "Virginia"
3) "surname"
4) "Woolf"
2) 1) "1601372577811-1"
2) 1) "name"
2) "Jane"
3) "surname"
4) "Austen"
redis>
XREVRANGE
- 反向获取消息列表,ID 从大到小
XREVRANGE key end start [COUNT count]
# key :队列名
# end :结束值, + 表示最大值
# start :开始值, - 表示最小值
# count :数量
实例:
redis> XADD writers * name Virginia surname Woolf
"1601372731458-0"
redis> XADD writers * name Jane surname Austen
"1601372731459-0"
redis> XADD writers * name Toni surname Morrison
"1601372731459-1"
redis> XADD writers * name Agatha surname Christie
"1601372731459-2"
redis> XADD writers * name Ngozi surname Adichie
"1601372731459-3"
redis> XLEN writers
(integer) 5
redis> XREVRANGE writers + - COUNT 1
1) 1) "1601372731459-3"
2) 1) "name"
2) "Ngozi"
3) "surname"
4) "Adichie"
redis>
XREAD
- 以阻塞或非阻塞方式获取消息列表
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
# count :数量
# milliseconds :可选,阻塞毫秒数,没有设置就是非阻塞模式
# key :队列名
#id :消息 ID
实例:
# 从 Stream 头部读取两条消息
> XREAD COUNT 2 STREAMS mystream writers 0-0 0-0
1) 1) "mystream"
2) 1) 1) 1526984818136-0
2) 1) "duration"
2) "1532"
3) "event-id"
4) "5"
5) "user-id"
6) "7782813"
2) 1) 1526999352406-0
2) 1) "duration"
2) "812"
3) "event-id"
4) "9"
5) "user-id"
6) "388234"
2) 1) "writers"
2) 1) 1) 1526985676425-0
2) 1) "name"
2) "Virginia"
3) "surname"
4) "Woolf"
2) 1) 1526985685298-0
2) 1) "name"
2) "Jane"
3) "surname"
4) "Austen"
XGROUP CREATE
- 使用 XGROUP CREATE 创建消费者组,语法格式:
XGROUP [CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]
# key :队列名称,如果不存在就创建
# groupname :组名。
# $ : 表示从尾部开始消费,只接受新消息,当前 Stream 消息会全部忽略。
从头开始消费:
XGROUP CREATE mystream consumer-group-name 0-0
从尾部开始消费:
XGROUP CREATE mystream consumer-group-name $
XREADGROUP GROUP
- 使用 XREADGROUP GROUP 读取消费组中的消息
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] ID [ID ...]
# group :消费组名
# consumer :消费者名。
# count : 读取数量。
# milliseconds : 阻塞毫秒数。
# key : 队列名。
# ID : 消息 ID。
XREADGROUP GROUP consumer-group-name consumer-name COUNT 1 STREAMS mystream >
消费者组相关命令:
XGROUP CREATE
- 创建消费者组XREADGROUP GROUP
- 读取消费者组中的消息XACK
- 将消息标记为"已处理"XGROUP SETID
- 为消费者组设置新的最后递送消息IDXGROUP DELCONSUMER
- 删除消费者XGROUP DESTROY
- 删除消费者组XPENDING
- 显示待处理消息的相关信息XCLAIM
- 转移消息的归属权XINFO
- 查看流和消费者组的相关信息;XINFO GROUPS
- 打印消费者组的信息;XINFO STREAM
- 打印流信息
数据备份与恢复
Redis SAVE
Redis SAVE
命令用于创建当前数据库的备份。运行该命令将在 redis 安装目录中创建dump.rdb文件。
redis 127.0.0.1:6379> SAVE
恢复数据
如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可。
CONFIG GET dir
输出的 redis 安装目录
创建 redis 备份文件也可以使用命令 BGSAVE
,该命令在后台执行。
requirepass
- 获取Redis当前设置的密码:
CONFIG get requirepass
,如果requirepass 参数是空的,则表示没有设置密码。
可以通过
CONFIG set requirepass "runoob”
设置密码。 - 设置密码后,客户端连接 redis 服务就需要密码验证,否则无法执行命令。
AUTH password
。
Redis持久化
- Redis提供的持久化是
RDB
与AOF
RDB
持久化方式能够在指定的时间间隔能对你的数据进行快照存储.AOF
持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF
命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
RDB
RDB
是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份RDB
是一个紧凑的单一文件,非常适用于灾难恢复.RDB
在保存RDB文件时父进程唯一需要做的就是fork
出一个子进程,后续的操作均有子进程执行。所以RDB持久化方式可以最大化redis的性能.- 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。
- RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的。
AOF
- 使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略
- AOF文件是一个只进行追加的日志文件,追加Redis的写操作(不追加读操作),所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写:AOF的重写机制。
- AOF 文件有序地保存了对数据库执行的所有写入操作。
- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。
快照(snapshotting)
在默认情况下, Redis 将数据库快照保存在名字为 dump.rdb
的二进制文件中。你可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。你也可以通过调用 SAVE或者 BGSAVE , 手动让 Redis 进行数据集保存操作。
日志重写
在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。
Redis缓存穿透和雪崩
服务的高可用问题!
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也都有比较流行的解决方案。
缓存穿透
简单理解就是,redis查询数据时,在缓存里查,查不到再到数据库里查。当访问量较少,可以直接返回不存在,因为这种少量的缓存穿透实际上是不可避免的;但是,当进行巨大量的并发访问时候,过多的访问会直接导致数据库挂掉。对此,有三种解决方案
-
在API接口层,对传递的参数进行检验,在接口层中定义自己的规则。
-
在缓存中设置一个空对象,使用空对象完成后续请求
-
布隆过滤器(Bloom Filter),布隆过滤器使用的是一个bit数组和一个hash算法实现的数据结构,并且内存的使用率极低。使用布隆过滤器可以快速判断当前key是否存在,和Java的Optional类有点相似,布隆过滤器告诉你这个key不存在,那么它就一定不存在。【简单的理解就是,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力】
缓存击穿
-
缓存击穿,它是缓存穿透的一种特殊情况,是指在非常高频的热点key某一个时刻过期,与此同时又有非常非常多的请求并发访问这个key,因为缓存时间已过,现在全部的请求又开始全部压在数据库上面了,很容易导致服务器挂掉
-
解决方案
-
可以设置为当前key永不过期,但是不推荐这种做法,因为这样会长期占用Redis的内存空间
-
还有一种就是Redis的分布式锁,如果说当前有非常非常多的请求传进来,这个时候有分布式锁的保护,可以允许这些请求中的其中一个放进来,缓存找不到就直接查数据库嘛,查完了再把数据放到缓存中让其他的请求直接查缓存即可
-
缓存雪崩
-
缓存雪崩指的是,在同一个时间内,一大批的缓存集体失效,就好像没有被缓存过一样,这个时候大量的请求直接压上数据库。如果服务器在没有加锁的情况下,这种流量几乎是瞬间达到一个峰值的,对于一个服务器来说,也是会出现宕机的情况
一句话总结就是:某一个时间段,缓存集中过期失效。Redis 宕机
其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然
形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就
是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知
的,很有可能瞬间就把数据库压垮。 -
解决方案
- redis高可用:搭建Redis集群,在高可用的情况下,配置多台服务器,在服务器中保存同样的数据,这样即使是集群中的一台甚至是多台挂掉的情况下,也依旧能够正常工作
- 数据预热:如果不搭建集群,也可以这么做:项目启动时,将数据库和Redis进行数据同步,将数据库中部分的数据信息首先加载进入Redis缓存,并且在加载进入缓存时,可以将这些缓存数据的存活时间设置为随机,并且在数据库和Redis缓存需要在一定的时间之内进行同步更新
- 限流降级
lettuce和jedis区别
参考文章:https://www.cnblogs.com/taiyonghai/p/9454764.html
Jedis和Lettuce都是Redis Client,所以都可以连接Redis service。
- Jedis 是直连模式,在多个线程间共享一个 Jedis 实例时是线程不安全的, 如果想要在多线程环境下使用 Jedis,需要使用连接池, 每个线程都去拿自己的 Jedis 实例,当连接数量增多时,物理连接成本就较高了。
- Lettuce的连接是基于Netty的,连接实例可以在多个线程间共享,所以,一个多线程的应用可以使用同一个连接实例,而不用担心并发线程的数量。当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
- 通过异步的方式可以让我们更好的利用系统资源,而不用浪费线程等待网络或磁盘I/O Lettuce 是基于 netty 的,netty 是一个多线程、事件驱动的 I/O 框架,所以 Lettuce 可以帮助我们充分利用异步的优势。