libmemcached库api接口讲解四
*_by_key
是 libmemcached 中一个相对高级但关键的概念,尤其在使用**一致性哈希(consistent hashing)**时,它决定了数据如何分布到服务器。下面我们来系统、全面地讲清楚它。
🧠 *_by_key
函数全面讲解
📌 一句话定义:
*_by_key
是 libmemcached 提供的一类函数,用于通过一个 “主键” (group_key
) 来决定哈希路由位置(即数据该存到哪台 Memcached 服务器),而不只是根据实际操作的key
决定。
🏗️ 它解决了什么问题?
假设场景:
你有以下数据:
用户 ID | 存储的 key |
---|---|
1001 | name |
1001 | email |
1002 | name |
1002 | email |
如果你直接使用 memcached_set()
:
memcached_set(memc, "name", ...);
memcached_set(memc, "email", ...);
那么:
name
和email
可能被路由到 不同的服务器,因为它们的 key 不同,哈希值不同。- 用户 1001 的两个字段可能跨了两台机器,造成分布不一致。
🛠️ 如何解决?
使用:
memcached_set_by_key(memc, "1001", 4, "name", 4, value, value_length, 0, 0);
1001
是 主 key(group_key)name
是你实际的字段 key
现在,libmemcached 会:
使用
group_key
来做一致性哈希选择服务器,
使用key
来存储具体的数据。
👉 这样,用户 1001 的所有 key(name, email…)都会路由到同一台服务器。
📦 应用场景总结
应用场景 | 是否使用 _by_key |
---|---|
普通缓存键值 | ❌ 用普通 set/get |
逻辑上属于同一“用户”、“session”、“商品”等的数据要存在一起 | ✅ 强烈推荐使用 _by_key |
分布式系统中缓存命名空间隔离 | ✅ 可作为逻辑 namespace |
🔁 对应函数对比
普通函数 | _by_key 函数 | 是否支持 group_key |
---|---|---|
memcached_set() | memcached_set_by_key() | ✅ 是 |
memcached_get() | memcached_get_by_key() | ✅ 是 |
memcached_delete() | memcached_delete_by_key() | ✅ 是 |
🧪 示例代码对比
不用 _by_key
:
memcached_set(memc, "user_1001:name", ..., ...);
memcached_set(memc, "user_1001:email", ..., ...);
这些 key 哈希后可能落在不同的服务器。
用 _by_key
:
const char* group_key = "user_1001";
memcached_set_by_key(memc, group_key, strlen(group_key), "name", 4, value, val_len, 0, 0);
memcached_set_by_key(memc, group_key, strlen(group_key), "email", 5, value2, val_len2, 0, 0);
这些 key 会确保落到相同服务器,便于一致管理和快速访问。
🧩 为什么需要这样设计?
因为 Memcached 客户端做一致性哈希时,它只能对一个 key 做哈希,来决定“这条数据该放到哪台服务器”。而如果你希望逻辑上属于同一组的数据分布在一台服务器上,就需要提供一个统一的“分布依据”——这就是 group_key
。
⚠️ 注意事项
_by_key
并不会影响你在服务器端看到的 key,它只用于决定 hash ring 上服务器的选择。- key 依然是你提供的
key
,group_key
仅参与路由。 - 通常你只在需要跨字段聚合、保持分布一致性时才使用它。
📌 总结一句话:
如果你想让多个不同的 key 一定落在同一台 Memcached 服务器上,请用
*_by_key
系列函数,把统一的group_key
提供给它。
怎么取数据
很好,这个问题非常关键。你通过 *_by_key
存储的数据,依然是用你原来的 key 来存的,只是客户端用 group_key
来决定该 key 分布在哪台服务器。
✅ 简洁回答:
用
memcached_get_by_key()
来读取!你需要提供和写入时一样的group_key
和key
。
🧪 示例演示
1. 存储数据(使用 _by_key
):
const char* group_key = "user_1001"; // 用来决定一致性哈希的节点
const char* key = "email";
const char* value = "user1001@example.com";memcached_set_by_key(memc,group_key, strlen(group_key),key, strlen(key),value, strlen(value),0, 0);
此时:
- 数据存的是
"email"
这个 key - 它被放在了一台通过
"user_1001"
计算出来的服务器上
2. 读取数据(使用 _by_key
):
size_t value_length;
uint32_t flags;
memcached_return rc;char* result = memcached_get_by_key(memc,"user_1001", strlen("user_1001"),"email", strlen("email"),&value_length,&flags,&rc);if (rc == MEMCACHED_SUCCESS && result != NULL) {printf("Got: %s\n", result);free(result); // 不要忘记释放内存
} else {printf("Get failed: %s\n", memcached_strerror(memc, rc));
}
🔁 必须用同样的
group_key
,否则客户端可能去了一台错误的服务器,自然拿不到 key!
❓如果用普通 memcached_get()
可以吗?
❌ 不能保证能取到!
因为 memcached_get()
是用 key
做哈希选服务器,而你用 group_key
写入的数据可能在别的机器上了。
结果就是:
memcached_get(memc, "email", 5, ...); // ⚠️ 可能失败(访问错服务器)
🧠 再总结一遍:
操作 | 使用函数 | 哈希依据 | 能否成功 |
---|---|---|---|
写入(指定 group) | memcached_set_by_key() | group_key | ✅ 正常存储 |
读取(指定同样 group) | memcached_get_by_key() | group_key | ✅ 正常读取 |
读取(不指定 group) | memcached_get() | key | ❌ 可能取不到 |
✅ 最佳实践
在使用 _by_key
写入时,始终使用 _by_key
读取、删除、替换等操作,并保持 group_key
一致。
🔍 为什么不直接用 group_key
作为 key?
你问的是:既然我们已经有 group_key
了,为什么还要额外指定一个 key
?直接用 group_key
作为 key 不就完了?
我们用一个类比来解释这背后的设计初衷。
📦 类比:group_key 是“房间号”,key 是“抽屉名”
你可以想象:
group_key
是一个“房间号” → 决定这堆数据放在哪个房间(即服务器)key
是房间里的“抽屉名” → 真正存取数据用的 key
所以:
memcached_set_by_key(memc, "user_1001", ..., "email", ..., value);
表示:
🗃️ “user_1001” 决定去哪个房间,
🔐 “email” 是房间里的 key,
📄 存的数据是 “user1001@example.com”
✅ 优点:支持“多个字段统一存储”
你可以将用户 1001
的多个字段统一放在一台机器:
memcached_set_by_key(memc, "user_1001", ..., "email", ...);
memcached_set_by_key(memc, "user_1001", ..., "name", ...);
memcached_set_by_key(memc, "user_1001", ..., "avatar", ...);
而不是每次把全部信息塞进一个 key 中,这样灵活性更强、结构更清晰。
❌ 如果只用 group_key
,你会遇到的问题:
1. 无法存多个字段
你只能存一条记录,例如:
memcached_set(memc, "user_1001", ..., json_blob);
那么你必须把整个用户对象序列化为一个 JSON,一起读一起写,灵活性差。
2. 粒度太粗,不适合高并发写操作
不同系统模块可能只关心一个字段(如 email / name),如果把它们都合并成一个 key,意味着写任何一个字段,都要先取出、修改、再写回——非常低效。
3. 缺少灵活的数据访问
你想只更新或删除用户 email 就做不到了,必须操作整个 blob。
🧠 回答总结:
做法 | 是否推荐 | 原因 |
---|---|---|
用 group_key + key | ✅ 推荐 | 灵活、支持字段拆分、分布清晰 |
只用 group_key 当 key | ❌ 不推荐 | 粒度粗、访问/更新不方便、读写效率低 |
🚀 设计哲学
libmemcached 的 _by_key
系列函数:
把 “路由逻辑”(group_key) 和 “实际 key”(key) 分离开来,
实现灵活的逻辑分组 + 精准访问,更适合复杂业务场景。