
三种特殊数据结构、事务
更新: 2025/2/24 字数: 0 字 时长: 0 分钟
三种特殊数据结构
Redis除了前面五种常用的数据结构,还有三种特殊的数据结构,它们同样可以应用在生活的方方面面。
Geospatial位置
**Redis在3.2版本就推出了用来存储和推算地理位置信息的,两地之间的距离geospatial数据类型,简称Geo。**应用场景:例如朋友定位、附近的人、计算打车距离等。
添加位置
Redis中添加地理位置的命令如下:
# geoadd 键:值 经度 纬度 定位名称
# 有效的经度从-180度到180度,有效的维度从-85.05度到85.05度
# 添加北京的经度纬度
geoadd china:city 116.40 39.90 beijing
# 添加上海、重庆、深圳、杭州、西安的经度纬度
geoadd china:city 121.47 31.23 shanghai 106.50 29.53 chongqing 114.05 22.52 shengzheng 120.16 30.24 hangzhou 108.96 34.26 xian
!> 注意:使用该命令添加经度纬度超出有效值范围会报错。
获取位置
Redis中获取地理位置的命令如下:
# geopos 键:值 定位名称
# 获取北京的地理位置信息
geopos china:city beijing
# 获取重庆、西安的地理位置信息
geopos china:city chongqing xian
相对距离
利用两个经度纬度计算出两地之间的直线距离可以使用如下命令:
# geodist 键:值 定位名称1 定位名称2 距离单位
# 距离单位 m:单位米;km单位千米;mi单位英里;ft单位英尺
# 计算北京到上海的直线距离,单位千米
geodist china:city beijing shanghai km
附近的人
以某一个经度纬度为中心,筛选出半径范围的地理位置信息:
# 以经度纬度为中心
georadius 键:值 经度 纬度 半径 距离单位
# 筛选以经度110纬度30为中心,半径1000千米内的城市
georadius china:city 110 30 1000 km
# 筛选以经度110纬度30为中心,半径500千米内的城市以及直线距离
georadius china:city 110 30 500 km withdist
# 筛选以经度110纬度30为中心,半径500千米内的城市以及城市的经度纬度
georadius china:city 110 30 500 km withcoord
# 筛选以经度110纬度30为中心,半径500千米内的城市以及城市的经度纬度,结果显示1个
georadius china:city 110 30 500 km withcoord count 1
以某一个定位名称为中心,筛选出半径范围的地理位置信息:
# 以经度纬度为中心
georadiusbymember 键:值 定位名称 半径 距离单位
# 以北京为中心,半径1000千米的城市
georadiusbymember china:city beijing 1000 km
删除位置
Redis并没提供官方的删除位置的命令,但是Geo的底层原理就是Zset数据类型,我们可以使用Zset命令来操作geo!
# 查询所有城市
zrange china:city 0 -1
# 删除北京的地理信息
zrem china:city beijing
Hyperloglog基数
在Reids中Hyperloglog数据结构主要用于基数统计的算法(去重统计),而且占内存是固定的,存储2^64不同元素的基数,只需要12KB内存。
# 添加元素
pfadd 键 元素1 元素2 元素3 ...
# 统计元素个数
pfcount 键
# 将键1、键2合并为键3
pfmerge 键3 键1 键2
Bitmaps位图
**在Redis中有一种Bitmaps位图数据结构,专门用于统计两个状态的信息,底层是操作二进制位来记录,只有0和1两个状态。**例如:感染、未感染;登录、未登录;打卡、未打卡...
设置状态
Bitmaps设置状态:
setbit 键 位置 状态
# 记录一周的打卡,0代表未打卡,1代表打卡
# 周一打卡
setbit sign 0 1
# 周二打卡
setbit sign 1 1
# 周三打卡
setbit sign 2 1
# 周四打卡
setbit sign 3 1
# 周五打卡
setbit sign 4 1
# 周六未打卡
setbit sign 5 0
# 周日未打卡
setbit sign 6 0
查看状态
Bitmaps查看状态:
# 查看周四是否打卡
getbit sign 3
# 查看周日是否打卡
getbit sign 6
统计次数
Bitmaps统计次数:
# 统计一周的打卡次数
bitcount sign
事务
基本操作
**简单地说,事务表示一组被序列化的命令,在事务执行过程中,会按照顺序执行。**因此具有三个特点:
- 一次性:整个事务只会执行一次;
- 顺序性:事务会按照顺序一条条的执行命令;
- 排他性:执行事务过程中,不会执行事务外的命令。
执行事务
Redis提供了简单的事务功能,它分为三个部分:
- 开始事务(
multi
) - 命令队列(需要执行的命令)
- 命令队列结束,开始执行事务(
exec
)
可以看到下面 sadd
命令此时的返回结果是QUEUED,代表命令并没有真正执行,而是暂时保存在队列中,只有当 exec
执行后,两条命令才开始执行:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd user:a:follow user:b
QUEUED
127.0.0.1:6379> sadd user:b:fans user:a
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1
127.0.0.1:6379> sismember user:a:follow user:b
(integer) 1
放弃事务
如果要停止事务的执行,可以使用 discard
命令代替 exec
命令即可。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd user:a:follow user:b
QUEUED
127.0.0.1:6379> sadd user:b:fans user:a
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> sismember user:a:follow user:b
(integer) 0
异常事务
在Redis中单条命令是保证原子性的,但是事务不保证原子性。
- 语法错误(例如执行不存在的命令),整个事务都会无法执行;
- 语法正确,命令错误(例如给字符串加1),则只有错误命令则会抛出异常,则其他命令正常执行。
!> 注意:Redis不支持回滚功能。
监控
两种锁
在Redis中有两种类型的锁:乐观锁、悲观锁。
悲观锁:很悲观,担心任何时候都会出现问题,无论做什么都会加锁,而且很影响程序的性能。(很少使用)
乐观锁:很乐观,认为任何时候都不会出现问题,所以不会上锁,只在更新数据的时候去判断一下在此期间是否修改过这个数据,判断的依据是基于数据版本(version)的记录机制实现的。(常用)
实现乐观锁
在Redis中实现乐观锁很简单,只需一个watch命令就能实现:
watch 键
单线程事务
多线程事务
第一个线程:设置money值,并进行监控,且设置好事务,但不执行。
第二个线程:重新设置money值。
返回第一个线程:现在再执行事务,发现返回一个空。
出现上述现象的原因就是:在第一个线程事务建立但还未执行的过程当中,处于监控的money值被第二个线程重新设置了,当第一个线程再来执行该事务的时候发现money值被改变了,就拒绝了执行该事务。如果还想再次执行该事务,就需要对money值重新监控,因为 exec
、discard
、unwatch
命令都会清除事务中的监控,然后再重新添加事务中的命令,再通过 exec
命令执行事务。