【转】MySQL品质优化的特等经验,随时补充

9、使用 ENUM 而不是 VARCHAR ?

ENUM 类型是分外快和严峻的。在实质上,其保存的是
TINYINT,但其外表上突显为字符串。那样一来,用那么些字段来做一些增选列表变得卓绝的两全。

尽管您有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你了解这一个字段的取值是个别而且一定的,那么,你应该拔取ENUM 而不是 VAKugaCHA汉兰达。

12、把IP地址存成 UNSIGNED INT

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

大家必需求运用UNSIGNED INT,因为 IP地址会动用所有32位的无符号整形

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语句的差别就是 CU奥德赛DATE()
,MySQL的询问缓存对那一个函数不起功能。所以,像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是会不定的易变的。所以,你所急需的就是用一个变量来代替MySQL的函数,从而拉开缓存。

8、永远为两张表安装一个ID

我们应该为数据库里的每张表都设置一个ID作为其主键,而最好的是一个INT型(推荐使用UNSIGNED),并设置上自动增长的AUTO INCREMENT标志。
就算是你 users 表有一个主键叫 “email”的字段,你也别让它成为主键。使用 VARCHAR 类型来当主键会使用得性能下降。另外,在你的程序中,你应该使用表的ID来构造你的数据结构。

与此同时,在MySQL数据引擎下,还有一对操作须求利用主键,在那个意况下,主键的质量和设置变得可怜关键,比如,集群,分区……

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']}";

2、EXPLAIN 你的SELECT查询

使用EXPLAIN根本字可以让您精通MySQL是什么样处理你的SQL语句的。

有表关联的查询,如下列:

select username, group_name
from users u
joins groups g on (u.group_id = g.id)

察觉查询缓慢,然后在group_id字段上增添索引,则会加快查询

6、千万不要OMuranoDE瑞鹰 BY RAND()

17、选用一个毋庸置疑的存储引擎

在 MySQL 中有几个存储引擎 MyISAM 和
InnoDB,每种引擎都有利有弊。酷壳以前小说《MySQL: InnoDB 仍旧MyISAM?》研讨和那一个业务。

MyISAM
适合于部分急需多量查询的行使,但其对于有雅量写操作并不是很好。甚至你只是亟需update一个字段,整个表都会被锁起来,而其他进度,就到底读进度都心有余而力不足操作直到读操作落成。其余,MyISAM
对于 SELECT COUNT(*) 这类的猜想是超快无比的。

InnoDB 的取向会是一个万分复杂的储存引擎,对于一些小的选用,它会比 MyISAM
还慢。他是它协理“行锁”
,于是在写操作比较多的时候,会更精良。并且,他还援助越多的高档应用,比如:事务。

10、从 PROCEDURE ANALYSE() 取得提议 ?

PROCEDURE ANALYSE() 会让 MySQL
帮您去分析你的字段和其实际的数量,并会给你有些管用的提出。唯有表中有实际的数目,那个提议才会变得有用,因为要做一些大的支配是须求有数量作为基础的。

比如说,要是你成立了一个 INT
字段作为你的主键,然则并从未太多的多寡,那么,PROCEDURE
ANALYSE()会提出你把这几个字段的花色改成 MEDIUMINT 。或是你利用了一个
VA帕杰罗CHA奥迪Q3 字段,因为数量不多,你或然会拿走一个让您把它改成 ENUM
的提出。那一个提出,都以只怕因为数量不够多,所以决定做得就不够准。

18、小心“永久链接”

“永久链接”的目标是用来减弱重复创建MySQL链接的次数。当一个链接被创建了,它会永远处于连接的情形,就到底数据库操作已经终止了。而且,自从我们的Apache先河选定它的子进程后——也等于说,下五回的HTTP请求会引用Apache的子进度,并选定相同的
MySQL 链接。

PHP手册:mysql_pconnect()
在争鸣上的话,那听起来格外的不易。不过从个体经验(也是多数人的)上的话,这些效应制造出来的小事越来越多。因为,你唯有半点的链接数,内存难题,文件句柄数,等等。

还要,Apache
运行在最为并行的环境中,会创造很多浩大的了经过。那就是干吗那种“永久链接”的编制工作地不好的因由。在您决定要运用“永久链接”从前,你须求美丽地考虑一下你的整整连串的架构。

参考

14、垂直细分

“垂直细分”是一种把数据库中的表按列变成几张表的章程,那样可以下落表的复杂度和字段的数据,从而达到优化的目标。(以前,在银行做过项目,见过一张表有100多少个字段,很恐怖)

以身作则一:在Users表中有一个字段是家中地址,这些字段是可选字段,相比起,而且你在数据库操作的时候除了个人消息外,你并不须求常常读取或是改写这几个字段。那么,为啥不把她放到其它一张表中呢?
那样会让您的表有更好的特性,大家想想是否,多量的时候,小编对此用户表来说,只有用户ID,用户名,口令,用户剧中人物等会被平时拔取。小一些的表总是会有好的性质。

示范二: 你有一个叫 “last_login”
的字段,它会在历次用户登录时被更新。但是,每便换代时会导致该表的询问缓存被清空。所以,你可以把那一个字段放到另一个表中,那样就不会潜移默化您对用户ID,用户名,用户角色的不停地读取了,因为查询缓存会帮您扩张很多性质。

其余,你必要留意的是,那个被分出来的字段所形成的表,你不会平常性地去Join他们,不然的话,那样的性质会比不分割时还要差,而且,会是极数级的下挫。

11、尽大概的使用 NOT NULL

除非您有一个很尤其的缘由去接纳 NULL 值,你应有总是让你的字段保持 NOT
NULL。那看起来好像有些争议,请往下看。

第一,问问你自身“Empty”和“NULL”有多大的区分(若是是INT,那就是0和NULL)?如果您觉得它们之间一直不什么样界别,那么你就绝不使用NULL。(你领会啊?在
Oracle 里,NULL 和 Empty 的字符串是一模一样的!)

无须以为 NULL
不必要空间,其急需额外的空间,并且,在您进行比较的时候,你的先后会更复杂。
当然,那里并不是说您就不可以运用NULL了,现实情形是很复杂的,依旧会稍稍处境下,你必要利用NULL值。

下边摘自MySQL自个儿的文档

“NULL columns require additional space in the row to record whether
their values are NULL. For MyISAM tables, each NULL column takes one
bit extra, rounded up to the nearest byte.”

16、 越小的列会越快

对此绝大部分的数据库引擎来说,硬盘操作只怕是最重点的瓶颈。所以,把您的数码变得紧密会对这种气象越发有协助,因为那收缩了对硬盘的造访。

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

设若一个表只会有几列罢了(比如说字典表,配置表),那么,我们就从未理由使用
INT 来做主键,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT
会更经济部分。假若您不需求记录时间,使用 DATE 要比 DATETIME 好得多。

理所当然,你也须求留够充足的伸张空间,不然,你之后来干这一个事,你会死的很丢脸,参看Slashdot的例证(二零零六年10月06日),一个简易的ALTEEnclaveTABLE语句花了3个多小时,因为其中有一千六百万条数据。

15、拆分大的 DELETE 或 INSELacrosseT 语句

假如你要求在一个在线的网站上去执行一个大的 DELETE 或 INSERubiconT
查询,你必要充足小心,要幸免你的操作让您的满贯网站甘休相应。因为这多少个操作是会锁表的,表一锁住了,其他操作都进不来了。

Apache
会有好多的子进程或线程。所以,其工作起来至极有功效,而大家的服务器也不希望有太多的子进度,线程和数据库链接,那是宏大的占服务器资源的政工,特别是内存。

比方你把您的表锁上一段时间,比如30分钟,那么对于一个有很高访问量的站点来说,那30秒所累积的访问进度/线程,数据库链接,打开的文件数,或然非但会让你泊WEB服务Crash,还大概会让您的整台服务器霎时掛了。

所以,即便你有一个大的拍卖,你定你早晚把其拆分,使用 LIMIT
条件是一个好的点子。下边是一个示范:

while (1) {
//每次只做1000条
mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000");
if (mysql_affected_rows() == 0) {
    // 没得可删了,退出!
    break;
}
// 每次都要休息一会儿
usleep(50000);

}

3、当只要一行数据时接纳LIMIT 1

当您查询表的多少时候,你已经明白结果只会有一条结果,单因为您或者须求去fetch游标,或是你大概会去检查重返的记录数。
在那种状态下,加上LIMIT 1 方可追加品质。这样同样,
MySQL数据库引擎会在找到一条数据后终止搜索,而不是一而再以后搜索下一条符合记录的数据。
上面的言传身教,只是为了找一下是否有“中国”的用户,很显眼,前面的会比前边的更有效能。(请留意,第一条中是Select
*,第二条是Select 1)

// 没有效率的:
$r = mysql_query("SELECT * FROM user WHERE country = 'China'");
if (mysql_num_rows($r) > 0) {
   // ...
}

// 有效率的:
$r = mysql_query("SELECT 1 FROM user WHERE country = 'China' LIMIT 1");
if (mysql_num_rows($r) > 0) {
// ...
}

13、固定长度的表会更快

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

定位长度的表会进步质量,因为MySQL搜寻得会更快一些,因为这么些定位的长度是很简单统计下一个多少的偏移量的,所以读取的本来也会很快。而只要字段不是定长的,那么,每两回要找下一条的话,须要程序找到主键。

同时,固定长度的表也更便于被缓存和重建。可是,唯一的副功效是,固定长度的字段会浪费一些上空,因为定长的字段无论你用不用,他都是要分配那么多的半空中。

宣传语

历经八个半月的备选,五次大改版,十七次小改版。le1024终归要和豪门照面了。

le1024每一日推荐1~3段,有趣、有爱、有传说的录像。

为你工作、学习、生活之余增加某些载歌载舞的感觉到。程序员一定要看的高兴录制网站

5、在Join表的时候利用一定类型的列,并将其索引

如果你的应用程序有很多JOIN查询,你应该确认两个表中Join的字段是被建过索引的。这样,MySQL内部会启动为你优化Join的SQL语句的机制。
而且,这些被用来Join的字段,应该是相同的类型的。例如:如果你要把DECIMAL字段和一个INT字段JOIN在一起,MYSQL就无法使用他们的索引。对于那些STRING类型,还需要有相同的字符集才行(两个表的字符集有可能不一样)

4、为寻找字段建索引

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

19、当查问较慢的时候,可用Join来改写一下该查询来拓展优化

    mysql> select sql_no_cache * from guang_deal_outs where deal_id in (select id from guang_deals where id = 100017151) ;
 Empty set (18.87 sec)

    mysql> select sql_no_cache a.* from guang_deal_outs a inner join guang_deals b on a.deal_id = b.id where b.id = 100017151;
    Empty set (0.01 sec)

原因
mysql> desc select sql_no_cache * from guang_deal_outs where deal_id in (select id from guang_deals where id = 100017151) ;
+----+--------------------+-----------------+-------+---------------+---------+---------+-------+----------+-------------+
| id | select_type        | table           | type  | possible_keys | key     | key_len | ref   | rows     | Extra       |
+----+--------------------+-----------------+-------+---------------+---------  +---------+-------+----------+-------------+
|  1 | PRIMARY            | guang_deal_outs | ALL   | NULL          | NULL    |     NULL    | NULL  | 18633779 | Using where |
|  2 | DEPENDENT SUBQUERY | guang_deals     | const | PRIMARY       | PRIMARY |     4       | const |        1 | Using index |
+----+--------------------+-----------------+-------+---------------+---------  +---------+-------+----------+-------------+
2 rows in set (0.04 sec)

mysql> desc select sql_no_cache a.* from guang_deal_outs a inner join guang_deals b on a.deal_id = b.id where b.id = 100017151;
+----+-------------+-------+-------+----------------------  +----------------------+---------+-------+------+-------------+
| id | select_type | table | type  | possible_keys        | key                     | key_len | ref   | rows | Extra       |
+----+-------------+-------+-------+----------------------  +----------------------+---------+-------+------+-------------+
|  1 | SIMPLE      | b     | const | PRIMARY              | PRIMARY                 | 4       | const |    1 | Using index |
|  1 | SIMPLE      | a     | ref   | idx_guang_dlout_dlid |     idx_guang_dlout_dlid | 4       | const |    1 |             |
+----+-------------+-------+-------+----------------------    +----------------------+---------+-------+------+-------------+  
 2 rows in set (0.05 sec)

其实在 guang_deal_outs 在deal_id 上也是有目录的。
实际笔者想把子查询设置为

    select * from guang_deal_outs where deal_id in (select id from guang_deals where id = 100017151);

变成下面的楷模

select * from guang_deal_outs where deal_id in (100017151);

但不幸的是,实际情况正好相反。MySQL试图让它和外侧的表暴发联系来“帮忙”优化查询,它认为上面的exists格局更有功能

select * from guang_deal_outs where exists (select * from guang_deals where id = 100017151 and id = guang_deal_outs.deal_id);

那种in子查询的款型,在外部表(比如上边的guang_deals)数据量相比较大的时候功效是很差的(如若对于较小的表,不会促成分明地震慑)

参考:
http://codingstandards.iteye.com/blog/1344833
http://coolshell.cn/articles/1846.html
http://hi.baidu.com/yzx110/item/74892ab6fc4601a5eaba93e1

网站地图xml地图