Redis-2(三种特殊的数据类型及事务以及锁)

三种特殊的数据类型

Geospatial(地理位置)

使用经纬度定位地理坐标,并使用一个zset(有序集合)保存,所以Zset也可以使用
可以使用在查看直线距离,以及附近的人和微信的其他地图功能

  • geoadd key 经度(-180~+180) 纬度(-85~+85)将具体的经纬度存在一个Zset中
  • geopos key member [member...]获取集合中的一个/多个成员坐标
  • geodist key member1 member2 km返回两点之间的距离,默认为m作为单位
  • georadius key 经度 纬度 radius m/km/mi/ft [withcoord][withdist][withhash][count count]以给定的经纬为中心,返回集合包含的位置元素当中,与中心距离不超过给定的最大距离的所有位置元素
  • geohash key member1[member2]返回一个或者多个位置元素,使用geohash表示
    GEORADIUS的参数
    通过georadius就可以完成 附近的人功能
    withcoord:带上坐标
    withdist:带上距离,单位与半径单位相同
    COUNT n : 只显示前n个(按距离递增排序)
----------------georadius---------------------
127.0.0.1:6379> GEORADIUS china:city 120 30 500 km withcoord withdist # 查询经纬度(120,30)坐标500km半径内的成员
1) 1) "hangzhou"
   2) "29.4151"
   3) 1) "120.20000249147415"
      2) "30.199999888333501"
2) 1) "shanghai"
   2) "205.3611"
   3) 1) "121.40000134706497"
      2) "31.400000253193539"
------------geohash---------------------------

127.0.0.1:6379> geohash china:city yichang shanghai # 获取成员经纬坐标的geohash表示
"wmrjwbr5250"
"wtw6ds0y300"

Hyperloglog(基数统计)

Redis中的Hyperloglog是来做基数统计的算法,其他统计一般是用set保存用户的登陆信息,而Hyperloglog是花费了12kb来实现的
什么是基数?
类似于网站的访问量:一个用户多次访问,但只能算作一个人访问
传统实现:存储用户的id,然后进行比较,当用户增多时这种方法就会浪费空间,而我们的目地只有计数,所以Hyperloglog能帮我们以最小的空间来完成实现

  • pfadd key element1 [element2]添加指定的元素到hyperloglog中
  • pfcount key[key]返回给定的hyperloglog的基数估算值(不是很准确,24个字母给我返回了25艹)
  • pfmerge destkey sourcekey [sorcekey...]将多个hyperloglog合并为一个
----------PFADD--PFCOUNT---------------------
127.0.0.1:6379> PFADD myelemx a b c d e f g h i j k # 添加元素
(integer) 1
127.0.0.1:6379> type myelemx               # hyperloglog底层使用String
string
127.0.0.1:6379> PFCOUNT myelemx            # 估算myelemx的基数
(integer) 11
127.0.0.1:6379> PFADD myelemy i j k z m c b v p q s
(integer) 1
127.0.0.1:6379> PFCOUNT myelemy
(integer) 11
----------------PFMERGE-----------------------

127.0.0.1:6379> PFMERGE myelemz myelemx myelemy # 合并myelemx和myelemy 成为myelemz
OK

127.0.0.1:6379> PFCOUNT myelemz              # 估算基数
(integer) 17

BitMap(位图)

使用位进行存储,信息状态只有0,1
Bitmap是一个连串的二进制数字,每一位所在的位置为offset.
应用场景为签到统计,状态统计...类似于boolean

  • setbit key offset value 为指定key的offset位设置值
  • getbit key offset 获取offset位的值
  • bitcount key [start end] 统计字符串被设置为1的bit数,也可以指定统计范围按字节
  • bitop operration destkey key[key..] 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
  • BITPOS key bit [start] [end] 返回字符串里面第一个被设置为1或者0的bit位。start和end只能按字节,不能按位
------------setbit--getbit--------------
127.0.0.1:6379> setbit sign 0 1           # 设置sign的第0位为 1 
(integer) 0
127.0.0.1:6379> setbit sign 2 1           # 设置sign的第2位为 1  不设置默认 是0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> type sign
string
127.0.0.1:6379> getbit sign 2              # 获取第2位的数值
(integer) 1

127.0.0.1:6379> getbit sign 3
(integer) 1

127.0.0.1:6379> getbit sign 4              # 未设置默认是0
(integer) 0

-----------bitcount----------------------------

127.0.0.1:6379> BITCOUNT sign             # 统计sign中为1的位数
(integer) 4

事务

Redis的单条命令是保证原子性的,但是事务不能保证原子性
Redis事务本质:一组命令的集合。
----------------- 队列 set set set 执行 -------------------
事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰。

  • 一次性
  • 顺序性
  • 排他性

Redis事务没有隔离级别的概念
Redis单条命令是保证原子性的,但是事务不保证原子性!

Redis中事务操作过程

  • 开启事务multi
  • 命令入队
  • 执行事务exec
    所有事务在命令加入时都没有被执行,直到提交时才会开始执行,一次性完成

正常执行

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> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> exec               # 事务执行
1) OK
2) OK
3) "v1"
4) OK
5) 1) "k3"
   2) "k2"
   3) "k1"

取消事务

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> DISCARD                # 放弃事务
OK
127.0.0.1:6379> EXEC 
(error) ERR EXEC without MULTI         # 当前未开启事务
127.0.0.1:6379> get k1                 # 被放弃事务中命令并未执行
(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> error k1        # 这是一条语法错误命令
(error) ERR unknown command `error`, with args beginning with: `k1`,                           # 会报错但是不影响后续命令入队 
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors. # 执行报错
127.0.0.1:6379> get k1
(nil)                           # 其他命令并没有被执行

代码逻辑错误(RuntimeException)其他的命令还能正常执行,所以不保证原子性

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> INCR k1                                     # 这条命令逻辑错误(对字符串进行增量)
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range        # 运行时报错
4) "v2"                                                       # 其他命令正常执行

虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了。所以说Redis单条指令保证原子性,但是Redis事务不能保证原子性。

悲观锁:

  • 字面意思,大部分情况不使用,无论做什么都会加锁.
    乐观锁:
  • 很乐观,认为什么时候多不会出现问题,不会上锁,更行数据的时候通过version来判断版本号是否和开始的版本号一致
  • 获取version
  • 更行的时候比较version
    使用watch key监控指定的数据,相当于乐观锁加锁
    正常执行
127.0.0.1:6379> set money 100       # 设置余额:100
OK
127.0.0.1:6379> set use 0           # 支出使用: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 use 20
QUEUED
127.0.0.1:6379> exec               # 监视值没有被中途修改,事务正常执行
1) (integer) 80
2) (integer) 20

测试多线程修改值,使用watch可以当做redis的乐观锁操作,相当于mp中的getversion
启动另一台客户端来模拟线程插队

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 use 20
QUEUED
127.0.0.1:6379>                               # 此时事务并没有执行

模拟线程插队 线程2

127.0.0.1:6379> INCRBY money 500          # 修改了线程一中监视的money
(integer) 600

回到线程1中执行事务

127.0.0.1:6379> EXEC                      # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败
(nil)                                     # 没有结果,说明事务执行失败
127.0.0.1:6379> get money # 线程2 修改生效
"600"

127.0.0.1:6379> get use                   # 线程1事务执行失败,数值没有被修改
"0"

获取最新的值,然后进行加锁事务
unwatch进行解锁
注意:每次提交执行exec之后都会自动释放锁,不管是否成功

posted @ 2021-10-11 11:51  1_f  阅读(14)  评论(0)    收藏  举报