打造先进的内存KV数据库-1 B树索引的建立(1)

来源:互联网 发布:淘宝宝贝页面怎么装修 编辑:程序博客网 时间:2024/05/16 05:46

设计目的

在搜索引擎的设计中,往往需要使用倒排索引,在当前内存价格不断走低的情况下,内存数据库必然会成为主流。KV数据库由于适合Map-Reduce用于分布式处理。
本系统设计实现如下目标:
* 实现极高性能的查询
* 实现分布式集群储存
* 实现可靠的日志系统

索引设计

索引采用B数索引,这样做的目的是大大利用CPU的缓存,让每个节点的大小与CPU二级缓存相匹配,另外,将索引值连续储存在节点中,可以减少TLB失配的次数。由于B树的结构,树高不会太高,设想在16GB的内存中建立数据库,数据库使用8G内存,每条记录1K,那么就有8M个键,如果是每个节点储存256个节点的B树,那么log256(8M)=3,树高是3,每次查询节点跳转最多2次,节点内二分查找是在高速缓存中进行,高速缓存的效率大约是装载内存读取的4~5倍(6000次查询),节点内二分查找的最大次数为8次,数据可能可以优化,在后续实验中进行。
对于为搜索引擎定制的数据库,对于插入的性能要求不是很高,之前做过的实验表明,爬取的性能瓶颈在于网络IO,网络延迟远远大于数据库插入,故可以牺牲插入性能来获得较高的查询性能。

代码实现

以下是B树索引的查询以及部分插入实现:

//b_tree.gopackage index/*    最大程度提高查询效率,忽略插入效率,插入效率被爬虫爬取效率瓶颈*/const (    L2_cache_size = 256*1024            //256KB L2缓存    node_data_num = L2_cache_size / 64    max_support_key_length = 8          //最大支持8个字节的关键字查询)type node struct {    hash_key [node_data_num]uint64      //1/4    hash_key_num int    primary_key [node_data_num]uint64   //1/4    child [node_data_num + 1]*node      //1/4    is_leaf bool                        //0}type B_Tree struct {    root node   //树根    height int  //树高}func (bt * B_Tree) Init() {    bt.height = 1    bt.root.is_leaf = false    bt.root.hash_key_num = 0}type SelectError struct {    less bool}func (se SelectError) Error() (string) {    return "not found"}//获得hash值//取前8个字节作为hashfunc hash(text []byte) (uint64) {    hash_ := uint64(0)    length := len(text)    if length > max_support_key_length {        length = max_support_key_length    }    for i := 0;i < length;i++ {        hash_ *= 256        hash_ += uint64(text[i])    }    return hash_}//查找指定索引 如果找到返回找到的node和position,失败的话返回最近的node和position,//并返回返回值相对于查询值是大了还是小了 true->小 false->大 优先返回大 供插入时插在前面func (bt *B_Tree) Select(index []byte) (*node,int,uint64,error) {    if bt.root.hash_key_num == 0 {        return &bt.root,0,0,SelectError{false}    }    hash_key := hash(index)    node_ := &bt.root    pos_r := 0                                  //返回位置    var err SelectError                             //返回错误    var node_r *node    for node_ != nil{        if node_.hash_key_num > 0 {            //超出节点边界            if node_.hash_key[0] > hash_key {                node_r = node_              //返回node,防止变成nil                pos_r = 0                err.less = false                    //返回大于                node_ = node_.child[0]                break            }            if node_.hash_key[node_.hash_key_num - 1] < hash_key {                node_r = node_              //返回node,防止变成nil                pos_r = node_.hash_key_num                err.less = true                 //返回小于                node_ = node_.child[node_.hash_key_num]                break            }            //进行二分查找            pos := node_.hash_key_num / 2            c := node_.hash_key_num / 2            for {                if node_.hash_key[pos] == hash_key {                    return node_,pos,node_.primary_key[pos],nil //找到                } else if node_.hash_key[pos] < hash_key {                    if node_.hash_key[pos + 1] > hash_key {                        node_r = node_              //返回node,防止变成nil                        pos_r = pos + 1                        err.less = false                    //返回大于                        node_ = node_.child[pos]                        break                    }                    c /= 2                    if c == 0 { c=1 }                    pos += c                } else if node_.hash_key[pos] > hash_key {                    if node_.hash_key[pos - 1] < hash_key {                        node_r = node_              //返回node,防止变成nil                        pos_r = pos                        err.less = false                    //返回大于                        node_ = node_.child[pos - 1]                        break                    }                    c /= 2                    if c == 0 { c=1 }                    pos -= c                }            }        }    }    return node_r,pos_r,0,err}func (bt *B_Tree) Set(text []byte,primary_key uint64) (error) {    node_,pos,_,err := bt.Select(text)  //查询    if err == nil {                         //找到了        //TODO 和储存引擎协调进行    } else {        //TODO 储存引擎加入记录        if node_.hash_key_num < node_data_num { //有空间可以插入            if err.(SelectError).less {         //插在最后一个:只有查询到最后一个还小的时候才会返回less                node_.hash_key[node_.hash_key_num] = hash(text)                node_.primary_key[node_.hash_key_num] = primary_key                node_.hash_key_num++            }else {                             //插在前面                for i := node_.hash_key_num;i > pos;i-- {                    node_.hash_key[i] = node_.hash_key[i - 1]                    node_.primary_key[i] = node_.primary_key[i - 1]                }                node_.hash_key[pos] = hash(text)                node_.primary_key[pos] = primary_key                node_.hash_key_num++            }        }        //TODO 空间不足的情况    }    return nil}

单元测试(未测试多层~待续)

package indeximport (    _"fmt"    "testing")func expected(t *testing.T,expecting uint64,real uint64) {    if expecting != real {        t.Error("-Expected-")        t.Error(expecting)        t.Error("-Real-")        t.Error(real)    }}func Test_hash(t *testing.T) {    expected(t,15108241,hash([]byte("我")))    expected(t,14990752,hash([]byte("你")))    expected(t,14990230,hash([]byte("他")))    expected(t,0,hash([]byte{}))}func Test_Select(t *testing.T) {    bt := new(B_Tree)    bt.Init()    bt.root.hash_key[2] = 15108241    bt.root.hash_key[1] = 14990752    bt.root.hash_key[0] = 14990230    bt.root.hash_key_num = 3    bt.root.primary_key[2] = 2    bt.root.primary_key[1] = 1    bt.root.primary_key[0] = 0    _,_,i,_ := bt.Select([]byte("我"))    expected(t,2,i)    _,_,i,_ = bt.Select([]byte("你"))    expected(t,1,i)    _,_,i,_ = bt.Select([]byte("他"))    expected(t,0,i)    _,_,i,_ = bt.Select([]byte("呵呵"))    expected(t,0,i)    _,_,i,_ = bt.Select([]byte("呼呼"))    expected(t,0,i)}func Test_Set(t *testing.T) {    bt := new(B_Tree)    bt.Set([]byte("我"),0)    _,_,i,_ := bt.Select([]byte("我"))    expected(t,0,i)    bt.Set([]byte("蔡佳楠"),1)    _,_,i,_ = bt.Select([]byte("蔡佳楠"))    expected(t,1,i)    bt.Set([]byte("杨明亮"),2)    _,_,i,_ = bt.Select([]byte("杨明亮"))    expected(t,2,i)    bt.Set([]byte("杨奕辉"),3)    _,_,i,_ = bt.Select([]byte("杨奕辉"))    expected(t,3,i)}
1 0
原创粉丝点击