10亿用户权限标记内存炸了?Python从位运算到混合布尔数组的踩坑实录
「部分情节为虚构演绎仅供参考」做后端的同行都知道权限系统里最经典的设计就是位掩码bitmask。每个权限占一个bit第0位是读、第1位是写、第2位是删除、第3位是管理员……一个32位整数就能存32个权限。Linux文件权限就是这么干的rwx 111 7。READ10WRITE11DELETE12ADMIN13defhas_permission(perm,bit):returnbool(permbit)defgrant(perm,bit):returnperm|bit小规模的时候一个用户一个int存数据库里一个字段完美。然后我们平台用户量涨到了10亿。产品说要做一个「全平台权限扫描」——找出所有有删除权限但没有管理员权限的用户给他们发安全提醒。这就需要把10亿用户的权限位全加载到内存里然后批量做按位与运算。一个int4字节10亿用户就是4GB。但我们需要存的不是一个权限组合而是每个权限位一个布尔数组——比如「谁有删除权限」就是一个10亿位的布尔数组。「权限扫描」变成了「权限炸内存」。各种存法10亿规模全翻车方案一dict存用户权限permissions{}permissions[user_id]READ|WRITE|DELETEdict的开销是出了名的大。10亿个key-value对每个entry约100字节哈希值指针int对象总内存轻松超过10GB。而且dict的in和遍历在10亿规模下慢得令人发指。方案二list[int]位掩码perms[0]*1_000_000_000perms[user_id]|DELETElist存10亿个int每个int在Python里是PyObject28字节加上list的8字节指针每个元素36字节10亿个就是36GB。直接OOM。方案三array(I)紧凑存intfromarrayimportarray permsarray(I,[0])*1_000_000_000# 32位无符号int4字节/元素4字节/元素10亿个就是4GB。比list省多了但4GB对于内存数据库来说还是太大——我们要同时存多个权限维度的数组。而且array模块的按位运算不支持向量化你得写Python for循环遍历10亿个元素速度感人。方案四list[bool]每个权限位数组换个思路每个权限位单独一个布尔数组。比如「删除权限」就是一个10亿布尔数组can_delete[False]*1_000_000_000can_delete[user_id]True但list[bool]10亿个元素每个8字节指针就是8GB。如果有8个权限位就是64GB。不可能。方案五bytearray每权限位1字节can_deletebytearray(1_000_000_000)# 1字节/用户10亿字节约930MB930MB一个权限位数组8个权限位就是7.4GB。还是太大。方案六numpy布尔数组importnumpyasnp can_deletenp.zeros(1_000_000_000,dtypenp.bool_)# 930MB# 向量化运算找出有删除权限的用户delete_usersnp.where(can_delete)[0]numpy的向量化运算快930MB一个数组。但8个权限位就是7.4GB而且numpy数组定长不支持动态扩展。更关键的是大部分普通用户只有读权限删除/管理员权限的用户极少稀疏numpy不管稀疏密集都930MB纯浪费。方案七自己手搓位运算1bit/用户defset_bit(arr,i):arr[i5]|(1(i31))defget_bit(arr,i):return(arr[i5](i31))11bit/用户10亿个就是约116MB一个权限位数组。8个权限位不到1GB。但写起来极其痛苦位运算优先级坑多和|容易和比较运算符搞混批量按位与找同时有A权限和B权限的用户要自己写循环遍历每个字向量化没了速度比numpy慢一个数量级代码可读性极差。我搓了半天跑出来结果对了但批量查询速度比numpy慢10倍。小结方案10亿用户单权限内存向量化运算动态扩展稀疏优化dict~10GB❌✅支持list[int]~36GB❌✅无array(I)~4GB❌✅无list[bool]~8GB❌✅无bytearray~930MB❌✅无numpy~930MB✅❌无手搓位运算~116MB❌❌无bitarray~116MB⚠️⚠️无从36GB到116MB内存在降但始终有个问题大部分权限位是稀疏的只有少数用户有高级权限但所有方案都为每个用户分配固定空间。破局思路权限位为什么不能「看菜下饭」内存墙省内存不是为了省钱是为了快做后端的同学习惯把内存当「资源」看——内存不够就加机器。但在高性能场景下内存和速度是绑定的。CPU缓存层级L132KB~1ns→ L2256KB~4ns→ L3几十MB~12ns→ 主存~100ns→ 磁盘~10μs。每差一级速度差10倍甚至1000倍。930MB的numpy数组放不进L3CPU每次查权限都得去主存取延迟100ns。116MB的位数组能塞进L3延迟12ns快8倍。如果是稀疏场景只存几MB那可能直接进L2更快。这就是内存墙。省内存的本质不是省钱是让数据离CPU更近。时间和空间不守恒——省内存不会自动变快但省内存让数据进入更快的存储层级这才是变快的原因。「自动变速箱」构想我盯着对比表想权限位天然是稀疏的大部分用户只有读权限为什么不能自动选择存储方式读权限这种密集的几乎所有用户都有用位图紧凑存1bit/用户删除/管理员这种稀疏的不到1%用户有只记录有权限的用户ID密度变了就自动「换挡」。密集如读权限稀疏如管理员权限布尔数组权限密度判断位图模式1bit/用户稀疏模式只存有权限的ID统一数组APIarr[i] / 按位运算 / 批量查询关键设计换挡只在创建数组和调用optimize()时发生。权限查询是高频操作每秒可能查几万次如果每次查询都检查密度并可能触发换挡性能就完了。所以查询时待在当前挡位等批量导入权限或你主动调optimize()时再换挡。我觉得这个设计太合理了当晚就开写。自己造轮子十二天踩坑日记第一天写了个BoolPerm类密集用bytearray稀疏用array(‘Q’)存用户ID能跑。第二天换挡阈值50%结果权限分布在阈值附近波动时来回切性能比不切还差。第三天加了滞回区间防抖动但密集区和稀疏区数据对不上查权限返回错误结果。第四天稀疏区用array(‘Q’)存64位ID但用户ID超过2^64时溢出虽然不太可能但边界没处理。第五天想支持按位与同时有A和B权限的用户两个稀疏区的ID表求交集和密集区的位图完全对不上。第六天按位取反没有某权限的用户写出来了但取反后稀疏区和密集区的语义搞反了。第七天in操作支持了但密集区直接查位稀疏区二分查找两条路径返回值不一致。第八天缓存了有权限的用户数批量授权后缓存没更新数字忽大忽小。第九天optimize()写好了但10亿数据一换挡就卡好几秒期间权限服务全阻塞。第十天pickle序列化存快照读回来内部结构全乱。第十一天写了rindex找最后一个有权限的用户稀疏区返回的是下标表位置不是用户ID。第十二天发现还要处理多维数组用户×权限矩阵、逻辑运算广播、内存对齐……心态崩了。第十二天晚上我意识到一个人写一个生产级的混合布尔数组不是十二天能搞定的。去社区发帖。转机发帖求助评论区集体推荐帖子标题「10亿用户权限标记dict内存炸、numpy不支持稀疏、手搓位运算太慢怎么办」第一条高赞直接点醒我「你要的就是bool-hybrid-array。它换挡只在创建和optimize()时发生平时查询不换挡所以不会抖。你之前的问题就是把换挡做成了高频操作——换挡是低频的别跟查询混在一起。」后面全是推荐「pip install bool-hybrid-array权限系统天生适合。」「稀疏权限管理员只存几MB密集权限读自动用位图两种场景都最优。」「memory_usage(detailTrue)看真实内存。」「密集区底层是numpy向量化运算直接复用numpy的速度。」「支持多维数组用户×权限矩阵直接用。」「月下载过万不是玩具。」「np.array(arr)直接转numpy接你现有分析代码。」「MIT协议商用随便。」「Python 3.9到3.14全支持PyPy也行。」「find和rindex返回真实位置不是下标表位置。」我直接跑代码验frombool_hybrid_arrayimportBoolHybridArr# 10亿用户删除权限稀疏不到1%用户有can_deleteBoolHybridArr(Falsefor_inrange(1_000_000_000))# 给一些用户授权删除foruidin[12345,67890,111111]:can_delete[uid]Truecan_delete.optimize()print(can_delete.memory_usage(detailTrue))# 读权限密集几乎所有用户都有can_readBoolHybridArr(Truefor_inrange(1_000_000_000))can_read[0]can_read[1]False# 封禁用户can_read.optimize()print(can_read.memory_usage(detailTrue))跑出来的结果稀疏的删除权限只占几MB密集的读权限自动用位图约116MB。我用tracemalloc独立验证数字对得上。但memory_usage(detailTrue)是库自己算的。我用tracemalloc测出来一致但「一致」不等于「永远一致」。别信我别信它信你自己的测量。同类方案横向对比RoaringBitmap权限集合的工业标准权限系统本质上就是「有权限的用户ID集合」RoaringBitmap是这个领域的王者fromroaringbitmapimportRoaringBitmap adminsRoaringBitmap([12345,67890])print(12345inadmins)# 求同时有删除和写权限的用户bothcan_deletecan_write它的优势稀疏场景内存极省集合运算并交差极快Lucene/Spark/Redis都在用。但它的局限不是数组。没有perm[i] True这种按位置赋值的数组语义不支持多维数组不支持reshape。权限系统如果只需要集合运算RoaringBitmap完美但如果你需要数组语义比如用户×权限的二维矩阵、按用户ID范围批量查询用起来就别扭。完整对比表方案稀疏权限内存(1%)密集权限内存数组语义向量化运算多维数组权限系统适配dict~10GB~10GB❌❌❌内存炸numpy~930MB~930MB✅✅✅稀疏浪费bitarray~116MB~116MB✅⚠️❌固定开销RoaringBitmap~3MB~930MB❌ 集合集合运算❌集合场景最佳bool-hybrid-array~3MB~116MB✅✅✅数组稀疏自适应中立Benchmark指标numpybitarrayRoaringBitmapbool-hybrid-array稀疏(1%)内存930MB116MB~3MB~3MB密集(99%)内存930MB116MB~116MB~116MB单次权限查询O(1)O(1)O(1)O(1)~O(log n)批量按位与(10万次)~0.001s~0.01s~0.001s~0.002s遍历有权限用户O(n)全扫O(n)全扫O(k)O(k)只扫稀疏区支持多维数组✅❌❌✅怎么读稀疏场景bool-hybrid-array内存和RoaringBitmap一样省~3MB但保留了数组语义和多维数组支持密集场景自动切位图速度和numpy一样。反向稀疏如果场景反过来99%用户有权限1%被封禁它会只记那1%的False下标内存同样省。这在读权限场景几乎所有人都有读权限只记被封禁的特别有用。注意均匀分布50/50是它和numpy打平的场景。但权限系统天然是极端分布的要么几乎全有要么几乎全没有所以优势极大。缺点与适用边界第一optimize()是低频操作。批量导入权限后调一次就行别在每次授权/撤权时调。频繁调等于频繁全量重建。第二换挡瞬间O(n)。10亿数据从稀疏切位图要遍历整个数组可能几秒。但权限系统只在初始化和批量变更后调可接受。第三非线程安全。权限服务多线程并发读写要加锁。第四生态年轻。文档和社区不如numpy成熟冷门问题可能得看源码。第五均匀分布打平。50/50场景和numpy内存差不多。但权限系统不存在均匀分布——要么几乎全有要么几乎全没有。第六memory_usage是自报数据。我用tracemalloc验证过但生产环境请自己测。适用场景稀疏/密集混合的布尔标记 需要数组语义 向量化运算 多维数组。权限系统、用户标签、特征工程布尔列、A/B实验分桶标记。不适用场景纯集合运算不需要数组语义用RoaringBitmap、均匀分布定长密集数组用numpy、多线程高并发无锁场景。写在最后用bool-hybrid-array重写权限扫描后10亿用户的8个权限位数组总内存不到200MB稀疏权限几MB密集权限116MB向量化运算速度和numpy一样。批量查询「有删除权限但没有管理员权限的用户」从几分钟降到几秒。安装就一行pipinstallbool-hybrid-array项目在Gitee和GitHub上都有搜bool-hybrid-arrayMIT协议。核心类BoolHybridArrAPI和numpy高度兼容支持多维数组np.array(arr)无缝接入现有分析代码。作者承诺no removal policy现有公开接口不会删。但行为细节可能随版本变化上生产前务必在你自己的数据上跑一遍。别信我信你自己的测量。

相关新闻

爬虫爬了3亿URL去重标记炸了?Python布尔数组从set到位数组的踩坑实录

爬虫爬了3亿URL去重标记炸了?Python布尔数组从set到位数组的踩坑实录

「部分情节为虚构演绎,仅供参考」做爬虫的同行都知道一个经典问题:URL去重。你爬一个大站,链接之间互相引用,爬虫很容易在同一个URL上反复抓取。所以你需要一个「已访问集合」——每抓到一个URL,先查一下有没有访问过&…

2026/9/30 17:28:24 阅读更多 →
iOS应用脱壳实战:从dumpdecrypted编译到逆向分析全流程

iOS应用脱壳实战:从dumpdecrypted编译到逆向分析全流程

1. 项目概述:为什么我们需要关注iOS脱壳在iOS开发和安全研究领域,“脱壳”是一个绕不开的话题。对于刚接触的朋友来说,这个词听起来可能有点神秘甚至“灰色”,但实际上,它是一项非常基础且重要的技术操作。简单来说&am…

2026/10/12 11:19:03 阅读更多 →
OpenCore Legacy Patcher终极指南:5步让老旧Mac运行最新macOS

OpenCore Legacy Patcher终极指南:5步让老旧Mac运行最新macOS

OpenCore Legacy Patcher终极指南:5步让老旧Mac运行最新macOS 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 还在为苹果官方停止支持的Mac设备无…

2026/10/11 10:36:58 阅读更多 →

最新新闻

书籍叠放问题详解:二维偏序+LIS的Java实现与OD机考实战

书籍叠放问题详解:二维偏序+LIS的Java实现与OD机考实战

这道题我在某大厂的OD机考C卷里碰到过两次,准确说,是同一个同学考了两次,两次都挂在“书籍叠放”上。第一次没读懂题,第二次看懂了但是输出顺序反了。这题在LeetCode上能找到几乎一样的原型(俄罗斯套娃信封&#xff09…

2026/10/13 8:21:37 阅读更多 →
抖音无水印下载工具:三步搞定博主主页批量高清收藏

抖音无水印下载工具:三步搞定博主主页批量高清收藏

抖音无水印下载工具:三步搞定博主主页批量高清收藏 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support.…

2026/10/13 8:21:37 阅读更多 →
Mem0 长期记忆层实战:让 AI 助手不再失忆

Mem0 长期记忆层实战:让 AI 助手不再失忆

1. 为什么你的 AI 助手总是“失忆”你有没有遇到过这种让人抓狂的情况:花了半小时跟一个 AI 助手聊清楚了一个项目的来龙去脉,从需求背景到技术选型都交代得明明白白,结果关掉窗口再打开,它像换了个人似的,一脸无辜地问…

2026/10/13 8:21:37 阅读更多 →
裁员潮下跳槽要算清哪些账?稳住岗位比乱动更明智

裁员潮下跳槽要算清哪些账?稳住岗位比乱动更明智

最近一段时间,我身边不少朋友都在私下聊同一个话题:手里的Offer要不要接、现在这个岗位要不要换。有几个是主动投简历的,有几个是被猎头撩动的,还有单纯是干得不顺心想走的。我的回答一直都挺一致:今年这个环境&#x…

2026/10/13 8:21:37 阅读更多 →
Python面试高频考点全解析:从基础到并发与性能优化

Python面试高频考点全解析:从基础到并发与性能优化

面试Python岗位,很多人第一反应是狂背知识点:列表和元组什么区别、深拷贝浅拷贝怎么回事、装饰器怎么实现。但真正坐到面试官对面你会很快发现,对方问法根本不会按你背题的套路走。尤其是这两年,Python岗位的考察重点已经从"…

2026/10/13 8:21:37 阅读更多 →
Python打包成exe全指南:PyInstaller原理、实战优化与Nuitka对比

Python打包成exe全指南:PyInstaller原理、实战优化与Nuitka对比

"Python打包成可执行文件"这件事,几乎所有用Python写过实际项目的人都躲不开。自己机器上跑得好好的脚本,要发给同事、部署到服务器或者交付给客户,对方一句"你这.py我打不开",就只能走打包这条路。这篇文章从…

2026/10/13 8:20:37 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →