MySQL 索引创建内幕

use*_*770 5 mysql innodb index mysql-5.6

现在有两种方法可以在 MySQL 中构建表的索引:

  1. 首先创建表结构,然后导入数据并添加索引。
  2. 创建带有索引的表结构,然后导入数据。

在第一个过程中,我们将拥有连续的数据(所有字段)页面,然后是索引页面。所以当我们使用索引查询时,MySQL 必须首先加载索引页并找出匹配的键,并且必须在数据页上查找那些主键。为此,它必须再次加载数据页以获取数据。当我们有更大的索引扫描时这很有用,因为我们所有的索引都是连续加载的。

在索引创建的第二种方式中,过滤后的索引页很可能包含与它们同时创建的靠近它的数据页。所以我想小范围扫描的查找速度会更快。

我的理解正确吗?

更新:

我应该在第一种导入数据的方式中提到“PRIMARY KEY 已启用”(自动递增 id 列)。因此,不会生成内部 rowid,并且由于我们不打算添加 PRIMARY KEY,因此会保存大量 IO。

正如您所指出的,当我们使用第二种方法导入数据时会出现碎片。

考虑到我的要求是更大范围的扫描(扫描~100M 行),我想我会采用第一种导入数据的方式。

6 月 8 日 11:30 更新

CREATE TABLE `table_dummy` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `column1` bigint(20) DEFAULT NULL,
  `column2` bigint(20) DEFAULT NULL,
  `column3` bigint(20) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `column4` tinyint(1) DEFAULT NULL,
  `column5` tinyint(4) DEFAULT NULL,
  `column6` bigint(20) DEFAULT NULL,
  `column6_created_at` datetime DEFAULT NULL,
  `column7` int(11) DEFAULT NULL,
  `column8` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `twtaccount_id_2` (`column1`,`column2`),
  KEY `twtaccount_id` (`column1`,`created_at`,`column5`),
  KEY `twt_user_id` (`column3`,`created_at`,`column5`),
  KEY `original_status_id` (`column6`,`column3`,`followers_count`)
)
Run Code Online (Sandbox Code Playgroud)

引擎是 INNODB

这个表有大约 800M 的记录,我进行了转储并以两种方式导入它。

方法 1:仅使用主键创建表并导入整个数据。然后我给出了一个alter语句来添加剩余的索引。

方法 2:使用索引创建表并完成转储。这花费了更多的时间。

PS 与通过“方法 2”转储的表的大小相比,通过“方法 1”生成的表的大小要小约 30GB。方法 1 比“方法 2”花费的时间少得多,几乎是速度的两倍。

我主要关心的是当我选择扫描范围广泛的索引(“仅索引扫描”)时表的性能。

Rol*_*DBA 1

第一种方式,你是正确的。这就是为什么我说软的。

InnoDB 有一个聚集索引,内部称为 gen_clust_index。基本上,它是rowid在幕后为 InnoDB 表创建的索引(或者如果您像我一样来自新泽西州泽西城,则在幕后创建)。这些 rowid 附加到所有二级索引。

PRIMARY KEY如果组成KEYPRIMARY KEY的列的宽度PRIMARYrowid.

对于 InnoDB,第二种创建索引的方法更有意义,因为您没有PRIMARY KEY在幕后交换 的定义。这是您已经提到的好处 ( ) 的补充data page near to it as they were created at the same time。第一种索引方法在这方面没有任何收获,充其量只是收支平衡。

虽然第二种方法更适合需求(look up will be faster for a small range scans),但我对您有一个非常强烈的警告:如果您PRIMARY KEY用已经排序的列数据填充,您很可能最终会遇到一些索引碎片(请参阅我对帖子Oct 26, 2012How badly does innodb 片段面对有些无序插入?)如果您知道自己正在进行小范围扫描,那么这是一个不错的权衡。

更新时间 2015-05-26 16:30 美国东部时间

在这种情况下,同样的警告也适用。你将得到一个支离破碎的PRIMARY KEY. 证明在我对帖子May 01, 2014Why would the size of MySQL MyISAM table Index (aka MYI file) not match after mysqldump import? 的回答中得到了表达。

要手动证明​​,请执行以下操作

步骤01:从数据库服务器mysqldump数据

步骤02:将其加载到测试服务器中

运行查询

SELECT IFNULL(ENGINE,'Total') "Storage Engine",
LPAD(CONCAT(FORMAT(DAT/POWER(1024,pw1),2),' ',
SUBSTR(units,pw1*2+1,2)),17,' ') "Data Size",
LPAD(CONCAT(FORMAT(NDX/POWER(1024,pw2),2),' ',
SUBSTR(units,pw2*2+1,2)),17,' ') "Index Size",
LPAD(CONCAT(FORMAT(TBL/POWER(1024,pw3),2),' ',
SUBSTR(units,pw3*2+1,2)),17,' ') "Total Size" FROM
(SELECT ENGINE,DAT,NDX,TBL,IF(px>4,4,px) pw1,
IF(py>4,4,py) pw2,IF(pz>4,4,pz) pw3 FROM
(SELECT *,FLOOR(LOG(IF(DAT=0,1,DAT))/LOG(1024)) px,
FLOOR(LOG(IF(NDX=0,1,NDX))/LOG(1024)) py,
FLOOR(LOG(IF(TBL=0,1,TBL))/LOG(1024)) pz
FROM (SELECT ENGINE,SUM(data_length) DAT,SUM(index_length) NDX,
SUM(data_length+index_length) TBL FROM (SELECT engine,data_length,index_length
FROM information_schema.tables WHERE table_schema NOT IN
('information_schema','performance_schema','mysql') AND ENGINE IS NOT NULL) AAA
GROUP BY ENGINE WITH ROLLUP) AAA ) AA) A,(SELECT ' BKBMBGBTB' units) B;
Run Code Online (Sandbox Code Playgroud)

步骤04:来自测试服务器的mysqldump

步骤05:再次将测试服务器的转储重新加载回测试服务器

步骤 06:运行步骤 03 中的查询

有时,大组数据在重新加载mysql数据时会产生不同的大小

底线:对于 InnoDB,这两种方法都没有比另一种方法有显着优势。