redis基础知识
redis基础知识
Redis有9种基本数据类型,大部分盆友最熟悉的有这5种:string、hash、list、set、zset。其实,除此之外,经常用到的还有:bitmap、hyperloglog、geohash、stream。
下面来整理下各个数据类型的概念和基本用法。
一、 普通数据类型(5种)
Redis支持5种最基本的数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zset (sorted set:有序集合)。
1、String
1)String是简单的 key-value 键值对,value 可以是字符串,也可以是数字
相关命令:
SET key value
GET key
MSET
MGET
INCR
INCRBY
DECR
DECRBY
DEL
EXPIRE
TTL
EXISTS
补充:为什么incr/decr具有原子性?
答:Redis的操作之所以是原子性的,是因为Redis是单线程的。线程,是操作系统最小的执行单元,在单线程程序中,任务是要一个一个地做,必须做完一个任务后,才会去做另一个任务。
2、List(列表)
Redis列表是简单的字符串列表
相关命令:
1) LPUSH key value1 [value2]
将一个或多个值插入到列表的左侧(即队首)。新添加的元素将成为列表的第一个元素。
2) LLEN
3) LRANGE
4) LREM key count value
用于移除队列中指定数量与参数element值相同的元素,并返回实际移除的元素数量。
LREM命令执行的操作受到count参数影响,不同的count值执行的操作为:
- count > 0时,将从队首开始删除最多 count 个 与 value 相等的元素。
- count < 0时,将从队尾开始删除最多 count 个 与 value 相等的元素。
- count = 0,将移除列表中所有相同的元素。
5) LTRIM key start stop
在列表中只保留从指定起始索引到结束索引之间的元素
6) LPOP/RPOP
LPOP命令用于移除并返回队列中的第一个元素,当键不存在时返回nil。当列表中只有一个元素时,执行LPOP命令后键将被删除。
RPOP命令与LPOP相似,用于移除并返回列表中的最后一个元素。
7) RPUSH
将一个或多个值插入到列表的右侧(即队尾)。新添加的元素将成为列表的最后一个元素。
8) LPUSHX/RPUSHX
LPUSHX/RPUSHX命令与LPUSH/RPUSH类似,区别为它们仅在键已存在且为列表的情况下才执行插入的操作。当键不存在时,将无操作被执行;当键存在但不为列表类型时将返回错误。
9) BLPOP/BRPOP key [key ... ] timeout
BLPOP:从一个或多个列表的左侧(队首)弹出一个元素。
BRPOP:从一个或多个列表的右侧(队尾)弹出一个元素。
如果列表为空,BLPOP 将阻塞客户端直到有新的元素可以弹出,或达到指定的超时时间(timeout)。
说明:timeout: 等待的超时时间(以秒为单位),0 表示无限期等待。
10) BRPOPLPUSH LIST1 ANOTHER_LIST TIMEOUT
说明: BRPOPLPUSH: 从列表中取出最后一个元素,并插入到另外一个列表的头部; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。这个过程具有原子性。
3、Hash(字典)
相关命令:
HSET
HMSET
HLEN
HKEYS
HGET
HINCRBY
HGETALL
HSCAN
4、Set(集合)
Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
相关命令:
SADD
SMEMBERS
SCARD
SRANDMEMBER KEY [count]
SINTER
SISMEMBER
SREM
说明:删除集合中多个元素
1)单机:在 Redis 2.4 版本以前, SREM 只接受单个成员值,版本 >=2.4之后,可以用srem key 元素1 元素2(说明:多个元素之间用空格隔开)
2) 集群: Redis集群上删除多个key,需要登录到每个key对应的节点上去删除,因此不能使用单机命令的方式。不过可以通过脚本的方式来实现批量删除。
参考如下:
#!/bin/bash redis_comm=/usr/local/redis/bin/redis-cli redis_ser01=172.18.18.101 redis_ser02=172.18.18.102 redis_ser03=172.18.18.103 $redis_comm -c -h $redis_ser01 keys $1 | xargs -i ./redis-cli -h $redis_ser01 del {} $redis_comm -c -h $redis_ser02 keys $1 | xargs -i ./redis-cli -h $redis_ser02 del {} $redis_comm -c -h $redis_ser03 keys $1 | xargs -i ./redis-cli -h $redis_ser03 del {}
5、Sorted Set(有序集合)
Redis有序集合类似Redis集合,不同的是增加了一个功能,即集合是有序的。一个有序集合的每个成员带有分数,用于进行排序。有序集合的成员是唯一的,但分数(score)却可以重复。
相关命令:
ZADD key score1 member1 [score2 member2] 注意:score 值可以是整数值或双精度浮点数
ZCARD
ZCOUNT key min max
ZINCRBY
ZRANGE
ZRANGEBYSCORE
ZREVRANGE
ZRANK
关于zrank的使用,需要注意:
1) 返回有序集中指定成员的排名(索引下表)。
2) 其中有序集成员按分数值递增(从小到大)顺序排列
3) 如果需要依据分值从高到低来排列,需要使用zrevrank
二、高级数据结构(3种)
Redis中3种高级数据结构分别是HyperLogLog、bitmap、GEO
1、HyperLogLog
Redis 在 2.8.9 版本添加了 HyperLogLog 结构,它是用来做基数统计的算法。
相关命令:
PFADD key element [element...]
PFCOUNT
PFMERGE
说明:
1)HyperLogLog的计数统计是有一定的误差的,误差最大在1%以下
2)Redis中每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
3)因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素
4)什么是基数?
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
5)pfadd命令并不会一次性分配12k内存,而是随着基数的增加而逐渐增加内存分配;而pfmerge操作则会将sourcekey合并后存储在12k大小的key中,这由hyperloglog合并操作的原理(两个hyperloglog合并时需要单独比较每个桶的值)可以很容易理解。
6)Redis 对 HyperLogLog 的存储进行了优化,在计数比较小时,它的存储空间采用稀疏矩阵存储,空间占用很小,仅仅在计数慢慢变大,稀疏矩阵占用空间渐渐超过了阈值时才会一次性转变成稠密矩阵,才会占用 12k 的空间。
应用场景:
a. 统计注册 IP 数
b. 统计每日访问 IP 数
c. 统计页面实时 UV 数
d. 统计在线用户数
e. 统计用户每天搜索不同词条的个数
说明:
a. 基数不大,数据量不大就用不上,会有点大材小用浪费空间
b. 有局限性,就是只能统计基数数量,而没办法去知道具体的内容是什么
c. 和bitmap相比,属于两种特定统计情况,简单来说,HyperLogLog 去重比 bitmap 方便很多
一般可以将bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃,hyperloglog计数
2、bitmap 位图(大数据处理)
bitmap是什么?
通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。
说明:
1) BIT位图不是实际的数据类型,而是在String类型上定义的一组面向位的操作。而String类型的key是二进制格式的,所以任意二进制格式的数据都可以作为key。Redis中是以8位二进制的方式存储。
2) Redis从2.2.0版本开始新增了setbit,getbit,bitcount等几个bitmap相关命令。虽然是新命令,但是并没有新增新的数据类型,因为setbit等命令只不过是在set上的扩展。
常用命令:
1)setbit
SETBIT key offset value:给第offset位设置成value。当 key 不存在时,自动生成一个新的字符串值。
注意:每次SETBIT完毕之后,有一个(integer) 0或者(integer)1的返回值,这个是在你进行SETBIT 之前,该offset位的比特值。
2)getbit
GETBIT命令: getbit key offset,获取指定offset下标的二进制,返回0或者1。当offset超过下标时,一直返回0。
注意:offset是从左往右计数的,也就是从高位往低位。当超过位数时,结果是0。
3)bitcount
统计字符串被设置为1的bit数。
BITCOUNT key [start end]
不指定start和end的情况下,bitcount会对整个该key对应的value字符串进行行计数。
注意:
1)统计指定key位置为1的数量(区间统计不建议使用,bitcount用的是byte来计算位数,其他setbit和getbit用的是bit)
2)bitCount虽然提供了start和end参数,但是这个说的是字节的位置,而不是对应"位"的位置。而redis中一个汉字占3个字节,一个英文字符占1个字节
3)如果要指定区间,用bitcount并不是太合适,因为它的start或者end指的是第几个字节,不太好确定。不过,我们可以通过get把value取出来自己解析,一般情况下,这个value不会太大。
比如下面这段代码:
1 <?php 2 // 字符串(中文)转化为二进制 3 function StrToBin($str){ 4 $arr = preg_split('/(?<!^)(?!$)/u', $str); 5 foreach($arr as &$v){ 6 $temp = unpack('H*', $v); 7 $v = base_convert($temp[1], 16, 2); 8 while($v<8)$v='0'.$v; 9 unset($temp); 10 } 11 return join('',$arr); 12 } 13 14 15 $bin=StrToBin('中文'); 16 echo $bin; 17 echo PHP_EOL; 18 echo substr_count($bin,'1'); 19 20 // 输出结果 21 // 111001001011100010101101111001101001011010000111 22 // 26
使用场景:用户签到
4)bitop
使用时间作为cacheKey,然后用户ID为offset,如果当日活跃过就设置为1
那么该如何计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个redis的命令:
BITOP operation destkey key [key ...]
说明:
1)对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
2)BITOP 命令支持 AND(按位与)、 OR (按位或)、 NOT(按位取反)、 XOR(按位异或) 这四种操作中的任意一种参数
3)执行 BITOP 操作时,参与运算的 key 应具有相同的位数,如果长度不同,短的二进制会在左侧用零填充
4)操作的结果,返回的是最长的那个字符串的长度
使用场景:统计活跃用户
3、GEO
自Redis 3.2开始,Redis基于geohash和有序集合提供了地理位置相关功能。
Redis Geo命令实现
Redis将地理位置的52位GeoHash值作为有序集合的score,将地理位置存放在有序集合中进行保存。
后续按位置搜索时,依据GeoHash的特性搜索当前方块与环绕当前方块的8个方块来搜索目标位置集合。Redis中接受的有效的精度范围为-180到180度,有效维度范围为-85.05112878到 85.05112878度(靠近南北极的一小块地方是无法生成索引的)。
实现方式:
Redis内部使用有序集合来保存key,每一个member的score大小为一个52位的Geohash值(double类型精度为52位)。
实际上Redis内部实现的时候就是将GEOADD命令转换成ZADD命令来实现的。(这也解释了为什么没有专门的georem命令,地理位置信息是通过使用ZREM命令来删除成员。)
常用命令:
1) GEOADD key longitude latitude member [longitude latitude member ...]
2) GEOPOS
3) GEODIST key member1 member2 [m|km|ft|mi] 用于返回两个给定位置之间的距离,默认单位:m(米)
4)GEORADIUS 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
说明:
WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。
WITHCOORD: 将位置元素的经度和维度也一并返回。
WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
COUNT 限定返回的记录数。
ASC: 查找结果根据距离从近到远排序。
DESC: 查找结果根据从远到近排序。
5)GEORADIUSBYMEMBER :和GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是使用经度和纬度来决定中心点。
6)GEOHASH
用于获取一个或多个位置元素的 geohash 值
实现方式:
Redis在内部生成有序集合成员score时的geohash值与标准的算法略有差异(Redis内部使用-85,85作为维度范围,标准使用-90,90)。
这个命令返回的是标准值,与 https://en.wikipedia.org/wiki/Geohash 中标准算法和http://geohash.co/网站的计算结果一致。
三、Stream
这是Redis5.0引入的全新数据结构,用一句话概括Stream就是Redis实现的内存版kafka。
Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。
消息队列相关命令:
XADD key ID field value [field value ...] 说明: ID 指的是消息 id,我们使用 * 表示由 redis 生成,可以自定义,但是要自己保证递增性。
XTRIM
XDEL
XLEN
XRANGE
XREVRANGE
XREAD
消费者组相关命令:
XGROUP CREATE
XREADGROUP GROUP
XACK
XGROUP SETID
XGROUP DELCONSUMER
XGROUP DESTROY
XPENDING
XCLAIM
XINFO
XINFO GROUPS
XINFO STREAM
参考链接:
https://developer.aliyun.com/article/62844
https://segmentfault.com/a/1190000008188655