1.查询缓存优化你的查询php
大多数的MySQL服务器都开启了查询缓存。这是提升性最有效的方法之一,并且这是被MySQL的数据库引擎处理的。当有不少相同的查询被执行了屡次的时 候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操做表而直接访问缓存结果了。这里最主要的问题是,对于程序员来讲,这个事情是很容易 被忽略的。由于,咱们某些查询语句会让MySQL不使用缓存。请看下面的示例:
// 查询缓存不开启
$r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");mysql
// 开启查询缓存
$today = date("Y-m-d");
$r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");程序员
上面两条SQL语句的差异就是 CURDATE() ,MySQL的查询缓存对这个函数不起做用。因此,像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存,由于这些函数的返回是会不定的易变的。因此,你所须要的就是用一个变量来代替MySQL的函数,从而 开启缓存。
咱们能够看到,前一个结果显示搜索了 7883 行,然后一个只是搜索了两个表的 9 和 16 行。查看rows列可让咱们找到潜在的性能问题。sql
// 没有效率的:
$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) {
// ...
}缓存
从上图你能够看到那个搜索字串 “last_name LIKE ‘a%’”,一个是建了索引,一个是没有索引,性能差了4倍左右。另外,你应该也须要知道什么样的搜索是不能使用正常的索引的。例如,当你须要在一篇大的 文章中搜索一个词时,如: “WHERE post_content LIKE ‘%apple%’”,索引多是没有意义的。你可能须要使用MySQL全文索引 或是本身作一个索引(好比说:搜索关键词或是Tag什么的)
// 在state中查找company
$r = mysql_query("SELECT company_name FROM users
LEFT JOIN companies ON (users.state = companies.state)
WHERE users.id = $user_id");安全
// 两个state 字段应该是被建过索引的,并且应该是至关的类型,相同的字符集。服务器
下面的示例是随机挑一条记录
// 千万不要这样作:
$r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");网络
// 这要会更好:
$r = mysql_query("SELECT count(*) FROM user");
$d = mysql_fetch_row($r);
$rand = mt_rand(0,$d[0] - 1);
$r = mysql_query("SELECT username FROM user LIMIT $rand, 1");数据结构
// 不推荐
$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']}";
A. 尽量的使用 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.”
B. Prepared Statements
Prepared Statements很像存储过程,是一种运行在后台的SQL语句集合,咱们能够从使用 prepared statements 得到不少好处,不管是性能问题仍是安全问题。Prepared Statements 能够检查一些你绑定好的变量,这样能够保护你的程序不会受到“SQL注入式”攻击。固然,你也能够手动地检查你的这些变量,然而,手动的检查容易出问题, 并且很常常会被程序员忘了。当咱们使用一些framework或是ORM的时候,这样的问题会好一些。在性能方面,当一个相同的查询被使用屡次的时候,这 会为你带来可观的性能优点。你能够给这些Prepared Statements定义一些参数,而MySQL只会解析一次。虽然最新版本的MySQL在传输Prepared Statements是使用二进制形势,因此这会使得网络传输很是有效率。固然,也有一些状况下,咱们须要避免使用Prepared Statements,由于其不支持查询缓存。但听说版本5.1后支持了。在PHP中要使用prepared statements,你能够查看其使用手册:mysqli 扩展 或是使用数据库抽象层,如: PDO.
// 建立 prepared statement
if ($stmt = $mysqli->prepare("SELECT username FROM user WHERE state=?")) {
// 绑定参数 $stmt->bind_param("s", $state); // 执行 $stmt->execute(); // 绑定结果 $stmt->bind_result($username); // 移动游标 $stmt->fetch(); printf("%s is from %s\n", $username, $state); $stmt->close();
}
C. 无缓冲的查询
正常的状况下,当你在当你在你的脚本中执行一个SQL语句的时候,你的程序会停在那里直到没这个SQL语句返回,而后你的程序再往下继续执行。你可使用无 缓冲查询来改变这个行为。关于这个事情,在PHP的文档中有一个很是不错的说明: mysql_unbuffered_query() 函数:
“mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.”
上 面那句话翻译过来是说,mysql_unbuffered_query() 发送一个SQL语句到MySQL而并不像mysql_query()同样去自动fethch和缓存结果。这会至关节约不少可观的内存,尤为是那些会产生大 量结果的查询语句,而且,你不须要等到全部的结果都返回,只须要第一行数据返回的时候,你就能够开始立刻开始工做于查询结果了。然而,这会有一些限制。因 为你要么把全部行都读走,或是你要在进行下一次的查询前调用mysql_free_result() 清除结果。并且, mysql_num_rows() 或 mysql_data_seek() 将没法使用。因此,是否使用无缓冲的查询你须要仔细考虑。
D. 把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";
E. 固定长度的表会更快
若是表中的全部字段都是“固定长度”的,整个表会被认为是 “static” 或 “fixed-length”。 例如,表中没有以下类型的字段: VARCHAR,TEXT,BLOB。只要你包括了其中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL 引擎会用另外一种方法来处理。固定长度的表会提升性能,由于MySQL搜寻得会更快一些,由于这些固定的长度是很容易计算下一个数据的偏移量的,因此读取的 天然也会很快。而若是字段不是定长的,那么,每一次要找下一条的话,须要程序找到主键。而且,固定长度的表也更容易被缓存和重建。不过,惟一的反作用是, 固定长度的字段会浪费一些空间,由于定长的字段不管你用不用,他都是要分配那么多的空间。使用“垂直分割”技术(见下一条),你能够分割你的表成为两个一 个是定长的,一个则是不定长的。
F. 垂直分割
“垂直分割”是一种把数据库中的表按列变成几张表的方法,这样能够下降表的复杂度和字段的数目,从而达到优化的目的。(之前,在银行作过项目,见过一张表有100多个字段,很恐怖)
示 例一:在Users表中有一个字段是家庭地址,这个字段是可选字段,相比起,并且你在数据库操做的时候除了我的信息外,你并不须要常常读取或是改写这个字 段。那么,为何不把他放到另一张表中呢? 这样会让你的表有更好的性能,你们想一想是否是,大量的时候,我对于用户表来讲,只有用户ID,用户名,口令,用户角色等会被常用。小一点的表老是会有 好的性能。 示 例二: 你有一个叫 “last_login” 的字段,它会在每次用户登陆时被更新。可是,每次更新时会致使该表的查询缓存被清空。因此,你能够把这个字段放到另外一个表中,这样就不会影响你对用户 ID,用户名,用户角色的不停地读取了,由于查询缓存会帮你增长不少性能。另外,你须要注意的是,这些被分出去的字段所造成的表,你不会常常性地去 Join他们,否则的话,这样的性能会比不分割时还要差,并且,会是极数级的降低。
while (1) {
//每次只作1000条
mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000");
if (mysql_affected_rows() == 0) {
// 没得可删了,退出! break;
}
// 每次都要休息一下子
usleep(50000);
}
·target=”_blank”MyISAM Storage Engine
·InnoDB Storage Engine
been there down that