innodb_ruby:窥探InnoDB的神器

一个InnoDB文件的解析工具,通过解析InnoDB文件,可以窥探其中很多的奥秘,绝对是一款非常优秀,高逼格的研究InnoDB的工具。但是作者不建议在生产环境使用,因为肯定有BUG,O(∩_∩)O哈哈~,作者写这个工具的主要目的是作为一个学习研究InnoDB的工具。

github地址

Github地址:https://github.com/jeremycole/innodb_ruby

安装

安装比较简单,执行命令gem install innodb_ruby即可。安装完成后,执行如下命令验证innodb_ruby是否安装成功:

[afei@afei app]# innodb_space --help

Usage: innodb_space <options> <mode>

Invocation examples:

  innodb_space -s ibdata1 [-T tname [-I iname]] [options] <mode>
  ... ...

如果提示ruby版本过低,从淘宝ruby镜像中下载高版本编译安装即可:

镜像地址:https://ruby.taobao.org/mirrors/ruby/ruby-1.9.3-p551.tar.gz
./configure --prefix=/app/ruby-2.2.10
make && make install

建议安装1.9.3-p551版本,因为当我安装了2.2.10版本的ruby后,执行innodb_space还是得到这样的错误信息:/usr/lib/ruby/gems/1.8/gems/bindata-2.4.3/lib/bindata.rb:5: BinData requires ruby >= 1.9.3。相关issue地址:https://github.com/jeremycole/innodb_ruby/issues/40。另外,安装ruby时,一步一步来,确保make以及make install时没有任何error信息。

准备数据

创建表插入数据之前,检查MySQL环境,MySQL建议5.5以上的版本,并且属性值:innodb_file_per_table=ON,innodb_file_format=Barracuda。接下来创建一张表,并借助存储过程插入一些数据:

-- 创建表
DROP TABLE IF EXISTS t_afei;
CREATE TABLE t_afei (
  id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  num int not null
) ENGINE=InnoDB;

-- 创建存储过程
DROP PROCEDURE IF EXISTS insertbatch;
CREATE PROCEDURE insertbatch()
BEGIN
DECLARE i INT;
  SET i=1;
  WHILE(i<=1000000) DO
    INSERT INTO yyfax_afei.t_afei(num)VALUES(i);
    SET i=i+1; 
  END WHILE;
END;

-- 调用存储过程
call insertbatch();

用法示例

查看索引信息--name为索引名称,fseg为leaf表示属于叶子页的segment:

[afei@afei mysql]# innodb_space -s ibdata1 -T /data/mysql/yyfax_afei/t_afei space-indexes
id      name       root    fseg        used    allocated   fill_factor 
2405    PRIMARY    3       internal    3       3           100.00%     
2405    PRIMARY    3       leaf        1743    1760        99.03% 

命令中的/data/mysql是mysql的datadir,yyfax_afei是数据库名称,t_afei是表名称。建议切换到${datadir}目录下,那么执行命令时-T的值为yyfax_afei/t_afei即可。

index-level-summary

得到指定level的所有page信息:

# level=0的page太多,所以只统计行数:
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -I PRIMARY -l 0  index-level-summary | wc -l
1744
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -I PRIMARY -l 1  index-level-summary
page    index   level   data    free    records min_key 
36      2405    1       7813    8139    601     id=1
37      2405    1       14846   838     1142    id=344688
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -I PRIMARY -l 2  index-level-summary
page    index   level   data    free    records min_key 
3       2405    2       26      16226   2       id=1 
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -I PRIMARY -l 3  index-level-summary
page    index   level   data    free    records min_key

我们知道level值和索引树的高度是强相关的(叶子节点的level都是0),所以通过这个命令也可以知道InnoDB索引树高度。由上面执行命令的结果可知,level=3时没有任何数据,而level等于1和2都有,所以示例100w数据的表的索引树高度是3。

space-page-type-regions

统计相同类型页的连续空间,如下所示,start/end表示起始页,count总计占的页数。

[afei@afei mysql]# innodb_space -f /data/mysql/yyfax_afei/t_afei.ibd space-page-type-regions
start       end         count       type                
0           0           1           FSP_HDR             
1           1           1           IBUF_BITMAP         
2           2           1           INODE               
3           37          35          INDEX               
38          63          26          FREE (ALLOCATED)    
64          1774        1711        INDEX               
1775        1919        145         FREE (ALLOCATED)

通过结果可知,page为0,1,2类型名称分别是:FSP_HDR, IBUF_BITMAP, INODE。从page=3开始才是存放行数据和指针的页。

index-recurse

递归一个索引需要依赖一个ruby脚本文件simple_t_describer.rb,脚本内容如下:

class SimpleTDescriber < Innodb::RecordDescriber
  type :clustered
  key "i", :INT, :UNSIGNED, :NOT_NULL
end

执行如下命令:

innodb_space -f /data/mysql/yyfax_afei/t_afei.ibd -r ~/simple_t_describer.rb -d SimpleTDescriber -p 3 index-recurse > recurseindex.log

这条命令会从root开始,全表扫描,以升序的方式遍历整个B+Tree索引树,遍历过程中会输出每个page以及指针的信息,包括叶子页和非叶子页,由于输出结果行过大(稍微大于表的行数),所以将结果重定向到一个recurseindex.log文件中:

# 表有100w数据,recurse index结果有1003491行数据
[afei@afei mysql]# wc -l recurseindex.log 
1003491 recurseindex.log

# 部分内容如下:
[afei@afei mysql]# head -10 recurseindex.log 
ROOT NODE #3: 2 records, 26 bytes
  NODE POINTER RECORD ≥ (i=1) → #36
  INTERNAL NODE #36: 601 records, 7813 bytes
    NODE POINTER RECORD ≥ (i=1) → #4
    LEAF NODE #4: 287 records, 7462 bytes
      RECORD: (i=1) → ()
      RECORD: (i=2) → ()
      RECORD: (i=3) → ()
      RECORD: (i=4) → ()
      RECORD: (i=5) → ()

从结果可知,ROOT NODE即根节点是page=3的页。通过space-page-type-regions的分析可知,0,1,2这三个page类型是FSP_HDR, IBUF_BITMAP, INODE。其他的就是INTERNAL节点和LEAF节点。

page-records

统计某一页中的数据。以刚才index-recurse的结果为例,page=3是root页,这个page的数据如下,由结果可知,page=3有两个record,与recurseindex.log的结果是吻合的(ROOT NODE #3: 2 records, 26 bytes):

[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -p 3 page-records > 3.log
[afei@afei mysql]# cat 3.log 
Record 125: (id=1) → #36
Record 138: (id=344688) → #37

从这个结果也能看出来,root页是不保存具体数据,只保存主键索引的值和指针。

我们再从recurseindex.log中找几个LEAF节点,如下所示,page=1770,1771这些都是LEAF节点:

[afei@afei mysql]# grep "LEAF" recurseindex.log | tail -5
    LEAF NODE #1770: 574 records, 14924 bytes
    LEAF NODE #1771: 574 records, 14924 bytes
    LEAF NODE #1772: 574 records, 14924 bytes
    LEAF NODE #1773: 574 records, 14924 bytes
    LEAF NODE #1774: 379 records, 9854 bytes

其中,page=1770的部分数据如下,一个页大概能574条记录,而page=1774还没有填满,只有379条数据:

[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/t_afei -p 1770 page-records > 1770.log
[afei@afei mysql]# head 1770.log 
Record 125: (id=997326) → (num=997326)

Record 151: (id=997327) → (num=997327)

Record 177: (id=997328) → (num=997328)

Record 203: (id=997329) → (num=997329)

Record 229: (id=997330) → (num=997330)

... ...

从这个结果也能看出来,叶子页会保存具体数据,不只是主键,非主键其他列(num列)的数据也有保存。

tree height

根据通过innodb_space得到的结果,我们大概能计算出索引树的高度,假设树的高度是h:

  • 百万级数据量的表,574^h=1000000。即h=2.17,所以百万级数据量且主键是int类型的表的索引树高度是3。
  • 十万级数据量的表,574^h=100000。即h=1.81,所以十万级数据量且主键是int类型的表的索引树高度是2。

下面是笔者对一张十万数据量的表做得测试,通过结果可以看出level=0或者1都有结果,level=2没有结果,所以索引树高度是2:

[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/tsw_afei -I PRIMARY -l 0  index-level-summary | wc -l
176
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/tsw_afei -I PRIMARY -l 1  index-level-summary 
page    index   level   data    free    records min_key 
3       2416    1       2275    13893   175     id=1 
[afei@afei mysql]# innodb_space -s ibdata1 -T yyfax_afei/tsw_afei -I PRIMARY -l 2  index-level-summary 
page    index   level   data    free    records min_key 
[afei@afei mysql]# 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容