五千万用户标签状态怎么存最省内存?真机测了6种方案
五千万用户标签状态怎么存最省内存?真机测了6种方案
场景
做用户标签系统,5000万用户ID连续编号,每个用户有一个布尔标签(是否VIP、是否活跃、是否签到)。要随机读写arr[uid],内存越省越好。
6种方案真机对比
| 方案 | 5000万dense内存 | 稀疏场景 | 随机访问 |
|---|---|---|---|
| Python list[bool] | ~1.4GB | ~1.4GB | O(1) |
| numpy.bool_数组 | ~47.7MB | ~47.7MB | O(1) |
| bytearray手写位图 | 6.25MB | 固定6.25MB | O(1) |
| bitarray库 | 6.25MB | 固定6.25MB | O(1) |
| RoaringBitmap(pyroaring) | ~6MB | 远小于6MB | O(1) |
| bool-hybrid-array | ~6.25MB | 自适应压缩 | O(1) |
关键发现
1. numpy.bool_不是1bit是1byte
很多人第一反应用numpy:
import numpy as np
arr = np.zeros(50_000_000, dtype=bool)
看似简单,但numpy的bool是1字节存1个布尔,5000万就是~47.7MB,比位图贵8倍。
2. bitarray和bytearray都是固定6.25MB
bitarray库按位打包,5000万bit=6.25MB,比numpy省8倍。但它是固定位图,不管你只有1%还是99%的用户是True,都占6.25MB。
3. RoaringBitmap稀疏强但稠密不省
RoaringBitmap分块压缩,稀疏时只存True的ID,内存远小于6MB。但如果99%用户都是True,它要存4950万个ID,内存反而爆炸。
4. bool-hybrid-array自适应稠密+稀疏
bool-hybrid-array(v9.13.11,Apache-2.0 OR MulanPSL-2.0)专门解决这个问题:
- dense侧:
_CompactBoolArray按位打包成ceil(N/8)个uint8,1 bool=1 bit,省内存来自按位打包 - sparse侧:
array.array('I')升序存异常下标+bisect二分查找 - 自动切换:根据True密度自动在稠密/稀疏模式间切换
- 极性塌缩:99%用户是True时存少数False的ID,反向也省内存
from bool_hybrid_array import BoolHybridArr
# 容量5000万
bha = BoolHybridArr(size=50_000_000)
bha[10001] = True
print(bha[10001]) # True
print(bha.count()) # 统计True数量
选型决策
- 纯稠密、确定不稀疏:bitarray最简单稳定
- 纯稀疏、只存True ID:RoaringBitmap工业标准
- 分布不稳定、有时稠密有时稀疏、99%True也要省:bool-hybrid-array自适应
注意事项
- bool-hybrid-array的dense位图由numpy uint8承载,运行需numpy>=1.19
- 构造时用生成器表达式不要用列表推导式(避免临时list占峰值内存)
- 类名是
BoolHybridArr(单数Arr),不是BoolHybridArray - 当前版本license是Apache-2.0 OR MulanPSL-2.0双授权
浙公网安备 33010602011771号