疑问:
ssdb的zset底层编码如何
ssdb的zset从响应请求到存储的过程如何
分析:
- ssdb的zset底层编码
zset,还有其它的结构并不像redis那样自己实现存储引擎,像redis就是有各种各种的底层编码例如字典,压缩列表,双向链表啊,但是ssdb用的是leveldb做的存储引擎,统一使用编码后的k-v存储
接下来分析下:ssdb的zset从响应请求到存储的过程如何:
int proc_zset(NetworkServer *net, Link *link, const Request &req, Response *resp){
SSDBServer *serv = (SSDBServer *)net->data;
CHECK_NUM_PARAMS(4);
int ret = serv->ssdb->zset(req[1], req[2], req[3]);
resp->reply_int(ret, ret);
return 0;
}
很简单,从proc_zset入手了,
int SSDBImpl::zset(const Bytes &name, const Bytes &key, const Bytes &score, char log_type){
Transaction trans(binlogs);
int ret = zset_one(this, name, key, score, log_type);
if(ret >= 0){
if(ret > 0){
if(incr_zsize(this, name, ret) == -1){
return -1;
}
}
leveldb::Status s = binlogs->commit();
if(!s.ok()){
log_error("zset error: %s", s.ToString().c_str());
return -1;
}
}
return ret;
}
关于Bytes的说明可以看下bytes.h,其实就是对字符串例如const char*,string进行替换
如图:ssdb实例imple执行zset命令后需要执行zset(),主要的工作就是进行key,socre的编码转换,binlog队列的同步。我们重点看下编码转换:
static int zset_one(SSDBImpl *ssdb, const Bytes &name, const Bytes &key, const Bytes &score, char log_type){
if(name.empty() || key.empty()){
log_error("empty name or key!");
return 0;
//return -1;
}
if(name.size() > SSDB_KEY_LEN_MAX ){
log_error("name too long!");
return -1;
}
if(key.size() > SSDB_KEY_LEN_MAX){
log_error("key too long!");
return -1;
}
std::string new_score = filter_score(score);
std::string old_score;
int found = ssdb->zget(name, key, &old_score);
if(found == 0 || old_score != new_score){
std::string k0, k1, k2;
if(found){
// delete zscore key
k1 = encode_zscore_key(name, key, old_score);
ssdb->binlogs->Delete(k1);
}
// add zscore key
k2 = encode_zscore_key(name, key, new_score);
ssdb->binlogs->Put(k2, "");
// update zset
k0 = encode_zset_key(name, key);
ssdb->binlogs->Put(k0, new_score);
ssdb->binlogs->add_log(log_type, BinlogCommand::ZSET, k0);
return found? 0 : 1;
}
return 0;
}
如图所示,首先对放入的参数进行简单的检查,然后再进行find,并进行必要的旧数据覆盖(其实就是先删除旧的数据,关于binlog这块不是这里的讨论重点,但是说实话,binglog是相当重要也很复杂的,主要是进行sync),紧接着是分别对score和key进行编码:
static inline
std::string encode_zscore_key(const Bytes &key, const Bytes &val, const Bytes &score){
std::string buf;
buf.reserve(128);
buf.append(1, DataType::ZSCORE);
buf.append(1, (uint8_t)key.size());
buf.append(key.data(), key.size());
int64_t s = score.Int64();
if(s < 0){
buf.append(1, '-');
}else{
buf.append(1, '=');
}
s = encode_score(s);
buf.append((char *)&s, sizeof(int64_t));
buf.append(1, '=');
buf.append(val.data(), val.size());
return buf;
}
bytes.Int64(),是将字符串转化为int64类型的数据,实现代码最终调用的是strings.h中的str_to_int64(const std::string &str),
看下encode_score:
static inline
uint64_t big_endian(uint64_t v){
uint32_t h = v >> 32;
uint32_t l = v & 0xffffffffull;
return big_endian(h) | ((uint64_t)big_endian(l) << 32);
}
其实是big_endian的一个macro,
encode_zscore_key最终的结果就是type, len, key, score, =, val并返回。
好,回到zset_one,看下key的处理:
static inline
std::string encode_zset_key(const Bytes &name, const Bytes &key){
std::string buf;
buf.reserve(128);
buf.append(1, DataType::ZSET);
buf.append(1, (uint8_t)name.size());
buf.append(name.data(), name.size());
buf.append(1, (uint8_t)key.size());
buf.append(key.data(), key.size());
return buf;
}
结果就是:ZSET,name.name.size,key,key.size
接下来就是将编码后的key和socre写入binlog异步刷入leveldb。