0、本文包含内容
- Redis高级数据类型GEO&HyperLogLog详解
- Redis事务机制&乐观锁
- Redis内部事件订阅机制keyspace&keyevent
- Redis主从架构
1、Redis高级数据类型GEO&HyperLogLog详解
1.1. zset扩展类型geo
1.1.1. GEO功能介绍
GEO功能是在Redis 3.2版本后提供的,支持存储地理坐标的一个数据结构,可以用来做类似摇一摇,附近的人,周边搜索的功能
# 语法类型
geoadd key 经度 纬度 成员 [经度 纬度 成员...]
# geoadd命令必须以标准的x y member结构来接受参数,必须先输入经度后输入纬度
geoadd能够记录的坐标是有限:
- 非常接近两极的区域无法索引
- 精确的坐标限制是由 EPSG:900913等坐标系统定义
- 经度:-180到180度之间
- 纬度:-85.05112878到85.05112878度之间
- 如果超出这个范围则会报错
1.1.2. 新增和查询
# 添加一批城市
geoadd china:city 116.408 39.904 beijing 121.445 31.213 shanghai 113.265 23.108 guangzhou 114.109 22.544 shenzhen 108.969 34.285 xian 108.55 34.09 changan
# 查询
geopos china:city beijing shanghai
1.1.3.获得节点间距离
geodist china:city shanghai beijing km
unit:
- m : 米
- km :千米
- mi :英里
- ft : 英尺
- 默认是m
- 会有0.5%的误差
1.1.4. 周边搜索
georadius china:city 121.445 31.213 1300 km withdist withcoord
georadius china:city 121.445 31.213 1300 km withdist withcoord asc count 3
georadiusbymember china:city beijing 1300 km withdist withcoord asc count 3
- withdist : 返回节点距离
- withcoord : 带上坐标
#节点hash
geohash china:city xian changan
1) "wqj7p9ku9e0"
2) "wqj1yjgswk0"
1.1.5. 删除节点
zrem china:city changan
1.1.6. 业务实现
x y name1 x y nam2
x y canyin:123 x y canyin:456
1.2. hyperloglog
这个数据类型其实就做了一件事,统计不重复的数据量
比如要记录我们网站的UV量
sadd 20200208 1 2 3 4 5 6 5 4 3 2 1
scard 20200208
只用来存放基数
pfadd 20200207 1 2 3 4 5 6 5 4 3 2 1 #添加记录基数
pfadd 20200206 1 2 3 4 5 6 7 8 9 10
pfcount 20200206 #返回基数
pfmerge 202002 20200206 20200207 #pfmerge newkey sourcekey1 sourcekey2 合并生成了一个新key,原来的key不会消失
还有一个问题
- hyperloglog是一个基数估算算法,有一定误差的
- 误差值在0.81%
- hyperloglog的key占用空间很小只有12K,而你用set就需要把这些value都要保存,set存一年数据有多大
2. Redis事务&乐观锁
2.1. Redis事务
场景描述:
就是我们的数据操作是一个原子性的
# 看一下redis的场景
# client A client B
set name icoding(1) set name icodingedu(2)
get name(3) get name
# 这个时候数据就串了
事务需要边界
multi # 开启事务
set name icoding
get name
exec # 执行事务
discard # 回滚事务
事务执行过程中错误回退机制
- 如果命令错误则自动回滚
- 如果命令正确但语法错误,正确的语句都会被执行
2.2. Redis的乐观锁
业务场景距离
库存:store 1000-->100 / + 1000(三个业务人员incrby)
数据的状态:如果在你修改前已经被修改了,就不能再修改成功了?
# watch key1 key2 命令
# 只要在事务外watch了一个key或多个key,在事务还没有执行完毕的时候,watch其中的key被修改后,整个事务回滚
set store 10
watch store
multi
incrby store 100
get store
exec
- watch在事务执行完毕后就释放了
- watch的key如果在事务执行过程中失效了,事务也不受watch影响
- watch只能在事务外执行
- 客户端如果断开,watch也就失效了
事务执行过程中错误回退机制
- 如果命令错误则自动回退
- 如果命令正确但语法错误,正确的语句会被执行
3. Redis内部事件订阅机制keyspace&keyevent
# 进入redis的客户端
subscribe java php #订阅了两个通道
pubstribe java springboot #发布通道
psubscribe j* p* #通配频道
3.1. 应用的业务场景
订单超时2小时未支付,需要关闭,如何实现?
- Quartz来做任务调度,定时执行巡检任务,5分钟巡检一次,会有4分59秒的巡检超时
- Timer,java的定时器,秒为单位,数据量大的时候性能就成瓶颈了
- Quartz+Timer ,Quartz拿出5分钟内将要充实的订单,然后在启用多线程以Timer每秒的方式去检查,但业务功能就比较复杂了
- 有没有一种功能,有个hook能通知我们我失效了?通知我执行业务
- 假设:expire 7200 秒过期后通知我,这就是实现了第4种方案了?
Redis在2.8版本后,推出keyspace notifcations特性,类似数据库的trigger触发器
keyspace notifications是基于sub/pub发布订阅机制的,可以接收对数据库中影响key操作的所有事件:比如del、set、expire(过期时间)
3.2. 接收的事件类型
有两种:keyspace、keyevent
keyspace : 是key触发的事件的具体操作
keyevent : 是事件影响的键名
# pub这个动作是系统自动发布的
127.0.0.1:6379> del mykey
# 数据库0会发布以下两个信息
publish __keyspace@0__:mykey del
publish __keyevent@0__:del mykey
3.3. 开启系统通知
redis.conf配置文件里
# 这都是配置项
# K Keyspace events, published with __keyspace@<db>__ prefix.
# E Keyevent events, published with __keyevent@<db>__ prefix.
# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
# $ String commands
# l List commands
# s Set commands
# h Hash commands
# z Sorted set commands
# x Expired events (events generated every time a key expires)
# e Evicted events (events generated when a key is evicted for maxmemory)
# A Alias for g$lshzxe, so that the "AKE" string means all the events.
# 开关 在redis.conf配置里
notify-keyspace-events "" #默认空字符串,是关闭状态
notify-keyspace-events "KEx" #配置文件里只配置了space和event的expried事件,就只自动发布这个事件
统配订阅
notify-keyspace-events "KEA"
set username gavin ex 10 #set事件,expire事件
psubscribe __key*@*__:*
#返回事件通知
4) "set"
1) "pmessage"
2) "__key*@*__:*"
3) "__keyevent@0__:set"
4) "username"
1) "pmessage"
2) "__key*@*__:*"
3) "__keyspace@0__:username"
4) "expire"
1) "pmessage"
2) "__key*@*__:*"
3) "__keyevent@0__:expire"
4) "username"
1) "pmessage"
2) "__key*@*__:*"
3) "__keyspace@0__:username"
4) "expired"
1) "pmessage"
2) "__key*@*__:*"
3) "__keyevent@0__:expired"
4) "username"
subscribe __keyevent@0__:expired
3.4. Springboot订阅通知
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
4. Redis主从架构
4.1. 主从原理分析
主从出现的原因
- 高并发
- 官方数据表示Redis的读数据11w/s左右,写速度是8w/s左右
- Redis尽量少写多读,符合缓存的适用要求
- 可以通过主从架构来进行读写分离
- HA
- 如果有一个以上的从库就会对节点进行备份
同步的过程中一定要开启持久化
- 如果master出现宕机内存丢失,从库也会删除
- master进行功能性重启,内存数据丢失,也会同步给slave
主从复制原理
- 当slave第一次连接时,会触发全量同步,如果已经连接过了,只会同步新增数据
- 全量备份分为落盘和不落盘两种形式,默认是落盘
# 不落盘复制配置 yes不落盘,no落盘
repl-diskless-sync no
# 等待其他slave连接的一个时间周期,单位是秒
repl-diskless-sync-delay 5
- 支持断点续传
- 如果从库过多会导致占用带宽较大,所以从不易过多
主从的结构除了一对多,还可以是树形的,树形结构用的比较少
4.2. 主从设置
#可以通过命令看一下主从信息,v5.x版本前从是用slave表示,之后换成replication
info replication
#修改slave的redis.conf
replicaof 192.168.1.100 6379 #master的ip,master的端口
masterauth icoding #主机的访问密码
# yes 主从复制中,从服务器可以响应客户端请求
# no 主从复制中,从服务器将阻塞所有请求,有客户端请求时返回“SYNC with master in progress”;
replica-serve-stale-data yes
# slave节点只允许read 默认就是 yes
# 这个配置只对slave节点才生效,对master节点没作用
replica-read-only yes
# slave根据指定的时间间隔向master发送ping请求
# 时间间隔可以通过 repl-ping-replica-period 来设置,默认10秒
repl-ping-replica-period 10
# 复制连接超时时间。
# master和slave都有超时时间的设置。
# master检测到slave上次发送的时间超过repl-timeout,即认为slave离线,清除该slave信息。
# slave检测到上次和master交互的时间超过repl-timeout,则认为master离线。
# 需要注意的是repl-timeout需要设置一个比repl-ping-slave-period更大的值,不然会经常检测到超时。
repl-timeout 60
# 是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。
# 默认是no,即使用tcp nodelay,允许小包的发送。对于延时敏感型,同时数据传输量比较小的应用,开启TCP_NODELAY选项无疑是一个正确的选择
# 如果master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络带宽。
# 但是这也可能带来数据的延迟。
# 默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择yes。
repl-disable-tcp-nodelay no
# 复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。
# 这样在slave离线的时候,不需要完全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状态。
# 缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内存。
# 没有slave的一段时间,内存会被释放出来,默认1m。
repl-backlog-size 5mb
# master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。
# 单位为秒。
repl-backlog-ttl 3600
# 当master不可用,Sentinel会根据slave的优先级选举一个master。
# 最低的优先级的slave,当选master。
# 而配置成0,永远不会被选举。
# 注意:要实现Sentinel自动选举,至少需要2台slave。
replica-priority 100
# redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数小于N,mater就禁止写入。
# master最少得有多少个健康的slave存活才能执行写命令。
# 这个配置虽然不能保证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入来避免数据丢失。
# 设置为0是关闭该功能,默认也是0。
min-replicas-to-write 2
# 延迟小于min-replicas-max-lag秒的slave才认为是健康的slave。
min-replicas-max-lag 10