SSDB --- zset实现
来源:互联网 发布:徐老师淘宝店地址链接 编辑:程序博客网 时间:2024/06/11 16:20
zset
zset是一种很有用的数据结构,你可能听说过set,但什么是zset呢?zset就是sorted_set(排序集),STL里有set,底层是用红黑树实现的,并没有提供sorted_set的实现,要实现一个zset并不难,比如SSDB的作者就用map和set实现了一个简单的zset。代码在ssdb/src/util/sorted_set.h sorted_set.cpp中。
sorted_set.h
class SortedSet{public: bool empty() const{ return size() == 0; } int size() const; int add(const std::string &key, int64_t score); // 0: not found, 1: found and deleted int del(const std::string &key); // the first item is copied into key if SortedSet not empty int front(std::string *key, int64_t *score=NULL) const; int back(std::string *key, int64_t *score=NULL) const; int64_t max_score() const; int pop_front(); int pop_back();};
sorted_set.cpp
#include "sorted_set.h"int SortedSet::size() const{ return (int)sorted_set.size();}int SortedSet::add(const std::string &key, int64_t score){ int ret; std::map<std::string, std::set<Item>::iterator>::iterator it; it = existed.find(key); if(it == existed.end()){ // new item ret = 1; }else{ ret = 0; std::set<Item>::iterator it2 = it->second; const Item &item = *it2; if(item.score == score){ // not updated return 0; } // remove existing item sorted_set.erase(it2); } Item item; item.key = key; item.score = score; std::pair<std::set<Item>::iterator, bool> p = sorted_set.insert(item); existed[key] = p.first; return ret;}int SortedSet::del(const std::string &key){ int ret; std::map<std::string, std::set<Item>::iterator>::iterator it; it = existed.find(key); if(it == existed.end()){ // new item ret = 0; }else{ ret = 1; sorted_set.erase(it->second); existed.erase(it); } return ret;}int SortedSet::front(std::string *key, int64_t *score) const{ std::set<Item>::iterator it2 = sorted_set.begin(); if(it2 == sorted_set.end()){ return 0; } const Item &item = *it2; *key = item.key; if(score){ *score = item.score; } return 1;}int SortedSet::back(std::string *key, int64_t *score) const{ std::set<Item>::reverse_iterator it2 = sorted_set.rbegin(); if(it2 == sorted_set.rend()){ return 0; } const Item &item = *it2; *key = item.key; if(score){ *score = item.score; } return 1;}int64_t SortedSet::max_score() const{ int64_t score = 0; std::string key; this->back(&key, &score); return score;}int SortedSet::pop_front(){ if(sorted_set.empty()){ return 0; } std::set<Item>::iterator it = sorted_set.begin(); const Item &item = *it; existed.erase(item.key); sorted_set.erase(it); return 1;}int SortedSet::pop_back(){ if(sorted_set.empty()){ return 0; } std::set<Item>::iterator it = sorted_set.end(); it --; const Item &item = *it; existed.erase(item.key); sorted_set.erase(it); return 1;}
很简单是不是,sorted_set 根据score排序而不是key,但今天要说的zset实现不是上面的代码,上面的简单实现只是用来处理ttl的。
下面来说说基于leveldb的zset实现。
zset实现分析
zset的实现在 src/ssdb/t_zset.h t_zset.cpp 下。
zset的接口如下:
/* zset */ virtual int zset(const Bytes &name, const Bytes &key, const Bytes &score, char log_type=BinlogType::SYNC); virtual int zdel(const Bytes &name, const Bytes &key, char log_type=BinlogType::SYNC); // -1: error, 1: ok, 0: value is not an integer or out of range virtual int zincr(const Bytes &name, const Bytes &key, int64_t by, int64_t *new_val, char log_type=BinlogType::SYNC); //int multi_zset(const Bytes &name, const std::vector<Bytes> &kvs, int offset=0, char log_type=BinlogType::SYNC); //int multi_zdel(const Bytes &name, const std::vector<Bytes> &keys, int offset=0, char log_type=BinlogType::SYNC); virtual int64_t zsize(const Bytes &name); /** * @return -1: error; 0: not found; 1: found */ virtual int zget(const Bytes &name, const Bytes &key, std::string *score); virtual int64_t zrank(const Bytes &name, const Bytes &key); virtual int64_t zrrank(const Bytes &name, const Bytes &key); virtual ZIterator* zrange(const Bytes &name, uint64_t offset, uint64_t limit); virtual ZIterator* zrrange(const Bytes &name, uint64_t offset, uint64_t limit); /** * scan by score, but won't return @key if key.score=score_start. * return (score_start, score_end] */ virtual ZIterator* zscan(const Bytes &name, const Bytes &key, const Bytes &score_start, const Bytes &score_end, uint64_t limit); virtual ZIterator* zrscan(const Bytes &name, const Bytes &key, const Bytes &score_start, const Bytes &score_end, uint64_t limit); virtual int zlist(const Bytes &name_s, const Bytes &name_e, uint64_t limit, std::vector<std::string> *list); virtual int zrlist(const Bytes &name_s, const Bytes &name_e, uint64_t limit, std::vector<std::string> *list);
实现如下:
只看插入 即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;}
主要调用了 zset_one:
// returns the number of newly added itemsstatic 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);//先查询是否有旧的值,有的话记录old值 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);//将三个值编码成底层leveldb 操作的key。也就是旧的score ssdb->binlogs->Delete(k1);//删掉 } // add zscore key k2 = encode_zscore_key(name, key, new_score); ssdb->binlogs->Put(k2, "");//插入新的score // update zset k0 = encode_zset_key(name, key); ssdb->binlogs->Put(k0, new_score); //提交leveldb ssdb->binlogs->add_log(log_type, BinlogCommand::ZSET, k0); //写log return found? 0 : 1; } return 0;}
问题来了,新的记录插入,如何排序呢?
靠的是 encode_zscore_key函数,这个函数要确保 两个score 编码前后 大小关系一致,也就是说 如果编码前score1> score2,则编码后 e_score1 > e_score2 依然成立。
static inline std::string encode_zscore_key(const Bytes &key, const Bytes &val, const Bytes &score){//zset_one 调用此函数时 传入的参数分别是:name、key、score//例如 client 输入 命令 zset 'z' 1 100//再输入 第二条 zset 'z' 2 99// 'z' 对应 name 2 对应key 99 对应score// 传入到encode 函数后 'z' 对应 key, 2 对应 value , 99对应 score。//如何保证编码以score决定大小,而排除key的影响呢? std::string buf; buf.append(1, DataType::ZSCORE); //这个append 都一样 buf.append(1, (uint8_t)key.size()); //这个 append 也都一样 都是 'z' 的size buf.append(key.data(), key.size());//都是 'z' 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));//接着就append 了score 99 到这里已经比出了大小 string 只比较第一个不一样的 字符。 buf.append(1, '='); buf.append(val.data(), val.size()); //最后才append 2 return buf;}
为什么传入leveldb的key 可比较大小,就实现了soreted_set 呢?
这是因为 leveldb的内存实现是skiplist,它是排序的。磁盘文件sst也是排序的.
zget 很简单就不讲了。
0 0
- SSDB --- zset实现
- ssdb zset操作(Ⅰ)
- SSDB --- queue实现
- zset
- zset
- ssdb
- ssdb
- SSDB
- 一个lua版的zset数据结构实现
- 基于redis的zset实现排行榜功能
- Redis中zset的golang实现
- 使用 SSDB 来实现操作频率限制
- 用python实现一个redis的zset数据结构
- redis Zset
- redis-zset
- redis zset ( [
- SSDB使用
- ssdb 安装:
- 怎样登录和退出Linux系统----Linux学习笔记(1)
- 天 津 代 开 发 票
- 天 津 开 发 票
- Buffer同步 ,并行读取流文件
- 明晰C内存分配的五种方法的区别
- SSDB --- zset实现
- 开 天 津 发 票
- 压缩算法的比较
- 题目1371:最小的K个数
- SVN服务器搭建和使用(三)
- 成 都 开 发 票
- 远程连接阿里云服务器----Linux学习笔记(2)
- 使用DirectSound播放MP3文件
- HDU 5253 连接的管道 —— 并查集