Redis的bitmap使用不当,我内存爆了

背景

最近发现Redis的内存持续暴涨, 涨的有点吓人,机器都快扛不住了,不得不进行Redis内存可视化分析,发现大量的String类型的大key

经分析,最近上线了页面UV的统计,那目前如何做的呢?

  1. 通过访客的IP地址来标识和追踪访客。当一个访问者首次访问网站时,服务器会记录其IP地址,并将其计算为一个UV。随后,如果同一IP地址再次访问网站,服务器将不会将其计算为一个UV。
  2. 将IP地址转换为整数,用位图(Bitmap)进行存储IP,实现UV的统计

这方案看上去没啥问题,也达到了去重的效果,统计也比较精确,内存占用率也低(bitmap优势就是内存占用率低),那为什么实际内存占用的这么夸张呢?我接着继续分析。

IP4

IP4介绍

目前的全球因特网所采用的协议族是TCP/IP协议族。IP是TCP/IP协议族中网络层的协议,是TCP/IP协议族的核心协议。IP协议定义了一种地址编码,称为IP地址,它是网络中网络段、网络设备接口、主机的编码,它并不是一种物理地址,而是逻辑地址,即地址是可以被分配、并且非固定、可修改的。

IPv4,是互联网协议(Internet Protocol,IP)的第四版,也是第一个被广泛使用,构成现今互联网技术的基石的协议。1981年 Jon Postel 在RFC791中定义了IP,IP可以运行在各种各样的底层网络上,比如端对端的串行数据链路、卫星链路等等。局域网中最常用的是以太网。

IPv4的下一个版本就是IPv6,IPv6正处在不断发展和完善的过程中,它在不久的将来将取代目前被广泛使用的IPv4。

ip4构成

IP地址有是一个32位的二进制数逻辑地址。因此,除了全0,拥有2的32次方-1个地址。全0地址用来表示一个无效的,未知的,或者不可用的目标。

为了方便使用,把这32位二进制数分成八位一组,被称为八位组(octet)。每个八位组书写时用点分十进制的格式标识。每个八位组取值为0000000011111111(二进制数),使用十进制数表示则值为0255。

二进制与十进制的转化非常简单,用二进制数的每一位乘以2的N次方,N是相应的位,从低位到高位以0次方开始,将二进制是1的每位结果相加得到的就是相应的十进制数。

IP地址分类

IP地址(0.0.0.0——255.255.255.254)分类:

A类

0.0.0.0—127.255.255.255 (其中私有:10.0.0.0—10.255.255.255,保留:0.0.0.0,127.0.0.0—127.255.255.255)

B类

128.0.0.1—191.255.255.254(其中私有:172.16.0.0—172.31.255.255,保留:169.254.0.0-169.254.255.255,191.255.255.255是广播地址,不能分配)

C类:

192.0.0.1—223.255.255.254(其中:私有:192.168.0.0—192.168.255.255)

D类

224.0.0.1—239.255.255.254

E类

240.0.0.1—255.255.255.254

什么是公网IP(外网IP)

公网IP就是除了保留IP地址以外的IP地址,可以与Internet上的其他计算机随意互相访问。我们通常所说的IP地址,其实就是指的公网IP。互联网上的每台计算机都有一个独立的IP地址,该IP地址唯一确定互联网上的一台计算机。

IP如何转为整数

把一个IPv4地址的每段可以看成是一个0-255的整数,先把每段拆分成一个二进制形式组合起来,然后把这个二进制数转变成一个长整数。

以10.0.3.193这个IP地址为例

每段数字相对应的二进制数
1000001010
000000000
300000011
19311000001

组合起来即为:00001010 00000000 00000011 11000001,转换为十进制数就是:167773121,所以10.0.3.193这个IPv4地址转换为Int数字就是167773121。

得到数字 167773121,作为bitmap 的偏移量

BitMap

BitMap可以看下如何统计百万用户在线状态-bitmap这篇文章,有详细的介绍,这里就简单分析下:

BitMap 原本的含义是用一个比特位来映射某个元素的状态。由于一个比特位只能表示 0 和 1 两种状态,所以 BitMap 能映射的状态有限,但是使用比特位的优势是能大量的节省内存空间。

在 Redis 中,可以把 Bitmaps 想象成一个以比特位为单位的数组,数组的每个单元只能存储0和1,数组的下标在 Bitmaps 中叫做偏移量

位图不是实际的数据类型,而是在 String 类型上定义的一组面向位的操作,将其视为位向量。由于字符串是二进制安全 blob,其最大长度为 512 MB,因此它们适合设置最多 2^32 个不同位。

例子: 10.0.3.193 ****这个IP访问了页面page1

10.0.3.193 转换为数字167773121,167773121作为bitmap 的偏移量,值设置为1


setbit uv:page1 167773121 1
# 统计

内存分析

页面page1,第一次被10.0.3.193 访问,进行记录,偏移量是167773121

1Byte(Byte 字节) = 8Bit

167773121/8/1024/1024=20MB

一次就分配了20mb的内存空间,前面的空间就造成了浪费,使用都是后面的位

如果IP是224开头,比如:224.1.2.1,转为数字3758162433

3758162433/8/1024/1024=448MB

一次就分配448mb,这样的统计页面如果有上万个,我们的资源根本没法承受,想想都可怕

如何优化呢?分段统计

分段统计

IPv4地址是一个32位的二进制数,每8位作为一段,分为四段进行储存,比如:10.255.1.12分割,如图:

# 第一段
setbit uv:page1:seg1 10 1
# 第二段
setbit uv:page1:seg2 255 1
# 第三段
setbit uv:page1:seg3 1 1
# 第四段
setbit uv:page1:seg4 12 1

最大偏移量值是255位,四段占用内存:4*255/8/1024=0.12kb

假设10w个页面进行统计,10000*0.12kb=121mb ,最大内存也只占用121mb。统计的页面越多,效果也是明显。不过这里有个问题,都分段了,那如果统计这个页面的uv呢,没分段之前,我们可以

bitcount uv:page1

分段之后,

# 第一段
bitcount uv:page1:seg1 
# 第二段
bitcount uv:page1:seg2 
# 第三段
bitcount uv:page1:seg3 
# 第四段
bitcount uv:page1:seg4 

统计分段后的四个key,然后相加吗,明显不对,那怎么办呢?

# 第一段
setbit uv:page1:seg1 10 1
# 第二段
setbit uv:page1:seg2 255 1
# 第三段
setbit uv:page1:seg3 1 1
# 第四段
setbit uv:page1:seg4 12 1
# 记录UV,上面四个只要有一个返回0,说明是一个新的IP,那就加1
INCR uv:page1#统计uv
get uv:page1

使用Jedis客户端代码实现

 public static void main(String[] args) {Jedis jedis = new Jedis("10.1.250.157", 6379);jedis.auth("google00");jedis.del("ip");//添加四个IP统计uv,有一个是重复的,访问页面page1List<String> ipList = new ArrayList<>();ipList.add("10.1.255.10");ipList.add("255.1.255.10");ipList.add("10.1.195.10");ipList.add("10.1.255.10");for (String ip : ipList) {String[] ips = ip.split("\.");boolean seg1 = jedis.setbit("uv:page1:seg1",Long.valueOf(ips[0]).longValue(),true);boolean seg2 = jedis.setbit("uv:page1:seg2",Long.valueOf(ips[1]).longValue(),true);boolean seg3 = jedis.setbit("uv:page1:seg3",Long.valueOf(ips[2]).longValue(),true);boolean seg4 = jedis.setbit("uv:page1:seg4",Long.valueOf(ips[3]).longValue(),true);if (seg1&&seg2&&seg3&seg4){System.out.println(ip+"已访问过");}else {jedis.incr("uv:page1");}}String uv = jedis.get("uv:page1");System.out.println("页面page1的UV为:"+uv);}

结果:

10.1.255.10已访问过
页面page1的UV为:3

小结

bitmap最大的优势是节约内存空间,但是在使用的时候,需要根据实际的场景分析,上面的例子,就是没考虑偏移量的浪费。好多时候,理论跟实际差距还是有的,多实践。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/661614.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu重装kubernetes集群

重新初始化Kubernetes&#xff08;K8s&#xff09;通常是在集群出现问题或需要重新配置时的一种解决方法。以下是重新初始化K8s的一般步骤&#xff1a; 警告&#xff1a;重新初始化K8s将导致数据丢失&#xff0c;请确保在执行此操作之前进行充分备份。 1.备份数据 在重新初始…

Redux状态管理(运行机制及实例)

背景&#xff1a; JavaScript需要管理的状态越来越多&#xff0c;越来越复杂;这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等&#xff0c;也包括一些UI的状态&#xff0c;比如某些元素是否被选中&#xff0c;是否显示加载动效&#xff0c;当前分页。 状态之…

Camille-学习笔记-测试流程和测试设计

## 测试用例学习路线 startmindmap * 测试用例 ** 黑盒测试方法论 *** 等价类 *** 边界值 *** 因果图 *** 判定表 *** 场景法 *** 基于模型的测试 ** 白盒测试方法论 ** 测试用例基础概念 ** 测试用例设计 ** 面试测试用例设计 ** 常用测试策略与测试手段 endmindmap **测试用…

浅谈信号完整性分析

什么是信号完整性?在过去的低速时代,电平跳变时信号上升时间较长,通常几个 ns。器件间的互连线不至于影响电路的功能,没必要关心信号完整性问题。但在今天的高速时代,随着 IC输出开关速度的提高,很多都在皮秒级,不管信号周期如何,几乎所有设计都遇到了信号完整性问题。…

【ACL 2023】Enhancing Document-level EAE with Contextual Clues and Role Relevance

【ACL 2023】Enhancing Document-level Event Argument Extraction with Contextual Clues and Role Relevance 论文&#xff1a;https://aclanthology.org/2023.findings-acl.817/ 代码&#xff1a;https://github.com/LWL-cpu/SCPRG-master Abstract 与句子级推理相比&…

ZigBee学习——在官方例程基础实现点灯

IAR版本 :10.10.1 Z-stack版本 :3.0.2 文章目录 一、买的板子原理图二、实现过程2.1 重定义LED的物理映射(HAL层)2.2 创建LED事件(应用层)2.2.1 定义用户事件2.2.2 修改zclGenericApp_event_loop() 2.3 触发事件 一、买的板子原理图 二、实现过程 2.1 重定义LED的物理映射(HAL…

服务攻防-端口协议桌面应用QQWPS等RCEhydra口令猜解未授权检测

知识点&#xff1a; 1、端口协议-弱口令&未授权&攻击方式等 2、桌面应用-社交类&文档类&工具类等 章节点&#xff1a; 1、目标判断-端口扫描&组合判断&信息来源 2、安全问题-配置不当&CVE漏洞&弱口令爆破 3、复现对象-数据库&中间件&…

使用Logstash将MySQL中的数据同步至Elasticsearch

目录 1 使用docker安装ELK 1.1 安装Elasticsearch 1.2 安装Kibana 1.3 安装Logstash 2 数据同步 2.1 准备MySQL表和数据 2.2 运行Logstash 2.3 测试 3 Logstash报错(踩坑)记录 3.1 记录一 3.1.1 报错信息 3.1.2 报错原因 3.1.3 解决方案 3.2 记录二 3.2.1 报错信…

图像异或加密、解密的实现

很多论文提到了从左上角开始做异或,逐行推导得到结果。 解密过程是加密的逆过程。 先看其基本方法: 参考文献: A Chaotic System Based Image Encryption Scheme with Identical Encryption and Decryption Algorithm 大多数论文都用了这个思路,我们使用MATLAB实现代码…

Leetcode 热门百题斩(第一天)

介绍 针对leetcode的热门一百题&#xff0c;解决大多数实习生面试的基本算法题。通过我自己的思路和多种方法&#xff0c;供大家参考。 1.两数之和&#xff08;题号&#xff1a;1) 方法一 最先想到的就是两个for去遍历匹配。 class Solution {public int[] twoSum(int[]…

I2S、I2C、SPI和UART的区别

在嵌入式系统中&#xff0c;I2S&#xff08;Inter-IC Sound&#xff09;、I2C&#xff08;Inter-Integrated Circuit&#xff09;、SPI&#xff08;Serial Peripheral Interface&#xff09;和UART&#xff08;Universal Asynchronous Receiver-Transmitter&#xff09;是常见的…

重写Sylar基于协程的服务器(2、配置模块的设计)

重写Sylar基于协程的服务器&#xff08;2、配置模块的设计&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务器&#xff08;1、日志模…

vue3使用is动态切换组件报错Vue received a Component which was made a reactive object.

vue3使用is动态切换组件&#xff0c;activeComponent用ref定义报错 Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef ins…

学习日志以及个人总结 (16)

共用体 共用体 union 共用体名 { 成员列表&#xff1b; }&#xff1b;//表示定义一个共用体类型 注意&#xff1a; 1.共用体 初始化 --- 只能给一个值&#xff0c;默认是给到第一个成员变量 2.共用体成员变量辅助 3.可以判断大小端 ----※&#xff01;&#xff01; 实际用途…

cesium 多边形渐变颜色

cesium画一个渐变颜色的多边形 方式一&#xff1a;用一张颜色渐变的图片作为材质&#xff0c;结合color属性&#xff0c;可设置多边形的颜色&#xff0c;达到渐变效果。图片指向正北方向。 viewer.entities.add({polygon: {hierarchy: Cesium.Cartesian3.fromDegreesArray([115…

bs4模块

bs4模块与案例 使用指南 bs4&#xff0c;全称BeautifulSoup 4&#xff0c;是Python中一个强大的网页解析库&#xff0c;它可以帮助我们方便地从网页中提取数据。bs4将复杂HTML文档转换成树形结构&#xff0c;每个节点都是Python对象&#xff0c;所有对象可以归纳为4种&#xf…

Springboot3.x+dubbo3.x+jdk17 开发的微服务框架

项目名字&#xff1a;Aspen 项目简介&#xff1a; 项目使用的jdk17、springboot3.1.2、dubbo3.2.10、nacos2.2.1、mybatis-flex1.7.7为基础搭建了一套简易的微服务框架 做这个项目的初衷就是心血来潮想试试dubbo3.X和springboot3.x和jdk17融合结果&#xff0c;简单来看效果还…

【PaddleSpeech】语音合成-男声

环境安装 系统&#xff1a;Ubuntu > 16.04 源码下载 使用apt安装 build-essential sudo apt install build-essential 克隆 PaddleSpeech 仓库 # github下载 git clone https://github.com/PaddlePaddle/PaddleSpeech.git # 也可以从gitee下载 git clone https://gite…

Python 异步任务队列实现

在实际开发中,我们经常需要进行异步任务的处理,以提高程序的效率和响应速度。本文介绍了使用 Python 中的 queue 模块和多线程实现一个简单的异步任务队列。通过该队列,我们可以异步执行任务,并在任务完成后进行回调处理。 代码实现 首先,我们定义了一些简单的示例函数,…

EBC金融英国CEO:高波动性周期下,如何寻找市场的稳定性?

利率主导的市场&#xff0c;将在2024年延续。目前&#xff0c;固收市场对于降息的定价&#xff0c;正通过利率传导至不同资产中。尽管市场迫切利用通胀去佐证降息&#xff0c;但各国央行仍囿于通胀目标的政策桎梏。政策和市场预期的博弈将继续牵动市场脉搏&#xff0c;引发价格…