Set(集合)

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集合中,是的话返回1  不是返回0
(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 "hello" # 如果set重复的值不会成功,因为set不能重复
(integer) 0
127.0.0.1:6379> sadd myset "hello2"
(integer) 1
127.0.0.1:6379> scard myset
(integer) 4
===========================================
#rem
127.0.0.1:6379> srem myset hello # 移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379> smembers myset
1) "hello2"
2) "lovekuangshen"
3) "kuangshen"
============================================
set 无序不重复集合。随机!
127.0.0.1:6379> smembers myset
1) "hello2"
2) "lovekuangshen"
3) "kuangshen"
127.0.0.1:6379> srandmember myset # 随机抽选出一个元素
"kuangshen"
127.0.0.1:6379> srandmember myset
"kuangshen"
127.0.0.1:6379> srandmember myset
"hello2"
127.0.0.1:6379> srandmember myset
"lovekuangshen"
127.0.0.1:6379> srandmember myset 2# 随机抽选出指定个数的元素
1) "hello2"
2) "lovekuangshen"
===================================================
随机删除key!
127.0.0.1:6379> smembers myset
1) "hello2"
2) "lovekuangshen"
3) "kuangshen"
127.0.0.1:6379> spop myset # 随机移除一些set集合中的元素!
"lovekuangshen"
127.0.0.1:6379> spop myset
"hello2"
127.0.0.1:6379> smembers myset
1) "kuangshen"
======================================================
#将一个指定的值,移动到另外一个set集合中!
127.0.0.1:6379> sadd myset "hello"
(integer) 1
127.0.0.1:6379> sadd myset "world"
(integer) 1
127.0.0.1:6379> sadd myset "kuangshen"
(integer) 1
127.0.0.1:6379> sadd myset2 "set2"
(integer) 1
127.0.0.1:6379> smove myset myset2 "kuangshen"#将一个指定的值,移动到另外一个set集合中!
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello"
2) "world"
127.0.0.1:6379> smembers myset2
1) "kuangshen"
2) "set2"
====================================================
微博 或者B站或者....有共同关注这个提示(这是个交集)
数学集合类:
- 差集 sdiff
- 交集 sinter
- 并集 sunion
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
127.0.0.1:6379> sdiff key1 key2 # 差集
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2 #交集
1) "c"
127.0.0.1:6379> sunion key1 key2 # 并集
1) "b"
2) "c"
3) "a"
4) "e"
5) "d"

Hash(哈希)

127.0.0.1:6379> hset myhash field1 kuangshen #set 一个具体的key-value
(integer) 1
127.0.0.1:6379> hget myhash field1
"kuangshen"
127.0.0.1:6379> hmset myhash field1 hello field2 world
OK # 如果field1 里面有值  那么现在set的值会覆盖前面的值   set多个key-value
127.0.0.1:6379> hmget myhash field1 field2 #获取多个字段的值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash #获取全部的值  以key-value形式
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> hdel myhash field1 #删除hash指定的key字段,对应的value值也就消失了
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
====================================================
#hlen

127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
127.0.0.1:6379> hlen myhash
(integer) 1
127.0.0.1:6379> hmset myhash field1 hello field2 world# 如果field2 里面有值  那么现在set的值会覆盖前面的值
OK
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
3) "field1"
4) "hello"
127.0.0.1:6379> hlen myhash #获取hash表的字段数量
(integer) 2
======================================================
127.0.0.1:6379> hexists myhash field1 #判断hash中指定的 字段是否存在
(integer) 1
127.0.0.1:6379> hexists myhash field3
(integer) 0
=====================================================
# 只获得所有的key
# 只获得所有的value

127.0.0.1:6379> hkeys myhash# 只获得所有的key
1) "field2"
2) "field1"
127.0.0.1:6379> hvals myhash# 只获得所有的value
1) "world"
2) "hello"
===================================================
#hincrby指定增量
#hsetnx 如果不存在则可以设置 如果存在则设置失败
127.0.0.1:6379> hset myhash field3 5
(integer) 1
127.0.0.1:6379> hincrby myhash field3 1 #指定增量
(integer) 6
127.0.0.1:6379> hincrby myhash field3 -1
(integer) 5
127.0.0.1:6379> hsetnx myhash field4 hello#如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 world# 如果存在则设置失败
(integer) 0

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

127.0.0.1:6379> hset user:1 name qinjiang
(integer) 1
127.0.0.1:6379> hget user:1 name
"qinjiang

Zset(有序集合)

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

127.0.0.1:6379> zadd myset 1 one  #添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three #添加多个值
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
==============================================
排序如何实现
#zrevrange 在排序的设置返回的成员范围,通过索引,下令从分数高到低
# zrangebyscore 返回的有序集合中指定分数区间内的成员 分数由低到高
127.0.0.1:6379> zadd salary 2500 xiaoming #添加3个用户
(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) "xiaoming"
3) "zhangsan"
127.0.0.1:6379> zrevrange salary 0 -1 #zrevrange 在排序的设置返回的成员范围,通过索引,下令从分数高到低
1) "zhangsan"
2) "xiaoming"
3) "kuangshen"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores#显示全部的用户并且附带成绩
1) "kuangshen"
2) "500"
3) "xiaoming"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores#显示工资小于2500员工的升序排序
1) "kuangshen"
2) "500"
3) "xiaoming"
4) "2500"
==========================================================================
移除元素
127.0.0.1:6379> zrange salary 0 -1
1) "kuangshen"
2) "xiaoming"
3) "zhangsan"
127.0.0.1:6379> zrem salary xiaoming #移除有序集合中的指定元素
(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
(integer) 1
127.0.0.1:6379> zadd myset 2 world 3 kuangshen
(integer) 2
127.0.0.1:6379> zcount myset 1 3 #获取指定区间的成员数量
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2
127.0.0.1:6379> zcount myset 2 3
(integer) 2

查看命令的官网地址:Redis命令中心(Redis commands) – Redis中国用户组(CRUG)

案例思路:set排序 存储班级成绩表 工资表排序

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

三种特殊数据类型

geospatial :地理位置

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

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

可以查询城市的经纬度的网址:城市经纬度查询-国内城市经度纬度在线查询工具 (jsons.cn)

经纬度查询 - 坐标拾取系统 (bmcx.com)

只有6个命令:

redis hex值怎么用命令 set进去 redis zset hash_redis

getadd

  • 有效的经度从-180度到180度。
  • 有效的纬度从-85.05112878度到85.05112878度

官方文档:Redis GEOADD 命令_将指定的地理空间位置(纬度、经度、名称)添加到指定的key中

#getadd 添加地理位置
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50.29.53 chongqing 114.05 22.52 shengzhen
(error) ERR syntax error
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2

Redis GEOPOS 命令 - 从key里返回所有给定位置元素的位置(经度和纬度)

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

127.0.0.1:6379> geopos china:city beijing  #获取指定城市的经度和纬度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing chongqing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "106.49999767541885376"
   2) "29.52999957900659211"

Redis GEODIST 命令 - 返回两个给定位置之间的距离

如果两个位置之间的其中一个不存在, 那么命令返回空值。

指定单位的参数 unit 必须是以下单位的其中一个:

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

Redis GEORADIUS 命令 - 以给定的经纬度为中心, 找出某一半径内的元素

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

127.0.0.1:6379> georadius china:city 110 30 1000 km # 以110 30 这个经纬度为中心,寻找方圆1000km的城市
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqing"
2) "xian"
127.0.0.1:6379> georadius china:city 110 30 500 withdist
(error) ERR unsupported unit provided. please use m, km, ft, mi
127.0.0.1:6379> georadius china:city 110 30 500 km withdist # 显示到中心距离的位置
1) 1) "chongqing"
   2) "341.9374"
2) 1) "xian"
   2) "483.8340"
127.0.0.1:6379> georadius china:city 110 30 500 km withcoord # 显示他人的定位信息
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) 1) "108.96000176668167114"
      2) "34.25999964418929977"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 1 # 删选出指定的结果
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 2
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) "483.8340"
   3) 1) "108.96000176668167114"
      2) "34.25999964418929977"
127.0.0.1:6379> georadius china:city 110 30 500 km withdist withcoord count 3
1) 1) "chongqing"
   2) "341.9374"
   3) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) "483.8340"
   3) 1) "108.96000176668167114"
      2) "34.25999964418929977"

Redis GEORADIUSBYMEMBER 命令 - 找出位于指定范围内的元素,中心点是由给定的位置元素决定

# 找出位于指定元素周围的其他元素
127.0.0.1:6379> georadiusbymember china:city beijing 500 km
1) "beijing"
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> georadiusbymember china:city chongqing 1000 km
1) "chongqing"
2) "xian"

Redis GEOHASH 命令 - 返回一个或多个位置元素的 Geohash 表示

该命令将返回11个字符的Geohash字符串 它将失去精度,但仍将指向同一地区

# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么距离越近
127.0.0.1:6379> geohash china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"

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

127.0.0.1:6379> zrange china:city 0 -1 # 查看地图中全部的元素
1) "chongqing"
2) "xian"
3) "shengzhen"
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) "shengzhen"
4) "hangzhou"
5) "shanghai"
Hyperloglog

什么是基数

简单来说,基数(cardinality,也译作势),是指一个集合(这里的集合允许存在重复元素)中不同元素的个数。

简介

Redis 2.8.9 版本更新了Hyperloglog 数据结构

Reddis Hyperloglog 是基数统计的算法!

优点:占用的内存是固定的,2^64不同的元素的基数,只需要费12KB内存!如果从内存角度来比较的话Hyperloglog首选!

网页的UV(一个人访问一个网站多次,但是还是算作一个人) PV(Page view, 页面浏览量 )UV( unique visitor 网站独立访客)

传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断!这个方式如果保存大量的用户id就会比较麻烦。我们的目的是为了计数,而不是保存用户id

测试使用

127.0.0.1:6379> pfadd mykey a b c d e f g h i j # 创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey#统计mykey元素的基数数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i o p j k h g v n n
(integer) 1
127.0.0.1:6379> pfcount mykey2 #统计mykey2元素的基数数量 不统计重复的
(integer) 9
127.0.0.1:6379> pfmerge mykey3 mykey mykey2# 合并两组 mykey mykey2 => mykey3并集
OK
127.0.0.1:6379> pfcount mykey3  # 查看并集的数量!
(integer) 15

如果允许容错,那么一定可以使用Hyperloglog

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

Bitmaps

位存储

Bitmaps位图,数据结构!都是操作二进制位来进行记录,就只有0和1两个状态!

用例:

使用bitmap来记录周一到周日的打卡! 0代表未打卡 1 代表打卡

redis hex值怎么用命令 set进去 redis zset hash_Redis_02

127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0 
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379> getbit sign 3 # 查看某一天是否打卡
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
127.0.0.1:6379> bitcount sign  #统计操作,统计打卡的天数
(integer) 3

事务

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

一次性 顺序性 排他性 执行一系列的命令!

Redis事务本质: 一组命令的集合!

Redis事务没有隔离级别的概念!

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

Redis单条命令保存是保存原子性的,但是事务不保证原子性!

redis的事务:

  • 开启事务(multi)
  • 命令入队(…)
  • 执行事务(exec)

正常执行事务!

127.0.0.1:6379> multi #开启事务
OK
# 命令入队
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec #执行事务
1) OK
2) OK
3) "v2"
4) OK

执行完这个事务,这个事务就结束了,下次使用要重新开启事务

放弃事务

127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> discard # 取消事务
OK
127.0.0.1:6379> get k4 #事务队列中的命令都不会被执行!
(nil)

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

127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379(TX)> set k1 #错误命令
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> exec #执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 #所有的命令都不会被执行!
(nil)

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

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 "v1"
QUEUED
127.0.0.1:6379(TX)> incr k1 # 执行的时候失败 自增是不能增字符串的 只能是数字
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) ERR value is not an integer or out of range #虽然第一条命令报错了,但是其他命令依旧正常执行成功了!
3) OK
4) OK
5) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379> get k1
"v1"

监控 Watch(面试常问)

悲观锁:

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

乐观锁:

  • 很乐观,认为无论什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据。
  • 获取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(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20

一旦事务执行成功后,监控就会自动取消掉

测试多线程修改值,使用watch 可以当做redis的乐观锁操作!

redis hex值怎么用命令 set进去 redis zset hash_ci_03

redis hex值怎么用命令 set进去 redis zset hash_redis_04

注意。在这个过程中不要有编译出错 保证都执行了 不然会报这个错误(error) EXECABORT Transaction discarded because of previous errors.

第一个线程

127.0.0.1:6379> watch money #监视
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec #这一步是执行完线程2之后再来执行  线程2修改了money的值,这个时候,就会导致事务执行失败!
(nil)

第二个线程

127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK

解决事务执行失败的方法:如果修改失败,获取最新的值就好 在线程1进行修改

127.0.0.1:6379> unwatch # 如果发现事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money # 获取最新的值,再次监视, 相当于 select version
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 1
QUEUED
127.0.0.1:6379(TX)> incrby out 1
QUEUED
127.0.0.1:6379(TX)> exec# 对比监视的值是否发生了变化,如果没有变化,那么就可以执行成功,如果变化了就执行失败,这里应该是指的线程2没有修改值
1) (integer) 999
2) (integer) 21