能快速回答半径与范围问题的空间索引。

Redis 按距离处理地理位置

Redis 地理命令为带名称的位置建立索引,可以直接回答附近有哪些点,不必把全部坐标传回应用。它适合能够接受球面距离模型的场景,但不会提供道路路线、考虑海拔的空间距离、任意多边形分析,也不会保证查询到的每个位置仍然新鲜。这些不是查询速度更快就能顺带解决的问题。 经度必须放在纬度前面 GEOADD 按经度、纬度、成员名的顺序接收参数。有效经度从负一百八十度到正一百八十度,纬度则限制在大约负 85.05112878 度到正 85.05112878 度之间,极区不属于该索引支持的范围。如果把两个各自仍合法的数值交换位置,命令可能正常成功,却把成员放到完全不同的地方。 索引底层是有序集合,分数保存五十二位地理哈希编码,不能用普通排行榜分数覆盖这些位置编码。成员是唯一的:更新同名成员只会改变位置,不会增加一份记录。GEOADD 默认统计新增成员数,加 CH 才统计新增及实际变化。删除位置使用 ZREM,读取存储坐标则使用 GEOPOS;不要把一次更新返回零当成更新失败。 坐标编码具有有限精度,因此 GEOPOS 不一定原样返回输入的小数。GEODIST 使用理想球面地球计算表面距离,不是穿过地球内部的直线弦长,更不是驾车路线。默认单位为米,也可以选择公里、英尺和英里。任何一个端点缺失时,返回空结果,而不是把不存在的位置当作距离为零的重合点。 把查询条件写成明确契约 Redis 6.2 开始提供 GEOSEARCH,可以围绕已有成员或显式经纬度查询。BYRADIUS 指定圆形范围,BYBOX 则按所选单位指定轴向对齐矩形的宽和高。新的查询应使用该命令,而不是已弃用的 GEORADIUS 系列。请求中的单位与结果解释必须一致,否则正确的数值也会被应用解读成错误的距离。 ASC 按距离从近到远排列,COUNT 限制返回数量。要找最近的若干个结果,应使用 ASC 和 COUNT,避免 ANY;ANY 允许找到足够数量后提前结束,不能保证拿到查询区域内全局最近的那些点。另外,返回数量少不代表查询一定便宜:范围巨大且点位密集时,即使只要求少量结果,也可能需要检查很多候选。 FROMMEMBER 查询可以包含作为中心的成员自身。如果业务要找“附近的其他人”,就需要处理这个结果,而不是直接把第一条当作别人。WITHDIST 返回所选单位下的距离,WITHCOORD 返回坐标。结果反映的是查询时索引里存放的数据,并不等于已经独立确认某台设备或某个商户此刻就在该位置。 坏掉的样子 文档说明,在边界情况下,球面近似与地球实际形状之间的误差可以达到约百分之零点五。因此,“城市尺度”并不自动意味着误差小于一米。应用应明确可接受误差及距离边界如何判定;测绘、安全关键接近判断、道路规划或严格地理围栏,可能需要更合适的空间模型,不能只凭一次示例距离看起来合理就验收精度。 ...

一月 25, 2028 · 3 分钟 · XB Field Notes
一次持锁重写,化解严重的表膨胀。

VACUUM FULL 重建一张表而不是修剪

大量删除之后,PostgreSQL 表可能只剩少量记录,却仍保留很大的文件。普通清理让旧空间可以在表内复用,完全清理则把存活内容写入新文件,同时重建索引。更彻底的空间回收,需要另一套锁与容量预算,应作为有计划的维护操作,而不是一看到文件大小偏高就立刻执行的日常反应。 坏掉的样子 重写需要的是 ACCESS EXCLUSIVE 表锁,普通读取和写入都与它冲突。某个查询虽然已经执行完,但事务仍然打开、继续持有表锁,也会挡住重写。把它叫成独占行锁,或者以为只有写请求受影响,会低估实际中断范围。排队等待重写的强锁请求,还可能影响随后进入的其他取锁请求,不能只看当前命令有没有开始复制数据。 替代文件成功建立之前,原文件仍然存在,所以操作需要额外磁盘空间。等文件系统已经写满才着手完全清理,可能反而没有容纳新文件的余量。应提前评估替代堆表与索引、磁盘读写和日志影响,以及能够承受的持锁时长。常规自动回收不会自动替你执行这类完整重写。 快速验证 在同一个 PostgreSQL 18 可丢弃数据库中,开启甲、乙两个全新的 psql -X -v ON_ERROR_STOP=1 会话。清理命令必须在显式事务块之外执行。乙先创建五千行、删除前四千五百行,然后进行普通清理: CREATE SCHEMA compact_demo; CREATE TABLE compact_demo.items (id integer PRIMARY KEY,payload text) WITH (autovacuum_enabled=false); INSERT INTO compact_demo.items SELECT g,repeat(md5(g::text),10) FROM generate_series(1,5000) AS g; CREATE TABLE compact_demo.baseline AS SELECT pg_relation_size('compact_demo.items') AS heap_bytes, pg_relation_filenode('compact_demo.items') AS file_id; DELETE FROM compact_demo.items WHERE id<=4500; VACUUM (ANALYZE) compact_demo.items; SELECT pg_relation_size('compact_demo.items')=heap_bytes AS same_heap_size, pg_relation_filenode('compact_demo.items')=file_id AS same_file FROM compact_demo.baseline; 两个比较都返回真:堆文件大小与文件标识没有改变。存活行位于原文件尾部,末尾不存在可以截断的连续空页,所以普通清理不能通过截断来缩小这个特定文件。这不代表普通清理永远不会缩文件;如果末尾确实存在可释放的空页,就可能归还相应空间。这里要区分样本布局和命令的一般能力。 ...

一月 3, 2028 · 1 分钟 · XB Field Notes