twemproxy上做交并集合操作

业务线上出错,返回给用户的结果不正确。

使用了ZINTERSTORE这个接口对多个集合做交并集返回预期外的空集。如下图:

error操作

这是因为twemproxy只是把指令转发给redis而已,而多个集合被哈希算法分片在不同的redis节点上,所以转发这个redis并没有这几个集合,会直接返回空集。

这并不是BUG,redis3.0也存在这个特性,当多个集合做交并集操作时,需要使用hash_tag特性。

首先DBA需要修改配置文件

配置文件

然后业务逻辑的KEY这样操作

此时twemproxy只会对括号内的test关键字进行哈希,能够强制性保证这三个集合在同一个redis上。

正确操作

hash_tag的说明

twemproxy使用一致性哈希来对key进行分片到不同的redis后端上。

由于一些特殊的需求使得两个key必须分布在同一个redis后端上(例如交并集合等等),那么此时不能对整个key进行分片。

使用hash_tag对key做截断,截取其中相同的部分进行哈希,就能使得分片在同一个后端redis上。

在代码中的逻辑很简单:

req_forward(struct context *ctx, struct conn *c_conn, struct msg *msg)
{
    ...
    /*  
     * If hash_tag: is configured for this server pool, we use the part of
     * the key within the hash tag as an input to the distributor. Otherwise
     * we use the full key
     */
    if (!string_empty(&pool->hash_tag)) {
        struct string *tag = &pool->hash_tag;
        uint8_t *tag_start, *tag_end;

        tag_start = nc_strchr(msg->key_start, msg->key_end, tag->data[0]);
        if (tag_start != NULL) {
            tag_end = nc_strchr(tag_start + 1, msg->key_end, tag->data[1]);
            if (tag_end != NULL) {
                key = tag_start + 1;
                keylen = (uint32_t)(tag_end - key);
            }   
        }   
    }   

    if (keylen == 0) {
        key = msg->key_start;
        keylen = (uint32_t)(msg->key_end - msg->key_start);
    }
    ...
}

hash_tag由两个符号组成,分别是tag->data[0]和tag->data[1]

逻辑上对每个key做字符寻找操作,找到key中第一个出现的tag->data[0]以及紧跟着出现的tag->data[1]

把这中间的部分作为新的key来hash

也就说如果hash_tag设置为{}后

对于值为"a{b}c{d}e"这样的key来说,只会对b进行hash。