Redis 中的位图(也称位数组或位向量)是由多个二进制位组成的数组结构。
Redis 中的 bitmap 不是一种新的数据类型,实际上它的底层仍然是字符串,因为字符串本质上是二进制大对象(BLOB, Binary Large Object),所有字符串也可以视作位图。
Redis 中的 bitmap 因为直接用bit位来保存数据,每一位所在的位置为偏移量(offset),用户可以在bitmap上可执行AND,OR,XOR以及其它位操作。所以某些情况下可以节省内存空间。
位图(bitmap)的应用场景可以实现用户上线次数统计、活跃用户统计,查询最近n天的打卡次数等
对于位数组我们可以做以下操作:
- 设置其中某一个位的为1或者是0。
- 统计当前位数组中有多少个1。
- 获取指定下标的位值。
- 获取第一个1或者是0的位下标。
- 获取某一段区域内的位数组所对应的数值。
基本命令
1、SETBIT 命令
命令描述SETBIT key offset value设置或者清空key的value(字符串)在offset处的bit值。返回值:返回在offset处原来的bit值。设置一个字符串,第6个offset从0设置为1,第7个offset从1设置为0,则a就变成b.
a的ASCII码是97。转换为二进制是:01100001。
b的ASCII码是98,转换为二进制是:01100010。
c的ASCII码是99,转换为二进制是:01100011。
127.0.0.1:6379[1]> set kbitmap a
OK
127.0.0.1:6379[1]> setbit kbitmap 6 1
(integer) 0
127.0.0.1:6379[1]> get kbitmap
"c"
127.0.0.1:6379[1]> setbit kbitmap 7 0
(integer) 1
127.0.0.1:6379[1]> get kbitmap
"b"
2、BITCOUNT 命令
命令描述BITCOUNT key [start end]统计字符串在指定起始位置被设置为1的bit数。如果key不存在,则返回0.127.0.0.1:6379[1]> bitcount kbitmap
(integer) 3
3、GETBIT 命令
命令描述GETBIT key offset返回key对应的string在offset处的bit值127.0.0.1:6379[1]> getbit kbitmap 6
(integer) 1
4、BITOP 命令
命令描述BITOP operation destkey key [key ...]对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作。
返回值:保存到 destkey 的字符串的长度,和输入 key 中最长的字符串长度相等。
127.0.0.1:6379[1]> set kbit2 abcd1234
OK
127.0.0.1:6379[1]> bitop and kbitOR kbitmap kbit2
(integer) 8
二、设置键的生存时间或过期时间
在经过指定的生存时间(Time To Live , TTL)之后,redis可以自动删除对应的key-value,默认情况下键是没有生存时间的,也就是永不过期,除非清空内存。
通过下面命令设置和查看生存时间,通常用 EXPIRE 命令单位到秒就可以了
命令描述EXPIRE key seconds设置key的生存时间,超过时间后,将会自动删除该key。以秒为单位
- 设置成功,返回 1
- key 不存在或设置失败,返回 0
设置key的生存时间,以毫秒为单位
- 设置成功,返回 1
- key 不存在或设置失败,返回 0
设置一个UNIX时间戳的生存时间,以秒为单位
- 如果生存时间设置成功,返回 1 。
- 当 key 不存在或没办法设置生存时间时,返回 0 。 (查看: EXPIRE命令获取更多信息).
设置一个UNIX时间戳的生存时间,以毫秒为单位
- 如果生存时间设置成功,返回 1 。
- 当 key 不存在或没办法设置生存时间时,返回 0 。 (查看: EXPIRE命令获取更多信息).
获取key的有效时间(单位:妙)
- 如果key不存在或者已过期,返回
-2
- 如果key存在并且没有设置过期时间(永久有效),返回
-1
。
获取key的有效时间(单位:毫妙)
- 如果key不存在或者已过期,返回
-2
- 如果key存在并且没有设置过期时间(永久有效),返回
-1
。
移除给定key的生存时间
- 当生存时间移除成功时,返回 1 .
- 如果 key 不存在或 key 没有设置生存时间,返回 0 .
实例:
127.0.0.1:6379> set kttl asd123
OK
127.0.0.1:6379> ttl kttl
(integer) -1
127.0.0.1:6379> expire kttl 15
(integer) 1
127.0.0.1:6379> ttl kttl
(integer) 8
127.0.0.1:6379> get kttl
(nil)
127.0.0.1:6379> set kttl2 adsf
OK
127.0.0.1:6379> expire kttl2 50
(integer) 1
127.0.0.1:6379> ttl kttl2
(integer) 35
127.0.0.1:6379> persist kttl2
(integer) 1
127.0.0.1:6379> ttl kttl2
(integer) -1
三、使用 SORT命令
Redis 中的列表list和集合set都是无序保存的,有序set集合也只是对权重值进行排序,不是对元素本身。如果需要对元素本身进行排序,需要使用 sort命令。redis支持对list,set,sorted set元素的排序。
命令SORT key [BY pattern] [LIMIT offset count] [GET pattern] [ASC|DESC] [ALPHA] destination 描述返回或存储key的list、 set 或sorted set 中的元素。默认是按照数值类型排序的,并且按照两个元素的双精度浮点数类型值进行比较。
返回值:返回排序后的元素列表。
1、 一般 SORT 用法:sort key [ASC|DESC] (list)
这是最简单的情况,没有任何选项对集合自身元素排序并返回排序结果,默认以value升序(asc)。逆序使用desc选项。
127.0.0.1:6379> lpush ksort1 15 12 2 16 3
(integer) 5
127.0.0.1:6379> lrange ksort1 0 -1
1) "3"
2) "16"
3) "2"
4) "12"
5) "15"
127.0.0.1:6379> sort ksort1 desc
1) "16"
2) "15"
3) "12"
4) "3"
5) "2"
127.0.0.1:6379> lrange ksort1 0 -1
1) "3"
2) "16"
3) "2"
4) "12"
5) "15"
2、使用 ALPHA 修饰符对字符串进行排序: [ASC|DESC] [ALPHA] (list)
sort 默认以数值排序,字母使用默认的sort排序,会报错,想按字母顺序排必须加上 alpha选项。
127.0.0.1:6379> rpush ksort2 abcd abef jlkl sdls bbb
(integer) 5
127.0.0.1:6379> lrange ksort2 0 -1
1) "abcd"
2) "abef"
3) "jlkl"
4) "sdls"
5) "bbb"
127.0.0.1:6379> sort ksort2
(error) ERR One or more scores can't be converted into double
127.0.0.1:6379> sort ksort2 alpha desc
1) "sdls"
2) "jlkl"
3) "bbb"
4) "abef"
5) "abcd"
3、使用 LIMIT 修饰符限制返回结果:[[LIMIT offset count]
LIMIT 选项可以限定返回结果的数量。 修饰符接受 offset 和 count 两个参数,offset 下标是从 0 开始。
127.0.0.1:6379> sort ksort2 alpha limit 1 3
1) "abef"
2) "bbb"
3) "jlkl"
4、使用外部 key 进行排序:[BY pattern]
除了可以按集合元素自身值(数字,字母)排序外,还可以将集合元素内容按照给定pattern组合成新的key,并按照新key中对应的内容进行排序。
假设现在有用户数据如下:
127.0.0.1:6379> lpush uid 1 2 3 4
(integer) 4
127.0.0.1:6379> mset user_name_1 admin user_name_2 jack user_name_3 peter user_name_4 mary
OK
127.0.0.1:6379> mset user_level_1 9999 user_level_2 10 user_level_3 25 user_level_4 70
OK
BY 选项
默认情况下, SORT uid 直接按 uid 中的值排序:
127.0.0.1:6379> sort uid by user_levle_*
1) "1"
2) "2"
3) "3"
通过使用 BY 选项,可以让 uid 按其他键的元素来排序。 比如: 按照 user_level_{uid} 的大小来降序排序:
127.0.0.1:6379> sort uid by user_level_* desc
1) "1"
2) "4"
3) "3"
4) "2"
GET 选项
上面的例子都是返回的集合中的数值元素,使用 GET 选项, 可以根据排序的结果来取出相应的键值。
get选项可以有多个,# 特殊符号 获取 GET
元素本身
127.0.0.1:6379> sort uid by user_level_* desc get # get user_name_* get user_level_*
1) "1"
2) "admin"
3) "9999"
4) "4"
5) "mary"
6) "70"
7) "3"
8) "peter"
9) "25"
10) "2"
11) "jack"
12) "10"
获取外部键,但不进行排序
通过将一个不存在的键作为参数传给 BY 选项, 可以让 SORT 跳过排序操作, 直接返回结果。
通常将这种用法和 GET 选项配合, 就可以在不排序的情况下, 获取多个外部键, 相当于执行一个整合的获取操作
127.0.0.1:6379> sort uid by user_notkey desc get # get user_name_* get user_level_*
1) "1"
2) "admin"
3) "9999"
4) "2"
5) "jack"
6) "10"
7) "3"
8) "peter"
9) "25"
10) "4"
11) "mary"
12) "70"
将哈希表作为 GET 或 BY 的参数
除了可以将字符串键之外, 哈希表也可以作为 GET 或 BY 选项的参数来使用。字符串 -> 用于区分key名称和哈希属性的名称。
127.0.0.1:6379> HMSET user_info_1 name admin level 9999
OK
127.0.0.1:6379> HMSET user_info_2 name jack level 10
OK
127.0.0.1:6379> HMSET user_info_3 name peter level 25
OK
127.0.0.1:6379> HMSET user_info_4 name mary level 70
OK
127.0.0.1:6379> sort uid by user_info_*->level desc get # get user_info_*->level get user_info_*->name
1) "1"
2) "9999"
3) "admin"
4) "4"
5) "70"
6) "mary"
7) "3"
8) "25"
9) "peter"
10) "2"
11) "10"
12) "jack"
5、保存排序结果: STORE resultkey
默认情况下, SORT 操作只是简单地返回排序结果,并不进行任何保存操作。
SORT ... STORE的一种有趣应用模式,是联合 EXPIRE 超时命令返回key,以便在应用中可以缓存SORT操作的返回结果。 其他客户端将会使用已缓存的列表,代替每个请求的 SORT 调用。当key即将过期时,一个更新版本的缓存将会通过 SORT ... STORE 再次创建。如果被指定的 key 已存在,那么原有的值将被排序结果覆盖。
127.0.0.1:6379> sort uid by user_info_*->level desc get # get user_info_*->level get user_info_*->name store ksortsave
(integer) 12
127.0.0.1:6379> lrange ksortsave 0 -1
1) "1"
2) "9999"
3) "admin"
4) "4"
5) "70"
6) "mary"
7) "3"
8) "25"
9) "peter"
10) "2"
11) "10"
12) "jack"
四、在Redis中使用管道
Redis是一种基于客户端--服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
1.客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
2.服务器接收到客户端的请求命令,放入到执行队列等待执行(Redis是单线程的执行模式)
3.执行命令,获取到查询结果
4.服务器将结果返回给客户端
上面的4个步骤消耗的所有时间,称为往返时间(RTT),
2、3两步的时间(a时间)消耗取决于Redis服务器本身,
1、4两步的时间(b时间)消耗取决于客户端和服务端直接的网络延迟,
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。管道技术最显著的优势是提高了 redis 服务的性能。如果我们想要节约总的往返时间,可以把多条命令集中传给Redis,Redis把命令的执行结果最终一次性地在集中返回给客户端,这个就是Redis管道的基本思想!
实例:
[root@centos7 redis]# cat ./pipeline.txt
set mykey1 myvalue1
set mykey2 myvalue2
set mykey3 myvalue3
set mykey4 myvalue4
[root@centos7 redis]# cat ./pipeline.txt | /usr/local/redis/bin/redis-cli --pipe
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 4
[root@centos7 redis]# ./bin/redis-cli
127.0.0.1:6379> get mykey1
"myvalue1"
五、Redis的事务(了解)
在关系型数据库中,把多条sql命令整合成一个事务,要么都执行成功,要么都执行失败。关系型数据库中事务的目的是保证数据的完整性,安全。Redis中的事务完全不是一回事,Redis的事务只是为了让Redis命令批量化执行。
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
实例:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 lisi
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k2 houzi
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "houzi"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> EXEC
(error) ERR EXEC without MULTI
六、Redis的pub/sub
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
在这个模式中,发送者(发送信息的客户端)不是将信息直接发送给特定的接收者(接收信息的客户端), 而是将信息发送给频道(channel), 然后由频道将信息转发给所有对这个频道感兴趣的订阅者。
一旦客户端进入订阅状态,客户端就只可接受订阅相关的命令SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE和PUNSUBSCRIBE除了这些命令,其他命令一律失效。
命令描述SUBSCRIBE channel [channel ...]监听频道发布的信息UNSUBSCRIBE [channel [channel ...]]停止频道监听,若没有指定频道,则退订所有频道。PSUBSCRIBE pattern [pattern ...]订阅给定的模式(patterns)。支持的模式(patterns)有:
h?llo
subscribes tohello
,hallo
andhxllo
h*llo
subscribes tohllo
andheeeello
h[ae]llo
subscribes tohello
andhallo,
but nothillo
如果想输入普通的字符,可以在前面添加\
简单实例:两个客户端订阅者,一个发布者,这里没有使用指定模式。
订阅者1和2
127.0.0.1:6379> SUBSCRIBE channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "message"
2) "channel1"
3) "publish channel111 message content"
127.0.0.1:6379> SUBSCRIBE channel1 channel2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
1) "subscribe"
2) "channel2"
3) (integer) 2
1) "message"
2) "channel1"
3) "publish channel111 message content"
1) "message"
2) "channel2"
3) "publish channe2222 message content"
发布者
127.0.0.1:6379> publish channel1 "publish channel111 message content"
(integer) 2
127.0.0.1:6379> publish channel2 "publish channe2222 message content"
(integer) 1
七、在Redis中运行 Lua脚本(先简单了解)
首先在 Redis中安装Lua: 官网下载安装包:http://www.lua.org/
注意:要确保安装 Lua 之前系统已安装 readline 和 readline-devel。
安装:make linux test
测试,命令行中键入 lua -v
[root@centos7 ~]# yum install -y readline readline-devel
...
[root@centos7 ~]# tar -zxvf lua-5.3.5.tar.gz
[root@centos7 ~]# cd lua-5.3.5/
[root@centos7 lua-5.3.5]# make linux test
[root@centos7 lua-5.3.5]# lua -v
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
命令描述EVAL script numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。
EVAL的第一个参数是一段 Lua 5.1 脚本程序。 这段Lua脚本不需要(也不应该)定义函数。它运行在 Redis 服务器中。
EVAL的第二个参数是参数的个数,后面的参数(从第三个参数),表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
在命令的最后,那些不是键名参数的附加参数 arg [arg …] ,可以在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
实例:
127.0.0.1:6379> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"
参考文章:Redis sort 排序命令详解
—— Stay Hungry. Stay Foolish. 求知若饥,虚心若愚。