<?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; MySQL</title>
	<atom:link href="http://www.fuchaoqun.com/category/mysql/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>高性能LAMP程序设计</title>
		<link>http://www.fuchaoqun.com/2011/07/high-performance-lamp-website/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=high-performance-lamp-website</link>
		<comments>http://www.fuchaoqun.com/2011/07/high-performance-lamp-website/#comments</comments>
		<pubDate>Sun, 17 Jul 2011 23:51:29 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[Arch]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">http://www.fuchaoqun.com/?p=484</guid>
		<description><![CDATA[周六分享的PPT，一些比较common的大杂烩，看不到slides的同学在这里查看。]]></description>
			<content:encoded><![CDATA[<p>周六分享的PPT，一些比较common的大杂烩，看不到slides的同学在<a href="http://www.slideshare.net/fuchaoqun/lamp-8611126" target="_blank">这里</a>查看。<br />
<iframe src="http://www.slideshare.net/slideshow/embed_code/8611126?rel=0" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe></p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2011/07/high-performance-lamp-website/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<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>根据status信息对MySQL服务器进行优化（二）</title>
		<link>http://www.fuchaoqun.com/2009/03/mysql-tuning-by-status-ii/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mysql-tuning-by-status-ii</link>
		<comments>http://www.fuchaoqun.com/2009/03/mysql-tuning-by-status-ii/#comments</comments>
		<pubDate>Wed, 25 Mar 2009 07:06:56 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Tuning]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=204</guid>
		<description><![CDATA[续根据status信息对MySQL服务器进行优化（一），直入主题。 六、进程使用情况 mysql&#62; show global status like 'Thread%'; +-------------------+-------+ &#124; Variable_name     &#124; Value &#124; +-------------------+-------+ &#124; Threads_cached    &#124; 46    &#124; &#124; Threads_connected &#124; 2     &#124; &#124; Threads_created   &#124; 570   &#124; &#124; Threads_running   &#124; 1     &#124; +-------------------+-------+ 如果我们在MySQL服务器配置文件中设置了thread_cache_size，当客户端断开之后，服务器处理此客户的线程将会缓存起来以响应下一个客户而不是销毁（前提是缓存数未达上限）。Threads_created表示创建过的线程数，如果发现Threads_created值过大的话，表明MySQL服务器一直在创建线程，这也是比较耗资源，可以适当增加配置文件中thread_cache_size值，查询服务器thread_cache_size配置： mysql&#62; show variables like 'thread_cache_size'; +-------------------+-------+ &#124; Variable_name     &#124; Value &#124; +-------------------+-------+ &#124; thread_cache_size &#124; 64    &#124; [...]]]></description>
			<content:encoded><![CDATA[<p>续<a href="http://chaoqun.17348.com/2009/03/mysql-tuning-by-status/" target="_blank">根据status信息对MySQL服务器进行优化（一）</a>，直入主题。</p>
<p><strong>六、进程使用情况</strong></p>
<blockquote>
<pre>mysql&gt; show global status like 'Thread%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_cached    | 46    |
| Threads_connected | 2     |
| Threads_created   | 570   |
| Threads_running   | 1     |
+-------------------+-------+</pre>
</blockquote>
<p>如果我们在MySQL服务器配置文件中设置了thread_cache_size，当客户端断开之后，服务器处理此客户的线程将会缓存起来以响应下一个客户而不是销毁（前提是缓存数未达上限）。Threads_created表示创建过的线程数，如果发现Threads_created值过大的话，表明MySQL服务器一直在创建线程，这也是比较耗资源，可以适当增加配置文件中thread_cache_size值，查询服务器thread_cache_size配置：</p>
<blockquote>
<pre>mysql&gt; show variables like 'thread_cache_size';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| thread_cache_size | 64    |
+-------------------+-------+</pre>
</blockquote>
<p>示例中的服务器还是挺健康的。</p>
<p><strong>七、查询缓存(query cache)</strong></p>
<blockquote>
<pre>mysql&gt; show global status like 'qcache%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Qcache_free_blocks      | 22756     |
| Qcache_free_memory      | 76764704  |
| Qcache_hits             | 213028692 |
| Qcache_inserts          | 208894227 |
| Qcache_lowmem_prunes    | 4010916   |
| Qcache_not_cached       | 13385031  |
| Qcache_queries_in_cache | 43560     |
| Qcache_total_blocks     | 111212    |
+-------------------------+-----------+</pre>
</blockquote>
<p>MySQL查询缓存变量解释：</p>
<blockquote><p>Qcache_free_blocks：缓存中相邻内存块的个数。数目大说明可能有碎片。FLUSH QUERY CACHE会对缓存中的碎片进行整理，从而得到一个空闲块。<br />
Qcache_free_memory：缓存中的空闲内存。<br />
Qcache_hits：每次查询在缓存中命中时就增大<br />
Qcache_inserts：每次插入一个查询时就增大。命中次数除以插入次数就是不中比率。<br />
Qcache_lowmem_prunes：缓存出现内存不足并且必须要进行清理以便为更多查询提供空间的次数。这个数字最好长时间来看；如果这个数字在不断增长，就表示可能碎片非常严重，或者内存很少。（上面的 free_blocks和free_memory可以告诉您属于哪种情况）<br />
Qcache_not_cached：不适合进行缓存的查询的数量，通常是由于这些查询不是 <code>SELECT</code> 语句或者用了now()之类的函数。<br />
Qcache_queries_in_cache：当前缓存的查询（和响应）的数量。<br />
Qcache_total_blocks：缓存中块的数量。</p></blockquote>
<p>我们再查询一下服务器关于query_cache的配置：</p>
<blockquote>
<pre>mysql&gt; show variables like 'query_cache%';
+------------------------------+-----------+
| Variable_name                | Value     |
+------------------------------+-----------+
| query_cache_limit            | 2097152   |
| query_cache_min_res_unit     | 4096      |
| query_cache_size             | 203423744 |
| query_cache_type             | ON        |
| query_cache_wlock_invalidate | OFF       |
+------------------------------+-----------+</pre>
</blockquote>
<p>各字段的解释：</p>
<blockquote><p>query_cache_limit：超过此大小的查询将不缓存<br />
query_cache_min_res_unit：缓存块的最小大小<br />
query_cache_size：查询缓存大小<br />
query_cache_type：缓存类型，决定缓存什么样的查询，示例中表示不缓存 select sql_no_cache 查询<br />
query_cache_wlock_invalidate：当有其他客户端正在对MyISAM表进行写操作时，如果查询在query cache中，是否返回cache结果还是等写操作完成再读表获取结果。</p></blockquote>
<p>query_cache_min_res_unit的配置是一柄&#8221;双刃剑&#8221;，默认是4KB，设置值大对大数据查询有好处，但如果你的查询都是小数据查询，就容易造成内存碎片和浪费。</p>
<blockquote><p>查询缓存碎片率 = Qcache_free_blocks / Qcache_total_blocks * 100%</p></blockquote>
<p>如果查询缓存碎片率超过20%，可以用FLUSH QUERY CACHE整理缓存碎片，或者试试减小query_cache_min_res_unit，如果你的查询都是小数据量的话。</p>
<blockquote><p>查询缓存利用率 = (query_cache_size &#8211; Qcache_free_memory) / query_cache_size * 100%</p></blockquote>
<p>查询缓存利用率在25%以下的话说明query_cache_size设置的过大，可适当减小；查询缓存利用率在80％以上而且Qcache_lowmem_prunes &gt; 50的话说明query_cache_size可能有点小，要不就是碎片太多。</p>
<blockquote><p>查询缓存命中率 = (Qcache_hits &#8211; Qcache_inserts) / Qcache_hits * 100%</p></blockquote>
<p>示例服务器 查询缓存碎片率 ＝ 20.46％，查询缓存利用率 ＝ 62.26％，查询缓存命中率 ＝ 1.94％，命中率很差，可能写操作比较频繁吧，而且可能有些碎片。</p>
<p><strong>八、排序使用情况</strong></p>
<blockquote>
<pre>mysql&gt; show global status like 'sort%';
+-------------------+------------+
| Variable_name     | Value      |
+-------------------+------------+
| Sort_merge_passes | 29         |
| Sort_range        | 37432840   |
| Sort_rows         | 9178691532 |
| Sort_scan         | 1860569    |
+-------------------+------------+</pre>
</blockquote>
<p>Sort_merge_passes 包括两步。MySQL 首先会尝试在内存中做排序，使用的内存大小由系统变量 Sort_buffer_size 决定，如果它的大小不够把所有的记录都读到内存中，MySQL 就会把每次在内存中排序的结果存到临时文件中，等 MySQL 找到所有记录之后，再把临时文件中的记录做一次排序。这再次排序就会增加 Sort_merge_passes。实际上，MySQL 会用另一个临时文件来存再次排序的结果，所以通常会看到 Sort_merge_passes 增加的数值是建临时文件数的两倍。因为用到了临时文件，所以速度可能会比较慢，增加 Sort_buffer_size 会减少 Sort_merge_passes 和 创建临时文件的次数。但盲目的增加 Sort_buffer_size 并不一定能提高速度，见 <a href="http://www.mysqlperformanceblog.com/2007/08/18/how-fast-can-you-sort-data-with-mysql/" target="_blank">How fast can you sort data with MySQL?</a>（引自<a href="http://qroom.blogspot.com/2007/09/mysql-select-sort.html" target="_blank">http://qroom.blogspot.com/2007/09/mysql-select-sort.html</a>，貌似被墙）</p>
<p>另外，增加read_rnd_buffer_size(3.2.3是record_rnd_buffer_size)的值对排序的操作也有一点的好处，参见：<a href="http://www.mysqlperformanceblog.com/2007/07/24/what-exactly-is-read_rnd_buffer_size/" target="_blank">http://www.mysqlperformanceblog.com/2007/07/24/what-exactly-is-read_rnd_buffer_size/</a></p>
<p><strong>九、文件打开数(open_files)</strong></p>
<blockquote>
<pre>mysql&gt; show global status like 'open_files';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_files    | 1410  |
+---------------+-------+
mysql&gt; show variables like 'open_files_limit';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| open_files_limit | 4590  |
+------------------+-------+</pre>
</blockquote>
<p>比较合适的设置：Open_files / open_files_limit * 100% &lt;= 75％</p>
<p><strong>十、表锁情况</strong></p>
<blockquote>
<pre>mysql&gt; show global status like 'table_locks%';
+-----------------------+-----------+
| Variable_name         | Value     |
+-----------------------+-----------+
| Table_locks_immediate | 490206328 |
| Table_locks_waited    | 2084912   |
+-----------------------+-----------+</pre>
</blockquote>
<p>Table_locks_immediate表示立即释放表锁数，Table_locks_waited表示需要等待的表锁数，如果Table_locks_immediate / Table_locks_waited &gt; 5000，最好采用InnoDB引擎，因为InnoDB是行锁而MyISAM是表锁，对于高并发写入的应用InnoDB效果会好些。示例中的服务器Table_locks_immediate / Table_locks_waited ＝ 235，MyISAM就足够了。</p>
<p><strong>十一、表扫描情况</strong></p>
<blockquote>
<pre>mysql&gt; show global status like 'handler_read%';
+-----------------------+-------------+
| Variable_name         | Value       |
+-----------------------+-------------+
| Handler_read_first    | 5803750     |
| Handler_read_key      | 6049319850  |
| Handler_read_next     | 94440908210 |
| Handler_read_prev     | 34822001724 |
| Handler_read_rnd      | 405482605   |
| Handler_read_rnd_next | 18912877839 |
+-----------------------+-------------+</pre>
</blockquote>
<p>各字段解释参见<a href="http://hi.baidu.com/thinkinginlamp/blog/item/31690cd7c4bc5cdaa144df9c.html" target="_blank">http://hi.baidu.com/thinkinginlamp/blog/item/31690cd7c4bc5cdaa144df9c.html</a>，调出服务器完成的查询请求次数：</p>
<blockquote>
<pre>mysql&gt; show global status like 'com_select';
+---------------+-----------+
| Variable_name | Value     |
+---------------+-----------+
| Com_select    | 222693559 |
+---------------+-----------+</pre>
</blockquote>
<p>计算表扫描率：</p>
<blockquote><p>表扫描率 ＝ Handler_read_rnd_next / Com_select</p></blockquote>
<p>如果表扫描率超过4000，说明进行了太多表扫描，很有可能索引没有建好，增加read_buffer_size值会有一些好处，但最好不要超过8MB。</p>
<p><strong>后记：</strong></p>
<p>文中提到一些数字都是参考值，了解基本原理就可以，除了MySQL提供的各种status值外，操作系统的一些性能指标也很重要，比如常用的top,iostat等，尤其是iostat，现在的系统瓶颈一般都在磁盘IO上，关于iostat的使用，可以参考：<a href="http://www.php-oa.com/2009/02/03/iostat.html" target="_blank">http://www.php-oa.com/2009/02/03/iostat.html</a></p>
<p>&#8220;根据status信息对MySQL服务器进行优化（一）、（二）&#8221;是最近学习MySQL status信息的读书笔记，谬讹之处，望请斧正。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2009/03/mysql-tuning-by-status-ii/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>根据status信息对MySQL服务器进行优化（一）</title>
		<link>http://www.fuchaoqun.com/2009/03/mysql-tuning-by-status/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mysql-tuning-by-status</link>
		<comments>http://www.fuchaoqun.com/2009/03/mysql-tuning-by-status/#comments</comments>
		<pubDate>Tue, 24 Mar 2009 06:01:21 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Tuning]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=201</guid>
		<description><![CDATA[网上有很多的文章教怎么配置MySQL服务器，但考虑到服务器硬件配置的不同，具体应用的差别，那些文章的做法只能作为初步设置参考，我们需要根据自己的情况进行配置优化，好的做法是MySQL服务器稳定运行了一段时间后运行，根据服务器的&#8221;状态&#8221;进行优化。 mysql&#62; show global status; 可以列出MySQL服务器运行各种状态值，另外，查询MySQL服务器配置信息语句： mysql&#62; show variables; 一、慢查询 mysql&#62; show variables like '%slow%'; +------------------+-------+ &#124; Variable_name    &#124; Value &#124; +------------------+-------+ &#124; log_slow_queries &#124; ON    &#124; &#124; slow_launch_time &#124; 2     &#124; +------------------+-------+ mysql&#62; show global status like '%slow%'; +---------------------+-------+ &#124; Variable_name       &#124; Value &#124; +---------------------+-------+ &#124; Slow_launch_threads &#124; 0     &#124; &#124; Slow_queries        &#124; 4148 [...]]]></description>
			<content:encoded><![CDATA[<p>网上有很多的文章教怎么配置MySQL服务器，但考虑到服务器硬件配置的不同，具体应用的差别，那些文章的做法只能作为初步设置参考，我们需要根据自己的情况进行配置优化，好的做法是MySQL服务器稳定运行了一段时间后运行，根据服务器的&#8221;状态&#8221;进行优化。</p>
<blockquote><p>mysql&gt; show global status;</p></blockquote>
<p>可以列出MySQL服务器运行各种状态值，另外，查询MySQL服务器配置信息语句：</p>
<blockquote><p>mysql&gt; show variables;</p></blockquote>
<p><strong>一、慢查询</strong></p>
<blockquote>
<pre>mysql&gt; show variables like '%slow%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| log_slow_queries | ON    |
| slow_launch_time | 2     |
+------------------+-------+
mysql&gt; show global status like '%slow%';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| Slow_launch_threads | 0     |
| Slow_queries        | 4148 |
+---------------------+-------+</pre>
</blockquote>
<p>配置中打开了记录慢查询，执行时间超过2秒的即为慢查询，系统显示有4148个慢查询，你可以分析慢查询日志，找出有问题的SQL语句，慢查询时间不宜设置过长，否则意义不大，最好在5秒以内，如果你需要微秒级别的慢查询，可以考虑给MySQL打补丁：<a href="http://www.percona.com/docs/wiki/release:start" target="_blank">http://www.percona.com/docs/wiki/release:start</a>，记得找对应的版本。</p>
<p>打开慢查询日志可能会对系统性能有一点点影响，如果你的MySQL是主－从结构，可以考虑打开其中一台从服务器的慢查询日志，这样既可以监控慢查询，对系统性能影响又小。</p>
<p><strong>二、连接数</strong></p>
<p>经常会遇见&#8221;MySQL: ERROR 1040: Too many connections&#8221;的情况，一种是访问量确实很高，MySQL服务器抗不住，这个时候就要考虑增加从服务器分散读压力，另外一种情况是MySQL配置文件中max_connections值过小：</p>
<blockquote>
<pre>mysql&gt; show variables like 'max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 256   |
+-----------------+-------+</pre>
</blockquote>
<p>这台MySQL服务器最大连接数是256，然后查询一下服务器响应的最大连接数：</p>
<blockquote>
<pre>mysql&gt; show global status like 'Max_used_connections';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| Max_used_connections | 245   |
+----------------------+-------+</pre>
</blockquote>
<p>MySQL服务器过去的最大连接数是245，没有达到服务器连接数上限256，应该没有出现1040错误，比较理想的设置是：</p>
<blockquote><p>Max_used_connections / max_connections  * 100% ≈ 85%</p></blockquote>
<p>最大连接数占上限连接数的85％左右，如果发现比例在10%以下，MySQL服务器连接数上限设置的过高了。</p>
<p><strong>三、Key_buffer_size</strong></p>
<p>key_buffer_size是对MyISAM表性能影响最大的一个参数，下面一台以MyISAM为主要存储引擎服务器的配置：</p>
<blockquote>
<pre>mysql&gt; show variables like 'key_buffer_size';
+-----------------+------------+
| Variable_name   | Value      |
+-----------------+------------+
| key_buffer_size | 536870912 |
+-----------------+------------+</pre>
</blockquote>
<p>分配了512MB内存给key_buffer_size，我们再看一下key_buffer_size的使用情况：</p>
<blockquote>
<pre>mysql&gt; show global status like 'key_read%';
+------------------------+-------------+
| Variable_name          | Value       |
+------------------------+-------------+
| Key_read_requests      | 27813678764 |
| Key_reads              | 6798830     |
+------------------------+-------------+</pre>
</blockquote>
<p>一共有27813678764个索引读取请求，有6798830个请求在内存中没有找到直接从硬盘读取索引，计算索引未命中缓存的概率：</p>
<blockquote><p>key_cache_miss_rate ＝ Key_reads / Key_read_requests * 100%</p></blockquote>
<p>比如上面的数据，key_cache_miss_rate为0.0244%，4000个索引读取请求才有一个直接读硬盘，已经很BT了，key_cache_miss_rate在0.1%以下都很好（每1000个请求有一个直接读硬盘），如果key_cache_miss_rate在0.01%以下的话，key_buffer_size分配的过多，可以适当减少。</p>
<p>MySQL服务器还提供了key_blocks_*参数：</p>
<blockquote>
<pre>mysql&gt; show global status like 'key_blocks_u%';
+------------------------+-------------+
| Variable_name          | Value       |
+------------------------+-------------+
| Key_blocks_unused      | 0           |
| Key_blocks_used        | 413543      |
+------------------------+-------------+</pre>
</blockquote>
<p>Key_blocks_unused表示未使用的缓存簇(blocks)数，Key_blocks_used表示曾经用到的最大的blocks数，比如这台服务器，所有的缓存都用到了，要么增加key_buffer_size，要么就是过渡索引了，把缓存占满了。比较理想的设置：</p>
<blockquote><p>Key_blocks_used / (Key_blocks_unused + Key_blocks_used) * 100% ≈ 80%</p></blockquote>
<p><strong>四、临时表</strong></p>
<blockquote>
<pre>mysql&gt; show global status like 'created_tmp%';
+-------------------------+---------+
| Variable_name           | Value   |
+-------------------------+---------+
| Created_tmp_disk_tables | 21197   |
| Created_tmp_files       | 58      |
| Created_tmp_tables      | 1771587 |
+-------------------------+---------+</pre>
</blockquote>
<p>每次创建临时表，Created_tmp_tables增加，如果是在磁盘上创建临时表，Created_tmp_disk_tables也增加,Created_tmp_files表示MySQL服务创建的临时文件文件数，比较理想的配置是：</p>
<blockquote>
<pre>Created_tmp_disk_tables / Created_tmp_tables * 100% &lt;= 25%</pre>
</blockquote>
<p>比如上面的服务器Created_tmp_disk_tables / Created_tmp_tables * 100% ＝ 1.20%，应该相当好了。我们再看一下MySQL服务器对临时表的配置：</p>
<blockquote>
<pre>mysql&gt; show variables where Variable_name in ('tmp_table_size', 'max_heap_table_size');
+---------------------+-----------+
| Variable_name       | Value     |
+---------------------+-----------+
| max_heap_table_size | 268435456 |
| tmp_table_size      | 536870912 |
+---------------------+-----------+</pre>
</blockquote>
<p>只有256MB以下的临时表才能全部放内存，超过的就会用到硬盘临时表。</p>
<p><strong>五、Open Table情况</strong></p>
<blockquote>
<pre>mysql&gt; show global status like 'open%tables%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables   | 919   |
| Opened_tables | 1951  |
+---------------+-------+</pre>
</blockquote>
<p>Open_tables表示打开表的数量，Opened_tables表示打开过的表数量，如果Opened_tables数量过大，说明配置中table_cache(5.1.3之后这个值叫做table_open_cache)值可能太小，我们查询一下服务器table_cache值：</p>
<blockquote>
<pre>mysql&gt; show variables like 'table_cache';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| table_cache   | 2048  |
+---------------+-------+</pre>
</blockquote>
<p>比较合适的值为：</p>
<blockquote><p>Open_tables / Opened_tables  * 100% &gt;= 85%<br />
Open_tables / table_cache * 100% &lt;= 95%</p></blockquote>
<p>待续，本文参考以下网页：</p>
<p>1.<a href="http://dev.mysql.com/doc/refman/5.1/en/server-status-variables.htm" target="_blank">http://dev.mysql.com/doc/refman/5.1/en/server-status-variables.htm</a></p>
<p>2.<a href="http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html" target="_blank">http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html</a></p>
<p>3.<a href="http://www.ibm.com/developerworks/cn/linux/l-tune-lamp-3.html" target="_blank">http://www.ibm.com/developerworks/cn/linux/l-tune-lamp-3.html</a></p>
<p>4.<a href="http://www.day32.com/MySQL/tuning-primer.sh" target="_blank">http://www.day32.com/MySQL/tuning-primer.sh</a> 具体数值主要参考此工具</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2009/03/mysql-tuning-by-status/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>我也不知道的MySQL</title>
		<link>http://www.fuchaoqun.com/2009/03/mysql-use-multi-index/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mysql-use-multi-index</link>
		<comments>http://www.fuchaoqun.com/2009/03/mysql-use-multi-index/#comments</comments>
		<pubDate>Fri, 13 Mar 2009 06:37:11 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[index_merge]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=189</guid>
		<description><![CDATA[以前看过很多文章说MySQL一次查询只能用到一个索引，在实践中也没注意，一直奉为金科玉律，以此教育自己不要乱建索引，殊不知已是明日黄花。 MySQL5.0以后引入了index_merge，在一些特定的查询中可以合并索引，详细的内容查看[中文] [英文]，接着测试一下，还是那张表，数据还是10,000条。 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; MUL &#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; [...]]]></description>
			<content:encoded><![CDATA[<p>以前看过很多文章说MySQL一次查询只能用到一个索引，在实践中也没注意，一直奉为金科玉律，以此教育自己不要乱建索引，殊不知已是<a href="http://baike.baidu.com/view/46697.htm" target="_blank">明日黄花</a>。</p>
<p><strong>MySQL5.0以后引入了index_merge</strong>，在一些特定的查询中可以合并索引，详细的内容查看[<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>]，接着测试一下，还是那张表，数据还是10,000条。</p>
<blockquote>
<pre>mysql&gt; desc tbl_name;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| uid   | int(11)      | NO   | MUL | NULL    |       |
| sid   | mediumint(9) | NO   |     | NULL    |       |
| times | mediumint(9) | NO   |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)</pre>
</blockquote>
<p>执行查询测试：</p>
<blockquote>
<pre>mysql&gt; explain select * from tbl_name where uid = 104460 and times = 38\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: ref
possible_keys: uid
          key: uid
      key_len: 4
          ref: const
         rows: <span style="color: #ff0000;">9</span>
        Extra: Using where
1 row in set (0.00 sec)</pre>
</blockquote>
<p>用到了uid索引，扫描了9行。在times上也加上索引：</p>
<blockquote><p>mysql&gt; create index times on tbl_name(times);</p></blockquote>
<p>再执行上面的查询：</p>
<blockquote>
<pre>mysql&gt; explain select * from tbl_name where uid = 104460 and times = 38\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: index_merge
possible_keys: uid,times
          key: uid,times
      key_len: 4,3
          ref: NULL
         rows: <span style="color: #ff0000;">1</span>
        Extra: Using intersect(uid,times); Using where
1 row in set (0.00 sec)</pre>
</blockquote>
<p>用到了索引合并交集访问算法，只扫描了一行。注意索引合并只对一些特定的查询有用（注意看文档），比如下面的：</p>
<blockquote>
<pre>mysql&gt; explain select * from tbl_name where uid = 104460 and times &gt; 38\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tbl_name
         type: ref
possible_keys: uid,times
          key: uid
      key_len: 4
          ref: const
         rows: 9
        Extra: Using where
1 row in set (0.00 sec)</pre>
</blockquote>
<p>挂了，还是只用到了uid索引，貌似还和MySQL的版本有一些关系，可以看看这篇博文<a href="http://www.alidba.net/index.php/archives/81" target="_blank">http://www.alidba.net/index.php/archives/81</a></p>
<p>致歉，我传讹了，请大家更新&#8221;大脑数据库&#8221;，感谢xiaowei同学提醒，不知道的还很多，共同学习，一起进步。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2009/03/mysql-use-multi-index/feed/</wfw:commentRss>
		<slash:comments>2</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>
		<item>
		<title>改善MySQL上16进制标识符性能的5种方法</title>
		<link>http://www.fuchaoqun.com/2009/02/5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql</link>
		<comments>http://www.fuchaoqun.com/2009/02/5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql/#comments</comments>
		<pubDate>Fri, 13 Feb 2009 06:23:57 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=164</guid>
		<description><![CDATA[前言： 原文地址：5 ways to make hexadecimal identifiers perform better on MySQL，作者是《High Performance MySQL, Second Edition.》的主要作者。这里是原文的节译。 在我们的业务中，经常会用md5()或者uuid()作为作为数据的标识，比如B2C网站，经常会有一些电子优惠券，常用就是通过md5()获得一个32位字符串，给字符串建立索引，然后用户输入优惠券代码之后系统查出此优惠券对应的折扣率：select discount from coupon where id = &#8217;0cc175b9c0f1b6a831c399e269779527&#8242;，这样的做法存在两个问题：数据和索引很大以及非顺序数据，本文忽略“非顺序数据”的问题，因为顺序数据和非顺序数据的优劣很大程度上由技术特性决定。这里讲得是如何在使用16进制大数据的情况下保持好的性能，主要讲的是MySQL数据库，对其他数据库应该也起作用。 一、小心你的字符编码 看一下下面这个SQL语句： mysql&#62; explain select * from t where id = &#8217;0cc175b9c0f1b6a831c399e269772661&#8242;\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t type: const possible_keys: PRIMARY key: PRIMARY key_len: 98 ref: const [...]]]></description>
			<content:encoded><![CDATA[<p>前言：</p>
<p>原文地址：<a href="http://www.xaprb.com/blog/2009/02/12/5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql/" target="_blank">5 ways to make hexadecimal identifiers perform better on MySQL</a>，作者是《High Performance MySQL, Second Edition.》的主要作者。这里是原文的节译。</p>
<p>在我们的业务中，经常会用md5()或者uuid()作为作为数据的标识，比如B2C网站，经常会有一些电子优惠券，常用就是通过md5()获得一个32位字符串，给字符串建立索引，然后用户输入优惠券代码之后系统查出此优惠券对应的折扣率：select discount from coupon where id = &#8217;0cc175b9c0f1b6a831c399e269779527&#8242;，这样的做法存在两个问题：数据和索引很大以及非顺序数据，本文忽略“非顺序数据”的问题，因为顺序数据和非顺序数据的优劣很大程度上由技术特性决定。这里讲得是如何在使用16进制大数据的情况下保持好的性能，主要讲的是MySQL数据库，对其他数据库应该也起作用。</p>
<p><strong>一、小心你的字符编码</strong></p>
<p>看一下下面这个SQL语句：</p>
<blockquote><p>mysql&gt; explain select * from t where id = &#8217;0cc175b9c0f1b6a831c399e269772661&#8242;\G<br />
*************************** 1. row ***************************<br />
id: 1<br />
select_type: SIMPLE<br />
table: t<br />
type: const<br />
possible_keys: PRIMARY<br />
key: PRIMARY<br />
key_len: 98<br />
ref: const<br />
rows: 1<br />
Extra: Using index</p></blockquote>
<p>为什么索引是98byte？简单，因为我们用的是UTF-8：</p>
<blockquote><p>CREATE TABLE `t` (<br />
`id` varchar(32) NOT NULL,<br />
PRIMARY KEY  (`id`)<br />
) ENGINE=MyISAM DEFAULT CHARSET=utf8</p></blockquote>
<p>没有必要用UTF-8存储16进制数据，采用UTF-8存储16进制数据不会增加磁盘空间的占用，但是当你使用排序(order by)、统计(group by)、隐式临时表（MySQL查询时自建的临时表）等的时候，<strong>需要耗费多达3倍的内存和硬盘空间</strong>，至少在MySQL上是这样的。</p>
<p><strong>二、使用固定长度，不要有空值</strong></p>
<p>可以看到上面那个表采用的是varchar字段，我们都知道varchar是一个变长字段，如果你确认所有的数据都一样长（比如像md5()出来的，都是32个字节），最好使用char()定长字段，另外就是如果字段中不可能有空值，最好指定为not null</p>
<p><strong>三、使用二进制数据存储</strong></p>
<p>实际上，你并不需要存储字符串，16进制字符串不过是数字的另一种表现形式，直接保存数字。比如：00000000000000000000000000002E2A是什么呢？这正是16进制数字11818，使用一个4字节(或者更少)的整型代替一个32字节的字符存储更好。</p>
<p>问题是MySQL没有合适的类型来存储这么大的数字，它们比BIGINT还要大很多，不过MySQL允许我们存储到BINARY字段，数据更紧凑比较起来更快速，可以使用HEX()和UNHEX()来转换格式，或者16进制操作符&#8217;x&#8217;</p>
<blockquote><p>mysql&gt; select x&#8217;7861707262&#8242;;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| x&#8217;7861707262&#8242; |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
| xaprb         |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;+</p></blockquote>
<p>用BINARY(16)代替varchar(32)之后：</p>
<blockquote><p>explain select * from t where id = x&#8217;0cc175b9c0f1b6a831c399e269772661&#8242;\G<br />
*************************** 1. row ***************************<br />
id: 1<br />
select_type: SIMPLE<br />
table: t<br />
type: const<br />
possible_keys: PRIMARY<br />
key: PRIMARY<br />
key_len: 16<br />
ref: const<br />
rows: 1<br />
Extra: Using index</p></blockquote>
<p>索引长度变成16字节了（对比原来98字节），减小了不少，如果你使用的是UUID()，存入之前先用replace()把&#8221;-&#8221;题换掉。</p>
<p><strong>四、使用前缀索引</strong></p>
<p>很多时候，我们不需要索引全部字段，索引字段的前8～10个字符就可以了，如果你当前存储的是字符串，这很有用，不用转换成BINARY，只是改变索引策略而已。</p>
<p>你可以通过类似下面的SQL语句判断合适的前缀索引个数：</p>
<blockquote><p>mysql&gt; select count(distinct id), count(distinct left(id, 8)), count(distinct left(id, 9)) from t\G<br />
*************************** 1. row ***************************<br />
count(distinct id): 2<br />
count(distinct left(id, 8)): 2<br />
count(distinct left(id, 9)): 2</p></blockquote>
<p>找一个差不多行就可以，不一定要索引“唯一”。</p>
<p><strong>五、创建hash索引</strong></p>
<p>直接上代码，不用多余的解释：</p>
<blockquote><p>mysql&gt; alter table t add crc int unsigned not null, add key(crc);<br />
mysql&gt; update t set crc=crc32(id);<br />
mysql&gt; explain select * from t use index(crc) where id = &#8217;0cc175b9c0f1b6a831c399e269772661&#8242; and crc=crc32(&#8217;0cc175b9c0f1b6a831c399e269772661&#8242;)\G<br />
*************************** 1. row ***************************<br />
id: 1<br />
select_type: SIMPLE<br />
table: t<br />
type: ref<br />
possible_keys: crc<br />
key: crc<br />
key_len: 4<br />
ref: const<br />
rows: 1<br />
Extra: Using where</p></blockquote>
<p>使用crc32()获取到字符串的校验值，一般这样的碰撞概率不会太大，索引数字比索引字符不知道要快多少，极力推荐，不仅仅适用16进制字符，任意字符也适合：</p>
<blockquote><p>
mysql&gt; select crc32(&#8216;good good study, and day day up!&#8217;);<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
| crc32(&#8216;good good study, and day day up!&#8217;) |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
|                                2265998365 |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+<br />
1 row in set (0.00 sec)
</p></blockquote>
<p><strong>总结：</strong></p>
<p>16进制标识符让表和索引的变大，降低比较和查找的速度，建议非不得已不要使用，如果非要使用，希望上面的五条建议对你有用。</p>
<p><strong>后记：</strong></p>
<p>有时候我们确实必须使用字符串作为唯一标识符，比如我这篇博客地址：http://chaoqun.17348.com/2009/02/5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql，系统需要通过“5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql”去找出相应的博文，这个时候使用前面的建议就非常有用，很不幸的是WordPress没有用。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2009/02/5-ways-to-make-hexadecimal-identifiers-perform-better-on-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于Infobright的MySQL数据仓库方案测试</title>
		<link>http://www.fuchaoqun.com/2009/02/infobright-brighthouse-mysql-data-warehouse/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=infobright-brighthouse-mysql-data-warehouse</link>
		<comments>http://www.fuchaoqun.com/2009/02/infobright-brighthouse-mysql-data-warehouse/#comments</comments>
		<pubDate>Tue, 10 Feb 2009 07:36:10 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[brighthouse]]></category>
		<category><![CDATA[data warehouse]]></category>
		<category><![CDATA[infobright]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=155</guid>
		<description><![CDATA[数据仓库之父Bill Inmon在1991年出版的“Building the Data Warehouse”一书中所提出的定义被广泛接受——数据仓库（Data Warehouse）是一个面向主题的（Subject Oriented）、集成的（Integrated）、相对稳定的（Non-Volatile）、反映历史变化（Time Variant）的数据集合，用于支持管理决策(Decision Making Support)。 上面这段是抄的，简单的讲数据仓库是一个面向主题的、集成的、不可更新的、随时间不断变化的数据集合，它用于支持企业或组织的决策分析处理，这一句也是抄的，更直白的举例就是公司的历史交易数据集合，网站的历史访问数据集合，这一句是原创的。 Infobright是开源的MySQL数据仓库解决方案，引入了列存储方案，高强度的数据压缩，优化的统计计算(类似sum/avg/group by之类)，下面是Infobright的架构图： 安装篇： Infobright目前还不支持windows系列操作系统，不过你可以装在Linux虚拟机上，或者从官方直接下载做好的VMWARE虚拟机，这里的测试环境是CentOS 5.2 32bit操作系统。Infobright也不支持以插件的形式集成到已有的MySQL系统中去，官方的说法是对MySQL做了很多修改，不支持以插件的形式使用。 详细的安装参照http://www.infobright.org/wiki/Install_Guide/，需要注意的是如果你的系统中已经有MySQL(默认端口3306)，你需要重新设定一下安装参数，比如像我的： ./install-infobright.sh &#8211;datadir=/data/infobright/data &#8211;cachedir=/data/infobright/cache &#8211;port=9527 &#8211;config=/etc/my-ib.cnf &#8211;socket=/tmp/mysql-ib.sock &#8211;user=mysql &#8211;group=mysql 配置文件包括MySQL配置文件（/etc/my-ib.cnf）和Infobright配置文件（在安装时候指定的数据目录内，比如/data/infobright/data/brighthouse.ini），如果需要支持MySQL原生的SQL查询，需要修改brighthouse.ini设定 AllowMySQLQueryPath = 1 这样不至于有些SQL语句不能执行，安装完成，启动服务： /etc/init.d/mysqld-ib start 通过命令行：mysql-ib -uroot直接连接，默认密码为空，修改密码： /usr/local/infobright/bin/mysqladmin  -u root -p password NEWPASSWORD 测试篇： 测试数据是一个1.5GB大的文本数据，数据格式类似： 用户ID  内容ID  用户打分 765331  3868    5 716091  3868    3 1663216 3868    3 [...]]]></description>
			<content:encoded><![CDATA[<p>数据仓库之父Bill Inmon在1991年出版的“Building the Data Warehouse”一书中所提出的定义被广泛接受——数据仓库（Data Warehouse）是一个面向主题的（Subject Oriented）、集成的（Integrated）、相对稳定的（Non-Volatile）、反映历史变化（Time Variant）的数据集合，用于支持管理决策(Decision Making Support)。</p>
<p>上面这段是<a href="http://baike.baidu.com/view/19711.htm" target="_blank">抄的</a>，简单的讲数据仓库是一个面向主题的、集成的、不可更新的、随时间不断变化的数据集合，它用于支持企业或组织的决策分析处理，这一句也是抄的，更直白的举例就是公司的历史交易数据集合，网站的历史访问数据集合，这一句是原创的。</p>
<p>Infobright是开源的MySQL数据仓库解决方案，引入了<a href="http://en.wikipedia.org/wiki/Column-oriented_DBMS" target="_blank">列存储</a>方案，高强度的数据压缩，优化的统计计算(类似sum/avg/group by之类)，下面是Infobright的架构图：</p>
<p><img title="Infobright架构图" src="http://www.mysql.com/common/images/products/infobright_architecture.png" alt="" width="500" height="373" /></p>
<p><strong>安装篇：</strong></p>
<p>Infobright目前还不支持windows系列操作系统，不过你可以装在Linux虚拟机上，或者从官方直接下载做好的<a href="http://www.infobright.org/images/uploads/VM/ice32-3.1.zip" target="_blank">VMWARE虚拟机</a>，这里的测试环境是CentOS 5.2 32bit操作系统。Infobright也不支持以插件的形式集成到已有的MySQL系统中去，官方的说法是对MySQL做了很多修改，不支持以插件的形式使用。</p>
<p>详细的安装参照<a href="http://www.infobright.org/wiki/Install_Guide/" target="_blank">http://www.infobright.org/wiki/Install_Guide/</a>，需要注意的是如果你的系统中已经有MySQL(默认端口3306)，你需要重新设定一下安装参数，比如像我的：</p>
<blockquote><p>./install-infobright.sh &#8211;datadir=/data/infobright/data &#8211;cachedir=/data/infobright/cache &#8211;port=<span style="color: #ff0000;">9527</span> &#8211;config=/etc/my-ib.cnf &#8211;socket=/tmp/mysql-ib.sock &#8211;user=mysql &#8211;group=mysql</p></blockquote>
<p>配置文件包括MySQL配置文件（/etc/my-ib.cnf）和Infobright配置文件（在安装时候指定的数据目录内，比如/data/infobright/data/brighthouse.ini），如果需要支持MySQL原生的SQL查询，需要修改brighthouse.ini设定</p>
<blockquote><p>AllowMySQLQueryPath = 1</p></blockquote>
<p>这样不至于有些SQL语句不能执行，安装完成，启动服务：</p>
<blockquote><p>/etc/init.d/mysqld-ib start</p></blockquote>
<p>通过命令行：mysql-ib -uroot直接连接，默认密码为空，修改密码：</p>
<blockquote><p>/usr/local/infobright/bin/mysqladmin  -u root -p password NEWPASSWORD</p></blockquote>
<p><strong>测试篇：</strong></p>
<p>测试数据是一个1.5GB大的文本数据，数据格式类似：</p>
<blockquote>
<pre>用户ID  内容ID  用户打分
765331  3868    5
716091  3868    3
1663216 3868    3
51971   3868    5</pre>
</blockquote>
<p>在测试数据库中新建两张表，一个为Infobright支持的brighthouse存储引擎，一个为MySQL原生的MyISAM存储引擎，其他内容一致：</p>
<blockquote><p>CREATE TABLE `t_ib` (<br />
`uid` mediumint(9) NOT NULL,<br />
`cid` smallint(6) NOT NULL,<br />
`rating` tinyint(4) NOT NULL<br />
) ENGINE=BRIGHTHOUSE;</p>
<p>CREATE TABLE `t_mis` (<br />
`uid` mediumint(9) NOT NULL,<br />
`cid` smallint(6) NOT NULL,<br />
`rating` tinyint(4) NOT NULL<br />
) ENGINE=MyISAM</p></blockquote>
<p>将数据load进表：</p>
<blockquote><p>load data infile &#8216;path/to/data.txt&#8217; into table table_name fields terminated by &#8220;\t&#8221;;</p></blockquote>
<p>我们比较一下文件大小：</p>
<blockquote>
<pre>数据类型      数据大小
data.txt      1.5GB
data.tar.gz   429MB
MyISAM表      671MB
Infobight表   280MB</pre>
</blockquote>
<p>超过5：1的压缩比，虽然没有传说中10:1，但数据的大小比tar.gz过还要小近一半，压缩能力可见一斑。</p>
<p>准备进行SQL的测试，不能在BRIGHTHOUSE存储引擎上建索引，因为根本就不需要建，我们在MyISAM引擎表上建立如下索引：</p>
<blockquote><p>create index id on t_mis(cid);</p></blockquote>
<p>执行下列SQL语句，查询内容ID大于9527的条目数（为了节省篇幅，略去结果集，只返回执行时间）：</p>
<blockquote>
<pre>mysql&gt; select count(*) from t_mis where cid &gt; 9527;
1 row in set (41.81 sec)
mysql&gt; select count(*) from t_ib where cid &gt; 9527;
1 row in set (13.66 sec)</pre>
</blockquote>
<p>Infobright花费的时间只有MyISAM的1/4左右，再测试一下找出被用户打分最多的10条内容：</p>
<blockquote>
<pre>mysql&gt; select cid from t_mis group by cid order by count(*) desc limit 10;
10 rows in set (1 min 21.30 sec)
mysql&gt; select cid from t_ib group by cid order by count(*) desc limit 10;
10 rows in set (39.02 sec)</pre>
</blockquote>
<p>Infobright大概只花费了MyISAM 1/3多一点的时间。再查询一下评价最好的10条内容：</p>
<blockquote>
<pre>mysql&gt; select cid from t_mis group by cid order by avg(rating) desc limit 10;
10 rows in set (6 min 16.15 sec)
mysql&gt; select cid from t_ib group by cid order by avg(rating) desc limit 10;
10 rows in set (1 min 1.25 sec)</pre>
</blockquote>
<p>不到1/6时间。</p>
<p><strong>后记：</strong></p>
<p>强悍的压缩比率，高效的SQL统计性能，Infobright的表现很是不错。对于网站开发者来说，建立一个Infobright数据仓库系统用来保存网站的历史访问元数据应该是一个不错的选择，尤其是需要<a href="http://en.wikipedia.org/wiki/OLAP" target="_blank">OLAP</a>或者更为灵活的统计功能的时候。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2009/02/infobright-brighthouse-mysql-data-warehouse/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>MySQL统计函数GROUP_CONCAT陷阱</title>
		<link>http://www.fuchaoqun.com/2008/12/mysql-trap-of-group-concat/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mysql-trap-of-group-concat</link>
		<comments>http://www.fuchaoqun.com/2008/12/mysql-trap-of-group-concat/#comments</comments>
		<pubDate>Sat, 13 Dec 2008 06:42:20 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[group_concat]]></category>
		<category><![CDATA[group_concat_max_len]]></category>
		<category><![CDATA[max_join_size]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=105</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/ 最近在用MySQL做一些数据的预处理，经常会用到group_concat函数，比如类似下面一条语句 mysql&#62;select aid,group_concat(bid) from tbl group by aid limit 1; sql语句比较简单，按照aid分组，并且把相应的bid用逗号串起来。这样的句子大家可能都用过，也可能不会出问题，但是如果bid非常多的话，你就要小心了，比如下面的提示信息： Query OK, XXX rows affected, 1 warning (3 min 45.12 sec) 怎么会有警告呢，打出来看看： mysql&#62; show warnings; +---------+------+-----------------------------------------+ &#124; Level   &#124; Code &#124; Message                                 &#124; +---------+------+-----------------------------------------+ &#124; Warning &#124; 1260 &#124; 1 line(s) were cut by GROUP_CONCAT() &#124; +---------+------+-----------------------------------------+ 居然被GROUP_CONCAT截断了我的结果，查了一下手册，原来GROUP_CONCAT有个最大长度的限制，超过最大长度就会被截断掉，你可以通过下面的语句获得： mysql&#62; SELECT @@global.group_concat_max_len; +-------------------------------+ &#124; @@global.group_concat_max_len [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/" target="_blank">http://chaoqun.17348.com/2008/12/mysql-trap-of-group-concat/</a></p></blockquote>
<p>最近在用MySQL做一些数据的预处理，经常会用到group_concat函数，比如类似下面一条语句</p>
<blockquote><p>mysql&gt;select aid,group_concat(bid) from tbl group by aid limit 1;</p></blockquote>
<p>sql语句比较简单，按照aid分组，并且把相应的bid用逗号串起来。这样的句子大家可能都用过，也可能不会出问题，但是如果bid非常多的话，你就要小心了，比如下面的提示信息：</p>
<blockquote><p>Query OK, XXX rows affected, 1 warning (3 min 45.12 sec)</p></blockquote>
<p>怎么会有警告呢，打出来看看：</p>
<blockquote>
<pre>mysql&gt; show warnings;
+---------+------+-----------------------------------------+
| Level   | Code | Message                                 |
+---------+------+-----------------------------------------+
| Warning | 1260 | 1 line(s) were cut by GROUP_CONCAT()    |
+---------+------+-----------------------------------------+</pre>
</blockquote>
<p>居然被GROUP_CONCAT截断了我的结果，查了一下手册，原来GROUP_CONCAT有个最大长度的限制，超过最大长度就会被截断掉，你可以通过下面的语句获得：</p>
<blockquote>
<pre>mysql&gt; SELECT @@global.group_concat_max_len;
+-------------------------------+
| @@global.group_concat_max_len |
+-------------------------------+
|                      1024     |
+-------------------------------+</pre>
</blockquote>
<p>1024这就是一般MySQL系统默认的最大长度，如果你的bid串起来大于这个就会出问题，好在有解决的办法：</p>
<p>1.在MySQL配置文件中加上</p>
<blockquote><p>group_concat_max_len = 102400 #你要的最大长度</p></blockquote>
<p>2.可以简单一点，执行语句：</p>
<blockquote>
<pre>mysql&gt; SET GLOBAL group_concat_max_len=102400;
Query OK, 0 rows affected (0.01 sec)</pre>
</blockquote>
<p>再执行group_concat语句就不会出问题了，另外手册上还给出了group_concat的详细用法，给个示例你就明白了：</p>
<blockquote>
<pre>mysql&gt;select aid,group_concat(bid order by bid separator ',') as bid_str from tbl group by aid;</pre>
</blockquote>
<p>还可以排序和设置分隔符，功能强大。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2008/12/mysql-trap-of-group-concat/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>MySQL数据类型迷惑之整型xxxINT</title>
		<link>http://www.fuchaoqun.com/2008/11/mysql-data-types-int/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mysql-data-types-int</link>
		<comments>http://www.fuchaoqun.com/2008/11/mysql-data-types-int/#comments</comments>
		<pubDate>Fri, 07 Nov 2008 03:54:50 +0000</pubDate>
		<dc:creator>超群.com</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[data_types]]></category>
		<guid isPermaLink="false">http://chaoqun.17348.com/?p=100</guid>
		<description><![CDATA[本博客所有原创文章采用知识共享署名-非商业性使用-相同方式共享，转载请保留链接http://chaoqun.17348.com/2008/11/mysql-data-types-int/ 最近在做一些利用MySQL进行数据挖掘方面的尝试，处理的大多是海量的数据（一般是5000W条以上），由于数据量巨大，数据库表字段数据类型的选择就显示出重要性来了。 比如有下面的一个表： mysql&#62; desc test; +--------+---------+------+-----+---------+-------+ &#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; cid    &#124; int(11) &#124; NO   &#124;     &#124; NULL    &#124;       &#124; &#124; rating &#124; int(11) &#124; NO   &#124;     &#124; NULL    &#124;       &#124; &#124; [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>本博客所有原创文章采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" target="_blank"><span style="color: #356aa0;">知识共享署名-非商业性使用-相同方式共享</span></a>，转载请保留链接<a href="http://chaoqun.17348.com/2008/11/mysql-data-types-int/">http://chaoqun.17348.com/2008/11/mysql-data-types-int/</a></p></blockquote>
<p>最近在做一些利用MySQL进行数据挖掘方面的尝试，处理的大多是海量的数据（一般是5000W条以上），由于数据量巨大，数据库表字段数据类型的选择就显示出重要性来了。</p>
<p>比如有下面的一个表：</p>
<blockquote>
<pre>mysql&gt; desc test;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| uid    | int(11) | NO   |     | NULL    |       |
| cid    | int(11) | NO   |     | NULL    |       |
| rating | int(11) | NO   |     | NULL    |       |
| day    | date    | NO   |     | NULL    |       |
+--------+---------+------+-----+---------+-------+
4 rows in set (0.00 sec)</pre>
</blockquote>
<p>其中uid是用户ID，cid是内容ID，rating是打分值，取值是1～5，day是打分日期，就是这样一个表，往里面导入数据，大概1亿条，然后执行：</p>
<blockquote><p>mysql&gt; show table status like &#8216;test&#8217;;</p></blockquote>
<p>数据库大概1.5G大小，注意里面的Avg_row_length字段，这个字段的意思平均每行占用的字节数，test表每行占用字节数（行大小）是16（3个int是12个字节，一个date是3个字节，然后再加1就是16个字节）。好大的数据，我们的优化开始了。</p>
<p>查看最大的一个uid是多少</p>
<blockquote><p>mysql&gt; select max(uid) from test;</p></blockquote>
<p>发现最大的uid是2 649 429，只有7位数</p>
<p>查看最大的一个aid是多少</p>
<p>mysql&gt; select max(cid) from test;</p>
<p>发现最大的cid是17 770，区区5位数</p>
<p>rating字段只有1～5这5个值，一位就搞定了</p>
<p>于是更改了test表设计</p>
<blockquote>
<pre>mysql&gt; desc test;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| uid    | int(7)  | NO   |     | NULL    |       |
| cid    | int(5)  | NO   |     | NULL    |       |
| rating | int(1)  | NO   |     | NULL    |       |
| day    | date    | NO   |     | NULL    |       |
+--------+---------+------+-----+---------+-------+
4 rows in set (0.00 sec)</pre>
</blockquote>
<p>重新测试一下，大状况了，还是1.5G，每行还是16个字节，怎么会这样？查了一下手册得知：<strong>int(m) m表示最大显示宽度，注意是显示宽度，不会影响它的取值范围</strong>，你大可以在int(1)的字段中插入9999的数字，m不会影响此列的取值范围，也就是说int(1)和int(11)占用的字节数是一样多的，你是不是和我一样想当然了？下面是手册上关于xxxINT类型的详细说明：</p>
<table class="FCK__ShowTableBorders" border="0" cellspacing="0" cellpadding="0" width="600">
<tbody>
<tr>
<td style="width: 160px;">
<pre> MySQL数据类型</pre>
</td>
<td>
<pre> 含义</pre>
</td>
</tr>
<tr>
<td>
<pre> TINYINT(m)</pre>
</td>
<td>
<pre> 8位整数（1字节，取值范围-128～+127）；可选参数m表示最大显示宽度，对取值范围无影响，如果使用了UNSIGNED，则取值范围为0~255</pre>
</td>
</tr>
<tr>
<td>
<pre> SMALLINT(m)</pre>
</td>
<td>
<pre> 16位整数（2字节，取值范围-32 768～+32 767）</pre>
</td>
</tr>
<tr>
<td>
<pre> MEDIUMINT(m)</pre>
</td>
<td>
<pre> 24位整数（3字节，取值范围-8 388 608～+8 388 607）</pre>
</td>
</tr>
<tr>
<td>
<pre> INT(m)、INTERGER(m)</pre>
</td>
<td>
<pre> 32位整数（4字节，取值范围-2 147 483 648～+2 147 483 647）</pre>
</td>
</tr>
<tr>
<td>
<pre> BIGINT(m)</pre>
</td>
<td>
<pre> 64位整数（8字节，取值范围±9.22*1018</pre>
</td>
</tr>
<tr>
<td>
<pre> SERIAL</pre>
</td>
<td>
<pre> BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY的缩写</pre>
</td>
</tr>
</tbody>
</table>
<p>于是再次更改表的设计：</p>
<blockquote>
<pre>mysql&gt; desc test;
+--------+-----------------------+------+-----+---------+-------+
| Field  | Type                  | Null | Key | Default | Extra |
+--------+-----------------------+------+-----+---------+-------+
| uid    | mediumint(8) unsigned | NO   |     | NULL    |       |
| cid    | smallint(5) unsigned  | NO   |     | NULL    |       |
| rating | tinyint(1)            | NO   |     | NULL    |       |
| day    | date                  | NO   |     | NULL    |       |
+--------+-----------------------+------+-----+---------+-------+</pre>
</blockquote>
<p>再执行</p>
<p>mysql&gt; show table status like &#8216;test&#8217;;</p>
<p>test表大小变成不到960MB了，行大小变成10字节（3＋2＋1＋3＋1），苗条了许多。</p>
<p>另外，手册上说<strong>两个UNSIGNED的字段相减，值还是UNSIGNED</strong>，这就意味着如果用3-5的话，最后得到的结果肯定不是-2而是一个溢出的超大整数，注意安全。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fuchaoqun.com/2008/11/mysql-data-types-int/feed/</wfw:commentRss>
		<slash:comments>2</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! -->
