innodb_ruby项目的简介

第二次编辑

innodb_ruby项目的简介


[这篇文章截至到2014年2月3日,innodb_ruby的版本是0.8.8]

安装 innodb_ruby

如果你熟悉Ruby和gems(或者你刚好有一个配置好的Ruby),我会有规律的推送innodb_ruby gems到RubyGems,所有你只需要:

Ruby有windows环境。参考安装教程:菜鸟教程-Ruby
在我翻译时,时间来到2023年12月16日,innodb_ruby的版本是0.12.0,而ruby的版本是:3.2.2。而且最重要的是MySQL的版本已更新到8.0

gem install innodb_ruby

如果上述安装没有生效,你可能需要试着检查下RubyGems 手册,让你的安装生效,或者直接放弃。😄

原文的手册地址已不可用,译者也不懂Ruby,建议自行搜索,或者使用菜鸟教程亦或是参考我收集的安装手册Ruby中文文档或者官方文档

当你安装好之后,”innodb_space“命令应该可以执行,参考:

$ innodb_space
Error: File must be provided with -f argument

Usage: innodb_space -f <file> [-p <page>] [-l <level>] <mode> [<mode>, ...]

造点数据

对于以下示例,为了更加适当的测试不同的数据结构,要的不只是几行数据。确保你有一个足够新的MySQL服务器(最好是MySQL 5.5),并且文件格式是"Barrracuda"还要启用"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万行数据的表(以随机顺序插入数据,因为我想搞事情),大约48MiB大小,3017个16KiB的page页。(注意:如果你在家里尝试这些,你应该要先看看SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_page_dirty等待所有的脏页数据刷新进去。因为下面的命令行工具将访问磁盘上的表空间文件不会和正在运行的InnoDB实例相互协调)

上述命令中需要先安装ruby需要的mysql连接包。我使用的方式是安装mysql2替代原命令中的mysql。下面是执行步骤
1.启动cmd先执行D:\your\path\Ruby32-x64\bin\ridk.cmd enable
2.然后再执行:gem install mysql2 --platform=ruby -- --with-mysql-dir=D:/your/path/to/Ruby27-x64/msys64/mingw64。命令行中的目录需要自行替换,它在你自己的ruby安装目录下。解决办法参考文档
mysql2参考:
require ‘mysql2’
client = Mysql2::Client.new(
:host => ‘127.0.0.1’, # 主机
:username => ‘root’, # 用户名
:password => ‘’, # 密码
:database => ‘test’, # 数据库
:encoding => ‘utf8’ # 编码
)
client.query(“DROP TABLE IF EXISTS m”)
client.query(“CREATE TABLE m (i INT UNSIGNED NOT NULL, PRIMARY KEY(i)) ENGINE=InnoDB”)
(1…1000000).to_a.shuffle.each_with_index do |i, index|
client.query(“INSERT INTO m (i) VALUES (#{i})”)
puts “Inserted #{index} rows…” if index % 10000 == 0
end

使用ruby脚本插入mysql数据特别慢(批量提交可解决,我不会Ruby),可使用java代码造数据提交sql。

表空间文件概览

space-page-type-regions可能是innodb_space最高层次概述之一,它为每一个给定的页类型的连续块(contiguous block)打印一行信息。

$ 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的薄记结构(FSR_HDR,IBUF_BITMAP,和INODE页),实际的表数据(INDEX pages),和可用空间(FREE(ALLOCATED))

每个索引(实际上是每个文件段(“file segment”)或者每个索引的文件段)以页为单位的空间消耗列表,也相当有意思:

$ 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%  

每个索引都有一个内部文件段(“internal file segment”)用于非叶子页和一个叶子文件段(”leaf file segment“)用于叶子页。页分配给文件段,但是不会被立即使用(比如类型为:FREE(ALLOCATED)的页),所以“file_factor”展示的是已用空间和未用空间的比值。(注意:它和索引页的填充程度无关,这是另外一回事儿)

页结构信息

page-dump模式转储它所知道关于页的所有内容。目前,它严重依赖”Ruby pretty-printer“模块”pp“打印页结构–在将来,这是一件要麻烦事儿。innodb_ruby库最初使用最小的 ”Innodb::Page类“解析页结构,然后使用通用头部(“common header”)中的类型字段选择性的将不同的页类型交给特定的类(例如INDEX类型交给“Innodb::Page::Index”)做进一步解析。

开始看到的一个好的页面是第一页索引页,它是上面创建的测试表索引树的根节点,位于第4页(“即page 3”):

$ innodb_space -f test/t.ibd -p 3 page-dump

第一行将告诉你哪个类正在处理该页

#<Innodb::Page::Index:0x007fe304855360>:

接着是文件头(“FIL header)信息

fil header:
{:checksum=>621772966,
 :offset=>3,
 :prev=>nil,
 :next=>nil,
 :lsn=>102947976,
 :type=>:INDEX,
 :flush_lsn=>0,
 :space_id=>1}

文件头部和尾部(“FIL header” 和 ”footer“) 是所有页类型都有的结构且主要包含了关于页本身的信息。

该内容后面跟着的额外信息,它取决于页类型;对于索引页来说是如下内容:

  • “page header”(页头部),关于索引页的信息
  • “fseg header”(文件段头部),被索引页使用的文件段(区组)的空间管理相关的信息
  • 页不同部分大小(以字节为单位)的摘要信息:可用空间(”free space“),数据空间(”data space“),记录大小(”record size“),等等
  • “the system records”(系统记录),下确界和上确界
  • “page directory”(页目录)的内容,它被用于提高数据(”record“)的搜索效率
  • ”user records“(数据),用户所存储的真实数据(除非一条记录的描述者(“describer”)被加载,否则字段不会被解析)

索引的空间消耗

通过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   

通过这个命令可以让你很轻松的看到一些数据量和可用空间,以及这张表的记录计数(”records count“)。

如果你有一个正在运行的gnupolt并且Ruby gnupolt gem也安装好了,制作一个非常有用的空间消耗散点图也同样简单。

$ innodb_space -f test/t.ibd space-index-pages-free-plot
Wrote t_free.png

通过space-index-pages-free-plot制作的散点图如下:
可用空间散点图
可用空间散点图–Y坐标表示每页可用空间的数量,而X坐标表示页编号同样也表示文件的偏移量。点击图片查看全尺寸版本。

如果是windows系统,那么文中所用的gnupolt要使用gem install gnuplot,还需下载exe文件,并设置环境变量。下载页官方地址。安装好之后设置windows系统环境变量即可运行上述画图命令。此外,命令在哪里执行,图片就生成在那个目录下面。比如D:\soft\ruby下执行的命令,那么图片就在这个目录下。

理解行数据

为了让innodb_ruby项目在查看表结构时更有用,需要提供某种方式来更好地理解表数据(”table schema“)。它通过描述者类(“describer class”)完成查看,并且可以被动态的加载。这个是innodb_ruby库中的一个内容,目前还没有很好的文档记录(或尚未设计完好)。上面那个表(i INT UNSIGNED NOT NULL, PRIMARY KEY (i) and no other columns or indexes)的一个简单描述类(”describer class“)表示如下:

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

如果这个文件被保存为simple_t_describer.rb,它可以被innodb_space命令以-r <file>加上-d <class>参数加载:

$ innodb_space -f test/t.ibd -r /path/to/simple_t_describer.rb -d SimpleTDescriber <mode>

最后的”mode“可以使用innodb_space --help查看有哪些"mode"选项。上述命令中我所使用的模式是page-dump即为
innodb_space -f test/t.ibd -r /path/to/simple_t_describer.rb -d SimpleTDescriber -p 4 page-dump这里-p 4是我推断MySQL8的根索引是page4

加载描述者(“describer”)主要做两件事:

  • page-dump模式启用记录解析和转储。这将导致:主键索引(”:key“)和其他行索引(”*:row keys *“)被填充到转储记录中,同样也可获得事务ID和回滚指针(它们被存储在索引和非索引字段之间,因此至少要知道如何解析索引字段否则根本不能拿到这些数据)。
  • 允许所有的索引递归函数,包括indes-recurse模式。InnoDB的内部B+tree“node pointer records”(”节点指针记录“)和 B+tree page连在一起,为了解析它们需要解析数据(”records“)的能力。

下面提供了一些完整记录的page转储的样本:
test_t_page_3_page_dump.txt (根页索引)和test_t_tpage_4_dump(一个叶子页索引)

递归一个索引

一旦拿到了数据的描述者(”describer),就可以使用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+tree(基本上是全表扫描),然后打印出它所遇到的关于每个节点(页)的信息并转储用户记录(“user records”)到页子页上。以下是一个一个非常大的样本大约10k行数据:
test_t_page_3_index_recurse.txt

未来将会有更多的内容

我希望这是个很有用的介绍。在将来将会有有更多的内容。非常欢迎提出修正,评论和建议。

更新 Davi指出几个错别字和错误,已经得到修正。确保您使用的是上述示例中的最新代码。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值