mysql索引结构原理、性能分析与优化(八)

2015-01-21 11:32:26 · 作者: · 浏览: 33
------+-------+-------+-------------+ 1 row in set, 1 warning (0.00 sec) 容易看出建的索引已起效 mysql> explain select * from one where last_login +1= 8388606 ; +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+ | 1 | SIMPLE | one | ALL | NULL | NULL | NULL | NULL | 2049 7 | Using where | +----+-------------+-------+------+---------------+------+---------+------+-------+-------------+ 1 row in set (0.00 sec) last_login +1=8388608非隔离列的出现导致查找的列20197,说明是遍历整张表且索引不能使用。 这是因为这条语句要找出所有last_login的数据,然后+1再和20197比较,优化器在这方面比较差,性能很差。 所以要尽可能的把列隔离出来,如last_login +1= 8388606改成login_login=8388607,或者把计算、转换等操作先用php函数处理 过再传递给mysql服务器

OR、IN、UNION ALL,可以尝试用UNION ALL

or会遍历表就算有索引

mysql> explain select * from one where username = 'abgvwfnt' or password='123456';
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows| Extra |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
| 1 | SIMPLE | one | ALL | username | NULL | NULL | NULL | 20259 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
1 row in set (0.00 sec)

对于in,这个是有争议的,网上很多优化方案中都提到尽量少用in,这不全面,其实在in里面如果是常量的话,可一大胆的用in, 这个也是赶集网石展、阿里hellodab的观点(笔者从微博中获知)。应用hellodab一句话“MySQL用IN效率不好,通常是指in中 嵌套一个子查询,因为MySQL的查询重写可能会产生一个不好的执行计划,而如果in里面是常量的话,我认为性能没有任何问题, 可以放心使用”---------当然对于这个比较的话,没有实战数据的话很难辩解,就算有,影响性能的因素也很多,也许会每个 dba都有不同的测试结果.这也签名最左前缀中“补洞”一个方法

UNION All 直接返回并集,可以避免去重的开销。之所说“尝试”用UNION All 替代 OR来优化sql语句,因为这不是一直能优化的了, 这里只是作为一个方法去尝试。

索引选择性

索引选择性是不重复的索引值也叫基数(cardinality)表中数据行数的比值,索引选择性=基数/数据行,基数可以通过 “show index from 表名”查看。高索引选择性的好处就是mysql查找匹配的时候可以过滤更多的行,唯一索引的选择性最佳,值为1。 那么对于非唯一索引或者说要被创建索引的列的数据内容很长,那就要选择索引前缀。这里就简单说明一下:

mysql> select count(distinct(username))/count(*)  from one;  
+------------------------------------+  
| count(distinct(username))/count(*) |  
+------------------------------------+  
|                             0.2047 |  
+------------------------------------+  
1 row in set (0.09 sec)  

count(distinct(username))/count( )就是索引选择性,这里0.2太小了。假如username列数据很长,则可以通过 select count(distinct(concat(first_name, left(last_name, N))/count( ) from one;测试出接近1的索引选择性, 其中N是索引的长度,穷举法去找出N的值,然后再建索引。

重复或多余索引

很多phper开始都以为建索引相对多点性能就好点,压根没考虑到有些索引是重复的,比如建一个(username),(username,password), (username,password,last_login),很明显第一个索引是重复的,因为后两者都能满足其功能。要有个意识就是,在满足功能需求的 情况下建最少索引。对于INNODB引擎的索引来说,每次修改数据都要把主键索引,辅助索引中相应索引值修改,这可能会出现大量数 据迁移,分页,以及碎片的出现。

系统配置与维护优化

重要的一些变量

key_buffer_size索引块缓存区大小, 针对MyISAM存储引擎,该值越大,性能越好.但是超过操作系统能承受的最大值,反而会使mysql变得不稳定. ----这是很重要的参数sort_buffer_size 这是索引在排序缓冲区大小,若排序数据大小超过该值,则创建临时文件,注意和MyISAM_sort_buffer_size的区别----这是很重要的参数read_rnd_buffer_size当排序后按排序后的顺序读取行时,则通过该缓冲区读取行,避免搜索硬盘。将该变量设置为较大的值可以大大改进ORDER BY的性能。但是,这是为每个客户端分配的缓冲区,因此你不应将全局变量设置为较大的值。相反,只为需要运行大查询的客户端更改会话变量join_buffer_size用于表间关联(join)的缓存大小tmp_table_size缓存表的大小table_cache允许 MySQL 打开的表的最大个数,并且这些都cache在内存中delay_key_write