CockroachDB开发学习文档03 KV编码

在上一篇文档中,export导出有一些问题

为了深入理解KV的存储结构,我们参考文档:

https://github.com/cockroachdb/cockroach/blob/master/docs/tech-notes/encoding.md

CRDB中的数据结构编码方式

像其他的键值数据库数据库一样,CRDB将数据以“键-值对”的这种形式存储,这种存储方式在不断地发展,能够保证向前向后兼容。

CRDB的键值存储有三个版本,

第一个版本在https://www.cockroachlabs.com/blog/sql-in-cockroachdb-mapping-table-data-to-key-value-storage/

第二个版本在https://www.cockroachlabs.com/blog/sql-cockroachdb-column-families/

下面创建一张表,并插入一条数据,简单看看3个版本是如何存储数据的。

CREATE TABLE test (
      key       INT PRIMARY KEY,
      floatVal  FLOAT,
      stringVal STRING
)
INSERT INTO test VALUES (10, 4.5, "hello”)
 
  • 第1个版本的存储格式如下:

    KeyValue
    /test/10/floatVal4.5
    /test/10/stringVal"hello”

    这种方式很简单,key的存储方式为:/表名/主键值/列名,value直接记录这个值。

    事实上,还存在一张元数据的表:

    test Table ID1000
    key Column ID1
    floatVal Column ID2
    stringVal Column ID3

    那么将上面各位置的信息替换成元数据表中的值,键值对的存储形式如下:

    KeyValue
    /1000/10/24.5
    /1000/10/3"hello”

    是不是更简单了。

  • 第2个版本的存储方式如下:
    首先,版本2引入了一个重要的概念,列簇(column family),加入列簇后,KV的插入、更新、删除操作相比于版本1平均快了5倍。
    版本1的编码格式有以下几个问题:
    (1)key的主键前缀重复,如有100个字段,“/表名/主键值/”重复了100次,并且value中前4个字节是循环冗余校验码,当数据量大的时候,非常浪费磁盘空间和网络带宽。

    (2)最严重的问题是在提交事务时,每个key都要以“write intent”的方式与其关联,以便进行后续的解析。

    自然而然,很多的NOSQL数据库设计就会不约而同地提出一种解决方案:列簇(column family),将多个相似字段汇聚成一个值。

    现在的存储格式如下所示:

    KeyValue
    /<tableid>/0/11/0
    <crc>/1/string/"Hal"/1/string/"hal@cockroachlabs.com"
    /<tableid>/0/13/0
    <crc>/1/string/"Orin"/1/string/"orin@cockroachlabs.com"

     

    key:/tableid/index/pk val/cf0/
    value:/crc/col id0/value/.../col idn/value/

     

  • 第3个版本的存储方式如下:

    版本3还是利用了列簇的特性,因此,一行数据的存储会以一个或多个的键值对(KV-pairs)的形式存在,主键值会存放在key中,而数据项会存放在value中。
    这里还分为主键索引(primary indexes)和非主键索引(secondary indexes)。

    主键索引的key格式为:/Table/<id>/<index>/<pk val>/<family>

    主键索引的value格式为:<crc><type>/TUPLE/colIDDiff:colID:type/val/.../colIDDiff:colID:type

    对于这样一张表

    CREATE TABLE test (
    	id INT,
    	name STRING PRIMARY KEY,
    	age INT,
    	school STRING,
    	INDEX idx (age ASC)
    )

     

    idnameageschool
    123lml25scut

    实际存储结构如下

    key: /Table/52/1/"lml"/0  value:/TUPLE/1:1:Int/123/2:3:Int/26/1:4:Bytes/scut

    现在,执行

    ./cockroach debug rocksdb scan --db=cockroach-data --value_hex | grep "lml"

    看看数据

    /Table/52/1/"lml"/0/1531803756.900601973,0 : 0xE5ACBDEE0A13F6012334160473637574
    /Table/52/2/26/"lml"/0/1531803756.900601973,0 : 0xF4D2D1CB03

    有两行数据,这是因为为创建了INDEX idx(age),即非主键索引。
    非主键索引的存储格式:

    非主键索引的key格式为:/Table/<id>/<index>/<Data from where the row intersects the indexed columns>/0

    非主键索引的value格式为:说实话,我也没看懂- -。但绝大多数都是空值。

    温馨提示:

    通过tableDesc.PrimaryIndexSpan()可以过滤出主键索引的Span。

     

     

    从版本3的存储方式来看,上篇文档我们留下的第一个问题:

  • 若设置主键则会导致value值中没有主键字段的数据,并且不能确定字段的顺序
    因为主键索引是在key中的(比如key=/Table/60/1/"tom"/88/0,这里"tom"/88就是双主键)。
    那么为了解决这个问题,我们可以简单地将key中的多主键字段取出来,然后用分隔符‘/’进行切分,得到主键字段数据,然后拼接value的数据。

    但是,这种做法是很蠢的,有没有什么更好的办法?

    可以回过头来看看SELECT * FROM TABLE;的处理逻辑。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值