打造先进的内存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
- 打造先进的内存KV数据库-1 B树索引的建立(1)
- 打造先进的内存KV数据库-2 B树索引的建立(2)
- 打造先进的内存KV数据库-3 最终选择-C & B-Tree
- 打造先进的内存KV数据库-5 TCP侦听
- 打造先进的内存KV数据库-6 PHP支持
- 打造先进的内存KV数据库-4 伙伴内存管理系统
- 打造先进的内存KV数据库-7 反射以及并发锁
- 打造先进的SOA应用
- [数据库]索引的建立
- 数据库建立索引的原则
- 数据库建立索引的原则
- 数据库建立索引的原则
- 数据库建立索引的原则
- 数据库建立索引的原则
- 数据库建立索引的原则
- 数据库建立索引的原则
- 数据库建立索引的原则
- 数据库建立索引的原则
- Java 获取字符串中第N次出现的字符位置
- Android学习笔记:[1]Option Menu(选项菜单)
- Java总结
- JAVA的JS引擎实例(包含Excel读取)
- 【学习笔记--视频拼接】如何提高计算效率 part_1
- 打造先进的内存KV数据库-1 B树索引的建立(1)
- css3媒体查询
- Yarn的初步学习
- 使用jQuery+PHP+Mysql实现抽奖程序
- ViewFlipper的使用---自动播放(轮播图)的实现以及多个view进行切换
- keepalived 负载均衡安装配置
- MAC 活动监视器中 nsurlsessiond 狂走流量问题
- SpringMVC简单构造restful, 并返回json
- Java中 Happen-before 规则总结