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!
-
获取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 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 的乐观锁来进行操作!
如果修改失败! 获取最新的值就好!