DCache 分布式存储系统|Set, ZSet 缓存模块的创建与使用

2021-03-30 16:10:24 浏览数 (1)

在之前的系列文章中,我们介绍了 DCache 及其 KV, K-K-Row 和 List 缓存模块的使用,本文将继续介绍如何使用 DCache 中的集合类型缓存模块 —— Set 和 ZSet 缓存模块。

  • Set 与 ZSet 模块简介
  • 创建 Set/ZSet 缓存模块
  • 调用 Set/ZSet 缓存模块服务
    • Set 模块读写操作
    • ZSet 模块读写操作
    • 实例
  • 其它 Set/ZSet 缓存模块服务接口
  • 总结

DCache 是一个基于 TARS 框架开发的分布式 NoSQL 存储系统,支持多种数据结构,包括了 key-value(键值对),k-k-row(多键值),list(列表),set(集合),zset(有序集合)等,满足多种业务需求。

在前面的文章中,我们介绍过 key-value, k-k-rowlist 两种类型缓存模块的使用方式,本文将继续介绍集合类型,setzset 缓存模块的使用。

set 即集合,与 list 类似,以列表形式存储数据。不同的地方在于 set 是会对添加的数据进行排重的。如果你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择。

zset 为有序集合,使用场景与 set 类似,但 set 并不是自动有序的。在 zset 中,提供了一个的参数 score 来为数据成员排序。当你需要一个有序的并且不重复的集合列表,那么可以选择 zset 数据结构。比如微信朋友圈可以以发表时间作为 score 来存储,这样获取时就是自动按时间排好序的。

set 相比,zset 关联了一个 double 类型权重参数 score,使得集合中的元素能够按 score 进行有序排列。

同样地,与其它模块相似,使用 setzset 缓存服务的步骤如下

  1. 创建 Set/ZSet 缓存模块
  2. 获取 DCache 接口文件
  3. 创建缓存服务代理
  4. 调用 Set/ZSet 缓存模块服务

接下来将继续基于 TestDemo 介绍如何创建和使用 Set/ZSet 缓存模块。

本文使用的示例可以在 GitHub 仓库 DCacheDemo(文末附链接) 中查看。

前面的文章我们已经介绍过缓存模块的创建,各类型缓存模块创建流程是相似的。这里 Set 缓存服务命名为 TestDemoSetcache 类型 选择 Set(MKVCache)

新建 ZSet 缓存服务命名为 TestDemoZSetcache 类型 选择

对于步骤 2 和 3,我们已经在前面的系列文章中介绍过,本文不再赘述。还不了解的朋友请移步DCache 分布式存储系统|Key-Value 缓存模块的创建与使用

本部分将通过简单示例,介绍 setzset 类型缓存模块部分接口的使用。关于其它接口的信息,参见 Proxy 接口指南(文末附链接)

我们继续使用 TestDemo,新增模块名 ModuleTestDemoSetModuleTestDemoZSet,值为我们前面创建的模块名 TestDemoSetTestDemoZSet,用于之后通过代理调用这两个模块,如下。

代码语言:javascript复制
// main.cpp...static string ModuleTestDemoSet= "TestDemoSet";static string ModuleTestDemoZSet= "TestDemoZSet";...

接口调用流程与 TARS 服务接口调用流程一致。如果你还不清楚 TARS 服务的调用方式和流程,可以阅读文章TARS RPC 通信框架|提供多种远程调用方式了解 TARS 服务的调用方式

后面的示例中,会使用到三个工具函数,定义如下

代码语言:javascript复制
// 构建 UpdateValueDCache::UpdateValue genUpdateValue(DCache::Op op, const string &value){    DCache::UpdateValue updateValue;    updateValue.op = op;    updateValue.value = value;    return updateValue;}// 打印 map<string, string> 类型数据void printMapData(const map<string, string> &data){    map<string, string>::const_iterator it = data.begin();    while(it != data.end())    {        cout << "|"<< it->first << ":"<< it->second;          it;    }    cout << endl;}// 打印 vector<map> 数据void printVectorMapData(const vector<map<string, string>> &data){    for(auto item : data)    {        printMapData(item);    }}

那么接下来,我们来看看怎么使用 DCache 的 Set/ZSet 缓存模块。

Set 模块读写操作

Set 为集合缓存模块。这里介绍写接口 addSet 和读接口 getSet,其它接口用法相似。

向集合添加值

接口 addSet 用于向特定集合添加值,定义如下

代码语言:javascript复制
int addSet(const AddSetReq &req)

其中结构 AddSetReq 及其嵌套结构 AddSetKeyValue 的定义如下

代码语言:javascript复制
struct AddSetReq{ 1 require string moduleName;     // 模块名 2 require AddSetKeyValue value;  // 待写入数据};struct AddSetKeyValue{ 1 require string mainKey;  // 主key 2 require map<string, UpdateValue> data;  // 其他字段数据 3 require int expireTime;  // 过期时间 4 require bool dirty = true;  // 是否设置为脏数据};

使用示例如下

代码语言:javascript复制
void testAddSet(const string &mainKey, const map<string, string> &data, DCache::ProxyPrx prx){    cout << SUBTEST_PREFIX << "addSet ";    // 构造请求    DCache::AddSetReq req;    req.moduleName = ModuleTestDemoSet;    req.value.mainKey = mainKey;    req.value.expireTime = time(NULL)   60* 60* 24;        map<string, string>::const_iterator it = data.begin();    while(it != data.end())    {        req.value.data[it->first] = genUpdateValue(DCache::SET, it->second);          it;    }    int ret = prx->addSet(req);    if(ret == DCache::ET_SUCC)    {        printMapData(data);        return;    }    cout << "ret:"<< ret << endl;}
获取集合

接口 getSet 用于获取集合中的数据,定义如下

代码语言:javascript复制
int getSet(const GetSetReq &req, BatchEntry &rsp)

其中请求消息结构 GetSetReq 和返回消息结构 BatchEntry 定义如下

代码语言:javascript复制
struct GetSetReq{    1 require string moduleName;  //模块名    2 require string mainKey;  //主key    3 require string field;  //需要查询的字段集,多个字段用','分隔如 "a,b", "*"表示所有    
    4 require string idcSpecified = "";  //idc区域};struct BatchEntry{1 require vector<map<string, string>> entries;  //查询结果集合};

使用示例如下

代码语言:javascript复制
void testGetSet(const string &mainKey, DCache::ProxyPrx prx){    cout << SUBTEST_PREFIX << "getSet "<< endl;    
    // 构造请求    DCache::GetSetReq req;    req.moduleName = ModuleTestDemoSet;    req.mainKey = mainKey;    req.field = "*";    DCache::BatchEntry rsp;    int ret = prx->getSet(req, rsp);    if(ret == DCache::ET_SUCC)    {            // 打印返回值        printVectorMapData(rsp.entries);        return;    }    cout << "ret:"<< ret << endl;}

ZSet 模块读写操作

ZSet 即有序集合缓存模块,这里介绍写接口 addZSet 和读接口 getZSetByPos,其它接口用法类似。

向集合添加值和权重

接口 addZSet 用于向集合添加数据值及其权重,定义如下

代码语言:javascript复制
int addZSet(const AddZSetReq &req)

其中请求消息结构体 AddZSetReq 及其嵌套结构体 AddSetKeyValue的定义如下

代码语言:javascript复制
AddZSetReq{    1 require string moduleName;  //模块名    2 require AddSetKeyValue value;  //待写入数据    3 require double score;  //待写入数据分值};struct AddSetKeyValue{    1 require string mainKey;  //主key    2 require map<string, UpdateValue> data; //其他字段数据    3 require int expireTime;  //数据过期时间    4 require bool dirty = true;  //是否设置为脏数据};

使用示例如下

代码语言:javascript复制
void testAddZSet(const string &mainKey, const map<string, string> &data, const double &score, DCache::ProxyPrx prx){    cout << SUBTEST_PREFIX << "addZSet ";    // 构造请求    DCache::AddZSetReq req;    req.moduleName = ModuleTestDemoZSet;    req.value.mainKey = mainKey;    req.score = score;        map<string, string>::const_iterator it = data.begin();    while(it != data.end())    {        req.value.data[it->first] = genUpdateValue(DCache::SET, it->second);          it;    }    int ret = prx->addZSet(req);    if(ret == DCache::ET_SUCC)    {        printMapData(data);        return;    }    cout << "ret:"<< ret << endl;}
获取集合

接口 getZSetByPos 用于获取集合中指定索引区间内的数据,定义如下

代码语言:javascript复制
int getZSetByPos(const GetZsetByPosReq &req, BatchEntry &rsp)

其中请求消息结构体 GetZsetByPosReq 的定义如下

代码语言:javascript复制
struct GetZsetByPosReq{    1 require string moduleName;  //模块名    2 require string mainKey;  //主key    3 require string field;  //需要查询的字段集,多个字段用','分隔如 "a,b", "*"表示所有    4 require long start;  //开始索引    5 require long end;  //结束索引    6 require bool positiveOrder = true; //true表示返回的结果按递增排序,false表示递减    7 require string idcSpecified = "";  //idc区域};struct BatchEntry{    1 require vector<map<string, string>> entries;  //查询结果数据集合};

使用示例如下

代码语言:javascript复制
void testGetZSet(const string &mainKey, DCache::ProxyPrx prx){    cout << SUBTEST_PREFIX << "getZSet "<< endl;    
    // 构造请求    DCache::GetZsetByPosReq req;    req.moduleName = ModuleTestDemoZSet;    req.mainKey = mainKey;    req.field = "*";    req.start = 0;    req.end= 3;    
    DCache::BatchEntry rsp;    int ret = prx->getZSetByPos(req, rsp);   
    if(ret == DCache::ET_SUCC)    {        // 打印返回值        printVectorMapData(rsp.entries);        return;    }    cout << "ret:"<< ret << endl;}

实例

我们来实际运行一下上面的使用示例。完整的使用示例可以在 GitHub 仓库 DCacheDemo 中获取。

我们通过 testSettestZSet 测试上节提到的接口,分别向 Set 和 ZSet 缓存服务中依次添加值 hello, hello, hi, test;并且向 ZSet 服务添加的值附带权重,如下

代码语言:javascript复制
void testSet(DCache::ProxyPrx prx){    cout << START << " testSet"<< endl;    string mainKey = "testKey";    map<string, string> data1, data2, data3, data4;    data1["VALUE"] = "hello";    data2["VALUE"] = "hello";    data3["VALUE"] = "hi";    data4["VALUE"] = "test";   
    testAddSet(mainKey, data1, prx);    testAddSet(mainKey, data2, prx);    testAddSet(mainKey, data3, prx);    testAddSet(mainKey, data4, prx);        testGetSet(mainKey, prx);    cout << END<< " testSet"<< endl;}void testZSet(DCache::ProxyPrx prx){    cout << START << " testZSet"<< endl;   
    string mainKey = "testKey";    map<string, string> data1, data2, data3, data4;    double score1, score2, score3, score4;    data1["VALUE"] = "hello";    score1 = 0.1;    data2["VALUE"] = "hello";    score2 = 0.1;    data3["VALUE"] = "hi";    score3 = 0.8;    data4["VALUE"] = "test";    score4 = 0.5;        testAddZSet(mainKey, data1, score1, prx);    testAddZSet(mainKey, data2, score2, prx);    testAddZSet(mainKey, data3, score3, prx);    testAddZSet(mainKey, data4, score4, prx);        testGetZSet(mainKey, prx);    cout << END<< " testZSet"<< endl;}

接着,在 main 函数中执行

代码语言:javascript复制
int main(int argc, char*argv[]){    ...        auto prx = comm->stringToProxy<DCache::ProxyPrx>(DCacheTestDemoObj);        
        // 调用 DCache 缓存服务        testSet(prx);        testZSet(prx);    ...}

执行结果如下

除了前面提到的向集合添加和获取数据,DCache 中还提供了丰富的集合操作接口,如下

代码语言:javascript复制
/**************** Set ****************/// 查询集合数据int getSet(GetSetReq req, out BatchEntry rsp);// 向集合添加数据int addSet(AddSetReq req);// 删除指定的一条集合数据int delSet(DelSetReq req);/**************** ZSet ****************/// 根据指定条件,查询某条记录的 score 值int getZSetScore(GetZsetScoreReq req, out double score);// 根据指定条件,查询某条记录在已排序列表的索引位置int getZSetPos(GetZsetPosReq req, out long pos);// 查询集合内指定索引区间[start, end]内的数据int getZSetByPos(GetZsetByPosReq req, out BatchEntry rsp);// 查询分值区间[minScore, maxScore]内的数据int getZSetByScore(GetZsetByScoreReq req, out BatchEntry rsp);// 将带有给定分值的数据添加到有序集合中,如果数据已存在,则重置 score 值int addZSet(AddZSetReq req);// 修改有序集合中某条记录的分值,若数据不存在,则新建一条数据int incScoreZSet(IncZSetScoreReq req);// 删除有序集合中符合指定条件的某条数据int delZSet(DelZSetReq req);// 从有序集合中删除分值在区间[minScore, maxScore)的数据int delZSetByScore(DelZSetByScoreReq req);// 根据指定条件更新有序集合的某条数据int updateZSet(UpdateZSetReq req);

接口的使用方式与前面介绍的类似,关于接口的具体入参和出参结构可以参考 Proxy 接口指南(文末附链接)

本文简要介绍了 DCache 中的 setzset 缓存模块的原理和使用流程,同时通过具体实例对部分接口的使用进行了详细介绍,帮助读者理解并能够快速上手使用 setzset 缓存模块。

文中链接:

DCacheDemo:

https://github.com/ETZhangSX/DCacheDemo

Proxy 接口指南:

https://github.com/Tencent/DCache/blob/master/docs/proxy_api_guide.md

0 人点赞