如何优雅地实现Redis命令setbits与getbits

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:

在之前的文章《如何优雅地使用Redis之位图操作》和《再谈如何优雅地使用Redis之位图操作》中,笔者介绍了关于Redis位图操作的高级应用,其中就讲到了如何优雅地实现getbits。Redis官方提供了getbit命令,其可以获取某个key对应比特位的比特值,而getbits顾名思义就是支持一次性获取多个比特位的比特值的命令,遗憾的是,Redis官方并没有提供getbits命令。在上述2篇文章中,笔者是通过解析字节数组的方式来实现getbits命令的,虽然可以实现,但是却有2个不足之处:1、这种方式实现的getbits命令不是原子性的,因为这种方式实现的getbits命令其实是分2步进行的,先读取字节数组,再解析字节数组,在这2个步骤之间,Redis是可以执行其他命令的,所以可能会出现数据不一致的现象。2、当存储的位图数据空间占用比较大时,一次性读取整个字节数组,会造成Redis服务器阻塞,严重的还会造成客户端内存溢出,虽然可以通过分多次去读取字节数组来避免这个问题,但是这样一来就增加了网络开销,不是特别优雅。

今天给大家介绍一种通过Redis原生命令bitfield实现setbits和getbits的方法。

bitfield命令

首先介绍一下bitfield命令的用途。官方对bitfield命令的介绍是:通过bitfield命令可以一次性操作多个比特位域,它会执行一系列操作并返回一个响应数组,这个数组中的元素对应参数列表中的相应操作的执行结果。说白了就是通过bitfield命令我们可以一次性对多个比特位域进行操作。需要注意的是,这里提到的是比特位域,不是比特位,所谓比特位域,指的是连续的多个比特位,也就是说,bitfield不仅仅可以对多个单个的比特位进行操作,还支持对多个比特位域进行操作,所以功能是十分强大的。

这里的操作指的是以下几种:

  • GET <type> <offset>

  • SET <type> <offset> <value>

  • INCRBY <type> <offset> <increment>

其中,get命令的作用是读取指定位域的值,set命令的作用是设置指定位域的值并返回旧的值,increby命令的作用是增加或减少指定位域的值并返回新的值。

举个例子:

BITFIELD mykey SET i5 100 10 GET u4 2

这个命令包含了2个子操作,分别是SET i5 100 10和GET u4 2。SET i5 100 10的作用是从第100位开始,将接下来的5位用有符号数10代替,其中i表示的是有符号整数。GET u4 2的作用是从第2位开始,将接下来的4位当成无符号整数并取出,其中u表示的是无符号整数。

上述只是bitfield的一部分应用,实际上bitfield还有很多更高级的用法,有兴趣的可以去Redis官网查阅,这里就不再详细介绍了。

实现setbits

接下来说下如果使用bitfield实现setbits原子命令。如果我们要将某一位(比如说第3位)设置为1,可以使用这样的子操作:set u1 3 1,同样地,如果我们需要将多个位设置成1,只要将多个set子操作拼接在一起可以了。举个例子:如果我们要将第1位、第3位、第6位设置成1,则可以使用如下命令:

bitfield k1 set u1 1 1 set u1 3 1 set u1 6 1

其中k1指的是key。

我们可以写个程序验证下。验证的方法是先使用上述命令对相应的比特位进行设值,然后使用redis原生的getbit命令遍历每个比特位,看看是不是对应的位都可以被设置成1。

程序如下:

 @Test

 public void testBitField1(){

 Jedis jedis=new Jedis(HOST,PORT);

 String key="test_"+System.currentTimeMillis();

 jedis.bitfield(key,"set","u1","1","1","set","u1","3","1","set","u1","6","1");

for(int i=0;i<8;i++){
 System.out.println(i+"---"+jedis.getbit(key,i));
 }
 }

我们看下结果输出:

de7a738c7b8805a96cfb15b1fbf068f36eecf564

可以看到,第1位、第3位、第6位都被设置成了true,而其他位还是原始值flase。所以这种方式确实可以实现一次性对多个比特位进行设值。

实现getbits

接下来我们使用bitfield命令来实现getbits原子命令。如果想读取某个位(比如说第3位)的比特值,可以使用这样的子操作:get u1 3。如果需要同时读取多个比特位,则只需要将多个get子操作拼接在一起就可以了。还是举个例子,如果我们想同时读取第2位、第4位、第7位的比特值,可以使用如下命令:

bitfield k1 get u1 2 get u1 4 get u1 7

其中k1指的是key。

我们还是写个程序验证下。验证的方式是使用Redis原生的setbit命令分别将第2位、第4位、第7位比特位设置成1,然后使用上述bitfield命令分别读取每个比特位的值,看看是否只有对应的比特位被设置成了1。

程序如下:

@Test

 public void testBitField2(){

 Jedis jedis=new Jedis(HOST,PORT);

 String key="test_"+System.currentTimeMillis();

jedis.setbit(key,2,true);
 jedis.setbit(key,7,true);

jedis.setbit(key,4,true);
 "get", "u1", "1",

List<Long> result = jedis.bitfield(key,
 "get", "u1", "2",
 "get", "u1", "6",

"get", "u1", "3",
 "get", "u1", "4",
 "get", "u1", "5",
 "get", "u1", "7",
 }

"get", "u1", "8");
 System.out.println(result);

结果输出如下:

e00d57cb2de1b4400b218f62e2526fd1b1b5872f

可以看到,第2位、第4位、第7位都被设置成了1,而其他位还是原始的值0。说明确实可以使用bitfield命令来实现getbits。

总结

使用bitfield实现getbits和setbits的好处有2个:1、原子性保证,由于所有操作都是在一个bitfield命令中完成的,所以可以保证操作的原子性。2、由于这种方式是在Redis服务端解析后再返回给客户端的,客户端并不需要一次性读取整个字节数组,所以不会造成客户端内存溢出。


原文发布时间为:2018-08-11

本文作者:黄泽杰

本文来自云栖社区合作伙伴“Java架构沉思录”,了解相关信息可以关注“Java架构沉思录”。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
9天前
|
NoSQL Redis 数据库
Redis的全局命令及相关误区
Redis的全局命令及相关误区
21 0
|
18天前
|
NoSQL Redis 数据库
Redis中的常用命令有哪些?
Redis常用命令包括:PING测试连接,ECHO回显,SELECT切换数据库,QUIT关闭连接;KEYS查找key,EXISTS检查存在,DEL删除key,EXPIRE设置过期时间,TTL查看剩余生存时间,TYPE检测数据类型;STRING操作如SET/GET/MSET/MGET,INCR/DECR增减计数;HASH命令如HSET/HGET/HMSET/HMGET/HGETALL管理字段;LIST操作如LPUSH/RPUSH/LPOP/RPOP/LRANGE;
9 0
|
23天前
|
NoSQL Redis
Redis集群(六):集群常用命令及说明
Redis集群(六):集群常用命令及说明
15 0
|
2月前
|
缓存 NoSQL 数据处理
Redis事务悄然而至:命令的背后故事
Redis事务悄然而至:命令的背后故事
26 0
|
3月前
|
存储 NoSQL Redis
redis中Hash命令的基础操作
redis中Hash命令的基础操作
39 1
|
3月前
|
存储 NoSQL 安全
Redis相关命令详解及其原理:Redis基本操作、数据结构以及应用
Redis相关命令详解及其原理:Redis基本操作、数据结构以及应用
90 0
|
3月前
|
存储 NoSQL 网络协议
redis相关命令详解及其原理
redis相关命令详解及其原理
20 0
|
3月前
|
SQL NoSQL Redis
|
1月前
|
缓存 NoSQL Java
【Redis】5、Redis 的分布式锁、Lua 脚本保证 Redis 命令的原子性
【Redis】5、Redis 的分布式锁、Lua 脚本保证 Redis 命令的原子性
57 0
|
3月前
|
SQL NoSQL 定位技术
Redis基本命令和常用数据类型
Redis基本命令和常用数据类型
136 0

热门文章

最新文章