在之前的系列文章中,我们介绍了 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-row
和 list
两种类型缓存模块的使用方式,本文将继续介绍集合类型,set
和 zset
缓存模块的使用。
set
即集合,与 list
类似,以列表形式存储数据。不同的地方在于 set
是会对添加的数据进行排重的。如果你需要存储一个列表数据,又不希望出现重复数据时,set
是一个很好的选择。
zset
为有序集合,使用场景与 set
类似,但 set
并不是自动有序的。在 zset
中,提供了一个的参数 score 来为数据成员排序。当你需要一个有序的并且不重复的集合列表,那么可以选择 zset
数据结构。比如微信朋友圈可以以发表时间作为 score 来存储,这样获取时就是自动按时间排好序的。
和 set
相比,zset
关联了一个 double
类型权重参数 score,使得集合中的元素能够按 score 进行有序排列。
同样地,与其它模块相似,使用 set
和 zset
缓存服务的步骤如下
- 创建 Set/ZSet 缓存模块
- 获取 DCache 接口文件
- 创建缓存服务代理
- 调用 Set/ZSet 缓存模块服务
接下来将继续基于 TestDemo
介绍如何创建和使用 Set/ZSet 缓存模块。
本文使用的示例可以在 GitHub 仓库 DCacheDemo(文末附链接) 中查看。
前面的文章我们已经介绍过缓存模块的创建,各类型缓存模块创建流程是相似的。这里 Set 缓存服务命名为 TestDemoSet
,cache 类型
选择 Set(MKVCache)
新建 ZSet 缓存服务命名为 TestDemoZSet
,cache 类型
选择
对于步骤 2 和 3,我们已经在前面的系列文章中介绍过,本文不再赘述。还不了解的朋友请移步DCache 分布式存储系统|Key-Value 缓存模块的创建与使用
本部分将通过简单示例,介绍 set
和 zset
类型缓存模块部分接口的使用。关于其它接口的信息,参见 Proxy 接口指南(文末附链接)。
我们继续使用 TestDemo
,新增模块名 ModuleTestDemoSet
和 ModuleTestDemoZSet
,值为我们前面创建的模块名 TestDemoSet
和 TestDemoZSet
,用于之后通过代理调用这两个模块,如下。
// 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
用于向特定集合添加值,定义如下
int addSet(const AddSetReq &req)
其中结构 AddSetReq
及其嵌套结构 AddSetKeyValue
的定义如下
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
用于获取集合中的数据,定义如下
int getSet(const GetSetReq &req, BatchEntry &rsp)
其中请求消息结构 GetSetReq
和返回消息结构 BatchEntry
定义如下
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
用于向集合添加数据值及其权重,定义如下
int addZSet(const AddZSetReq &req)
其中请求消息结构体 AddZSetReq
及其嵌套结构体 AddSetKeyValue
的定义如下
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
用于获取集合中指定索引区间内的数据,定义如下
int getZSetByPos(const GetZsetByPosReq &req, BatchEntry &rsp)
其中请求消息结构体 GetZsetByPosReq
的定义如下
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 中获取。
我们通过 testSet
和 testZSet
测试上节提到的接口,分别向 Set 和 ZSet 缓存服务中依次添加值 hello
, hello
, hi
, test
;并且向 ZSet 服务添加的值附带权重,如下
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
函数中执行
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 中的 set
和 zset
缓存模块的原理和使用流程,同时通过具体实例对部分接口的使用进行了详细介绍,帮助读者理解并能够快速上手使用 set
和 zset
缓存模块。
文中链接:
DCacheDemo:
https://github.com/ETZhangSX/DCacheDemo
Proxy 接口指南:
https://github.com/Tencent/DCache/blob/master/docs/proxy_api_guide.md