本文基于2014年3月innodb_ruby的0.8.8版本。
在前文《学习innoDB核心之旅》中,我在innodb_ruby的项目中引入了一个新的库和命令行工具。现在我来展示一下他的一些功能。我不会尝试解释所有公开的innoDB结构,因为那会让演示偏离我的本意。我们稍后会再来介绍这些结构。
如果你熟悉ruby和gems或者你恰好有一个良好配置的ruby环境。我会定期讲innodb_ruby gems推送到RubyGems中,所以你只需要执行:
gem install innodb_ruby
如果这还不起作用,你肯能需要检查下RubyGems手册,重试你的安装工作。或者放弃一切希望。
当你有一个可以工作的安装,你应该有一个innodb_space命令在你的路径中:
innodb_space
Error: File must be provided with -f argument
Usage: innodb_space -f <file> [-p <page>] [-l <level>] <mode> [<mode>, ...]
对于这些示例,我们需要更多的行,以便正确的检查不同的数据结构。确保运行了一个足够新的服务器,mysql5.5就很好。并启用了Barracuda表。启用innodb_file_per_table。用一点Ruby创建并填充一个非常简单的表。
#!/usr/bin/env ruby
require "mysql"
m = Mysql.new("127.0.0.1", "root", "", "test")
m.query("DROP TABLE IF EXISTS t")
m.query("CREATE TABLE t (i INT UNSIGNED NOT NULL, PRIMARY KEY(i)) ENGINE=InnoDB")
(1..1000000).to_a.shuffle.each_with_index do |i, index|
m.query("INSERT INTO t (i) VALUES (#{i})")
puts "Inserted #{index} rows..." if index % 10000 == 0
end
这讲生成一个包含100万行的表,为了让这个事情更加有趣,采用了随机插入的方式。大约有48M,共计3071个16KB的数据页。
请注意,如果你想在家里尝试做这个,你会想看显示全球的状态,像innodb_buffer_pool_pages_dirty邓所有脏页在刷新之前,因为下面的工具将访问磁盘上的表空间文件。没有配合innoDB运行的实例。
innodb_space最高级的概述之一space-page-type-regions,他对给定的页面类型每个相邻的块打印一行。
$ innodb_space -f test/t.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 2188 2125 INDEX
2189 2239 51 FREE (ALLOCATED)
2240 2240 1 INDEX
2241 2303 63 FREE (ALLOCATED)
2304 2304 1 INDEX
2305 2367 63 FREE (ALLOCATED)
2368 2368 1 INDEX
2369 2431 63 FREE (ALLOCATED)
2432 2432 1 INDEX
2433 2495 63 FREE (ALLOCATED)
2496 2496 1 INDEX
2497 2687 191 FREE (ALLOCATED)
在不涉及太多InnoDb内部实现细节的情况下,你可以看到一些InnoDB的存储结构(FSP_HDR, IBUF_BITMAP, and INODE pages)。实际表索引页和空闲空间分配的页。
列出每个索引实际上是每个文件段或者每个索引的FSEG,在页面中消耗的空间也相当有趣:
$ innodb_space -f test/t.ibd space-indexes
id root fseg used allocated fill_factor
15 3 internal 3 3 100.00%
15 3 leaf 2162 2528 85.52%
每个索引都有一个内部的文件段,用于non-leaf页,和另外一个leaf文件段。用于leaf页。页面可能被分配给一个文件段,但是当前未使用(类型为FREE (ALLOCATED))。因此,fill_factor将显示以使用与未使用的比率。记住,这与索引页有多满没有关系,那是另外一回事。
页面转储模式转储他所直到的关于单个页面的内容,他目前严重依赖于典型的Ruby的pretty-printer模块来打印。这是未来需要清理的一件大事。innodb_ruby库首先使用最小的innodb:Page累来解析文件。然后使用公共标提中的类型字段 可选地将不同的页面类型交给专门的类进行进一步的解析。如innodb::Page::index用于类型索引。
一个好的开始页面应该是第一个索引页面,它是上面创建的测试表的索引树的根节点,位于第三页:
$ innodb_space -f test/t.ibd -p 3 page-dump
初始行回告诉你哪个类正在处理这个页面:
#<Innodb::Page::Index:0x007fe304855360>:
接下来打印FIL标题:
fil header:
{:checksum=>621772966,
:offset=>3,
:prev=>nil,
:next=>nil,
:lsn=>102947976,
:type=>:INDEX,
:flush_lsn=>0,
:space_id=>1}
FIL的页眉和页脚对所有页面类型都是通用的,并且主要包含关于页面本身的信息。
以下是根据页面类型提供的其他信息,对于索引页,以下信息被转储:
通过使用space-index-pages-summary模式,可以看到所有的索引中一些最有用的空间消耗相关的数据:
$ innodb_space -f test/t.ibd space-index-pages-summary | head -n 10
page index level data free records
3 15 2 26 16226 2
4 15 0 9812 6286 446
5 15 0 15158 860 689
6 15 0 10912 5170 496
7 15 0 10670 5412 485
8 15 0 12980 3066 590
9 15 0 11264 4808 512
10 15 0 4488 11690 204
11 15 0 9680 6418 440
这允许您查看数据量和可用空间,以及表的记录数。
如果有一个工作的gnuplot并且安装了gnuplot gem,也可以很容易地制作一个有用的虽然不是很漂亮的信息散点图:
$ innodb_space -f test/t.ibd space-index-pages-free-plot
Wrote t_free.png
space-index-free-plot生成的图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VNxhbhBl-1597373920913)(1076193C84654D49A9E38D64E21C9D5B)]
Free Space图标:Y轴表示每个页面中的空闲的空间量,X轴是页码,也表示文件偏移量。
为了在检查真正的表的时候真正有用,需要为innodb_ruby提供一些理解表模式的方法。这事通过一个可以动态加载describer的类来实现的,这事innodb_ruby库的一个方面,他还没有得到很好的文档激励,或者还没有得到很好的设计,一个简单的描述类为上面的表如下:
class SimpleTDescriber < Innodb::RecordDescriber
type :clustered
key "i", :INT, :UNSIGNED, :NOT_NULL
end
如果这个类保存在simple_t_describer.rb文件中。它可以加载在innodb_space -r <文件>,启动 -d <类>参数:
$ innodb_space -f test/t.ibd -r /path/to/simple_t_describer.rb -d SimpleTDescriber <mode>
加载一个工作记录描述器主要做两件事:
可以使用一些带有完整记录的示例页面转储,test_t_page_3_page_dump.txt(索引根页面)和test_t_page_4_page_dump.txt索引页面。
一旦记录描述可用,索引可以使用index-recurse进行递归:
$ innodb_space -f test/t.ibd -r /path/to/simple_t_describer.rb -d SimpleTDescriber -p 3 index-recurse
ROOT NODE #3: 2 records, 26 bytes
NODE POINTER RECORD >= (i=252) -> #36
INTERNAL NODE #36: 1117 records, 14521 bytes
NODE POINTER RECORD >= (i=252) -> #4
LEAF NODE #4: 446 records, 9812 bytes
RECORD: (i=1) -> ()
RECORD: (i=2) -> ()
RECORD: (i=3) -> ()
RECORD: (i=4) -> ()
RECORD: (i=5) -> ()
这实际上将按升序遍历B+树,基本上是一个全表扫描。同时打印遇到的每个节点的一些信息,并将用户记录转储到叶子页面上,这里有一个更大的示例输出:test_t_page_3_index_recurse.txt。
希望这事一个有用的第一次介绍,未来还会有更多介绍。