返回顶部
首页 > 资讯 > 后端开发 > Python >聊聊Redis二进制数组Bitmap
  • 509
分享到

聊聊Redis二进制数组Bitmap

2024-04-02 19:04:59 509人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

好久没有更新了,之前公司在做 关注/粉丝 这块儿缓存的时候,我选择的就是 Bitmap ,那时是我第一次见识到这种数据数组形式,用到的有 SETBIT , GETBIT , BITC

好久没有更新了,之前公司在做 关注/粉丝 这块儿缓存的时候,我选择的就是 Bitmap ,那时是我第一次见识到这种数据数组形式,用到的有 SETBIT , GETBIT , BITCOUNT ,命令如何使用就不说了,今天来仔细看看这三个命令的实现和原理。

选用 bitmap 的考量:

位数组的实现

关注关系需求中 关注对象 和 被关注人 都是 0-几千万 的数据对象,存储这种对应关系时,采用bitmap 这种位数组,明显要比 uid 的 set 方式要节省存储空间,Redis 的 内存 是很宝贵的,这值得作为考量的地方。

位数组大致可表示为:0101010000100000....0100 这样的二进制串, 在 Redis 的 SDS字符串 一文中可以看到 Redis 中的字符串对象实现,SDS数据结构是二进制安全的,所以 Redis 可以使用字符串来表示 位数组 。 所以根据上面说的,位数组是以字符串的形式:buf[0]|buf[1]...这样一个一个字节存放的。

SETBIT 和 GETBIT

GETBIT 的实现:


  # 返回 位数组 bitarray 在 offset 偏移量上的二进制位(byte*8+bit)的值
  getbit <bitarray> <offset>
  # 字节
  byte = offset / 8  
  # 位
  bit = (offset mod 8) + 1
  # 可以看到 O(1)

SETBIT 的实现:


  # 将 位数组 bitarray 在offset 偏移量上的二进制位的值设置为 value
  setbit <bitarray> <offset> <value>
  # 计算保存二进制位需要多少 字节
  len = [offset / 8] + 1 
  # 鱿鱼 ? 二进制位数组使用的数据结构是 sds ,而 sds 记录长度的是len ,正常进行扩展,同空间预分配 ,扩展位为`00000`
  # 字节
  byte = offset / 8  
  # 位
  bit = (offset mod 8) + 1 
  # 记录 (byte*8+bit) 上 oldvalue ,再赋予新值,返回 oldvalue

Bitcount 的实现

BITCOUNT 统计给定位数组中,值为1 的数量,也就是统计汉明重量(见 LeetCode 191、338),其实是一个老问题,看看几种算法,和 redis 的做法。


#1. 粗暴遍历 O(n)

class Solution(object):
    def hammingWeight(self, n):
        rst = 0
        mask = 1
        for i in range(32):
            if n & mask :
                rst += 1
            mask = mask << 1
        return  rst
        
#2. 查表法 
# 查表法原理在于建立N个位数组,如果是8位,即减少查询次数/8 ,建表越大,则查询次数越少
# 空间换时间的典型操作,不禁让我想起了 计数排序O(n+k) 
# 内存考虑:这里可以算一下 8位的查表耗费的内存百字节,16位查表为百Kb,32位为十多个G,实际中,服务器只能接受16位
# CPU考虑:表长越大,miss 率越大。而 max 为16 也不能解决大数组的查表效率问题
# 这种算法 O(n/m) S(m) m<=16 

#3.variable-precision SWAR 算法 O(1)

#4.Redis 
# redis 未处理的二进制数 >= 128,使用variable-precision SWAR
# redis 未处理的二进制数 < 128,使用 查表法

Redis-高性能bitmap

实时指标

Redis bitmap可用于快速、简单的实现实时指标。

传统情况下,由批量job生成指标数据。但是redis的bitmap支持实时指标计算,而且具有极高的空间利用率。例如1.28亿用户,实时统计日UV,仅仅占用16MB内存空间,在mbp上耗时50ms。

bitset

可视为由0和1组成的数组。在bitset中的每个bit可为0或1,使用offset表示bit在数组的位置。支持多个bitset间进行位操作,如与或非等。

群体统计

bitset的群体统计表示bitset中数据为1的bit数量。使用bitset做群体统计是非常高效的。如具有十亿bit的bitset,其90%空间已设置数据,在mbp上进行群体统计仅耗时几十或几百ms。

redis bitmap

bitmap是二进制数据,可通过set key offset val指令设置具体offset位置bit的数据,且时间复杂度为O(1)。

日活用户

针对关注点(某个页面或某个操作),统计活跃用户数量。

key规则:关注点名称+日时间戳

val:创建bitmap,宽度为当前用户数量,每个用户的id作为offset,这个用户ID是记录ID,不可能是由特定规则生成的userID。

当用户访问关注点时,针对具体bitset,将用户IDoffset位置数据设置为1。之后对该bitset进行群体统计,即为关注点的日活用户量。

采取关注点名称+时间戳的方式,可以存储不同时间维度的活跃用户。

如每小时播放音乐的用户量,key定义为play_yyyyMMddhh;每天播放音乐的用户量,key定义为play_yyyyMMdd。

当需要统计较大时间范围的用户量时,可以先对多个bitset求并集,然后再群体统计,如统计一周、一个月的用户量。

性能对比

1.28亿用户,使用bitset记录日活,使用日活并集统计7日 15日日活。
PERIOD TIME (MS)
Daily 50.2
Weekly 392.0
Monthly 1624.8

特性分析

周维度访问关注点且绑定手机号的唯一用户,采用绑定手机号用户bitset 交集 周维度访问关注点的用户bitset

最近n天唯一用户量的滚动统计,对最近n天每天的日活用户bitset求并集

涉及的指令

群体统计使用bitcount key

交集并集使用bitop and/or dest key1 keyn

对用户IDoffset设置数据使用setbit key offset 1


java bitset.cardinality()/and(bitset)/or(bitset)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: 聊聊Redis二进制数组Bitmap

本文链接: https://lsjlt.com/news/130520.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 聊聊Redis二进制数组Bitmap
    好久没有更新了,之前公司在做 关注/粉丝 这块儿缓存的时候,我选择的就是 Bitmap ,那时是我第一次见识到这种数据数组形式,用到的有 SETBIT , GETBIT , BITC...
    99+
    2024-04-02
  • 聊聊php中二维数组的转化方法
    在PHP中,数组是一种非常常见的数据结构,也是处理数据的重要工具。在实际应用中,我们常常需要把一个二维数组转换成另外一个形式的数组。这个过程需要一些技巧和方法,本篇文章将为你介绍如何实现二维数组转化的方法。一、二维数组的定义首先,我们需要先...
    99+
    2023-05-14
    php php数组
  • 浅谈Redis位图(Bitmap)及Redis二进制中的问题
    Redis位图(Bitmap)及二进制的问题 SETBIT key offset value 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。位的设置或清除取决于...
    99+
    2024-04-02
  • 聊聊php怎么改变数组的value值
    PHP作为一门被广泛应用于web开发的脚本语言,在处理数据时经常需要使用数组。数组是一种存储一系列数据的结构,而数组的某个元素则是这种数据中的一个单独部分。当我们需要对数组中的某些元素的值进行修改时,就需要使用PHP提供的相应函数进行操作。...
    99+
    2023-05-14
  • 聊聊c++数组名称和sizeof的问题
    一维数组名称的用途: 可以统计整个数组在内存中的长度 可以获取数组在内存中的首地址 示例: int main() { //数组名用途 //1、可以获取整个数组占用内...
    99+
    2024-04-02
  • 聊一聊redis奇葩数据类型与集群知识
    目录多样的数据类型搞懂集群复制过程的细节需要一个管理者更强的横向伸缩性总结多样的数据类型 string 类型简单方便,支持空间预分配,也就是每次会多分配点空间,这样 string 如...
    99+
    2024-04-02
  • 聊聊php怎么让Swoole/Pool进程池实现Redis持久连接
    本篇文章给大家带来了关于php的相关知识,其中主要介绍了通过PHPphp让Swoole/Pool进程池实现Redis持久连接,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。php 让 Swoole | Pool进程池实现Redis持久连...
    99+
    2023-05-14
    php
  • 聊聊Golang中数组求交集的实现方式
    随着Go语言在互联网领域的越来越广泛应用,Golang语言的数组操作也变成了开发中经常涉及的一个问题。其中,Golang数组求交集也是常见的操作之一,下面就让我们来一起学习一下Golang中数组求交集的实现方式吧。一、Golang数组Gol...
    99+
    2023-05-14
  • 聊聊怎么用php去掉数组中的元素
    PHP中的数组是一种强大的数据结构,它能够方便地存储和操作多个值。然而,在实际开发中,我们经常需要从数组中移除某些元素,这就需要用到PHP的array去掉操作。一般来说,PHP的array去掉操作可以采用两种方式:一是通过循环遍历数组并移除...
    99+
    2023-05-14
  • 聊聊php如何声明和操作一维数组
    在PHP中,数组是一种非常重要的数据类型之一。数组可用于在一个变量中存储一个或多个值。数组分为以下两种类型:一维数组:只包含一个维度的数组。多维数组:包含多个维度的数组。在本文中,我们将重点讨论如何编写一维数组PHP。创建一维数组在PHP中...
    99+
    2023-05-14
  • 简单聊一聊Go语言中的数组和切片
    目录1. 数组2. 切片(Slice)append 函数总结1. 数组 数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此...
    99+
    2024-04-02
  • 聊聊php怎么将字符串强制转为整数
    在PHP中,经常会需要将字符串转换为整数。当你接收到用户的输入或从数据库中获取数据时,它们通常是以字符串的形式出现的。在处理这些数据时,我们有时需要将它们转换为整数。本篇文章将介绍PHP将字符串强制转换为整数的方法。方法1:使用intval...
    99+
    2023-05-14
    php 类型转换
  • 聊聊php判断一个数组是否为空的方法
    PHP是一种广泛使用的开源脚本语言,许多网站都使用PHP作为后端语言。在PHP中,处理数组是一项基本任务,其中判断一个数组是否为空是一个非常常见的需求。本文将介绍如何使用PHP语言来判断一个数组是否为空。首先,让我们看一下如何创建一个空数组...
    99+
    2023-05-14
  • C语言入门之聊聊基础知识(数据类型、变量、函数、数组等)
    本篇文章带大家学习一下C语言,聊聊C语言的基础知识(数据类型、变量、函数、数组等),希望对大家有所帮助!什么是C语言简单来说C语言就是一门计算机语言,广泛应用与底层开发,使用语言写代码程序,解决问题所以说对于计算机这一专业来说C语言和学好C...
    99+
    2022-07-08
    C语言
  • Redis | 第10章 二进制数组、慢查询日志和监视器《Redis设计与实现》
    目录前言1. 二进制位数组1.1 位数组的表示1.2 GETBIT 命令的实现1.3 SETBIT 命令的实现1.4 BITECOUNT 命令的实现1.5 BITOP 命令的实现2. 慢查询日志2.1 慢查询记录的保存2.2 慢查询日志的...
    99+
    2019-06-28
    Redis | 第10章 二进制数组 慢查询日志和监视器《Redis设计与实现》
  • Redis中如何处理二进制序列化数据
    在Redis中处理二进制序列化数据通常使用二进制安全字符串来存储数据。Redis的字符串值是二进制安全的,可以存储任意类型的数据,包...
    99+
    2024-04-29
    Redis
  • python处理二进制数据
    处理二进制数据离不开python的struct模块,struct理解上你可以把它理解为c语言的结构体,使用该模块的pack和unpack方法,可以很容易的把二进制数据转换为常用的类型数据,如整型、字符型等 结构体如下: str...
    99+
    2023-01-31
    二进制数 python
  • Python使用socket实现组播与发送二进制数据
    什么是组播 点对点连接可以处理很多通信需求,不过随着直接连接数的增加,在多对通信方之间传递相同的消息会变得越来越困难。 单独地向各个接收方发送消息会耗费额外的处理时间和带宽,这对于诸如完成流视频或音频操作的应用来说,...
    99+
    2022-06-02
    Python 组播 Python 发送二进制数据
  • jedis获取redis中二进制图片转Base64方式
    jedis获取redis图片 转成Base64 jedis存对象 public static byte[] serialize(Object object) { O...
    99+
    2024-04-02
  • C++实现十进制数转换为二进制数的数学算法
    一、十进制转换为二进制的数学算法 设目标十进制数为n,用短除法一直除以2,循环这个过程并记录余数,当商为0时结束循环,余数从后往前读就是转换为的二进制数 eg: 二、代码实现 1....
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作