1. 为查询缓存优化你的查询

大多数的MySQL做事器都开启了查询缓存。
这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。
当有很多相同的查询被实行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表而直接访问缓存结果了。

这里最紧张的问题是,对付程序员来说,这个事情是很随意马虎被忽略的。
由于,我们某些查询语句会让MySQL不该用缓存。
请看下面的示例:

// 查询缓存不开启 $r = mysql_query(\"大众SELECT username FROM user WHERE signup_date >= CURDATE()\"大众); // 开启查询缓存 $today = date(\公众Y-m-d\"大众); $r = mysql_query(\"大众SELECT username FROM user WHERE signup_date >= '$today'\公众);

上面两条SQL语句的差别便是 CURDATE() ,MySQL的查询缓存对这个函数不起浸染。
以是,像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存,由于这些函数的返回是会不定的易变的。
以是,你所须要的便是用一个变量来代替MySQL的函数,从而 开启缓存。

phprandsql优化MySQL机能优化的最佳20条经验 Python

2. EXPLAIN 你的 SELECT 查询

利用 EXPLAIN 关键字可以让你知道MySQL是如何处理你的SQL语句的。
这可以帮你剖析你的查询语句或是表构造的性能瓶颈。

EXPLAIN 的查询结果还会见告你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的……等等,等等。

挑一个你的SELECT语句(推举挑选那个最繁芜的,有多表联接的),把关键字EXPLAIN加到前面。
你可以利用phpmyadmin来做这个事。
然后,你会看到一张表格。
下面的这个示例中,我们忘却加上了group_id索引,并且有表联接:

当我们为 group_id 字段加上索引后:

我们可以看到,前一个结果显示搜索了 7883 行,而后一个只是搜索了两个表的 9 和 16 行。
查看rows列可以让我们找到潜在的性能问题。

3. 当只要一行数据时利用 LIMIT 1

当你查询表的有些时候,你已经知道结果只会有一条结果,但由于你可能须要去fetch游标,或是你大概会去检讨返回的记录数。

在这种情形下,加上 LIMIT 1 可以增加性能。
这样一样,MySQL数据库引擎会在找到一条数据后停滞搜索,而不是连续今后查少下一条符合记录的数据。

4. 为搜索字段建索引

索引并不一定便是给主键或是唯一的字段。
如果在你的表中,有某个字段你总要会常常用来做搜索,那么,请为其建立索引吧。

从上图你可以看到那个搜索字串 “last_name LIKE ‘a%’”,一个是建了索引,一个是没有索引,性能差了4倍旁边。

5. 在Join表的时候利用相称类型的例,并将其索引

如果你的运用程序有很多 JOIN 查询,你该当确认两个表中Join的字段是被建过索引的。
这样,MySQL内部会启动为你优化Join的SQL语句的机制。

而且,这些被用来Join的字段,该当是相同的类型的。
例如:如果你要把 DECIMAL 字段和一个 INT 字段Join在一起,MySQL就无法利用它们的索引。
对付那些STRING类型,还须要有相同的字符集才行。
(两个表的字符集有可能不一样)

// 在state中查找company $r = mysql_query(\"大众SELECT company_name FROM users LEFT JOIN companies ON (users.state = companies.state) WHERE users.id = $user_id\"大众); // 两个 state 字段该当是被建过索引的,而且该当是相称的类型,相同的字符集。
6. 千万不要 ORDER BY RAND()

想打乱返回的数据行?随机挑一个数据?真不知道谁发明了这种用法,但很多新手很喜好这样用。
但你确不理解这样做有多么恐怖的性能问题。

如果你真的想把返回的数据行打乱了,你有N种方法可以达到这个目的。
这样利用只让你的数据库的性能呈指数级的低落。
这里的问题是:MySQL会不得 不去实行RAND()函数(很耗CPU韶光),而且这是为了每一行记录去记行,然后再对其排序。
就算是你用了Limit 1也无济于事(由于要排序)

7. 避免 SELECT

从数据库里读出越多的数据,那么查询就会变得越慢。
并且,如果你的数据库做事器和WEB做事器是两台独立的做事器的话,这还会增加网络传输的负载。

以是,你该当养成一个须要什么就取什么的好的习气。

// 不推举 $r = mysql_query(\"大众SELECT FROM user WHERE user_id = 1\公众); $d = mysql_fetch_assoc($r); echo \公众Welcome {$d['username']}\公众; // 推举 $r = mysql_query(\公众SELECT username FROM user WHERE user_id = 1\"大众); $d = mysql_fetch_assoc($r); echo \"大众Welcome {$d['username']}\"大众; 8. 永久为每张表设置一个ID

我们该当为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推举利用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。

就算是你 users 表有一个主键叫 “email”的字段,你也别让它成为主键。
利用 VARCHAR 类型来当主键会利用得性能低落。
其余,在你的程序中,你该当利用表的ID来布局你的数据构造。

而且,在MySQL数据引擎下,还有一些操作须要利用主键,在这些情形下,主键的性能和设置变得非常主要,比如,集群,分区……

在这里,只有一个情形是例外,那便是“关联表”的“外键”,也便是说,这个表的主键,通过多少个别的表的主键构成。
我们把这个情形叫做“外键”。
比 如:有一个“学生表”有学生的ID,有一个“课程表”有课程ID,那么,“成绩表”便是“关联表”了,其关联了学生表和课程表,在成绩表中,学生ID和课 程ID叫“外键”其共同组成主键。

9. 利用 ENUM 而不是 VARCHAR

ENUM 类型是非常快和紧凑的。
在实际上,其保存的是 TINYINT,但其外表上显示为字符串。
这样一来,用这个字段来做一些选项列表变得相称的完美。

如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你该当利用 ENUM 而不是 VARCHAR。

MySQL也有一个“建议”(见第十条)见告你怎么去重新组织你的表构造。
当你有一个 VARCHAR 字段时,这个建议会见告你把其改成 ENUM 类型。
利用 PROCEDURE ANALYSE() 你可以得到干系的建议。

10. 从 PROCEDURE ANALYSE() 取得建议

PROCEDURE ANALYSE() 会让 MySQL 帮你去剖析你的字段和其实际的数据,并会给你一些有用的建议。
只有表中有实际的数据,这些建议才会变得有用,由于要做一些大的决定是须要有数据作为根本的。

例如,如果你创建了一个 INT 字段作为你的主键,然而并没有太多的数据,那么,PROCEDURE ANALYSE()会建议你把这个字段的类型改成 MEDIUMINT 。
或是你利用了一个 VARCHAR 字段,由于数据不多,你可能会得到一个让你把它改成 ENUM 的建议。
这些建议,都是可能由于数据不足多,以是决策做得就不足准。

一定要把稳,这些只是建议,只有当你的表里的数据越来越多时,这些建议才会变得准确。
一定要记住,你才是终极做决定的人。

11. 尽可能的利用 NOT NULL

除非你有一个很特殊的缘故原由去利用 NULL 值,你该当总是让你的字段保持 NOT NULL。
这看起来彷佛有点争议,请往下看。

首先,问问你自己“Empty”和“NULL”有多大的差异(如果是INT,那便是0和NULL)?如果你以为它们之间没有什么差异,那么你就不要利用NULL。
(你知道吗?在 Oracle 里,NULL 和 Empty 的字符串是一样的!)

不要以为 NULL 不须要空间,其须要额外的空间,并且,在你进行比较的时候,你的程序会更繁芜。
当然,这里并不是说你就不能利用NULL了,现实情形是很繁芜的,依然会有些情形下,你须要利用NULL值。

12. Prepared Statements

Prepared Statements很像存储过程,是一种运行在后台的SQL语句凑集,可以从利用 prepared statements 得到很多好处,无论是性能问题还是安全问题。

在性能方面,当一个相同的查询被利用多次的时候,这会为你带来可不雅观的性能上风。
你可以给这些Prepared Statements定义一些参数,而MySQL只会解析一次。

虽然最新版本的MySQL在传输Prepared Statements是利用二进制形势,以是这会使得网络传输非常有效率。

13. 无缓冲的查询

正常的情形下,当你在当你在你的脚本中实行一个SQL语句的时候,你的程序会停在那里直到没这个SQL语句返回,然后你的程序再往下连续实行。
你可以利用无缓冲查询来改变这个行为。

mysql_unbuffered_query() 发送一个SQL语句到MySQL而并不像mysql_query()一样去自动fethch和缓存结果。
这会相称节约很多可不雅观的内存,尤其是那些会产生大 量结果的查询语句,并且,你不须要等到所有的结果都返回,只须要第一行数据返回的时候,你就可以开始立时开始事情于查询结果了。

然而,这会有一些限定。
由于你要么把所有行都读走,或是你要在进行下一次的查询前调用 mysql_free_result() 打消结果。
而且, mysql_num_rows() 或 mysql_data_seek() 将无法利用。
以是,是否利用无缓冲的查询你须要仔细考虑。

14. 把IP地址存成 UNSIGNED INT

创建一个 VARCHAR(15) 字段来存放字符串形式的IP而不是整形的IP。
如果用整形来存放,只须要4个字节,并且你可以有定长的字段。
而且,这会为你带来查询上的上风,尤其是当 你须要利用这样的WHERE条件:IP between ip1 and ip2。

我们必需要利用UNSIGNED INT,由于 IP地址会利用全体32位的无符号整形。

而你的查询,你可以利用 INET_ATON() 来把一个字符串IP转成一个整形,并利用 INET_NTOA() 把一个整形转成一个字符串IP。
在PHP中,也有这样的函数 ip2long() 和 long2ip()。

$r = \"大众UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id\公众; 15. 固定长度的表会更快

如果表中的所有字段都是“固定长度”的,全体表会被认为是 “static” 或 “fixed-length”。
例如,表中没有如下类型的字段: VARCHAR,TEXT,BLOB。
只要你包括了个中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL 引擎会用另一种方法来处理。

固定长度的表会提高性能,由于MySQL征采得会更快一些,由于这些固定的长度是很随意马虎打算下一个数据的偏移量的,以是读取的自然也会很快。
而如果字段不是定长的,那么,每一次要找下一条的话,须要程序找到主键。

并且,固定长度的表也更随意马虎被缓存和重修。
不过,唯一的副浸染是,固定长度的字段会摧残浪费蹂躏一些空间,由于定长的字段无论你用不用,他都是要分配那么多的空间。

利用“垂直分割”技能(见下一条),你可以分割你的表成为两个一个是定长的,一个则是不定长的。

16. 垂直分割

“垂直分割”是一种把数据库中的表按列变成几张表的方法,这样可以降落表的繁芜度和字段的数目,从而达到优化的目的。
(以前,在银行做过项目,见过一张表有100多个字段,很胆怯)

示例:在Users表中有一个字段是家庭地址,这个字段是可选字段,比较起,而且你在数据库操作的时候除了个 人信息外,你并不须要常常读取或是改写这个字段。
那么,为什么不把他放到其余一张表中呢? 这样会让你的表有更好的性能,大家想想是不是,大量的时候,我对付用户表来说,只有用户ID,用户名,口令,用户角色等会被常常利用。
小一点的表总是会有 好的性能。

17. 拆分大的 DELETE 或 INSERT 语句

如果你须要在一个在线的网站上去实行一个大的 DELETE 或 INSERT 查询,你须要非常小心,要避免你的操作让你的全体网站停滞相应。
由于这两个操作是会锁表的,表一锁住了,别的操作都进不来了。

Apache 会有很多的子进程或线程。
以是,其事情起来相称有效率,而我们的做事器也不肯望有太多的子进程,线程和数据库链接,这是极大的占做事器资源的事情,尤其是内存。

如果你把你的表锁上一段韶光,比如30秒钟,那么对付一个有很高访问量的站点来说,这30秒所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让你的WEB做事Crash,还可能会让你的整台做事器立时挂了。

以是,如果你有一个大的处理,你定你一定把其拆分,利用 LIMIT 条件是一个好的方法。

18. 越小的列会越快

对付大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈。
以是,把你的数据变得紧凑会对这种情形非常有帮助,由于这减少了对硬盘的访问。

参看 MySQL 的文档 Storage Requirements 查看所有的数据类型。

如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有情由利用 INT 来做主键,利用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。
如果你不须要记录韶光,利用 DATE 要比 DATETIME 好得多。

当然,你也须要留够足够的扩展空间,不然,你日后来干这个事,你会去世的很丢脸,参看Slashdot的例子(2009年11月06日),一个大略的ALTER TABLE语句花了3个多小时,由于里面有一千六百万条数据。

19. 选择精确的存储引擎

MyISAM 适宜于一些须要大量查询的运用,但其对付有大量写操作并不是很好。
乃至你只是须要update一个字段,全体表都会被锁起来,而别的进程,就算是读进程都 无法操作直到读操作完成。
其余,MyISAM 对付 SELECT COUNT() 这类的打算是超快无比的。

InnoDB 的趋势会是一个非常繁芜的存储引擎,对付一些小的运用,它会比 MyISAM 还慢。
他是它支持“行锁” ,于是在写操作比较多的时候,会更精良。
并且,他还支持更多的高等运用,比如:事务。

20. 利用一个工具关系映射器(Object Relational Mapper)

利用 ORM (Object Relational Mapper),你能够得到可靠的性能增涨。
一个ORM可以做的所有事情,也能被手动的编写出来。
但是,这须要一个高等专家。

ORM 的最主要的是“Lazy Loading”,也便是说,只有在须要的去取值的时候才会去真正的去做。
但你也须要小心这种机制的副浸染,由于这很有可能会由于要去创建很多很多小的查询反而会降落性能。

上面是关于mysql优化的二十条建议,以为不错的朋友可以收藏下。
后面会分享更多关于运维DBA和devops内容,感兴趣的朋友可以关注下!