Redis常用的数据类型详解!

1.Redis五大基本数据类型

1、String(字符串)

常见操作命令

###############################################################
127.0.0.1:6379> set key1 v1   # 设置值
OK
127.0.0.1:6379> exists key1   # 判断key1是否存在
(integer) 1
127.0.0.1:6379> append key1 "hello"  # 向key1中增加值"hello"
(integer) 7
127.0.0.1:6379> get key1    # 获取值
"v1hello"
127.0.0.1:6379> exists key1  
(integer) 1
127.0.0.1:6379> strlen key1   # 获取key1的长度
(integer) 7
127.0.0.1:6379> strlen key2
(integer) 0
127.0.0.1:6379> strlen key1
(integer) 7

###############################################################
127.0.0.1:6379> set views 0  # 设置值
OK
127.0.0.1:6379> get views   # 获取值
"0" 
127.0.0.1:6379> type views  # 查看key的类型 
string
127.0.0.1:6379> incr views  # 自增 1
(integer) 1
127.0.0.1:6379> incr views   
(integer) 2
127.0.0.1:6379> incr views
(integer) 3
127.0.0.1:6379> decr views  # 自减1
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> incrby views 10  # 自增 步长为10
(integer) 12
127.0.0.1:6379> incrby views 10  
(integer) 22
127.0.0.1:6379> decrby views 8   # 自减 步长为8
(integer) 14
127.0.0.1:6379> get views  
"14"

###############################################################
127.0.0.1:6379> set key1 "hello,world"
OK
127.0.0.1:6379> get key1
"hello,world"
127.0.0.1:6379> getrange key1 0 3  # 截取key1 下标为[0,3]
"hell"
127.0.0.1:6379> getrange key1 0 -1  # 截取key1的全部 相当于get key1一般
"hello,world"
127.0.0.1:6379> set key2 abcdefg
OK
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> setrange key2 1 xx  # 将key2下标为1的位置(包括当前位置),开始替换为字符串xx
(integer) 7
127.0.0.1:6379> get key2
"axxdefg"

###############################################################
# setex(set with expire)  # 设置过期时间
# setnx(set if not exist)  # 不存在设置值(在分布式锁中经常会用到)
127.0.0.1:6379> setex key3 40 "hahahaha"   # 设置key3的值,在40秒后过期
OK
127.0.0.1:6379> ttl key3  # 查看key3的过期时间
(integer) 32
127.0.0.1:6379> setnx mykey "we are family"   # 如果mykey不存在,创建mykey并设置值
(integer) 1
127.0.0.1:6379> get mykey  
"we are family"
127.0.0.1:6379> ttl key3   
(integer) -2     # 当返回值为-2时,说明key3已经失效!
127.0.0.1:6379> keys *   # 查看本数据库(0号数据库)中所有的key
1) "mykey"
2) "key1"
3) "key2"
127.0.0.1:6379> setnx mykey "we are happy"   # mykey已经存在! 再次设置查看结果
(integer) 0
127.0.0.1:6379> get mykey   # 值未发生变化,所以存在时再设置起不了作用!
"we are family"
127.0.0.1:6379> flushdb   # 清空当前数据库
OK
127.0.0.1:6379> keys *  # 查看本数据库中所有的key
(empty list or set)


###############################################################
# mset  一次性设置多个值
# mget  一次性获得多个值
127.0.0.1:6379> mset key1 v1 key2 v2 key3 v3   # 设置多个值
OK
127.0.0.1:6379> keys *  # 查看所有的key
1) "key1"
2) "key3"
3) "key2"
127.0.0.1:6379> mget key2 key3  # 获取多个指定的key的值
1) "v2"
2) "v3"
127.0.0.1:6379> msetnx key1 vv1 key2 vv2 key4 v4   # 一次性如果可以不存在设置多个key和值
(integer) 0   # 注意: key1 和 key2 存在,key4不存在,按理说key4应该成功,结果失败!(因为此操作保持原子性)
127.0.0.1:6379> mget key1 key2  # 查看key1 key2的值,发现并未改变!
1) "v1"
2) "v2"

# 关于对象的操作
###############################################################
127.0.0.1:6379> set user:1 {
    
    name:zhangsan,age:3} # 设置一个user1 值为json字符串来保存一个对象!
OK 
127.0.0.1:6379> keys *
1) "key1"
2) "user:1"
3) "key3"
4) "key2"

# 这里的key是一个非常巧妙的设计! user:{id}:{filed}, 这种在reids中完全ok!
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 3  # 设置值 key为  user:id:属性   
OK
127.0.0.1:6379> mget user:1:name user:1:age  # 根据key获取值
1) "zhangsan"
2) "3"
127.0.0.1:6379> get user:1  # 查看存用户信息 json 字符串 的key 的值
"{name:zhangsan,age:3}" 

###############################################################
getset  # 先get 再 set
127.0.0.1:6379> getset key8 redis   # 如果不存在返回nil,并设置新的值!
(nil)
127.0.0.1:6379> get key8  
"redis"
127.0.0.1:6379> getset key8 linux  # 如果存在,先返回原来的值,再设置新的值!
"redis"
127.0.0.1:6379> get key8
"linux"
###############################################################

总结: String类似的使用场景: value除了是我们的字符串还可以是我们的数字!

  • 计数器
  • 统计多数量的单位
  • 粉丝数、关注量
  • 对象缓存存储!

2、List(列表)

基本的数据类型,列表

在redis中,我们可以把列表类型玩成 队列阻塞队列

所有的list命令都是用 L 开头的,在redis中,不区分大小写

常见操作命令

###############################################################
# lpush    将一个值插入到列表头部之中  最左边!
# rpush    将一个值插入到列表尾部之中  最右边!
# lrange   截取列表中的指定下标的值
127.0.0.1:6379> lpush list one   # 将一个值或者多个值插入到名为list的list的头部   最左边!  下表为0
(integer) 1
127.0.0.1:6379> lpush list two   
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1   # 查看列表list中所有的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1   # 查看列表指定的部分的值[0,1]
1) "three"
2) "two"
127.0.0.1:6379> rpush list four   #  将一个值或者多个值插入到名为list的list的尾部  最右边!
(integer) 4
127.0.0.1:6379> lrange list 0 -1   # 查看list中所有的值
1) "three"
2) "two"
3) "one"
4) "four"

###############################################################
lpop   # 左边弹出,头部弹出!
rpop   # 右边弹出,尾部弹出!
127.0.0.1:6379> lpop list     # 移除最左边的一个元素    头部!  下标为0
"three"
127.0.0.1:6379> rpop list     # 移除最右边的一个元素    尾部!  下边为最大的
"four"
127.0.0.1:6379> lrange list 0 -1    # 查看列表中所有的值!
1) "two"
2) "one"

###############################################################
lindex   # 获取  某个list 中的第n个值
127.0.0.1:6379> lrange list 0 -1    
1) "two"
2) "one"
127.0.0.1:6379> lindex list 0     #  获取列表 list 下标为0的值!
"two"
127.0.0.1:6379> lindex list 1     #  获取列表 list 下标为1的值!
"one"

###############################################################
llen   # 获取列表的长度!
127.0.0.1:6379> llen list   
(integer) 2

###############################################################
lrem   # 移除指定的值 !  # 取消关注  uid
127.0.0.1:6379> lrange list 0 -1  
1) "two"
2) "other"
3) "three"
4) "haha"
127.0.0.1:6379> lrem list 1 three   # 移除list 中 1个元素 值为three
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "other"
3) "haha"
127.0.0.1:6379> lpush list haha    
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "haha"
2) "two"
3) "other"
4) "haha"
127.0.0.1:6379> lrem list 2 haha   # 移除list 中 2个元素 值为haha
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "other"

###############################################################
ltrim  # 类似于修剪列表操作!
127.0.0.1:6379> lrange list 0 -1   
1) "three"
2) "one"
3) "two"
4) "other"
127.0.0.1:6379> ltrim list 1 2   # 修剪列表中下标为[1,2]的值!
OK
127.0.0.1:6379> lrange list 0 -1  # 修剪完成后查看
1) "one"
2) "two"

###############################################################
rpoplpush   # 移除列表中的最后一个元素、并且给另一个表
127.0.0.1:6379> rpush mylist "hello"     # 像列表尾部插入值
(integer) 1
127.0.0.1:6379> rpush mylist "hello1"
(integer) 2
127.0.0.1:6379> rpush mylist "hello2"
(integer) 3
127.0.0.1:6379> rpoplpush mylist otherlist   # 移除列表中的最后一个元素,并且赋给另一个表的表头!
"hello2"
127.0.0.1:6379> lrange mylist 0 -1  
1) "hello"
2) "hello1"
127.0.0.1:6379> lrange otherlist 0 -1
1) "hello2"

###############################################################
lset   # 将列表中指定下标的值替换为另一个值 ,类似于更新操作!
127.0.0.1:6379> lrange list 0 -1   # 查看list中所有的值!
1) "two"
2) "four"
3) "three"
4) "haha"
127.0.0.1:6379> lrange list 0 0     # 截取字符串第一个值
1) "two"
127.0.0.1:6379> lset list 1 other    # 将列表下标为1的值替换为other
OK
127.0.0.1:6379> lrange list 0 -1   # 查看list中所有的值! 发现1下标值已经更新了!
1) "two"
2) "other"
3) "three"
4) "haha"

###############################################################
linsert   # 将某个值插入到列表中某个元素的前面或者后面!
127.0.0.1:6379> rpush mylist "hello"
(integer) 1
127.0.0.1:6379> rpush mylist "world"
(integer) 2
127.0.0.1:6379> rpush mylist "hahaha"
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1 
1) "hello"
2) "world"
3) "hahaha"
127.0.0.1:6379> linsert mylist before "world" "apple"    # 向列表中指定字符串的前面插入一个值!
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "apple"
3) "world"
4) "hahaha"
127.0.0.1:6379> linsert mylist after hahaha tea    # 向列表中指定字符串的后面插入一个值!
(integer) 5
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "apple"
3) "world"
4) "hahaha"
5) "tea"

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

总结:

  • 实际上是一个链表,before Node after left right 都可以插入值
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有的值,就成了一个空链表,也代表不存在
  • 在两头插入或者改动值,效率更加高! 中间元素,相对来说效率会低一点

消息排队! 消息队列 (lpush rpop) ,栈 (lpush lpop)

3.Set( 集合 )

set中的值是不能重复的!

常见操作命令

###############################################################
127.0.0.1:6379> sadd myset hello   # set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset kuangshen
(integer) 1
127.0.0.1:6379> sadd myset lovekuangshen
(integer) 1
127.0.0.1:6379> smembers myset   # 查看指定set的所有值
1) "hello"
2) "lovekuangshen"
3) "kuangshen"
127.0.0.1:6379> sismember myset hello   # 判断某个值是不是在set中
(integer) 1
127.0.0.1:6379> sismember myset world
(integer) 0

###############################################################
127.0.0.1:6379> scard myset   # 获取set集合中的内容元素的个数!
(integer) 3
127.0.0.1:6379> sadd myset lovekuangshen2
(integer) 1
127.0.0.1:6379> scard myset
(integer) 4

###############################################################
127.0.0.1:6379> srem myset hello    # 移除set集合中的指定元素!
(integer) 1
127.0.0.1:6379> scard myset    # 获取set集合中内容元素的个数!
(integer) 3
127.0.0.1:6379> smembers myset   # 获得set集合中所有的元素
1) "lovekuangshen2"
2) "lovekuangshen"
3) "kuangshen"

###############################################################
set  无序不重复集合,抽随机!
127.0.0.1:6379> srandmember myset   # 随机抽选出set集合中一个元素
"lovekuangshen2"
127.0.0.1:6379> srandmember myset  
"kuangshen"
127.0.0.1:6379> srandmember myset 2   # 随机抽选出set集合中指定个数的元素!
1) "kuangshen"
2) "lovekuangshen"

###############################################################
删除指定的key,随即删除一个key!
127.0.0.1:6379> smembers myset  # 查看所有的set集合中的值!
1) "lovekuangshen2"
2) "lovekuangshen"
3) "kuangshen"
127.0.0.1:6379> spop myset   # 随即删除set集合中的一个元素!
"lovekuangshen"
127.0.0.1:6379> smembers myset
1) "lovekuangshen2"
2) "kuangshen"
###############################################################
将一个指定的值移动到另外一个set集合中!
127.0.0.1:6379> sadd myset hello world kuangshen   # 向一个set集合中添加多个值!
(integer) 3
127.0.0.1:6379> sadd myset2 set2  # 向一个set集合中添加个一值!
(integer) 1
127.0.0.1:6379> smembers myset2
1) "set2"
127.0.0.1:6379> smembers myset
1) "hello"
2) "kuangshen"
3) "world"
127.0.0.1:6379> smove myset myset2 hello  # 将myset中的 hello元素 移动到myset2集合中!
(integer) 1
127.0.0.1:6379> smembers myset2
1) "hello"
2) "set2"
127.0.0.1:6379> smembers myset
1) "kuangshen"
2) "world"

###############################################################
微博,b站,共同关注!(交集
数字集合类:
	- 差集
	- 并集
	- 交集

127.0.0.1:6379> sadd key1 a b c d   
(integer) 4
127.0.0.1:6379> sadd key2 c d e f
(integer) 4
127.0.0.1:6379> smembers key1
1) "d"
2) "a"
3) "c"
4) "b"
127.0.0.1:6379> smembers key2
1) "d"
2) "f"
3) "e"
4) "c"
127.0.0.1:6379> sdiff key1 key2   # 差集
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2   # 交集  共同好友就可以这么实现!
1) "d"
2) "c"
127.0.0.1:6379> sunion key1 key2  # 并集
1) "d"
2) "e"
3) "c"
4) "a"
5) "f"
6) "b"

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

总结:

微博,A用户将所有关注的人放在一个set集合之中!(关注的人,粉丝【用户id是唯一的】)

共同关注,共同爱好,二度好友(交集

4.Hash(哈希)

map 集合 ,key-<key,value> 这时候这个值是一个map集合! 本质和String类型没有太大区别,还是一个简单key-value!

常见操作命令

###############################################################
127.0.0.1:6379> hset myhash filed1 kuangshen    # set一个具体的 key-value
(integer) 1
127.0.0.1:6379> hget myhash filed1    # 获取一个字段值
"kuangshen"
127.0.0.1:6379> hmset myhash filed1 hello filed2 world    # set多个具体的 key-value
OK
127.0.0.1:6379> hmget myhash filed1 filed2     # 获取多个字段值!
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash   # 获取全部的数据 key-value
1) "filed1"
2) "hello"
3) "filed2"
4) "world"

###############################################################
hdel
127.0.0.1:6379> hdel myhash filed1  # 删除hash指定的key字段! 对应的value值也就消失了!
(integer) 1
127.0.0.1:6379> hgetall myhash    # 获取全部的数据 key-value
1) "filed2"
2) "world"

###############################################################
hlen
127.0.0.1:6379> hmset myhash filed1 hello filed2 world 
OK
127.0.0.1:6379> hgetall myhash
1) "filed2"
2) "world"
3) "filed1"
4) "hello"
127.0.0.1:6379> hlen myhash   #  获取hash表的字段数量!
(integer) 2

###############################################################
hexists
127.0.0.1:6379> hexists myhash filed1   # 判断hash中指定字段是否存在!
(integer) 1
127.0.0.1:6379> hexists myhash filed3   # 判断hash中指定字段是否存在!
(integer) 0

###############################################################
#  只获得所有的key
#  只获得所有的value
127.0.0.1:6379> hkeys myhash    # 获得所有的key
1) "filed2"
2) "filed1"
127.0.0.1:6379> hvals myhash    # 获得所有的value
1) "world"
2) "hello"

###############################################################
incr   decr
127.0.0.1:6379> hset myhash filed3 5    # 初始值为5
(integer) 1
127.0.0.1:6379> hincrby myhash filed3 1   # 指定增量1
(integer) 6
127.0.0.1:6379> hincrby myhash filed3 -1   # 指定增量-1
(integer) 5
127.0.0.1:6379> hsetnx myhash filed4 hahaha    # 如果不存在,则可以设置!
(integer) 1
127.0.0.1:6379> hsetnx myhash filed4 lalala    # 如果存在,则不可以设置!
(integer) 0

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

总结:

hash变更的数据 user name age ,尤其是用户信息之类的,经常变动的数据信息! hash更适合于对象的存储,String更适合字符串存储!

5.Zset(有序集合)

在set的基础上,增加了一个值,set k1 v1 zset k1 score v1

常用操作命令

###############################################################
127.0.0.1:6379> zadd myset 1 one   # 添加一个值!
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three 4 four   # 添加多个值!
(integer) 3
127.0.0.1:6379> zrange myset 0 -1   # 查看集合中所有的元素!
1) "one"
2) "two"
3) "three"
4) "four"

###############################################################
排序如何实现? zrangebyscore min max   , zrevrangebyscore max min
127.0.0.1:6379> zadd salary 2500 xiaohong  # 添加三个用户带薪水!
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 500 kuangshen
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf  # 显示全部的用户,按照从小到大排序!
1) "kuangshen"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrevrange salary 0 -1  # 从大到小进行排序!
1) "zhangsan"
2) "kuangshen"
127.0.0.1:6379> zrevrangebyscore salary +inf -inf  # 显示全部的用户,按照从大到小排序!
1) "zhangsan"
2) "xiaohong"
3) "kuangshen"
127.0.0.1:6379> zrevrangebyscore salary +inf -inf withscores  # 显示全部的用户,按照从大到小排序!附带上薪水!
1) "zhangsan"
2) "5000"
3) "xiaohong"
4) "2500"
5) "kuangshen"
6) "500"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores # 显示薪水小于2500的元素,从小到大进行排序,附带薪水!
1) "kuangshen"
2) "500"
3) "xiaohong"
4) "2500"

###############################################################
# 移除rem中的指定元素!
127.0.0.1:6379> zrange salary 0 -1  # 查看集合中所有的元素!
1) "kuangshen"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrem salary xiaohong   # 移除有序集合中的指定的元素!
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "kuangshen"
2) "zhangsan"
127.0.0.1:6379> zcard salary   # 获取有序集合中元素的个数!
(integer) 2

###############################################################
127.0.0.1:6379> zadd myset 1 hello 2 world 3 kuangshen  
(integer) 3
127.0.0.1:6379> zcount myset 1 3   # 查看指定区间有序集合元素的个数!
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2

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

其余的一些api,通过我们的学习,剩下的如果工作中有需要,可以去官网查看用法!

总结:

set 排序 存储班级成绩表工资表排序

普通消息,1重要消息 2带权重进行判断!

排行榜的应用实现! 取 Top N 测试!

2.Redis三种特殊的数据类型

1.geospatial(地理位置)

朋友的定位, 附近的人, 打车距离计算!

Redis 在 Geo 在Redis3.2版本就出现了! 这个功能可以推算地理位置的信息, 两地之间的距离 , 方圆几里的人

可以查询一些测试数据 : http://www.jsons.cn/lngcode/

只有6个命令

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yl7YCB6O-1621391815874)(C:\Users\86183\AppData\Roaming\Typora\typora-user-images\1621269019304.png)]

getadd

# getadd   添加地理位置
# 规则: 两级无法直接添加,我们一般下载城市数据,通过Java读配置文件一次性全部导入!
# 参数: key  (经度,纬度,名称!)
# 有效经度: -180~180
# 有效纬度: -85~85

127.0.0.1:6379> geoadd china:city 116.23 40.22 beijing  # 添加城市北京地理位置!
(integer) 1
127.0.0.1:6379> geoadd china:city 121.48 31.40 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.54 29.40 chongqing
(integer) 1
127.0.0.1:6379> geoadd china:city 108.93 34.23 xian
(integer) 1
127.0.0.1:6379> geoadd china:city 113.88 22.55 shenzhen
(integer) 1
127.0.0.1:6379> geoadd china:city 120.21 30.20 hangzhou
(integer) 1

geopos

获得当前定位: 一定是个坐标值!

127.0.0.1:6379> geopos china:city beijing xian  # 获取指定的多个城市的经度和纬度!
1) 1) "116.23000055551528931"
   2) "40.2200010338739844"
2) 1) "108.92999857664108276"
   2) "34.23000121926852302"
   

geodist

两个人之间的距离!

单位:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。
127.0.0.1:6379> geodist china:city shanghai xian km  # 查看上海到西安的直线距离!
"1213.7091"
127.0.0.1:6379> geodist china:city beijing chongqing km   # 查看北京到重庆的直线距离!
"1491.6716"

georadius 以给定的经纬度为中心 ,找出某一半径内的元素!

我附近的人? ( 获得所有附近的人的地址,定位!) 通过半径来查询!

获得指定数量的人!

所有的数据应该都录入到:china:city ,才会让结果更加精确!

# 找出位于指定的经度纬度范围内的城市!
127.0.0.1:6379> georadius china:city 110 30 1000 km   # 以110 30 这个经纬度为中心,寻找方圆1000km内的城市!
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km # 以110 30 这个经纬度为中心,寻找方圆500km内的城市!
1) "chongqing"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist # 显示到中心距离的位置长度!
1) 1) "chongqing"
   2) "340.8679"
2) 1) "xian"
   2) "481.1540"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord  # 显示他人的定位信息 
1) 1) "chongqing"
   2) 1) "106.54000014066696167"
      2) "29.39999880018641676"
2) 1) "xian"
   2) 1) "108.92999857664108276"
      2) "34.23000121926852302"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1 #筛选出指定的结果,1个!
1) 1) "chongqing"
   2) "340.8679"
   3) 1) "106.54000014066696167"
      2) "29.39999880018641676"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 2 #筛选出指定的结果 2个!
1) 1) "chongqing"
   2) "340.8679"
   3) 1) "106.54000014066696167"
      2) "29.39999880018641676"
2) 1) "xian"
   2) "481.1540"
   3) 1) "108.92999857664108276"
      2) "34.23000121926852302"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 3 # 山选出指定的结果 ,  3个!
1) 1) "chongqing"
   2) "340.8679"
   3) 1) "106.54000014066696167"
      2) "29.39999880018641676"
2) 1) "xian"
   2) "481.1540"
   3) 1) "108.92999857664108276"
      2) "34.23000121926852302"

georadiusbymember

# 找出位于指定元素周围的其他元素!
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km  # 查看北京周围1000km的城市
1) "beijing"
2) "xian"
127.0.0.1:6379> georadiusbymember china:city xian 1000 km   # 查看西安周围1000km的城市
1) "xian"
2) "chongqing"
3) "beijing"

geohash 返回一个或者多个位置元素的geohash表示!

该命令将返回11个字符的Geohash字符串!

# 将二维的经纬度转化为一维的字符串!降维打击! 如果两个字符串越接近,说明距离越近!
127.0.0.1:6379> geohash china:city beijing chongqing  
1) "wx4sucu47r0"
2) "wm5z22h53v0"

geo底层的是实现原理就是Zset! 我们可以使用Zset命令来操作geo!

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 beijing   # 移除地图中指定的元素!
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"

总结:做附近的人,距离!

2.hyperloglog

什么是基数? 说白了就是不重复的元素! 可以接受误差!

A {1,3,5,7,8,7} A的基数为5

B{1,3,5,7,8} B的基数为5

简介

Redis 2.8.9 版本就更新了hyperloglog数据结构!

Redis hyperloglog 基数统计的算法!

**优点: 占用的内存的固定的, 2^64 不同的元素的基数,只需要费12kb的内存!**如果要从内存角度来比较的话,hyperloglog应该成为首选

网页的UV( 一个人访问网站多次还是算作一个人! )

传统的方式 , set 保存用户的id , 然后就可以统计set中的元素数量作为标准判断!

这种方式如果保存大量的用户id , 就会比较麻烦!我们的目的是为了计数,而不是保存用户id!

0.81%错误率! 统计UV任务,是可以忽略不计的!

测试使用!

127.0.0.1:6379> pfadd mykey a b c d e f g i j  # 创建第一组元素mykey
(integer) 1
127.0.0.1:6379> pfcount mykey   # 统计mykey 元素的基数数量!
(integer) 9
127.0.0.1:6379> pfadd mykey2 i j x y z h   # 创建第二组元素mykey2
(integer) 1
127.0.0.1:6379> pfcount mykey
(integer) 9
127.0.0.1:6379> pfcount mykey2    # 统计mykey2 元素的基数数量!
(integer) 6
127.0.0.1:6379> pfmerge mykey3 mykey mykey2  # 合并两组 mykey mykey2 到 mykey3 中, 并集
OK
127.0.0.1:6379> pfcount mykey3  # 查看并集的数量!
(integer) 13

总结 : 如果业务忽略误差(0.81%),可以用它做统计( 如统计人数,访问量等等!)

如果不允许误差 , 就使用set 或者自己的数据类型即可!

3.bitmaps

位存储

统计全国疫情感染人数 : 0 1 0 1 0 0 0 1 … 14亿

统计用户信息 ,活跃(1),不活跃(0)! 登录(1) ,未登录(0)! 365打卡!

两个状态的都可以使用 bitmaps

都是操作二进制位进行存储的,就只有 0和1 两种状态!

365 天 = 365 bit 1字节=8bit 大约46个字节左右

使用bitmap记录周一到周日的打卡

周一0: 1 打卡了 ,周二1: 1 打卡了 , 周三2: 0 没打卡 …等等!

查看某一天是否有打卡!

127.0.0.1:6379> getbit sign 3   # 查看周四 打卡情况!   打卡啦!
(integer) 1
127.0.0.1:6379> getbit sign 6   # 查看周日 打卡情况!   打卡啦!
(integer) 1

统计打卡的天数!

127.0.0.1:6379> bitcount sign   #  统计这周的打卡记录!  就可以看到是否有全勤记录!
(integer) 4

这些在生活中或者开发中,都有十分多的应用场景,学习了,就是多一个思路!

技多不压身! 奥里给!

3.事务

Mysql : ACID 要么同时成功,要么同时失败!

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

Redis 事务的本质 : 一组命令的集合! 一个事务中的所有命令都会被序列化,在事务执行过程中, 会按照顺序进行执行!

Redis事务三大特性: 一致性、顺序性、排他性! 来执行一系列的命令!

----  队列 set set set 执行 ----

所有的命令在事务中,并没有直接的被执行!只有发起执行命令的时候才会执行! exec

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 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> keys *   # 事务队列中的命令都不会执行!
(empty list or set)

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

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 k1   # 所有的命令都不会被执行!
(nil)

运行时异常 (1/0),如果事务队列中存在语法性,那么执行命令的时候,其他命令可以正常执行的! 错误命令抛出异常!

127.0.0.1:6379> set k1 "v1"     # 注意 v1 为字符串!
OK
127.0.0.1:6379> get k1  
"v1"
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 k2
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range # 虽然第一条命令报错了,但是别的命令依旧可以正常的执行成功!
2) OK
3) OK
4) "v2"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"

监控! watch(实现乐观锁!)

悲观锁

  • 很悲观,认为什么时候都会出问题,无论做什么都会加锁!

乐观锁

  • 很乐观,认为什么时候都不会出现问题,所以 ,不会上锁! 更新数据的时候去判断一下!在此期间,是否有人修改过这个数据 version!
  1. 获取version

  2. 更新的时候比较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 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec   # 执行成功!
1) (integer) 90
2) (integer) 10

模拟另一个线程插队,再次测试!

# 正常执行!
127.0.0.1:6379> watch money  # 监控 money ,他会在事务开启后,执行前,发现出money被修改,不是原来的90了,变为了200,直接会导致他会让 事务 执行失败!
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   # 执行事务! 未修改任何值! 本质:因为另一个线程插队了,让监控的值发生了变化!
(nil)


# 另一个线程插队!
127.0.0.1:6379> get money   # 获取money
"90"
127.0.0.1:6379> set money 200   # 坏家伙! 直接把金额修改了!
OK
127.0.0.1:6379> get money
"200"


测试多线程修改值! 使用watch可以当作 Redis 的乐观锁来进行操作!

如果修改失败! 获取最新的值就好!

猜你喜欢

转载自blog.csdn.net/qq_46611668/article/details/117019178