在签到统计场景中,可以使用 bitmap 数据类型高效的存储签到数据,但 getbit 命令只能获取某一位值,就无法最优的满足部分业务场景了。

比如我们按年去存储一个用户的签到情况,365 天,只需要 365 / 8 ≈ 46 Byte,1KW 用户量一年也只需要 44 MB 就足够了。


setbit sign:uid:year 0 1 #第1天
setbit sign:uid:year 1 1 #第2天
...
setbit sign:uid:year 364 1 #第365天

但如果我想获取某个用户一年的签到统计,使用 bitget 命令的话...要循环读取 365 次,这是没办法接受的。

如果能一次读取到以字符串

"1000100010100100...001"

的形式表示的位状态数据,就很好做后续的处理了。

bitmap 其实也是一种特殊的字符串数据,使用 get 命令是可以读取出来的,但是以 16 进制的流数据返回的,这里就涉及到网络编程中数据传输的打包/解包的知识,redis 使用 get 命令读取 bitmap 数据时,将二进制数据打包成了 16 进制返回给我们,所以我们要对此数据包以 16 进制解包,然后转为二进制字符串。给出转换方法:

<?php

// 第1天的签到
$redis->setBit('sign:uid:year', 0, 1);
// 第234天的签到
$redis->setBit('sign:uid:year', 233, 1);
// 第365天的签到
$redis->setBit('sign:uid:year', 364, 1); // 使用 get 命令一次性读取用户的 bitmap 签到数据
$bitmap_str = $redis->get("sign:uid:year"); // 对数据流使用网络字节序(大端)解包拿到16进制数据的字符串形式
$hex_str = unpack("H*", $bitmap_str)[1]; // hex str 的长度
$hex_str_len = strlen($hex_str);
// 为了防止 hex to dec 时发生溢出
// 我们需要切分 hex str,使得每一份 hex str to dec 时都能落在 int 类型的范围内
// 因为 2 位 16 进制表示一个字节,所以用系统 int 类型的字节长度去分组是绝对安全的
$chunk_size = PHP_INT_SIZE; // 对 hex str 做分组对齐,否则 str 的最后几位可能会被当作低位数据处理
// 比如 fffff 以 4 位拆分 'ffff', 'f' 后 最后一组 'f' 就被低位数据处理了
// 对齐后 fffff000 分组 'ffff', 'f000' 就能保证 'f' 的数据位了
$hex_str = str_pad($hex_str, $hex_str_len + ($chunk_size - ($hex_str_len % $chunk_size)), 0, STR_PAD_RIGHT); // 防止 hexdec 时溢出 使用 PHP_INT_SIZE 个 16 进制字符一组做拆分
// 因 16 进制 2 位标识一个字节 所以 PHP_INT_SIZE 是绝对不会溢出的
$hex_str_arr = str_split($hex_str, $chunk_size); // 位数据的二进制字符串
$bitmap_bin_str = '';
array_walk($hex_str_arr, function($hex_str_chunk) use (&$bitmap_bin_str, $chunk_size) {
$bitmap_bin_str .= str_pad(decbin(hexdec($hex_str_chunk)), $chunk_size * 4, 0, STR_PAD_LEFT);
}); // 一次读取redis即可拿到 bitmap O(n)次操作的数据
echo $bitmap_bin_str{0} . PHP_EOL; //第1天
echo $bitmap_bin_str{233} . PHP_EOL;//第234天
echo $bitmap_bin_str{364} . PHP_EOL;//第365天

注释较多,业务代码不多,多多理解~

来源:https://segmentfault.com/a/1190000017470443

redis 使用 get 命令读取 bitmap 类型的数据的更多相关文章

  1. 问题:从键盘读取特定类型的数据(使用Scanner读取int类型)

    import java.util.Scanner; public class ScannerIntTest{ public static void main(String [] args){ int ...

  2. Spring+SpringMVC+MyBatis+easyUI整合进阶篇(九)Linux下安装redis及redis的常用命令和操作

    redis简介 Redis是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis与其他key-value缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存 ...

  3. 【python】-- Redis简介、命令、示例

    Redis简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久化 ...

  4. Redis数据类型及命令

    Redis简介 Redis是一个完全开源免费的, 是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存 ...

  5. redis 的简单命令

    以下实例讲解了如何启动 redis 客户端: 启动 redis 客户端,打开终端并输入命令 redis-cli.该命令会连接本地的 redis 服务. $redis-cli redis > re ...

  6. Redis的KEYS命令引起宕机事件

    摘要: 使用 Redis 的开发者必看,吸取教训啊! 原文:Redis 的 KEYS 命令引起 RDS 数据库雪崩,RDS 发生两次宕机,造成几百万的资金损失 作者:陈浩翔 Fundebug经授权转载 ...

  7. 总结Linux 下Redis 操作常用命令(转)

    Redis的配置 Linux下安装 ]# wget http://download.redis.io/releases/redis-2.8.17.tar.gz ]# tar xzf redis-2.8 ...

  8. redis 之相关命令

    为什么缓存数据库更要首选redis?如何使用redis? 一.使用缓存数据库为什么首选用redis? 我们都知道,把一些热数据存到缓存中可以极大的提高速度,那么问题来了,是用Redis好还是Memca ...

  9. Redis的常用命令与Java整合及高级应用篇

    一,redis是什么? ​ 首先数据库分为关系型数据库和非关系型数据库,关系型数据库是采用关系模型来组织数据的数据库,简单来说就是二维表格模型,同时保证事务的一致性. ​ 相反非关系型数据库采用key ...

随机推荐

  1. linux grep 查找文件内容

    自试: wang@wang:~$ grep -i "*args*" ~/IGV01-SW/src/bzrobot_diagnostics/bzrobot_lightbelt_man ...

  2. jQuery使用on()绑定动态生成元素的事件无效

    jquery on()方法是jquery1.7+后才使用的 由于需求:动态添加了以下代码 <tr class="pj" data-val="no"> ...

  3. ArcGIS 安装中,SQL的使用出现错误的解决

    1. SQL Server Configuration Manager 中 SQL Server Services出现 “远程调用失败..” 的问题 解决方法是卸载

  4. ES6新语法学习

    参考: 1.http://es6.ruanyifeng.com/#docs/let#let-命令 2.https://reactjs.org/tutorial/tutorial.html 3.http ...

  5. 2.【nuxt起步】-初始化创建nuxt项目

    1. 脚手架初始化: vue init nuxt-community/starter-template NuxtMyms 2.输入项目相关信息 3.切换到项目目录下 安装依赖 Cd nuxtmyms ...

  6. vue2.0 + vux (一)Header 组件

    1.main.js import Vue from 'vue' import FastClick from 'fastclick' import VueRouter from 'vue-router' ...

  7. Populating Next Right Pointers in Each Node I, II——生成next树

    1. Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode * ...

  8. POJ1830开关问题——gauss消元

    题目链接 分析: 第一个高斯消元题目,操作是异或.奇偶能够用0.1来表示,也就表示成bool类型的方程,操作是异或.和加法没有差别 题目中有两个未知量:每一个开关被按下的次数(0.1).每一个开关的转 ...

  9. 按照HashMap中value值进行排序

    import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; ...

  10. jquery动态加载脚本

    如果你使用的是jQuery,它里面有一个内置的方法可以用来加载单个JS文件.当你需要延迟加载一些js插件或其它类型的文件时,可以使用这个方法. 一.jQuery getScript()方法加载java ...