<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>超群.com的博客 &#187; Performance</title>
	<atom:link href="http://www.fuchaoqun.com/tag/performance/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.fuchaoqun.com</link>
	<description></description>
	<lastBuildDate>Thu, 08 Sep 2011 15:08:19 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>高效的MySQL分页</title>
		<link>http://www.fuchaoqun.com/2009/04/efficient-pagination-using-mysql/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=efficient-pagination-using-mysql</link>
		<comments>http://www.fuchaoqun.com/2009/04/efficient-pagination-using-mysql/#comments</comments>
		<pubDate>Wed, 29 Apr 2009 04:04:30 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Performance]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=222</guid>
		<description><![CDATA[PERCONA PERFORMANCE CONFERENCE 2009上，来自雅虎的几位工程师带来了一篇&#8221;Efficient Pagination Using MySQL&#8220;的报告，有很多亮点，本文是在原文基础上的进一步延伸。 首先看一下分页的基本原理： mysql&#62; explain SELECT * FROM message ORDER BY id DESC LIMIT 10000, 20\G ***************** 1. row ************** id: 1 select_type: SIMPLE table: message type: index possible_keys: NULL key: PRIMARY key_len: 4 ref: NULL rows: 10020 Extra: 1 row in set (0.00 sec) limit 10000,20的意思扫描满足条件的10020行，扔掉前面的10000行，返回最后的20行，问题就在这里，如果是limit 100000,100，需要扫描100100行，在一个高并发的应用里，每次查询需要扫描超过10W行，性能肯定大打折扣。文中还提到limit n性能是没问题的，因为只扫描n行。 文中提到一种&#8221;clue&#8221;的做法，给翻页提供一些&#8221;线索&#8221;，比如还是SELECT [...]]]></description>
			<content:encoded><![CDATA[<p>PERCONA PERFORMANCE CONFERENCE 2009上，来自雅虎的几位工程师带来了一篇&#8221;<a href="http://www.percona.com/ppc2009/PPC2009_mysql_pagination.pdf" target="_blank">Efficient Pagination Using MySQL</a>&#8220;的报告，有很多亮点，本文是在原文基础上的进一步延伸。</p>
<p>首先看一下分页的基本原理：</p>
<blockquote><p>mysql&gt; explain SELECT * FROM message ORDER BY id DESC LIMIT 10000, 20\G<br />
***************** 1. row **************<br />
id: 1<br />
select_type: SIMPLE<br />
table: message<br />
type: index<br />
possible_keys: NULL<br />
key: PRIMARY<br />
key_len: 4<br />
ref: NULL<br />
rows: 10020<br />
Extra:<br />
1 row in set (0.00 sec)</p></blockquote>
<p>limit 10000,20的意思扫描满足条件的10020行，扔掉前面的10000行，返回最后的20行，问题就在这里，如果是limit 100000,100，需要扫描100100行，在一个高并发的应用里，每次查询需要扫描超过10W行，性能肯定大打折扣。文中还提到limit n性能是没问题的，因为只扫描n行。</p>
<p>文中提到一种&#8221;clue&#8221;的做法，给翻页提供一些&#8221;线索&#8221;，比如还是SELECT * FROM message ORDER BY id DESC，按id降序分页，每页20条，当前是第10页，当前页条目id最大的是9527，最小的是9500，如果我们只提供&#8221;上一页&#8221;、&#8221;下一页&#8221;这样的跳转（不提供到第N页的跳转），那么在处理&#8221;上一页&#8221;的时候SQL语句可以是：</p>
<blockquote><p>SELECT * FROM message WHERE id &gt; 9527 ORDER BY id <span style="color: #ff0000;"><strong>ASC</strong></span> LIMIT 20;</p></blockquote>
<p>处理&#8221;下一页&#8221;的时候SQL语句可以是：</p>
<blockquote><p>SELECT * FROM message WHERE id &lt; 9500 ORDER BY id <span style="color: #ff0000;"><strong>DESC</strong></span> LIMIT 20;</p></blockquote>
<p>不管翻多少页，每次查询只扫描20行。</p>
<p>缺点是只能提供&#8221;上一页&#8221;、&#8221;下一页&#8221;的链接形式，但是我们的产品经理非常喜欢&#8221;&lt;上一页 1 2 3 4 <strong>5</strong> 6 7 8 9 下一页&gt;&#8221;这样的链接方式，怎么办呢？</p>
<p>如果LIMIT m,n不可避免的话，要优化效率，只有尽可能的让m小一下，我们扩展前面的&#8221;clue&#8221;做法，还是SELECT * FROM message ORDER BY id DESC，按id降序分页，每页20条，当前是第10页，当前页条目id最大的是9527，最小的是9500，比如要跳到第8页，我看的SQL语句可以这样写：</p>
<blockquote><p>SELECT * FROM message WHERE id &gt; 9527 ORDER BY id <span style="color: #ff0000;"><strong>ASC</strong></span> LIMIT 20,20;</p></blockquote>
<p>跳转到第13页：</p>
<blockquote><p>SELECT * FROM message WHERE id &lt; 9500 ORDER BY id <span style="color: #ff0000;"><strong>DESC</strong></span> LIMIT 40,20;</p></blockquote>
<p>原理还是一样，记录住当前页id的最大值和最小值，计算跳转页面和当前页相对偏移，由于页面相近，这个偏移量不会很大，这样的话m值相对较小，大大减少扫描的行数。其实传统的limit m,n，相对的偏移一直是第一页，这样的话越翻到后面，效率越差，而上面给出的方法就没有这样的问题。</p>
<p>注意SQL语句里面的ASC和DESC，如果是ASC取出来的结果，显示的时候记得倒置一下。</p>
<p>已在60W数据总量的表中测试，效果非常明显。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2009/04/efficient-pagination-using-mysql/feed/</wfw:commentRss>
		<slash:comments>30</slash:comments>
		</item>
		<item>
		<title>开心网(kaixin001)的首页为什么这样设计？</title>
		<link>http://www.fuchaoqun.com/2009/04/kaixin001-index/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=kaixin001-index</link>
		<comments>http://www.fuchaoqun.com/2009/04/kaixin001-index/#comments</comments>
		<pubDate>Fri, 24 Apr 2009 10:04:12 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[Arch]]></category>
		<category><![CDATA[kaixin001]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[YSLOW]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=218</guid>
		<description><![CDATA[不知大家有没有发现，就算你选择了&#8221;记录登录状态&#8221;，下次访问kaixin001.com的时候还是官方静态首页，过一会才跳转到个人首页，这样的用户体验反正我是不太舒服，查看kaixin001首页源代码，发现个中蹊跷： function _bodyonload&#40;&#41; &#123; .... var v_kx = getCookie&#40;'_kx'&#41;; if &#40;v_kx.length&#41; &#123; .... v_timeid = setTimeout&#40;&#34;gotohome()&#34;, 2000&#41;; &#125; &#125; &#160; function gotohome&#40;&#41; &#123; if &#40;v_timeid&#41; &#123; window.location = &#34;/home/?l=a&#34;; &#125; &#125; 代码翻译成白话就是：页面加载完毕后，检测是否有&#8217;_kx&#8217;的cookie，如果有的话2秒后跳转到&#8217;/home/?l=a&#8217;页面，有的时候页面加载时间就很长，再等个两秒跳转，你都恨不得重新登录。 不知道kaixin001设计的时候是如何考虑的，从用户体验的角度来说：囧；从技术的角度来说：相当囧，仅仅是增加了服务器请求数（第一次请求首页白瞎），不知道kaixin001慢是不是这个原因，可能有更囧的。 很多时候我们会需要处理登录状态和非登录状态，一般可以在程序层面判断，比如PHP： &#60;?php if &#40;empty&#40;$_COOKIE&#91;'_kx'&#93;&#41;&#41; &#123; &#160; // 显示官方首页 &#160; &#125;else &#123; &#160; // 显示个人首页，记得校验cookie &#160; &#125; ?&#62; kaixin001也是一个很大的SNS网站了，关于她的架构，未曾见诸网上，也许应该更开放一些，不过kaixin001网站整体的速度不太好，不知道是人太多、服务器太少，还是说存在伸展的问题。 用YSLOW测试了一下kaixin001&#8243;我的首页&#8221;，得分是F(49)，很多js没有压缩，js文件数有18个之多，css文件数5个，js文件放头部等等，同期测试豆瓣的&#8221;我的豆瓣&#8221;，得分C(79)，一直觉得douban的技术还是挺牛的，也比较开放。]]></description>
			<content:encoded><![CDATA[<p>不知大家有没有发现，就算你选择了&#8221;记录登录状态&#8221;，下次访问kaixin001.com的时候还是官方静态首页，过一会才跳转到个人首页，这样的用户体验反正我是不太舒服，查看kaixin001首页源代码，发现个中蹊跷：</p>
<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">function</span> _bodyonload<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    ....
    <span style="color: #003366; font-weight: bold;">var</span> v_kx <span style="color: #339933;">=</span> getCookie<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'_kx'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>v_kx.<span style="color: #660066;">length</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        ....
        <span style="color: #660066;">v_timeid</span> <span style="color: #339933;">=</span> setTimeout<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;gotohome()&quot;</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">2000</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> gotohome<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>v_timeid<span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        window.<span style="color: #660066;">location</span> <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;/home/?l=a&quot;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>
<p>代码翻译成白话就是：页面加载完毕后，检测是否有&#8217;_kx&#8217;的cookie，如果有的话2秒后跳转到&#8217;/home/?l=a&#8217;页面，有的时候页面加载时间就很长，再等个两秒跳转，你都恨不得重新登录。</p>
<p>不知道kaixin001设计的时候是如何考虑的，从用户体验的角度来说：囧；从技术的角度来说：相当囧，仅仅是增加了服务器请求数（第一次请求首页白瞎），不知道kaixin001慢是不是这个原因，可能有更囧的。</p>
<p>很多时候我们会需要处理登录状态和非登录状态，一般可以在程序层面判断，比如PHP：</p>
<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">empty</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_COOKIE</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'_kx'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// 显示官方首页</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// 显示个人首页，记得校验cookie</span>
&nbsp;
<span style="color: #009900;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>
<p>kaixin001也是一个很大的SNS网站了，关于她的架构，未曾见诸网上，也许应该更开放一些，不过kaixin001网站整体的速度不太好，不知道是人太多、服务器太少，还是说存在伸展的问题。</p>
<p>用YSLOW测试了一下kaixin001&#8243;我的首页&#8221;，得分是F(49)，很多js没有压缩，js文件数有18个之多，css文件数5个，js文件放头部等等，同期测试豆瓣的&#8221;我的豆瓣&#8221;，得分C(79)，一直觉得douban的技术还是挺牛的，也比较开放。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2009/04/kaixin001-index/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>你可能不知道的MySQL</title>
		<link>http://www.fuchaoqun.com/2009/03/something-you-may-do-not-know-about-mysql/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=something-you-may-do-not-know-about-mysql</link>
		<comments>http://www.fuchaoqun.com/2009/03/something-you-may-do-not-know-about-mysql/#comments</comments>
		<pubDate>Wed, 11 Mar 2009 09:53:15 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Tuning]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=179</guid>
		<description><![CDATA[前言： 实验的数据表如下定义： mysql&#62; desc tbl_name; +-------+--------------+------+-----+---------+-------+ &#124; Field &#124; Type         &#124; Null &#124; Key &#124; Default &#124; Extra &#124; +-------+--------------+------+-----+---------+-------+ &#124; uid   &#124; int(11)      &#124; NO   &#124;     &#124; NULL    &#124;       &#124; &#124; sid   &#124; mediumint(9) &#124; NO   &#124;     &#124; NULL    &#124;       &#124; &#124; times &#124; mediumint(9) &#124; NO   &#124;     &#124; NULL    &#124;       &#124; +-------+--------------+------+-----+---------+-------+ 3 [...]]]></description>
			<content:encoded><![CDATA[<p><strong>前言：</strong></p>
<p>实验的数据表如下定义：</p>
<blockquote>
<pre>mysql&gt; desc tbl_name;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| uid   | int(11)      | NO   |     | NULL    |       |
| sid   | mediumint(9) | NO   |     | NULL    |       |
| times | mediumint(9) | NO   |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)</pre>
</blockquote>
<p>存储引擎是MyISAM，里面有10,000条数据。</p>
<p><strong>一、&#8221;\G&#8221;的作用</strong></p>
<blockquote>
<pre>mysql&gt; select * from tbl_name limit 1;
+--------+--------+-------+
| uid    | sid    | times |
+--------+--------+-------+
| 104460 | 291250 |    29 |
+--------+--------+-------+
1 row in set (0.00 sec)
mysql&gt; select * from tbl_name limit 1<span style="color: #ff0000;">\G</span>;
*************************** 1. row ***************************
  uid: 104460
  sid: 291250
times: 29
1 row in set (0.00 sec)</pre>
</blockquote>
<p>有时候，操作返回的列数非常多，屏幕不能一行显示完，显示折行，试试&#8221;\G&#8221;，把列数据逐行显示（&#8221;\G&#8221;挽救了我，以前看explain语句横向显示不全折行看起来巨费劲，还要把数据和列对应起来）。</p>
<p><strong>二、&#8221;Group by&#8221;的&#8221;隐形杀手&#8221;</strong></p>
<blockquote>
<pre>mysql&gt; explain select uid,sum(times) from tbl_name group by uid\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10000
        Extra: Using temporary; <span style="color: #ff0000;">Using filesort</span>
1 row in set (0.00 sec)
mysql&gt; explain select uid,sum(times) from tbl_name group by uid <span style="color: #ff0000;">order by null</span>\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10000
        Extra: Using temporary
1 row in set (0.00 sec)</pre>
</blockquote>
<p>默认情况下，Group by col会对col字段进行排序，这就是为什么第一语句里面有Using filesort的原因，如果你不需要对col字段进行排序，加上order by null吧，要快很多，因为filesort很慢的。</p>
<p><strong>三、大批量数据插入</strong></p>
<p>最高效的大批量插入数据的方法：</p>
<blockquote><p>load data infile &#8216;/path/to/file&#8217; into table tbl_name;</p></blockquote>
<p>如果没有办法先生成文本文件或者不想生成文本文件，可以一次插入多行：</p>
<blockquote><p>insert into tbl_name values (1,2,3),(4,5,6),(7,8,9)&#8230;</p></blockquote>
<p>注意一条sql语句的最大长度是有限制的。如果还不想这样，可以试试MySQL的<a href="http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-prepared-statements.html" target="_blank">prepare</a>，应该都会比硬生生的逐条插入要快许多。</p>
<p>如果数据表有索引，建议先暂时禁用索引：</p>
<blockquote><p>alter table tbl_name disable keys;</p></blockquote>
<p>插入完毕之后再激活索引：</p>
<blockquote><p>alter table tbl_name enable keys;</p></blockquote>
<p>对MyISAM表尤其有用。避免每插入一条记录系统更新一下索引。</p>
<p><strong>四、最快复制表结构方法</strong></p>
<blockquote><p>mysql&gt; create table clone_tbl select * from tbl_name limit 0;<br />
Query OK, 0 rows affected (0.08 sec)</p></blockquote>
<p>只会复制表结构，索引不会复制，如果还要复制数据，把limit 0去掉即可。</p>
<p><strong>五、加引号和不加引号区别</strong></p>
<p>给数据表tbl_name添加索引：</p>
<blockquote><p>mysql&gt; create index uid on tbl_name(uid);</p></blockquote>
<p>测试如下查询：</p>
<blockquote>
<pre>mysql&gt; explain select * from tbl_name where uid = '1081283900'\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: ref
possible_keys: uid
          key: uid
      key_len: 4
          ref: const
         rows: 143
        Extra:
1 row in set (0.00 sec)</pre>
</blockquote>
<p>我们在整型字段的值上加索引，是可以用到索引的，网上不少人误传在整型字段上加引号无法使用索引。修改uid字段类型为varchar(12):</p>
<blockquote><p>mysql&gt; alter table tbl_name change uid uid varchar(12) not null;</p></blockquote>
<p>测试如下查询：</p>
<blockquote>
<pre>mysql&gt; explain select * from tbl_name where uid = 1081283900\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: ALL
possible_keys: uid
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10000
        Extra: Using where
1 row in set (0.00 sec)</pre>
</blockquote>
<p>我们在查询值上不加索引，结果索引无法使用，注意安全。</p>
<p><strong>六、前缀索引</strong></p>
<p>有时候我们的表中有varchar(255)这样的字段，而且我们还要对该字段建索引，一般没有必要对整个字段建索引，建立前8~12个字符的索引应该就够了，很少有连续8~12个字符都相等的字段。</p>
<p>为什么？更短的索引意味索引更小、占用CPU时间更少、占用内存更少、占用IO更少和很更好的性能。</p>
<p><strong>七、MySQL索引使用方式</strong></p>
<p>MySQL在一个查询中只能用到一个索引（5.0以后版本引入了index_merge合并索引，对某些特定的查询可以用到多个索引，具体查考[<a href="http://dev.mysql.com/doc/refman/5.1/zh/optimization.html#index-merge-optimization" target="_blank">中文</a>] [<a href="http://dev.mysql.com/doc/refman/5.0/en/index-merge-optimization.html" target="_blank">英文</a>]），所以要根据查询条件建立联合索引，联合索引只有第一位的字段在查询条件中能才能使用到。</p>
<p>如果MySQL认为不用索引比用索引更快的话，那么就不会用索引。</p>
<blockquote>
<pre>mysql&gt; create index times on tbl_name(times);
Query OK, 10000 rows affected (0.10 sec)
Records: 10000  Duplicates: 0  Warnings: 0
mysql&gt; explain select * from tbl_name where times &gt; 20\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: ALL
possible_keys: times
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10000
        Extra: Using where
1 row in set (0.00 sec)
mysql&gt; explain select * from tbl_name where times &gt; 200\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: range
possible_keys: times
          key: times
      key_len: 3
          ref: NULL
         rows: 1599
        Extra: Using where
1 row in set (0.00 sec)</pre>
</blockquote>
<p>数据表中times字段绝大多数都比20大，所以第一个查询没有用索引，第二个才用到索引。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2009/03/something-you-may-do-not-know-about-mysql/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
<!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->
