ssdb-zset浅析

疑问:
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。
























猜你喜欢

转载自blog.csdn.net/m0_37579159/article/details/79363447