MySQL 本质上只有一种索引方法:BTree。 (是的,还有空间和全文,但这是另一个讨论。)
一旦您了解了 BTree 的工作原理(参见 Wikipedia),我们就可以讨论进入 InnoDB 中叶节点的内容。
案例 1:“数据”BTree 包含所有列,并根据PRIMARY KEY 排序。在 MySQL 中,PK 是根据定义、“UNIQUE”和“Clustered”。 (其他供应商有其他选择。)
案例 2:“辅助”INDEX 存储在单独的 BTree 中。叶节点中有 (1) 二级索引中定义的列,以及 PK 列的副本。要使用二级索引完成SELECT,它必须首先使用索引BTree 获取PK,然后通过数据BTree 获取数据。 (如果索引是“覆盖”,则不需要这第二步。)
MySQL 中没有“Rownum”。
BTrees 实际上是 B+Trees,从而使范围扫描更有效。
InnoDB 将给定表的所有 BTree(一个用于数据+PK,一个用于每个二级索引)放入某个表空间。表空间可以是通用的(ibdata1 文件)、特定于表的(文件tablename.ibd)或(在较新版本中)可以包含多个表的“表空间”文件。
警告:我所描述的适用于 MySQL 的 InnoDB,可能不适用于任何其他引擎。
我不知道有什么好的工具可以在不深入细节的情况下检查 InnoDB 的 BTree。对于 Percona 版本:
SELECT i.INDEX_NAME as Index_Name,
IF(ROWS_READ IS NULL, 'Unused',
IF(ROWS_READ > 2e9, 'Overflow', ROWS_READ)) as Rows_Read
FROM (
SELECT DISTINCT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME
FROM information_schema.STATISTICS
) i
LEFT JOIN information_schema.INDEX_STATISTICS s
ON i.TABLE_SCHEMA = s.TABLE_SCHEMA
AND i.TABLE_NAME = s.TABLE_NAME
AND i.INDEX_NAME = s.INDEX_NAME
WHERE i.TABLE_SCHEMA = ?
AND i.TABLE_NAME = ?
ORDER BY IF(i.INDEX_NAME = 'PRIMARY', 0, 1)
对于 MySQL (Oracle):
SELECT last_update,
n_rows,
'Data & PK' AS 'Type',
clustered_index_size * 16384 AS Bytes,
ROUND(clustered_index_size * 16384 / n_rows) AS 'Bytes/row',
clustered_index_size AS Pages,
ROUND(n_rows / clustered_index_size) AS 'Rows/page'
FROM mysql.innodb_table_stats
WHERE ( ( database_name = ? AND table_name = ? )
OR ( database_name = LOWER(?) AND table_name = LOWER(?)
UNION
SELECT last_update,
n_rows,
'Secondary Indexes' AS 'BTrees',
sum_of_other_index_sizes * 16384 AS Bytes,
ROUND(sum_of_other_index_sizes * 16384 / n_rows) AS 'Bytes/row',
sum_of_other_index_sizes AS Pages,
ROUND(n_rows / sum_of_other_index_sizes) AS 'Rows/page'
FROM mysql.innodb_table_stats
WHERE ( ( database_name = ? AND table_name = ? )
OR ( database_name = LOWER(?) AND table_name = LOWER(?)
AND sum_of_other_index_sizes > 0