索引优化,查询优化,查询缓存,服务器设置优化,操做系统和硬件优化,应用层面优化(web服务器,缓存)等等。这里的记录的优化技巧更适用于开发人员,都是从网络上收集和本身整理的,主要是查询语句上面的优化,其它层面的优化技巧在此不作记录。mysql
查询的开销指标:web
执行时间算法
检查的行数sql
返回的行数数据库
创建索引的几个准则:缓存
(1)、合理的创建索引可以加速数据读取效率,不合理的创建索引反而会拖慢数据库的响应速度。服务器
(2)、索引越多,更新数据的速度越慢。网络
(3)、尽可能在采用MyIsam做为引擎的时候使用索引(由于MySQL以BTree存储索引),而不是InnoDB。但MyISAM不支持Transcation。分布式
(4)、当你的程序和数据库结构/SQL语句已经优化到没法优化的程度,而程序瓶颈并不能顺利解决,那就是应该考虑使用诸如memcached这样的分布式缓存系统的时候了。memcached
(5)、习惯和强迫本身用EXPLAIN来分析你SQL语句的性能。
1、count的优化
好比:计算id大于5的城市
(1). select count(*) from world.city where id > 5;
(2). select (select count() from world.city) – count() from world.city where id <= 5;
a语句当行数超过11行的时候须要扫描的行数比b语句要多, b语句扫描了6行,此种状况下,b语句比a语句更有效率。当没有where语句的时候直接select count(*) from world.city这样会更快,由于mysql老是知道表的行数。
2、避免使用不兼容的数据类型
例如float和int、char和varchar、binary和varbinary是不兼容的。数据类型的不兼容可能使优化器没法执行一些原本能够进行的优化操做。
在程序中,保证在实现功能的基础上,尽可能减小对数据库的访问次数;经过搜索参数,尽可能减小对表的访问行数,最小化结果集,从而减轻网络负担;可以分开的操做尽可能分开处理,提升每次的响应速度;在数据窗口使用SQL时,尽可能把使用的索引放在选择的首列;算法的结构尽可能简单;在查询时,不要过多地使用通配符如 SELECT * FROM T1语句,要用到几列就选择几列如:SELECT COL1,COL2 FROM T1;在可能的状况下尽可能限制尽可能结果集行数如:SELECT TOP 300 COL1,COL2,COL3 FROM T1,由于某些状况下用户是不须要那么多的数据的。不要在应用中使用数据库游标,游标是很是有用的工具,但比使用常规的、面向集的SQL语句须要更大的开销;按照特定顺序提取数据的查找。
3、索引字段上进行运算会使索引失效
尽可能避免在WHERE子句中对字段进行函数或表达式操做,这将致使引擎放弃使用索引而进行全表扫描。如:
SELECT * FROM T1 WHERE F1/2=100 应改成: SELECT * FROM T1 WHERE F1=100*2
4、避免使用!=或<>、IS NULL或IS NOT NULL、IN ,NOT IN等这样的操做符
由于这会使系统没法使用索引,而只能直接搜索表中的数据。例如: SELECT id FROM employee WHERE id != “B%” 优化器将没法经过索引来肯定将要命中的行数,所以须要搜索该表的全部行。在in语句中能用exists语句代替的就用exists.
5、尽可能使用数字型字段
一部分开发人员和数据库管理人员喜欢把包含数值信息的字段
设计为字符型,这会下降查询和链接的性能,并会增长存储开销。这是由于引擎在处理查询和链接回逐个比较字符串中每个字符,而对于数字型而言只须要比较一次就够了。
6、合理使用EXISTS,NOT EXISTS子句
以下所示:
(1). SELECT SUM(T1.C1) FROM T1 WHERE (SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0)
(2). SELECT SUM(T1.C1) FROM T1WHERE EXISTS(SELECT * FROM T2 WHERE T2.C2=T1.C2)
二者产生相同的结果,可是后者的效率显然要高于前者。由于后者不会产生大量锁定的表扫描或是索引扫描。若是你想校验表里是否存在某条纪录,不要用count(*)那样效率很低,并且浪费服务器资源。能够用EXISTS代替。如:
IF (SELECT COUNT() FROM table_name WHERE column_name = ‘xxx’)能够写成:IF EXISTS (SELECT FROM table_name WHERE column_name = ‘xxx’)
7、 可以用BETWEEN的就不要用IN
8、 可以用DISTINCT的就不用GROUP BY
9、尽可能不要用SELECT INTO语句。SELECT INTO 语句会致使表锁定,阻止其余用户访问该表
10、 必要时强制查询优化器使用某个索引
SELECT * FROM T1 WHERE nextprocess = 1 AND processid IN (8,32,45) 改为:
SELECT * FROM T1 (INDEX = IX_ProcessID) WHERE nextprocess = 1 AND processid IN (8,32,45)
则查询优化器将会强行利用索引IX_ProcessID 执行查询。
11、消除对大型表行数据的顺序存取
尽管在全部的检查列上都有索引,但某些形式的WHERE子句强迫优化器使用顺序存取。如:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
解决办法可使用并集来避免顺序存取:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001 UNION SELECT * FROM orders WHERE order_num=1008
这样就能利用索引路径处理查询。【jacking 数据结果集不少,但查询条件限定后结果集不大的状况下,后面的语句快】
12、尽可能避免在索引过的字符数据中,使用非打头字母搜索。这也使得引擎没法利用索引
见以下例子:
SELECT * FROM T1 WHERE NAME LIKE ‘%L%’
SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’
SELECT * FROM T1 WHERE NAME LIKE ‘L%’
即便NAME字段建有索引,前两个查询依然没法利用索引完成加快操做,引擎不得不对全表全部数据逐条操做来完成任务。而第三个查询可以使用索引来加快操做,不要习惯性的使用 ‘%L%’这种方式(会致使全表扫描),若是可使用`L%’相对来讲更好;
十3、虽然UPDATE、DELETE语句的写法基本固定,可是仍是对UPDATE语句给点建议
(1). 尽可能不要修改主键字段。
(2). 当修改VARCHAR型字段时,尽可能使用相同长度内容的值代替。
(3). 尽可能最小化对于含有UPDATE触发器的表的UPDATE操做。
(4). 避免UPDATE将要复制到其余数据库的列。
(5). 避免UPDATE建有不少索引的列。
(6). 避免UPDATE在WHERE子句条件中的列。
十4、能用UNION ALL就不要用UNION
UNION ALL不执行SELECT DISTINCT函数,这样就会减小不少没必要要的资源
在跨多个不一样的数据库时使用UNION是一个有趣的优化方法,UNION从两个互不关联的表中返回数据,这就意味着不会出现重复的行,同时也必须对数据进行排序,咱们知道排序是很是耗费资源的,特别是对大表的排序。
UNION ALL能够大大加快速度,若是你已经知道你的数据不会包括重复行,或者你不在意是否会出现重复的行,在这两种状况下使用UNION ALL更适合。此外,还能够在应用程序逻辑中采用某些方法避免出现重复的行,这样UNION ALL和UNION返回的结果都是同样的,但UNION ALL不会进行排序。
十5、字段数据类型优化
(1). 避免使用NULL类型:NULL对于大多数数据库都须要特殊处理,MySQL也不例外,它须要更多的代码,更多的检查和特殊的索引逻辑,有些开发人员彻底没有意识到,建立表时NULL是默认值,但大多数时候应该使用NOT NULL,或者使用一个特殊的值,如0,-1做为默认值。
(2). 尽量使用更小的字段,MySQL从磁盘读取数据后是存储到内存中的,而后使用cpu周期和磁盘I/O读取它,这意味着越小的数据类型占用的空间越小,从磁盘读或打包到内存的效率都更好,但也不要太过执着减少数据类型,要是之后应用程序发生什么变化就没有空间了。修改表将须要重构,间接地可能引发代码的改变,这是很头疼的问题,所以须要找到一个平衡点。
(3). 优先使用定长型
十7、关于大数据量limit分布的优化(当偏移量特别大时,limit效率会很是低)
附上一个提升limit效率的简单技巧,在覆盖索引(覆盖索引用通俗的话讲就是在select的时候只用去读取索引而取得数据,无需进行二次select相关表)上进行偏移,而不是对全行数据进行偏移。能够将从覆盖索引上提取出来的数据和全行数据进行联接,而后取得须要的列,会更有效率,看看下面的查询:
mysql> select film_id, description from sakila.film order by title limit 50, 5;
若是表很是大,这个查询最好写成下面的样子:
mysql> select film.film_id, film.description from sakila.film
inner join(select film_id from sakila.film order by title liimit 50,5) as film usinig(film_id);
十8、程序中若是一次性对同一个表插入多条数据
好比如下语句:
insert into person(name,age) values(‘xboy’, 14);
insert into person(name,age) values(‘xgirl’, 15);
insert into person(name,age) values(‘nia’, 19);
把它拼成一条语句执行效率会更高.
insert into person(name,age) values(‘xboy’, 14), (‘xgirl’, 15),(‘nia’, 19);
十9、不要在选择的栏位上放置索引,这是无心义的。应该在条件选择的语句上合理的放置索引,好比where,order by
SELECT id,title,content,cat_id FROM article WHERE cat_id = 1;
上面这个语句,你在id/title/content上放置索引是毫无心义的,对这个语句没有任何优化做用。可是若是你在外键cat_id上放置一个索引,那做用就至关大了。
二10、ORDER BY语句的MySQL优化
(1). ORDER BY + LIMIT组合的索引优化。若是一个SQL语句形如:
SELECT [column1],[column2],…. FROM [TABLE] ORDER BY [sort] LIMIT [offset],[LIMIT];
这个SQL语句优化比较简单,在[sort]这个栏位上创建索引便可。
(2). WHERE + ORDER BY + LIMIT组合的索引优化,形如:
SELECT [column1],[column2],…. FROM [TABLE] WHERE [columnX] = [VALUE] ORDER BY [sort] LIMIT [offset],[LIMIT];
这个语句,若是你仍然采用第一个例子中创建索引的方法,虽然能够用到索引,可是效率不高。更高效的方法是创建一个联合索引(columnX,sort)
(3). WHERE + IN + ORDER BY + LIMIT组合的索引优化,形如:
SELECT [column1],[column2],…. FROM [TABLE] WHERE [columnX] IN ([value1],[value2],…) ORDER BY [sort] LIMIT [offset],[LIMIT];
这个语句若是你采用第二个例子中创建索引的方法,会得不到预期的效果(仅在[sort]上是using index,WHERE那里是using where;using filesort),理由是这里对应columnX的值对应多个。
目前哥还木有找到比较优秀的办法,等待高手指教。
(4).WHERE+ORDER BY多个栏位+LIMIT,好比:
SELECT * FROM [table] WHERE uid=1 ORDER x,y LIMIT 0,10;
对于这个语句,你们多是加一个这样的索引:(x,y,uid)。但实际上更好的效果是(uid,x,y)。这是由MySQL处理排序的机制形成的。