新建查询,执行如下语句:

   
 Rocksdb是一个kv引擎,由facebook团队基于levelDB改进而来,Rocksdb采用LSM-tree存储数据,良好的读写特性以及压缩特性使得其非常受欢迎。此外,Rocksdb引擎作为插件已经集成在facebook维护的MySQL分支,用户可以通过SQL来访问rocksDB。本文主要通过分析Rocksdb引擎的记录格式,并通过对比innodb,来让大家了解Rocksdb。Rocksdb作为一个kv引擎,用户通过put(key,value)来写入key,或者通过get(key)接口来获取value,对rocksdb本身而言,每条记录都是一个key-value。当Rocksdb作为一个存储引擎接入到MySQL时,key-value结构如何存储表中各个索引,以及如何记录中各个列的信息是本文要具体讨论的。rocksdb引擎与innodb引擎类似,也是采用索引组织表,无论是表(主键索引)还是二级索引都是以LSM
tree方式组织,rocksdb记录主要包括三部分,key,value和meta三部分内容,具体见下表,然后我通过介绍一条具体记录在rocksdb引擎中的存储格式来说明问题。

 一、定义变量

EXEC sp_attach_db @dbname = ‘xxx’, /*数据库名称*/
@filename1 = ‘D:\数据库备份\预发布\201611241045 测试\xxx.mdf’,
/*mdf文件目录*/
@filename2 = ‘D:\数据库备份\预发布\201611241045
测试\xxx_log.ldf’/*ldf文件目录*/

rocksdb基本记录存储格式

–简单赋值 

遇到的问题:提示无法打开物理文件,解决办法:修改mdf ldf文件的权限。

key_size

declare @a int

澳门微尼斯人手机版 1

key

set @a=5 

澳门微尼斯人手机版 2

value_size

print @a 

 

value

  

PK/SecKey

–使用select语句赋值 

Columns data

declare @user1 nvarchar(50) 

SeqenceId,flag 

select @user1=’张三’

create table row_format(  
id int not null,  
c1 int,  
c2 char(10) not null,  
c3 char(10),  
c4 varchar(10),  
c5 varchar(10) not null,  
c6 blob,  
c7 binary(10) not null,  
c8 varbinary(10)) engine=rocksdb;
insert into row_format(id,c2,c4,c5,c7) values(1,'abc','abc','efg','111')

print @user1 

 key部分:

declare @user2 nvarchar(50) 

Index_id

key

4bytes

8bytes

select @user2 = Name from ST_User where ID=1 

0x7fdfa4278ea0: 0x00    0x00    0x01   
0x7b    0x00    0x00   
0x00    0x00

print @user2 

0x7fdfa4278ea8: 0x00    0x00    0x00   
0x05

  

Index_id:索引的编号,全局唯一。

–使用update语句赋值 

rowid:由于表没有主键,系统会产生一个bigint类型的rowid作为主键,占用8个字节,而innodb引擎的rowid占6个字节,需要注意的是rowid存储采用的大端的存储(高位存储低字节),这里主要是为了memcompare。

declare @user3 nvarchar(50) 

Value部分:

update ST_User set @user3 = Name where ID=1 

 

Null-flag

ID

C1

C2

C3

C4

C5

C6

C7

C8

Length

1B

4B

—-

30B

—-

4B

4B

—-

10B

—-

Value

 

1

 

abc0x20…

 

len+value

len+value

 

1110×00…

 

print @user3

0x7fdfa4251e50: 0x1b    0x01    0x00    0x00   
0x00    0x61    0x62   
0x63

 

0x7fdfa4251e58: 0x20    0x20    0x20    0x20   
0x20    0x20    0x20    0x20

 

0x7fdfa4251e60: 0x20    0x20    0x20    0x20   
0x20    0x20    0x20    0x20

二、表、临时表、表变量

0x7fdfa4251e68: 0x20    0x20    0x20    0x20   
0x20    0x20    0x20    0x20

 

0x7fdfa4251e70: 0x20    0x20    0x20    0x03    0x61    0x62   
0x63    0x03

–创建临时表1 

0x7fdfa4251e78: 0x65    0x66    0x67    0x31    0x31    0x31    0x00   
0x00

create table #DU_User1 

0x7fdfa4251e80: 0x00    0x00    0x00    0x00   
0x00

说明:

     [ID] [int]  NOT NULL, 

  1. Value的最前面部分(0x1b)就是存放记录的null信息。根据记录中可以为null字段的个数,确认需要占用的字节数,如果小于8个,则只需要一个字节。例子中,c1,c3,c4,c6,c8均可以为null,因此需要5个bit,所以用1个byte表示Null-flag即可,由于插入记录中,c4不为null,则对应的bit为0,也就是0x00011011。
  2. 对于null,无论是定长还是非定长数据类型,都不占用真实的存储空间,只需要一个bit位来表示为null即可。
  3. 空串’’与null,上面提到了null需要占一个标记位,而对于’’,如果是变长字段仍然需要存储长度信息,对于定长字段,则会补全。
  4. 对于变长字段,比如varchar,0x03 0x61
    0x62
    0x63数据有len+data组成,如果数据长度小于256,len只需要占用一个byte;如果len大于255,且小于65536,则需要占用2个字节,对于longblob类型,则需要占4个字节。
  5. 对于定长字段,不需要存长度信息直接存储data,如果不足则补充。补充字符有点诧异,对于char类型,补充0x20,对于binary类型,补充0x00。
  6. 对于lob类型,比如tinyblob,blob,mediumblob,longblob,以及对应的text类型,处理策略与varchar类似,存储长度的字节数根据数据类型的范围确定,比如blob长度占用2个字节,而longblob的长度占4个字节。所以在rocksdb里面,没有innodb中所谓“溢出页”的概念。对于innodb引擎,如果blob字段内容超过768字节,多余的data存储在溢出页,页内通过20个字节指向溢出页,主要包括第一个blob页的space_id,page_no和起始偏移,如果存在多个blob页,则页与页之间通过类似的方式进行关联。具体可以参考btr0cur.h文件中关于BTR_EXTERN_xxx相关的宏定义,以及接口btr_copy_externally_stored_field_prefix_low。
  7. 有关value部分的存储实现可以参考rocksdb引擎接口convert_record_to_storage_format,convert_record_from_storage_format和innodb引擎接口row_mysql_store_col_in_innobase_format,row_sel_field_store_in_mysql_format。

     [Oid] [int] NOT NULL, 

Meta部分:

     [Login] [nvarchar](50) NOT NULL, 

Meta部分主要是SequenceID,这个SequenceID在事务提交时产生,主要用于rocksDB实现MVCC,用于可见性判断,此外Meta中还包含flag信息,由于标示记录类型,put,delete,singleDelete等,具体而言Sequence占7个字节,flag占1个字节。

     [Rtx] [nvarchar](4) NOT NULL, 

rocksdb索引格式

     [Name] [nvarchar](5) NOT NULL, 

     
Rocksdb中,所有的数据都是通过索引来组织,与Innodb类似,也是索引组织表,每个索引有一个全局唯一的index_id。索引主要包括两类:主键索引和二级索引,前面介绍的记录格式,也就是主键索引的格式,包括key,value和meta三部分。二级索引也包含key,value和meta三部分,但是value中不包含任何数据,只是包含checksum信息。

     [Password] [nvarchar](max) NULL, 

主键索引

     [State] [nvarchar](8) NOT NULL

key

); 

Value

–向临时表1插入一条记录 

Meta

insert into #DU_User1 (ID,Oid,[Login],Rtx,Name,[Password],State)
values (100,2,’LS’,’0000′,’临时’,’321′,’特殊’); 

Index_id

  

PK

–从ST_User查询数据,填充至新生成的临时表 

NULL标记位

select * into #DU_User2 from ST_User where ID<8 

列数据

  

Checksum(可选)

–查询并联合两临时表 

SeqId,flag

select * from #DU_User2 where ID<3 union select * from
#DU_User1 

二级索引

  

key

–删除两临时表 

Value

drop table #DU_User1 

Meta

drop table #DU_User2

Index_id

 

SecondaryKey

–创建临时表 

PK

CREATE TABLE #t 

Checksum(可选)

SeqId,flag

    [ID] [int] NOT NULL, 

对比innodb引擎(innodb_file_format=Barracuda,row_format=compact)

    [Oid] [int] NOT NULL, 

innodb记录格式

    [Login] [nvarchar](50) NOT NULL, 

变长字段长度列表

NULL标记位

record_header

Trxid

Roll_ptr

列数据

    [Rtx] [nvarchar](4) NOT NULL, 

create table row_format(  
id int not null,  
c1 int,  
c2 char(10) not null,  
c3 char(10),  
c4 varchar(10),  
c5 varchar(10) not null,  
c6 blob,  
c7 binary(10) not null,  
c8 varbinary(10)) engine=innodb;
insert into row_format(id,c2,c4,c5,c7) values(1,'1234','ab','efg','111');

    [Name] [nvarchar](5) NOT NULL, 

记录内容:

    [Password] [nvarchar](max) NULL, 

0000c0b0  00 00 03 02 0a 1b 00 00  18 ff b5 00 00 00 00 28 
|……………(|

    [State] [nvarchar](8) NOT NULL, 

0000c0c0  00 00 00 00 01 01 03 83  00 00 01 36 01 10 80 00 
|………..6….|

0000c0d0  00 01 31 32 33 34 20 20  20 20 20 20
61 62 65 66  |..1234      abef|

  

0000c0e0  67 31 31 31 00 00 00 00  00 00
00 00 00 00 00 00  |g111…………|

–将查询结果集(多条数据)插入临时表 

说明:

insert into #t select * from ST_User 

     1.   03 02 0a,这里存的是长度信息,所有非null的变长列信息都逆序存在一起,这里按先后顺序是c5,c4,c2,这里innodb将char(10)也当作变长字段处理了。

–不能这样插入 

     2.  1b存储的是null信息,与rocksdb对null处理一致。00
00  18 ff b5存储的是record-header。

–select * into #t from dbo.ST_User 

     3. 00 00 00 00 28 00 00 00 00 01 01 03 83  00 00 01 36 01 10,
这三部分别是rowid,trxid和roll_ptr,分别占6个字节,6个字节和7个字节。

  

     4.
最后一部分是数据,null不占任何存储空间,与rocksdb处理类似。区别在于对于char类型的处理,innodb将字段c2类型char(10)补齐到10个字节,存储为31 32 33 34 20 20 20 20 20
20,将其作为varchar处理,记录了长度信息;而Rocksdb则是补齐到30个字节(utf8字符集),作为char处理,不记录长度信息。

–添加一列,为int型自增长子段 

     
整体而言,innodb记录格式包含了record_header(记录头信息),占5个字节,主要包括记录号(heap_no),列数目,下一条记录的位置以及是否删除等信息。rocksdb则相对简单,只有整体的value-size,以及通过Meta中flag标示记录的状态put
或者是delete。innodb将变长列长度信息集中存放在一起,使得查找任意列的代价都差不多,而rocksdb的变长列信息则是放在每列的前面,访问最后一列需要逐一计算前面的列,才能定位。此外,由于innodb引擎与rocksdb引擎由于实现MVCC的机制不同,导致innodb引擎和rocksdb引擎需要存储的额外信息也不同。Innodb实现MVCC依赖于回滚段信息,记录需要额外存储trxid和roll_ptr两个字段,分别是6个字节和7个字节(type,rsegid,pageNO,offset),其中type占一个bit位,标示insert
或者是update类型,rsegid回滚段id占7bit位,pageNo占4个字节,页内偏移占2个字节。Rocksdb实现MVCC则是依赖于SequenceID,通过SequenceID来判断记录的可见性,SequenceID占7个字节。

alter table #t add [myid] int NOT NULL IDENTITY(1,1) 

   
  细节上来说,RocksDB引擎和innodb引擎在处理null,char和varchar的方式类似,但innodb对于char类型做了优化,统一作为varchar处理。另外rocksdb引擎没有对blob做特殊处理。你可能会有疑问,rocksdb不是也有block_size吗,如果设置为16k,blob数据超过16k怎么办?对于innodb而言,由于表实质是以一个个page通过B-tree组织起来的,每个page是固定大小,当记录非常大时,就需要借助溢出页,通过链接的方式关联起来。而rocksdb中block_size只是一个压缩单位,并没有严格约束,文件内容以block组织,由于文件中block可能是压缩过的,因此每个block的大小不固定,通过偏移来定位具体某个block的位置。如果遇到大的blob数据,则可能这个block比较大,记录所有数据存储在一起,不会跨block。

–添加一列,默认填充全球唯一标识 

     
对于索引长度限制也有所不同,对于innodb引擎来说,索引中单列长度不能超过767个字节,而rocksdb引擎单列长度不超过2048个字节,具体可以参考max_supported_key_part_length各自的实现;整个索引的长度,rocksdb和innodb都限制在3072个字节,实际上是server层的限制,因为它们的各自限制的长度都比server层的大。具体可以参考各自max_supported_key_length的实现。

alter table #t add [myid1] uniqueidentifier NOT NULL
default(newid()) 

参考文档

  

select * from #t 

drop table #t

–给查询结果集增加自增长列 

 

  

–无主键时: 

select IDENTITY(int,1,1)as ID, Name,[Login],[Password] into #t from
ST_User 

select * from #t 

  

–有主键时: 

select (select SUM(1) from ST_User where ID<= a.ID) as myID,* from
ST_User a order by myID

–定义表变量 

declare @t table

    id int not null, 

    msg nvarchar(50) null

insert into @t values(1,’1′) 

insert into @t values(2,’2′) 

select * from @t

 三、循环

 

–while循环计算1到100的和 

澳门微尼斯人手机版,declare @a int

declare @sum int

set @a=1 

set @sum=0 

while @a<=100 

begin

    set @sum+=@a 

    set @a+=1 

end

print @sum

四、条件语句

发表评论

电子邮件地址不会被公开。 必填项已用*标注