Redis单大ZSET与多100m×100m小ZSET的GEORADIUS性能对比及存储疑问
嘿,咱们来一步步拆解你的问题,先聊聊两种地理数据存储方案的性能差异,再解答Redis ZSET名称相关的疑问~
方案一:单个大ZSET + GEORADIUS查询
这种方案的优势是开发成本极低,不需要提前做任何数据分片的逻辑,直接用Redis原生的GEORADIUS(或GEORADIUSBYMEMBER)命令就能完成查询。Redis底层为地理数据做了Geohash索引优化,在数据量不算特别夸张(比如百万级以内)的情况下,查询效率完全够用。
但它的短板也很明显:当数据量突破千万甚至达到亿级时,GEORADIUS需要从整个大ZSET里筛选符合100米范围的元素,即便有索引加持,全局扫描的内存和CPU开销也会显著上升,查询延迟会变得不稳定。
方案二:按100米方块分片的多ZSET
这种方案的核心是把全局数据拆分成小范围的子集,查询时先定位到目标方块的ZSET(还要注意边缘情况:如果查询点在方块角落,100米范围可能覆盖周围3-9个相邻方块,需要合并这些ZSET的结果)。
它的优势是查询性能更稳定:每个ZSET的数据量小,遍历和筛选的开销极低,哪怕数据量极大,也不会出现全局扫描的瓶颈。但缺点是开发复杂度高:你需要精准计算坐标对应的分片方块,处理边缘查询的合并逻辑,还要避免数据分布不均(比如热门商圈的ZSET数据量远大于偏远地区)。
性能总结
- 如果你的数据量在百万级及以下,选方案一就足够,简单高效;
- 如果数据量突破千万级,或者需要极致的查询稳定性,方案二更优——但一定要注意分片粒度的设计(后面会详细说)。
Redis里所有的键(包括ZSET的名称)都存在一个全局哈希表里,这个哈希表的作用就是把键名映射到对应的Redis对象(ZSET就是其中一种对象类型)。当你访问某个ZSET时,Redis会先通过键名在这个哈希表里做O(1)(平均情况)的查找,找到对应的ZSET对象后再操作内部数据。
只要你的键名设计合理(不要过长,过长的键会增加哈希计算的开销),单键的访问速度不会因为总键数多而明显下降。
虽然Redis的键哈希表效率很高,但当ZSET的数量达到百万级甚至更多时,还是会出现一些问题:
- 额外内存开销:每个键本身要占内存,加上每个ZSET对象的元数据(比如引用计数、类型标识等),累积起来会消耗不少内存。如果是小ZSET,这种元数据的占比会更高,有点得不偿失。
- 哈希表扩容开销:当全局键哈希表的键数量超过阈值时,Redis会触发扩容操作,需要重新哈希所有键,这个过程会占用CPU资源,可能导致短暂的性能波动。
- 运维复杂度提升:太多键会让备份、恢复变慢,用
SCAN遍历所有键的时间也会变长,日常排查问题的成本更高。
这里要特别提醒你:你提到的100米×100米方块分片,全球的方块数量是天文数字(估算下来大概是1.6e10个),根本不可能创建这么多ZSET!你当前的坐标截断方式(比如把49.2440408,28.5011694转为49.2440000,28.5010000)对应的实际范围远小于100米(纬度0.0001度约11米,经度0.0001度在28.5纬度约97.5米),这会导致ZSET数量暴增,直接把Redis搞垮。
实际做分片时,一定要先计算好合适的粒度:比如用1公里×1公里的方块,全球总数量大概是1.6e6个,这个量级Redis完全能扛住;或者根据数据分布动态调整——热门地区用小粒度分片,冷门地区用大粒度,平衡性能和键数量。
内容的提问来源于stack exchange,提问作者Midnight Guest

