5、Hash (哈希)
集合中的元素是唯一的,这就意味着集合中不能出现重复的数据。在Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。Hash更适合于对象的存储。本质和String类型是没有太大区别的,还是一个简单的key-value.
127.0.0.1:6379> hset myhash filed1 jsxs #设置单个hashMap (integer) 1 127.0.0.1:6379> HGET myhash filed1 #得到一个hashMap "jsxs" 127.0.0.1:6379> HMSET myhash filed1 "hello" filed2 "world" #设置多个hashMap OK 127.0.0.1:6379> HGET myhash filed1 "hello" 127.0.0.1:6379> HMGET myhash filed1 filed2 #获取多个hashMap 1) "hello" 2) "world" 127.0.0.1:6379> HGETALL myhash # 获得所有的hashMap 1) "filed1" 2) "hello" 3) "filed2" 4) "world" ------------------------------------------ 删除指定的hashMap 127.0.0.1:6379> HDEL myhash filed2 #删除指定的hashmap (integer) 1 127.0.0.1:6379> HGETALL myhash 1) "filed1" 2) "hello" ------------------------------------------ 查看有几个键值对 127.0.0.1:6379> HLEN myhash (integer) 1 127.0.0.1:6379> HMSET myhash filed2 world filed3 hh OK 127.0.0.1:6379> HGETALL myhash 1) "filed1" 2) "hello" 3) "filed2" 4) "world" 5) "filed3" 6) "hh" 127.0.0.1:6379> HLEN myhash # 获取hash表的字段数量 (integer) 3 ------------------------------------------ hash中指定字段是否存在 127.0.0.1:6379> HEXISTS myhash files1 #判断hash是否存在 (integer) 0 127.0.0.1:6379> HEXISTS myhash filed1 (integer) 1 ------------------------------------------ 仅仅获取字段和值 127.0.0.1:6379> HKEYS myhash # 单纯获取字段 1) "filed1" 2) "filed2" 3) "filed3" 127.0.0.1:6379> HVALS myhash #单纯获取值 1) "hello" 2) "world" 3) "hh" ------------------------------------------ 127.0.0.1:6379> HSET myhash fileds3 5 (integer) 1 127.0.0.1:6379> HINCRBY myhash fileds3 1 # 自增1 (integer) 6 127.0.0.1:6379> HINCRBY myhash fileds3 2 #自自增2 (integer) 8 127.0.0.1:6379> HINCRBY myhash fileds3 -1 # 自减1 (integer) 7 127.0.0.1:6379> HSETNX myhash filed4 hello #加入说不存在就创建,存在就不创建 (integer) 1 127.0.0.1:6379> HSETNX myhash filed4 hello (integer) 0
hash变更的数据user name age,尤其是用户信息之类的,经常变动的信息!
127.0.0.1:6379> HSET user:1 name jsxs # 设置id为1 键值对name (integer) 1 127.0.0.1:6379> HGET myhash user:1 (nil) 127.0.0.1:6379> HGET user:1 name #获取id为1 的键值对 "jsxs"
6、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 (integer) 1 127.0.0.1:6379> zadd myset 3 three 4 four #添加多个值 (integer) 2 127.0.0.1:6379> ZRANGE myset 0-1 (error) ERR wrong number of arguments for 'zrange' command 127.0.0.1:6379> ZRANGE myset 0 -1 # 查看添加的值 1) "one" 2) "two" 3) "three" 4) "four" ------------------------------------------ 根据score 进行排序 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 范围是正无穷 到 负无穷升序排序 1) "jsxs" 2) "xiaoming" 3) "zahngsan" 127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores # 范围是正无穷 到 负无穷升序排序 并显示分数 1) "jsxs" 2) "200" 3) "xiaoming" 4) "2500" 5) "zahngsan" 6) "5000" 127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores # 范围是正无穷 到 2500 1) "jsxs" 2) "200" 3) "xiaoming" 4) "2500" 127.0.0.1:6379> ZREVRANGEBYSCORE salary +inf -inf withscores # 范围是正无穷 到 负无穷降序排序 并显示分数 1) "zahngsan" 2) "5000" 3) "xiaoming" 4) "2500" 5) "jsxs" 6) "200" ------------------------------------------ 移除指定元素 127.0.0.1:6379> ZRANGE salary 0 -1 1) "jsxs" 2) "xiaoming" 3) "zahngsan" 127.0.0.1:6379> ZREM salary xiaoming # 移除指定的元素 (integer) 1 127.0.0.1:6379> ZRANGE salary 0 -1 1) "jsxs" 2) "zahngsan" 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 ".jsxs" (integer) 2 127.0.0.1:6379> ZCOUNT myset 1 3 # 获取1-3区间之间的数量 (integer) 3 127.0.0.1:6379> ZCOUNT myset 1 2 # 获取1-2区间之间的数量 (integer) 2
应用案例:
set排序 存储班级成绩表 工资表排序!
普通消息1 重要消息 2 ----带权重进行判断!
排行榜应用实现,取Top N测试!
(四)、三种特殊数据类型
1、Geospatial(地理位置)
使用经纬度定位地理坐标,并用一个有序集合Zset进行保存,GEO的底层实现原理就是Zset。
在这里我们先来增加一个经纬度的基本常识:
超链接: 在线经纬度查询工具
- 有效的经度从-180度到180度。
- 有效的纬度从-85.05112878度到85.05112878度。
1.geoAdd 添加地址
添加地理位置: geoadd key 值(经度,维度,名称) 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 (integer) 1 127.0.0.1:6379> geoadd china:city 114.05 22.52 shenzhen (integer) 1 127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian (integer) 2
2.geoPos 获取地址
获取指定城市的精度和维度 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"
3.geoDist 获取两点的距离
- m :米
- km :千米
- mi :英里
- ft :英尺
两点之间的距离 127.0.0.1:6379> GEODIST china:city beijing xian km # 北京到西安的千米 "910.0565" 127.0.0.1:6379> geoadd china:city 114.60 33.53 zhoukou (integer) 1 127.0.0.1:6379> GEODIST china:city beijing zhoukou "726411.7341" 127.0.0.1:6379> GEODIST china:city beijing zhoukou km #北京到周口的km "726.4117"
3.geoRadius 以给定的经纬度为中心,找出某一半径的元素
(我附近的人?) 获得所有附件的人的地址,定位! 通过半径来查询。
多用于找朋友
根据经纬度查找附近的城市 127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 查找以经度110 维度30 长度为1000km的城市 1) "chongqing" 2) "xian" 3) "shenzhen" 4) "hangzhou" 5) "zhoukou" 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 km withdist # 查找以经度110 维度30 长度为500km的城市 并标出直线距离 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 # 查找以经度110 维度30 长度为500km的城市 并标出经纬度 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# 查找以经度110 维度30 长度为500km的城市 并标出直线距离 经纬度以及最近的一个。 1) 1) "chongqing" 2) "341.9374" 3) 1) "106.49999767541885376" 2) "29.52999957900659211"
- geoRadiusByMber 找出指定范围的元素,中心点是给定的元素
多用于找城市
根据地名向附近查找: 127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km #以北京为中心半径为1000km查找所有的城市 1) "zhoukou" 2) "beijing" 3) "xian" 127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 1000 km 1) "hangzhou" 2) "shanghai" 3) "zhoukou"
5.geoHash 命令- 返回一个或多个位置元素的geoHash表示
该命令将返回11个字符的GeoHash字符串!
将二纬的经纬度转换为以为的字符串.如果两个字符串越接近,那么距离越近 127.0.0.1:6379> GEOHASH china:city beijing 1) "wx4fbxxfke0" 127.0.0.1:6379> GEOHASH china:city beijing zhoukou 1) "wx4fbxxfke0" 2) "wtcqxzcnmt0"
GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来对其进行相关操作.
验证: 我们可以利用Zset进行删除的操作
移除坐标 127.0.0.1:6379> ZRANGE china:city 0 -1 1) "chongqing" 2) "xian" 3) "shenzhen" 4) "hangzhou" 5) "shanghai" 6) "zhoukou" 7) "beijing" 127.0.0.1:6379> ZREM china:city shenzhen #移除坐标 (integer) 1 127.0.0.1:6379> ZRANGE china:city 0 -1 1) "chongqing" 2) "xian" 3) "hangzhou" 4) "shanghai" 5) "zhoukou" 6) "beijing"
2、Hyperloglog(基数统计)
基数就是一个集合中不重复的元素。举一个简单的例子:
- A {1,3,5,7,8,7}
- A(不重复的元素) = (1,3,5,8),可以接受误差!
简介
- Redis HyperLogLog 是用来做基数统计的算法。
- 优点:
- 输入元素的数量或者体积非常大时,计算基数所需的空间总是固定的、并且是很小的。
- 花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。
网页的UV (一个人访问一个网站多次,但是还是算作一个人!)
- 传统的解决方式: 利用set集合不重复的特性。set保存用户的id,然后就可以统计元素数量作为标准判断!
- 如果允许容错,那么一定可以使用 Hyperloglog 。
- 如果不允许容错,就使用传统的 set 或者自己的数据类型即可。
- HyperLogLog 底层使用string数据类型。
127.0.0.1:6379> PFADD myset a b c d e f g h i j k #设置一个基数统计 (integer) 1 127.0.0.1:6379> PFCOUNT myset #统计不重复的数量-基数 (integer) 11 127.0.0.1:6379> PFADD myset2 a b c d e f g h i j k s a a c b (integer) 1 127.0.0.1:6379> PFCOUNT myset (integer) 11 127.0.0.1:6379> PFCOUNT myset2 (integer) 12 127.0.0.1:6379> PFMERGE mykey3 myset myset2 # 进行多个集合的合并 OK 127.0.0.1:6379> PFCOUNT mykey3 (integer) 12
3、BitMap(位图)
BitMap可以用来干什么呢?
- 签到统计,使用bitmap 来记录 周一到周日的打卡!
- 状态统计
BitMap采用位存储,它是一连串的二进制数字,信息状态只有0和1,每一位所在的位置为(offset)
测试: 假设我们现在的任务是统计从星期一到星期天的打卡情况。0代表未打卡,1代表打卡
------------------------------------------ 添加一个 127.0.0.1:6379> setbit sign 0 0 # 假设第一个0是星期1 第二个0 是未打卡 127.0.0.1:6379> setbit sign 1 0 (integer) 0 127.0.0.1:6379> setbit sign 2 1 (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 1 (integer) 0 127.0.0.1:6379> setbit sign 6 1 (integer) 0 ------------------------------------------ 查看某一个具体的 127.0.0.1:6379> GETBIT sign 0 (integer) 0 127.0.0.1:6379> GETBIT sign 3 (integer) 1 127.0.0.1:6379> GETBIT sign 6 (integer) 1 ------------------------------------------ 统计总和就是 0和1 的 127.0.0.1:6379> BITCOUNT sign (integer) 5
(五)、事务
面试高频:
这里我们一定要知道Redis中的单条命令是保证原子性的,但是redis事务不能保证原子性。
Redis事务的本质
- 一次性
- 顺序性
- 排他性
在Redis事务没有没有隔离级别的概念;所有的命令在事务中,并没有直接被执行!只有发起执行命令Exec的时候才会执行!事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰(排他)
redis事务的三大过程
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
1.正常执行事务
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
2.取消事务
取消事务,代表事务里面的数据不奏效。但以前的数据仍然保留。
127.0.0.1:6379> get k2 # 事务 "v2" 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> get k2 # 事务之前的有效 "v2"
3.编译型异常(代码有问题, 命令有错!)
事务中的所有命令都不会被执行
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 k3 v3 QUEUED 127.0.0.1:6379(TX)> getset k3 # 编译型错误 (error) ERR wrong number of arguments for 'getset' command 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)> EXEC # 提交事务 (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k1 # 得不到值 (nil)
4.运行时异常 (错误的不被执行,其他的执行)
如果事务队列中存在错误语法性,那么执行命令的时候,其他命令是可以正常执行的,只有错误命令会抛出异常!
127.0.0.1:6379> flushdb OK 127.0.0.1:6379> set k1 "v1" OK 127.0.0.1:6379> multi OK 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)> EXEC 1) (error) ERR value is not an integer or out of range 2) OK 3) OK 127.0.0.1:6379> get k2 "v2" 127.0.0.1:6379> get k3 "v3"