让相同 key 稳定命中上游
nginx 的 consistent hash 将上游展开为有序哈希点,再用二分查找定位请求 key 的落点。
相关概念
工程问题#
缓存代理希望相同 key 尽量送到同一上游,并在节点变动时减少 key 的重新分配范围。
实际实现#
初始化构造并排序哈希点数组;请求计算 CRC32,查找首个大于等于请求哈希的点,再选择可用上游。
处理流程#
- 按上游与权重建立哈希点
- 排序并去掉重复点
- 二分定位请求 key 的位置
- 检查候选上游并按规则重试
源码片段#
固定版本 · 原文摘录src/http/modules/ngx_http_upstream_hash_module.crelease-1.26.3 · L438–L459
438 /* find first point >= hash */439440 point = &points->point[0];441442 i = 0;443 j = points->number;444445 while (i < j) {446 k = (i + j) / 2;447448 if (hash > point[k].hash) {449 i = k + 1;450451 } else if (hash < point[k].hash) {452 j = k;453454 } else {455 return k;456 }457 }458459 return i;ngx_http_upstream_find_chash_point
在已排序、去重的哈希点数组中二分查找第一个不小于请求哈希的下标。返回值可能等于点数;调用方随后取模实现环回,并另行检查上游可用性。O(log P) 描述的是点定位,不是含重试的整个请求流程。
连续函数主体;参数声明及外层花括号不在片段中。环由有序数组表达,不是环形链表。
收益与取舍#
工程分析带来的收益
- 有序数组具备紧凑存储与局部性
- 点查找为对数级,映射相对稳定
付出的代价
- 工程分析:虚拟点数量带来额外空间
- 热点 key 和上游故障仍可能造成负载不均
可比较的选择
简单取模哈希计算更简单,但上游数量改变时通常影响更多 key。
版本与适用范围#
对应 hash 指令的 consistent 模式;这里的哈希环由数组表达,并非环形链表。
参考来源#
源码、官方文档与原始论文01
nginx · ngx_http_upstream_init_chash / ngx_http_upstream_find_chash_point
源码release-1.26.3src/http/modules/ngx_http_upstream_hash_module.c · ngx_http_upstream_init_chash / ngx_http_upstream_find_chash_point
相关实现与比较#
2 个相关案例Git 把内存中的 index 条目指针保存在有序、可增长的数组中,按路径和合并 stage 二分查找位置。
附源码片段 · 索引与查找
CPython 的列表排序识别已有序段,补齐短段,再按 Powersort 策略稳定归并,兼顾局部有序输入与通用排序。
附源码片段 · 检索与计算