MYSQL索引
2018-06-17 22:50:13来源:未知 阅读 ()
- 索引优化应该是对查询性能优化最有效的手段了。
- mysql只能高效地使用索引的最左前缀列。
- mysql中索引是在存储引擎层而不是服务器层实现的
- 如果不是按照索引的最左列开始查找,则无法使用索引。
- 不能跳过索引中的列
- 如果查询中有某列的范围查询,则其右边所有列都无法使用索引优化查询。
- 哈希索引只包含哈希值和行指针,不存储字段值,所以不能使用"覆盖索引"的优化方式,去避免读取数据表。
- 哈希索引数据并不是按照索引值顺序存储的,索引也就无法用于排序
- 哈希索引页不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容计算哈希值的。
- 哈希索引只支持等值比较查询,包括=,in(),<=>,不支持任何范围查询。列入where price>100
- 访问哈希索引的数据非常快,除非有很多哈希冲突(不同的索引列值却有相同的哈希值)
- 如果哈希冲突很多的话,一些索引维护操作的代价也会很高。
mysql> select id from url where url='http://www.mysql.com';
mysql> select id from url where url='http://www.mysql.com' -> and url_crc=crc32("http://www.mysql.com");
create table URL ( id int unsigned NOT NULL auto_increment, url varchar(255) NOT NULL, url_crc int unsigned NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY (url_crc) );
delimiter // create trigger url_hash_crc_ins before insert on URL FOR EACH ROW BEGIN SET NEW.url_crc=crc32(NEW.url); END; // CREATE TRIGGER url_hash_crc_upd BEFORE UPDATE ON URL FOR EACH ROW BEGIN SET NEW.url_crc=crc32(NEW.url); END; // delimiter ; mysql> select * from URL; +----+-----------------------+------------+ | id | url | url_crc | +----+-----------------------+------------+ | 1 | htttp://www.mysql.com | 1727608869 | +----+-----------------------+------------+ 1 row in set (0.00 sec) mysql> insert into URL(url) values('htttp://www.'); Query OK, 1 row affected (0.00 sec) mysql> select * from URL; +----+-----------------------+------------+ | id | url | url_crc | +----+-----------------------+------------+ | 1 | htttp://www.mysql.com | 1727608869 | | 2 | htttp://www. | 1196108391 | +----+-----------------------+------------+ 2 rows in set (0.00 sec) mysql> UPDATE URL SET url='http://www.baidu.com' where id=2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from URL; +----+-----------------------+------------+ | id | url | url_crc | +----+-----------------------+------------+ | 1 | htttp://www.mysql.com | 1727608869 | | 2 | http://www.baidu.com | 3500265894 | +----+-----------------------+------------+ 2 rows in set (0.00 sec)
mysql> select id from url where url='http://www.mysql.com' -> and url_crc=crc32("http://www.mysql.com");
- 索引大大减少了服务器需要扫描的数据量
- 索引可以帮助服务器避免排序和临时表
- 索引可以将随机I/O变成顺序I/O
mysql> select actor_id from sakila.actor where actor_id + 1 = 5
mysql> select actor_id from sakila.actor where actor_id = 5 - 1
mysql> select count(DISTINCT city)/count(*) from table_name 前缀去重数 除 总数。 mysql> select count(DISTINCT LEFT(city,3)) / count(*) AS sel3, count(DISTINCT LEFT(city,4)) / count(*) AS sel4, count(DISTINCT LEFT(city,5)) / count(*) AS sel5, count(DISTINCT LEFT(city,6)) / count(*) AS sel6, count(DISTINCT LEFT(city,7)) / count(*) AS sel7 from city; +--------+--------+--------+--------+--------+ | sel3 | sel4 | sel5 | sel6 | sel7 | +--------+--------+--------+--------+--------+ | 0.7633 | 0.9383 | 0.9750 | 0.9900 | 0.9933 | +--------+--------+--------+--------+--------+
mysql> show create table film_actor; | film_actor | CREATE TABLE `film_actor` ( `actor_id` smallint(5) unsigned NOT NULL, `film_id` smallint(5) unsigned NOT NULL, `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`actor_id`), KEY `idx_fk_film_id` (`film_id`), CONSTRAINT `fk_film_actor_actor` FOREIGN KEY (`actor_id`) REFERENCES `actor` (`actor_id`) ON UPDATE CASCADE, CONSTRAINT `fk_film_actor_film` FOREIGN KEY (`film_id`) REFERENCES `film` (`film_id`) ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | mysql> explain select film_id,actor_id from film_actor where actor_id=1 or film_id =1\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: film_actor type: index_merge possible_keys: PRIMARY,idx_fk_film_id key: PRIMARY,idx_fk_film_id key_len: 2,2 ref: NULL rows: 29 Extra: Using union(PRIMARY,idx_fk_film_id); Using where
- 当出现服务器对多个索引做相交操作时(通常有多个AND条件),通常意味着需要一个包含所有相关列的多列索引,而不是多个独立的单列索引。
- 当服务器需要对多个索引做联合操作时(通常有多个OR条件),通常需要耗费大量CPU和内存资源在算法的缓存,排序,和合并操作上。特别是当其中有些索引的选择性不高,需要合并扫描返回大量数据的时候。
- 更重要的是,优化器不会把这些计算到"查询成本(cost)"中,优化器只关心随机页面读取。
mysql> select * from payment where staff_id =2 and customer_id=584;
mysql> select sum(staff_id=2),sum(customer_id=584) from payment \G; *************************** 1. row *************************** sum(staff_id=2): 7992 sum(customer_id=584): 30 1 row in set (0.04 sec)
mysql> select sum(staff_id=2) from payment where customer_id=584 \G; *************************** 1. row *************************** sum(staff_id=2): 17 1 row in set (0.00 sec)
mysql> select count(DISTINCT staff_id) / count(*) AS staff_id_first, count(DISTINCT customer_id) / count(*) AS customer_id_first from payment\G *************************** 1. row *************************** staff_id_first: 0.0001 customer_id_first: 0.0373
- 可以把相关数据保存在一起。减少磁盘I/O
- 数据访问更快
- 使用覆盖索引扫描的查询可以直接使用页节点中的主键值
- 聚簇数据最大限度地提高了I/O密集型应用的性能,但如果数据全部都放在内存中,则访问的顺序就没有那么重要了,聚簇索引也就没什么优势了。
- 插入速速严重依赖于插入顺序。
- 更新聚簇索引列的代价很高。
- 出入新行或者主键更新需要移动时,可能面临"页分裂(page split)"问题。当行的主键值要求必须插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次页分裂操作。页分裂会导致表占用更多的磁盘空间。
- 二级索引(非聚簇索引)即普通索引,在其叶子节点包含了引用行的主键列。
crate table layout_test( col1 int NOT NULL, col2 int NOT NULL, PRIMARY KEY(col1), KEY(col2) );
- 写入的目标页可能已经刷新到磁盘上并从缓存中移除,或者还没有加载到缓存中,这样innodb在插入前不得不先找到并从磁盘读取目标页到内存中。导致了大量的随机I/O。
- 因为写入是乱序的,innodb不得不频繁地做页分裂操作,以便为新的行分配空间。页分裂会导致移动大量数据,一次插入最少需要修改三个页而不是一个页。
- 由于频繁的页分裂,页会变得稀疏被不规则地填充,所以最终数据会有碎片。
- 索引条目通常远小于数据行大小,所以如果只需要读取索引,那么mysql就会极大地减少数据访问量。
- 因为索引是按照列值顺序存储的(至少在单个页内是如此),所以对于I/O密集型的范围查询会比随机从磁盘读取每一行数据的I/O要少得多。
- 一些存储引擎如Myisam在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用。
- 由于innodb的聚簇索引,覆盖索引对innodb表特别有用。
mysql> explain select store_id,film_id from inventory \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: inventory type: index possible_keys: NULL key: idx_store_id_film_id key_len: 3 ref: NULL rows: 3496 Extra: Using index 1 row in set (0.00 sec)
- 没有任何索引能够覆盖这个查询。
- mysql能在索引中最左前缀匹配的like比较如"Apoll%",而无法做通配符开头的like 如"%Apoll%"
- 第一个数据集,Sean Carrey 出演了30000部作品,其中有20000部标题包含了Apollo
- 第一个数据集,Sean Carrey 出演了30000部作品,其中有40部标题包含了Apollo
- 第一个数据集,Sean Carrey 出演了50部作品,其中有10部标题包含了Apollo
- 在第一个数据集中:
-
- 原查询:从索引actor中读到30000条数据,再根据得到的主键ID回数据表中再读30000条数据;总共读取60000条;
- 优化后的查询:先从索引actor2中读到30000条sena carrey,之后在所有Sean Carrey 中做like 比较 ,找到20000条prod_id;之后还是要回到数据表中,根据prod_id再读取20000条记录;总共读取50000条;
- 分析:总数虽然少了17%,但是子查询中的like比较开销会比较大,相抵之后效率并没有什么提升。
- 在第二个数据集中:
-
- 原查询:从索引actor中读到30000条数据,再根据得到的主键ID回数据表中再读30000条数据;总共读取60000条;
- 优化后的查询:先从索引actor2中读到30000条sena carrey,之后在所有Sean Carrey 中做like 比较 ,找到40条prod_id;之后还是要回到数据表中,根据prod_id再读取40条记录;总共读取30040条;
- 分析:读取总数降低了50%, 相比子查询中的开销 还是值得;
- 第三个数据集:显示了子查询效率反而下降的情况。因为索引过滤时符合第一个条件的结果集已经很小,索引子查询带来的成本反而比从表中直接提取完整行更高。
- order by a
-
- 满足最左前缀要求
- a = 3 order by b
-
- 满足最左前缀为常数
- order by a,b
-
- 满足最左前缀要求
- order by a desc,b desc
-
- 满足最左前缀要求
- a>5 order by a,b
-
- 满足最左前缀要求
- order by b
-
- 不满足最左前缀要求
- a >5 order by b
-
- 不满足最左前缀,且,最左前缀不是常数
- a in (1,3) order by b
-
- 不满足最左前缀,且,最左前缀不是常数
- oder by a asc ,b desc
-
- 排序方向不一致
- 搜索的索引列。
-
- 不一定是所要选择的列;即where 后面的查询条件加索引,而不是select 后面的选择列
- 使用唯一索引。
- 使用短索引。
-
- 如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。
- 利用最左前缀。
- 不要过度索引
- innodb表,指定主键,并且是自增的最好;
- 都可以用在,where col=1 or col in (15,18,20),这样的定值查询中;
- 而在范围查询中,where col>1 and col<10 或者 col like 'ab%' or col between 'lisa' and 'simon';此时只有BTREE索引能使用;HASH索引在这种情况中,不会被使用到,会对全表进行扫描;
- 找到并修复损坏的表
- 维护准确的索引统计信息
- 减少碎片
mysql> show index from actor\G; *************************** 1. row *************************** Table: actor Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: actor_id Collation: A Cardinality: 200 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: actor Non_unique: 1 Key_name: idx_actor_last_name Seq_in_index: 1 Column_name: last_name Collation: A Cardinality: 200 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) Cardinality,显示了存储引擎估算索引列有多少个不同的取值。 mysql5.6 以后可以通过参数innodb_analyze_is_persistent,来控制analyze 是否启动; 减少索引和数据的碎片 数据碎片三种类型: 行碎片(row fragmentation) 数据行被存储为多个地方的多个片段中。 行间碎片(Intra-row fragmentation) 逻辑上顺序的页,在磁盘上不是顺序存储的。 剩余空间碎片(Free space fragmentation) 数据页中有大量的空余空间。 使用命令: optimize table tb_name,清理碎片。 mysql> OPTIMIZE TABLE actor; +--------------+----------+----------+-------------------------------------------------------------------+ | Table | Op | Msg_type | Msg_text | +--------------+----------+----------+-------------------------------------------------------------------+ | sakila.actor | optimize | note | Table does not support optimize, doing recreate + analyze instead | | sakila.actor | optimize | status | OK | +--------------+----------+----------+-------------------------------------------------------------------+ 2 rows in set (0.02 sec)
mysql> alter table actor engine=innodb; Query OK, 200 rows affected (0.02 sec) Records: 200 Duplicates: 0 Warnings: 0
- 索引维护由数据库自动完成
- 插入/修改/删除每一个索引行都变成一个内部封装的事务
- 索引越多,事务越长,代价越高
- 索引越多对表的插入和索引字段修改就越慢
- 联合索引能为前缀单列,复列查询提供帮助
- 合理创建联合索引,避免冗余
- 男女比例相仿的表中性别不适合创建单列索引
- 如果男女比例极不平衡,要查询的又是少数方(理工院校查女生)可以考虑使用索引
- 在非常长的字段上建立索引影响性能
- innodb索引单字段(utf8)只能取前767bytes
- 对长字段处理的方法
- 最核心SQL考虑索引覆盖
- 索引列进行数学运算或函数运算
- 未含复合索引的前缀字段
- 前缀通配‘_’ 和‘%’通配符
- 用OR分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么所涉及的索引都不会被用到。因为后面的查询肯定要走全表扫描,在存在全表扫描的情况下,就没有必要多一次索引扫描增加I/O访问,一次全表扫描过滤条件就足够了。
- where条件使用NOT,<>,!=
- 字段类型匹配
mysql> select '18015376320243459'=18015376320243459; +---------------------------------------+ | '18015376320243459'=18015376320243459 | +---------------------------------------+ | 1 | mysql> select '1801'+0; +----------+ | '1801'+0 | +----------+ | 1801 | +----------+
mysql> explain select name from indextest where age='30'\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: indextest type: ref possible_keys: idx_age key: idx_age key_len: 1 ref: const rows: 1 Extra: 1 row in set (0.00 sec)
- BTREE
-
- 存储索引项与主键
- BTREE索引可用在定值查询,范围查询,
- HASH
-
- 存储哈希值与行指针
- 仅用于定值查询,创建伪哈希索引;
- 前缀的选择性计算(去重前缀数除总数)
-
- mysql> select count(DISTINCT city)/count(*) from table_name
- 索引合并(index merge):说明此时表上索引,表结构等需要优化了;
- 选择合适的索引列顺序:需要根据表中实际数据进行选择,选择性高的放在前;
- 聚簇索引:innodb的聚簇索引实际上在同一结构中保存了BTree索引和数据行
- myisam的数据分布
-
- myisam按照数据插入的顺序存储在磁盘上
- 主键索引时,自动增加行号,表的主键和行号在叶子节点中,且叶子节点根据主键顺序排列;
- 其他列索引和主键索引无区别;
- innodb数据分布:
-
- 使用聚簇索引;
- 二级索引包含索引项和主键值
- 覆盖索引:
-
- extra中using index;
- 延迟关联(defferred join);
- 当然覆盖索引并不是都能提升性能,需要根据集体数据集;
- 使用索引进行排序,不能跨越索引项进行排序;
- 索引维护:由数据库自动完成,将DML封装成内部事务,索引越多代价越高,
- 更新索引统计信息:
-
- records_in_range()获取范围中有多少键值,
- info()获取索引基数
- 清理碎片:
-
- optimize table tbl,
- alter table tbl engine=innodb;
- 使用索引
-
- where
- order by 、group by、distinct,
- 联合索引:注意冗余,选择性好的放在联合索引左侧;
- 长字段的索引:
-
- 建立前缀索引
- 分拆字段建立联合索引,
- 无法使用索引:
-
- 索引列进行数学运算或函数运算
- 未遵守最左前缀原则
- or条件后一列没有索引
- where条件使用not <> !=
- 字段类型不匹配;
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:mysql基础测试
- MySQL replace函数怎么替换字符串语句 2020-03-09
- PHP访问MySQL查询超时怎么办 2020-03-09
- mysql登录时闪退 2020-02-27
- MySQL出现1067错误号 2020-02-27
- mysql7.x如何单独安装mysql 2020-02-27
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash