【发布时间】:2013-05-24 05:35:33
【问题描述】:
假设我们有这个示例结构/数据:
@see fiddle at http://sqlfiddle.com/#!8/1f85e/1
-- SET GLOBAL innodb_file_per_table=1;
DROP TABLE IF EXISTS mysql_index_reading_myisam;
CREATE TABLE IF NOT EXISTS mysql_index_reading_myisam (
id INT NOT NULL AUTO_INCREMENT
, str VARCHAR(50) NOT NULL
, enm ENUM('thatis', 'thequestion') NOT NULL
, cnt TINYINT NOT NULL
, PRIMARY KEY (id)
, INDEX str_cnt (str, cnt)
, INDEX enm_cnt (enm, cnt)
) ENGINE=MyISAM CHARSET=Latin1;
INSERT INTO mysql_index_reading_myisam (str, enm, cnt) VALUES
('Tobeornottobe', 'Thatis', 1)
, ('toBeornottobe', 'thatIs', 2)
, ('tobeOrnottobe', 'ThatIs', 3)
, ('tobeorNottobe', 'thatis', 4)
, ('tobeornotTobe', 'THATIS', 5)
;
DROP TABLE IF EXISTS mysql_index_reading_innodb;
CREATE TABLE mysql_index_reading_innodb LIKE mysql_index_reading_myisam;
ALTER TABLE mysql_index_reading_innodb ENGINE InnoDB;
INSERT INTO mysql_index_reading_innodb SELECT * FROM mysql_index_reading_myisam;
EXPLAIN SELECT cnt FROM mysql_index_reading_myisam WHERE str = 'tobeornottobe';
EXPLAIN SELECT cnt FROM mysql_index_reading_innodb WHERE str = 'tobeornottobe';
EXPLAIN SELECT cnt FROM mysql_index_reading_myisam WHERE enm = 'thatis';
EXPLAIN SELECT cnt FROM mysql_index_reading_innodb WHERE enm = 'thatis';
让我们看看它是如何在内部存储的
# egrep --ignore-case --only-matching --text '(tobeornottobe|thatis)' *
mysql_index_reading_innodb.frm:thatis
mysql_index_reading_innodb.ibd:Tobeornottobe
mysql_index_reading_innodb.ibd:toBeornottobe
mysql_index_reading_innodb.ibd:tobeOrnottobe
mysql_index_reading_innodb.ibd:tobeorNottobe
mysql_index_reading_innodb.ibd:tobeornotTobe
mysql_index_reading_innodb.ibd:Tobeornottobe
mysql_index_reading_innodb.ibd:toBeornottobe
mysql_index_reading_innodb.ibd:tobeOrnottobe
mysql_index_reading_innodb.ibd:tobeorNottobe
mysql_index_reading_innodb.ibd:tobeornotTobe
mysql_index_reading_myisam.frm:thatis
mysql_index_reading_myisam.MYD:Tobeornottobe
mysql_index_reading_myisam.MYD:toBeornottobe
mysql_index_reading_myisam.MYD:tobeOrnottobe
mysql_index_reading_myisam.MYD:tobeorNottobe
mysql_index_reading_myisam.MYD:tobeornotTobe
mysql_index_reading_myisam.MYI:Tobeornottobe
mysql_index_reading_myisam.MYI:toBeornottobe
- 在两个引擎中,枚举都按原样存储在 *.frm 中。好的。
- 在两个引擎中,数据都存储在数据和数据/索引文件中。好的。
- 在 MyISAM 索引中有两条记录。
- 在 InnoDB 索引中,所有 5 条记录的大小写均正确。
我已经找到了什么
http://dev.mysql.com/doc/refman/5.1/en/mysql-indexes.html
在某些情况下,可以优化查询以检索值,而无需 查阅数据行。如果查询仅使用表中的列 是数字并且形成某个键的最左边的前缀, 可以从索引树中检索选定的值以获得更大的 速度:
SELECT key_part3 FROM tbl_name WHERE key_part1=1
http://www.mysqlperformanceblog.com/2009/09/12/3-ways-mysql-uses-indexes/
使用索引读取数据一些存储引擎(MyISAM 和 Innodb 包括)也可以使用索引来读取数据,从而避免读取 行数据本身。这不仅仅是节省每次读取 2 次 索引条目而不是一个,但它可以节省 IO 数量级 某些情况——索引是排序的(至少在页面边界上),所以 进行索引范围扫描,您通常会从 同一页面,但行本身可以分散在许多页面中 可能需要大量的 IO。最重要的是,如果您只需要 访问几列索引可以比 数据是覆盖索引有助于加快速度的原因之一 即使数据在内存中也可以查询。如果 MySQL 只读取索引和 不访问行,您将在 EXPLAIN 输出中看到“使用索引”。
然后在 sql_select.cc 的源代码中: http://bazaar.launchpad.net/~mysql/mysql-server/5.1/view/head:/sql/sql_select.cc#L12834
/*
We can remove binary fields and numerical fields except float,
as float comparison isn't 100 % secure
We have to keep normal strings to be able to check for end spaces
*/
if (field->binary() &&
field->real_type() != MYSQL_TYPE_STRING &&
field->real_type() != MYSQL_TYPE_VARCHAR &&
(field->type() != MYSQL_TYPE_FLOAT || field->decimals() == 0))
{
return !store_val_in_field(field, right_item, CHECK_FIELD_WARN);
}
所以我的问题是
在索引字符串列中存储是否可行,只需要作为数据? 例如有20列的表,我们经常需要strcolumn,即通过intcolumn搜索。 像 (intcolumn,strcolumn) 这样创建索引好还是我们真的只需要 (intcolumn) 在这里?
innodb 引擎中的 mysql 是否真的为 检索数据(当我们看到“Using where; Using index”时)?
ENUM 也是如此。它发生了,因为 Enum_field`s real_type 返回 MYSQL_TYPE_STRING。枚举也一样吗?
那么我们可以假设,枚举是超级邪恶的,我们应该总是 只使用简单的参考表吗?
对于 MyISAM,它是不可理解的,因为它在索引中存储的不是所有值。 但是为什么它会存储两个值——不是一个?
如果这一切真的发生了——这只是当前的限制吗? mysql内核,不依赖于具体的处理程序实现?
ps:我看到这个问题很大。如果有人会帮助 重新制定/打破它——这会很好。
更新 1:添加另一个关于“使用索引”与“使用索引;使用 where”的 SQL
@see fiddle at http://sqlfiddle.com/#!8/3f287/2
DROP TABLE IF EXISTS tab;
CREATE TABLE IF NOT EXISTS tab (
id INT NOT NULL AUTO_INCREMENT
, num1 TINYINT NOT NULL
, num2 TINYINT
, str3 CHAR(1) NOT NULL
, PRIMARY KEY (id)
, INDEX num1_num2 (num1, num2)
, INDEX num1_str3 (num1, str3)
, INDEX num2_num1 (num2, num1)
, INDEX str3_num1 (str3, num1)
) ENGINE=InnoDB;
INSERT INTO tab (num1, num2, str3) VALUES
(1, 1, '1')
, (2, 2, '2')
, (3, 3, '3')
, (4, 4, '4')
, (5, 5, '5')
, (6, 6, '6')
, (7, 7, '7')
, (8, 8, '8')
, (9, 9, '9')
, (0, 0, '0')
;
INSERT INTO tab (num1, num2, str3) SELECT num1, num2, str3 FROM tab;
-- Using index
EXPLAIN SELECT num2 FROM tab WHERE num1 = 5;
EXPLAIN SELECT str3 FROM tab WHERE num1 = 5;
-- Using where; Using index
EXPLAIN SELECT num1 FROM tab WHERE num2 = 5;
EXPLAIN SELECT num1 FROM tab WHERE str3 = '5';
问题 #2
为什么在非 null int 搜索的情况下我们只看到“使用索引”?
但在可为空的 int OR 字符串的情况下——我们还会看到“在哪里使用”?
mysql 在那里做了哪些附加操作?
【问题讨论】: