<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" version="2.0">

<channel>
	<title>OurMySQL</title>
	
	<link>http://ourmysql.com</link>
	<description>我们致力于一个MySQL知识的分享网站</description>
	<lastBuildDate>Mon, 26 Mar 2012 14:55:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ourmysql" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="ourmysql" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>MYSQL的DECIMAL和NUMERIC类型</title>
		<link>http://ourmysql.com/archives/1115</link>
		<comments>http://ourmysql.com/archives/1115#comments</comments>
		<pubDate>Mon, 26 Mar 2012 14:55:48 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL基础知识]]></category>
		<category><![CDATA[DECIMAL]]></category>
		<category><![CDATA[NUMERIC]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1115</guid>
		<description><![CDATA[MYSQL, DECIMAL和NUMERIC类型 说明:
DECIMAL和NUMERIC类型在MySQL中视为相同的类型。它们用于保存必须为确切精度的值，例如货币数据。
当声明该类型的列时，可以(并且通常要)指定精度和标度；]]></description>
			<content:encoded><![CDATA[<p>MYSQL, DECIMAL和NUMERIC类型 说明:</p>
<div id="_mcePaste">DECIMAL和NUMERIC类型在MySQL中视为相同的类型。它们用于保存必须为确切精度的值，例如货币数据。</div>
<div id="_mcePaste">当声明该类型的列时，可以(并且通常要)指定精度和标度；例如：</div>
<div id="_mcePaste">salary <a href="http://ourmysql.com/archives/tag/decimal" class="st_tag internal_tag" rel="tag" title="标签 DECIMAL 下的日志">DECIMAL</a>(5,2)</div>
<div id="_mcePaste">在该例子中，5是精度，2是标度。精度表示保存值的主要位数，标度表示小数点后面可以保存的位数。</div>
<div></div>
<div id="_mcePaste">在MySQL 5.1中以二进制格式保存DECIMAL和NUMERIC值。</div>
<div id="_mcePaste">标准SQL要求salary列能够用5位整数位和两位小数保存任何值。因此，在这种情况下可以保存在salary列</div>
<div id="_mcePaste">的值的范围是从-999.99到999.99。</div>
<div id="_mcePaste">在标准SQL中，语法DECIMAL(M)等价于DECIMAL(M,0)。同样，语法DECIMAL等价于DECIMAL(M,0)，可以通过</div>
<div id="_mcePaste">计算确定M的值。在MySQL 5.1中支持DECIMAL和NUMERIC数据类型的变量形式。M默认值是10。</div>
<div id="_mcePaste">DECIMAL或NUMERIC的最大位数是65，但具体的DECIMAL或NUMERIC列的实际范围受具体列的精度或标度约束</div>
<div id="_mcePaste">。如果此类列分配的值小数点后面的位数超过指定的标度允许的范围，值被转换为该标度。(具体操作与</div>
<div id="_mcePaste">操作系统有关，但一般结果均被截取到允许的位数）。</div>
<p>MYSQL, DECIMAL和NUMERIC类型 说明:<br />
DECIMAL和NUMERIC类型在MySQL中视为相同的类型。它们用于保存必须为确切精度的值，例如货币数据。<br />
当声明该类型的列时，可以(并且通常要)指定精度和标度；例如：<br />
salary DECIMAL(5,2)在该例子中，5是精度，2是标度。精度表示保存值的主要位数，标度表示小数点后面可以保存的位数。<br />
在MySQL 5.1中以二进制格式保存DECIMAL和NUMERIC值。<br />
标准SQL要求salary列能够用5位整数位和两位小数保存任何值。因此，在这种情况下可以保存在salary列<br />
的值的范围是从-999.99到999.99。<br />
在标准SQL中，语法DECIMAL(M)等价于DECIMAL(M,0)。同样，语法DECIMAL等价于DECIMAL(M,0)，可以通过<br />
计算确定M的值。在MySQL 5.1中支持DECIMAL和NUMERIC数据类型的变量形式。M默认值是10。<br />
DECIMAL或NUMERIC的最大位数是65，但具体的DECIMAL或NUMERIC列的实际范围受具体列的精度或标度约束<br />
。如果此类列分配的值小数点后面的位数超过指定的标度允许的范围，值被转换为该标度。(具体操作与<br />
操作系统有关，但一般结果均被截取到允许的位数）。</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>目前没有相关的文章</li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/decimal" title="DECIMAL" rel="tag">DECIMAL</a>, <a href="http://ourmysql.com/archives/category/basic" title="MySQL基础知识" rel="tag">MySQL基础知识</a>, <a href="http://ourmysql.com/archives/tag/numeric" title="NUMERIC" rel="tag">NUMERIC</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/oxpx4L0BZ6s35j83jegaV4V2Mpk/0/da"><img src="http://feedads.g.doubleclick.net/~a/oxpx4L0BZ6s35j83jegaV4V2Mpk/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/oxpx4L0BZ6s35j83jegaV4V2Mpk/1/da"><img src="http://feedads.g.doubleclick.net/~a/oxpx4L0BZ6s35j83jegaV4V2Mpk/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1115/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>mysql hash 破解提权</title>
		<link>http://ourmysql.com/archives/1112</link>
		<comments>http://ourmysql.com/archives/1112#comments</comments>
		<pubDate>Mon, 26 Mar 2012 14:54:07 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[Hash]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1112</guid>
		<description><![CDATA[mssql的hash保存在表master.dbo.sysxlogins中，用select name,password from master.dbo.sysxlogins可以提取用户hash]]></description>
			<content:encoded><![CDATA[<p>mssql的hash保存在表master.dbo.sysxlogins中，用</p>
<div>
<div>select name,password from master.dbo.sysxlogins</div>
</div>
<p>可以提取用户hash<br />
<code><br />
0x01004E04BE46023057E323AF27269E5B7DDCA140C98D225BDD3D06E8EFE8CFAEC02985B27B38059FA3B18349612B<br />
</code></p>
<p>分解：</p>
<p>&nbsp;</p>
<p>常量部分：0×0100<br />
Salt部分：4E04BE46<br />
混合密文：023057E323AF27269E5B7DDCA140C98D225BDD3D<br />
大写字母密文：06E8EFE8CFAEC02985B27B38059FA3B18349612B 后40位</p>
<p>有了HASH就可以破解了，用到的工具是CAIN。</p>
<p>转到CAIN的破解功能那里，添加一个待破解的MSSQL <a href="http://ourmysql.com/archives/tag/hash" class="st_tag internal_tag" rel="tag" title="标签 Hash 下的日志">HASH</a><br />
有三个需要写的文本框。</p>
<p>将salt ，混合密文 跟大写字母密文填入，就可以破解了。</p>
<div>
<p>mysql的用户名密码保存在mysql库user表中，找到MySQL\data\mysql\目录下的 user.MYD user.MYI user.frm 三个文件，拷贝到自己的mysql数据库目录下，就可以查看用户的hash了。<br />
用sql语句提取hash则如下：</p>
<div>
<div>use mysql;<br />
select user,password from user;</div>
</div>
<p>得到hash：<br />
<code><br />
test:*085D85329E1557C869A120C9157315A07D51E8A7<br />
ogame:*085D85329E1557C869A120C9157315A07D51E8A7<br />
iii6_com:*085D85329E1557C869A120C9157315A07D51E8A7<br />
root:*5D0B157F0A3BB4DB1A0092B4F270FBDA486EC6EB<br />
</code></p>
<p>冒号左边是用户，右边是HASH<br />
HASH值开头带”*”号的是MYSQL5的HASH<br />
不带*号的是旧版MYSQL的HASH(也就是MYSQL323)</p>
<p>破解：<br />
用passwordspro或者cain的破解器可以破解。</p>
</div>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2009-07-15 -- <a href="http://ourmysql.com/archives/623" title="memory型表支持Hash索引">memory型表支持Hash索引</a></li><li>2009-04-08 -- <a href="http://ourmysql.com/archives/510" title="有趣的bloom过滤器">有趣的bloom过滤器</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/hash" title="Hash" rel="tag">Hash</a>, <a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/_eyVwoBjb7YKJzlH72WhVSk--Ko/0/da"><img src="http://feedads.g.doubleclick.net/~a/_eyVwoBjb7YKJzlH72WhVSk--Ko/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/_eyVwoBjb7YKJzlH72WhVSk--Ko/1/da"><img src="http://feedads.g.doubleclick.net/~a/_eyVwoBjb7YKJzlH72WhVSk--Ko/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1112/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mysql 数据库查询随机数量条目的效率问题及解决办法</title>
		<link>http://ourmysql.com/archives/1110</link>
		<comments>http://ourmysql.com/archives/1110#comments</comments>
		<pubDate>Mon, 26 Mar 2012 14:51:57 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL优化设计]]></category>
		<category><![CDATA[随机]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1110</guid>
		<description><![CDATA[最近由于需要大概研究了一下MYSQL的随机抽取实现方法。举个例子，要从tablename表中随机提取一条记录，大家一般的写法就是：SELECT * FROM tablename ORDER BY RAND() LIMIT 1。
但是，后来我查了一下MYSQL的官方手册，里面针对RAND()的提示大概意思就是，在ORDER BY从句里面不能使用RAND()函数，因为这样会导致数据列被多次扫描。但是在MYSQL 3.23版本中，仍然可以通过ORDER BY RAND()来实现随机。
但是真正测试一下才发现这样效率非常低。一个15万余条的库，查询5条数据，居然要8秒以上。查看官方手册，也说rand()放在ORDER BY 子句中会被执行多次，自然效率及很低。]]></description>
			<content:encoded><![CDATA[<p>最近由于需要大概研究了一下MYSQL的随机抽取实现方法。举个例子，要从tablename表中随机提取一条记录，大家一般的写法就是：SELECT * FROM tablename ORDER BY RAND() LIMIT 1。</p>
<p>但是，后来我查了一下MYSQL的官方手册，里面针对RAND()的提示大概意思就是，在ORDER BY从句里面不能使用RAND()函数，因为这样会导致数据列被多次扫描。但是在MYSQL 3.23版本中，仍然可以通过ORDER BY RAND()来实现随机。</p>
<p>但是真正测试一下才发现这样效率非常低。一个15万余条的库，查询5条数据，居然要8秒以上。查看官方手册，也说rand()放在ORDER BY 子句中会被执行多次，自然效率及很低。</p>
<p>&nbsp;</p>
<p>搜索Google，网上基本上都是查询max(id) * rand()来随机获取数据。<br />
SELECT * FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2 WHERE t1.id &gt;= t2.id ORDER BY t1.id ASC LIMIT 5;</p>
<p>但是这样会产生连续的5条记录。解决办法只能是每次查询一条，查询5次。即便如此也值得，因为15万条的表，查询只需要0.01秒不到。</p>
<p>下面的语句采用的是JOIN，mysql的论坛上有人使用</p>
<p>SELECT * FROM `table` WHERE id &gt;= (SELECT FLOOR( MAX(id) * RAND()) FROM `table` ) ORDER BY id LIMIT 1;</p>
<p>我测试了一下，需要0.5秒，速度也不错，但是跟上面的语句还是有很大差距。总觉有什么地方不正常。</p>
<p>于是我把语句改写了一下。<br />
SELECT * FROM `table`<br />
WHERE id &gt;= (SELECT floor(RAND() * (SELECT MAX(id) FROM `table`)))<br />
ORDER BY id LIMIT 1;</p>
<p>这下，效率又提高了，查询时间只有0.01秒</p>
<p>最后，再把语句完善一下，加上MIN(id)的判断。我在最开始测试的时候，就是因为没有加上MIN(id)的判断，结果有一半的时间总是查询到表中的前面几行。<br />
完整查询语句是：<br />
SELECT * FROM `table`<br />
WHERE id &gt;= (SELECT floor( RAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`)) + (SELECT MIN(id) FROM `table`)))<br />
ORDER BY id LIMIT 1;</p>
<p>SELECT *<br />
FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`))+(SELECT MIN(id) FROM `table`)) AS id) AS t2<br />
WHERE t1.id &gt;= t2.id<br />
ORDER BY t1.id LIMIT 1;</p>
<p>最后在php中对这两个语句进行分别查询10次，<br />
前者花费时间 0.147433 秒<br />
后者花费时间 0.015130 秒<br />
看来采用JOIN的语法比直接在WHERE中使用函数效率还要高很多。</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009-04-14 -- <a href="http://ourmysql.com/archives/524" title="MySql 随机读取数据">MySql 随机读取数据</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/optimize" title="MySQL优化设计" rel="tag">MySQL优化设计</a>, <a href="http://ourmysql.com/archives/tag/%e9%9a%8f%e6%9c%ba" title="随机" rel="tag">随机</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/sp9z4o1bU_YzAI_9Ash-FyIEnW4/0/da"><img src="http://feedads.g.doubleclick.net/~a/sp9z4o1bU_YzAI_9Ash-FyIEnW4/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/sp9z4o1bU_YzAI_9Ash-FyIEnW4/1/da"><img src="http://feedads.g.doubleclick.net/~a/sp9z4o1bU_YzAI_9Ash-FyIEnW4/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1110/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习——没那么简单的Hash</title>
		<link>http://ourmysql.com/archives/1107</link>
		<comments>http://ourmysql.com/archives/1107#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:49:55 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[Hash]]></category>
		<category><![CDATA[源码]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1107</guid>
		<description><![CDATA[Hash 链表的应用比较常见，其目的就是为了将不同的值映射到不同的位置，查找的时候直接找到相应的位置，而不需要传统的顺序遍历或是二分查找，从而达到减少查询 时间的目的。常规的hash是预定义一定的桶(bucket)，规定一个hash函数，然后进行散列。然而Mysql中的hash没有固定的 bucket，hash函数也是动态变化的，本文就进行非深入介绍。]]></description>
			<content:encoded><![CDATA[<div id="cnblogs_post_body">
<blockquote>
<p align="justify"><span style="font-size: small;"><a href="http://ourmysql.com/archives/tag/hash" class="st_tag internal_tag" rel="tag" title="标签 Hash 下的日志">Hash</a> 链表的应用比较常见，其目的就是为了将不同的值映射到不同的位置，查找的时候直接找到相应的位置，而不需要传统的顺序遍历或是二分查找，从而达到减少查询 时间的目的。常规的hash是预定义一定的桶(bucket)，规定一个hash函数，然后进行散列。然而Mysql中的hash没有固定的 bucket，<span style="color: #ff00ff;">hash函数也是动态变化的</span>，本文就进行非深入介绍。</span></p>
</blockquote>
<p align="justify">
<ul>
<li>
<h1><strong>基本结构体 </strong></h1>
</li>
</ul>
<p><span style="font-size: medium;">    </span><span style="font-size: small;">Hash的结构体定义以及相关的函数接口定义在<em><strong>include/hash.h</strong></em>和<em><strong>mysys/hash.c</strong></em>两个文件中。下面是HASH结构体的定义。</span></p>
<div>
<div id="highlighter_429494">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>typedef</code> <code>struct</code> <code>st_hash {</code></div>
<div><code>  </code><code>size_t</code> <code>key_offset,key_length;     </code><code>/* Length of key if const length */</code></div>
<div><code>  </code><code>size_t</code> <code>blength;</code></div>
<div><code>  </code><code>ulong records;</code></div>
<div><code>  </code><code>uint flags;</code></div>
<div><code>  </code><code>DYNAMIC_ARRAY array;              </code><code>/* Place for hash_keys */</code></div>
<div><code>  </code><code>my_hash_get_key get_key;</code></div>
<div><code>  </code><code>void</code> <code>(*</code><code>free</code><code>)(</code><code>void</code> <code>*);</code></div>
<div><code>  </code><code>CHARSET_INFO *charset;</code></div>
<div><code>} HASH;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<table width="400" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td align="center" width="199">成员名</td>
<td align="center" width="199">说明</td>
</tr>
<tr>
<td align="center" width="199">key_offset</td>
<td align="center" width="199">hash时key的offset，在不指定hash函数的情况下有意义</td>
</tr>
<tr>
<td align="center" width="199">key_length</td>
<td align="center" width="199">key的长度，用于计算key值</td>
</tr>
<tr>
<td align="center" width="199"><span style="color: #ff00ff;">blength</span></td>
<td align="center" width="199"><span style="color: #ff00ff;">非常重要的辅助结构，初始为1，动态变化，用于hash函数计算，这里理解为bucket length（其实不是真实的bucket数）</span></td>
</tr>
<tr>
<td align="center" width="199">records</td>
<td align="center" width="199">实际的记录数</td>
</tr>
<tr>
<td align="center" width="199">flags</td>
<td align="center" width="199">是否允许存在相同的元素，取值为HASH_UNIQUE(1)或者0</td>
</tr>
<tr>
<td align="center" width="199">array</td>
<td align="center" width="199">存储元素的数组</td>
</tr>
<tr>
<td align="center" width="199">get_key</td>
<td align="center" width="199">用户定义的hash函数，可以为NULL</td>
</tr>
<tr>
<td align="center" width="199">free</td>
<td align="center" width="199">析构函数，可以为NULL</td>
</tr>
<tr>
<td align="center" width="199">charset</td>
<td align="center" width="199">字符集</td>
</tr>
</tbody>
</table>
<div>
<div id="highlighter_669241">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>&lt;font size=</code><code>"4"</code><code>&gt; &lt;/font&gt;&lt;font size=</code><code>"3"</code><code>&gt;HASH 结构体里面包含了一个动态数组结构体DYNAMIC_ARRAY，这里就一并介绍了。其定义 在&lt;em&gt;&lt;strong&gt;include/my_sys.h&lt;/strong&gt;&lt;/em&gt; 中。&lt;/font&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div id="highlighter_649863">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>typedef</code> <code>struct</code> <code>st_dynamic_array</code></div>
<div><code>{</code></div>
<div><code>  </code><code>uchar *buffer;</code></div>
<div><code>  </code><code>uint elements,max_element;</code></div>
<div><code>  </code><code>uint alloc_increment;</code></div>
<div><code>  </code><code>uint size_of_element;</code></div>
<div><code>} DYNAMIC_ARRAY;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>&nbsp;</p>
<table width="400" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td align="center" width="199">成员名</td>
<td align="center" width="199">说明</td>
</tr>
<tr>
<td align="center" width="199">buffer</td>
<td align="center" width="199">一块连续的地址空间，用于存储数据，可以看成一个数组空间</td>
</tr>
<tr>
<td align="center" width="199">elements</td>
<td align="center" width="199">元素个数</td>
</tr>
<tr>
<td align="center" width="199">max_element</td>
<td align="center" width="199">元素个数上限</td>
</tr>
<tr>
<td align="center" width="199">alloc_increment</td>
<td align="center" width="199">当元素达到上限时，即buffer满时，按照alloc_increment进行扩展</td>
</tr>
<tr>
<td align="center" width="199">size_of_element</td>
<td align="center" width="199">每个元素的长度</td>
</tr>
</tbody>
</table>
<ul>
<li>
<h1><strong>初始化函数</strong></h1>
</li>
</ul>
<p><span style="font-size: small;">Hash初始化函数对外提供两个，my_hash_init和my_hash_init2，其区别即是否定义了growth_size（用于设置DYNAMIC_ARRAY的alloc_increment）。代码在<strong><em>mysys/hash.c</em></strong>中。</span></p>
<div>
<div id="highlighter_248302">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>#define my_hash_init(A,B,C,D,E,F,G,H) \</code></div>
<div><code>          </code><code>_my_hash_init(A,0,B,C,D,E,F,G,H)</code></div>
<div><code>#define my_hash_init2(A,B,C,D,E,F,G,H,I) \</code></div>
<div><code>          </code><code>_my_hash_init(A,B,C,D,E,F,G,H,I)</code></div>
<div></div>
<div><code>/**</code></div>
<div><code>  </code><code>@brief Initialize the hash</code></div>
<div><code>  </code></div>
<div><code>  </code><code>@details</code></div>
<div></div>
<div><code>  </code><code>Initialize the hash, by defining and giving valid values for</code></div>
<div><code>  </code><code>its elements. The failure to allocate memory for the</code></div>
<div><code>  </code><code>hash-&gt;array element will not result in a fatal failure. The</code></div>
<div><code>  </code><code>dynamic array that is part of the hash will allocate memory</code></div>
<div><code>  </code><code>as required during insertion.</code></div>
<div></div>
<div><code>  </code><code>@param[in,out] hash         The hash that is initialized</code></div>
<div><code>  </code><code>@param[in]     charset      The charater set information</code></div>
<div><code>  </code><code>@param[in]     size         The hash size</code></div>
<div><code>  </code><code>@param[in]     key_offest   The key offset for the hash</code></div>
<div><code>  </code><code>@param[in]     key_length   The length of the key used in</code></div>
<div><code>                              </code><code>the hash</code></div>
<div><code>  </code><code>@param[in]     get_key      get the key for the hash</code></div>
<div><code>  </code><code>@param[in]     free_element pointer to the function that</code></div>
<div><code>                              </code><code>does cleanup</code></div>
<div><code>  </code><code>@return        inidicates success or failure of initialization</code></div>
<div><code>    </code><code>@retval 0 success</code></div>
<div><code>    </code><code>@retval 1 failure</code></div>
<div><code>*/</code></div>
<div><code>my_bool</code></div>
<div><code>_my_hash_init(HASH *hash, uint growth_size, CHARSET_INFO *charset,</code></div>
<div><code>              </code><code>ulong size, </code><code>size_t</code> <code>key_offset, </code><code>size_t</code> <code>key_length,</code></div>
<div><code>              </code><code>my_hash_get_key get_key,</code></div>
<div><code>              </code><code>void</code> <code>(*free_element)(</code><code>void</code><code>*), uint flags)</code></div>
<div><code>{</code></div>
<div><code>  </code><code><a href="http://ourmysql.com/archives/tag/dbug" class="st_tag internal_tag" rel="tag" title="标签 DBUG 下的日志">DBUG</a>_ENTER(</code><code>"my_hash_init"</code><code>);</code></div>
<div><code>  </code><code>DBUG_PRINT(</code><code>"enter"</code><code>,(</code><code>"hash: 0x%lx  size: %u"</code><code>, (</code><code>long</code><code>) hash, (uint) size));</code></div>
<div></div>
<div><code>  </code><code>hash-&gt;records=0;</code></div>
<div><code>  </code><code>hash-&gt;key_offset=key_offset;</code></div>
<div><code>  </code><code>hash-&gt;key_length=key_length;</code></div>
<div><code>  </code><code>hash-&gt;blength=1;</code></div>
<div><code>  </code><code>hash-&gt;get_key=get_key;</code></div>
<div><code>  </code><code>hash-&gt;</code><code>free</code><code>=free_element;</code></div>
<div><code>  </code><code>hash-&gt;flags=flags;</code></div>
<div><code>  </code><code>hash-&gt;charset=charset;</code></div>
<div><code>  </code><code>DBUG_RETURN(my_init_dynamic_array_ci(&amp;hash-&gt;array, </code></div>
<div><code>                                       </code><code>sizeof</code><code>(HASH_LINK), size, growth_size));</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div id="highlighter_887311">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>&lt;font size=</code><code>"3"</code><code>&gt;   可以看到，_my_hash_init函数主要是初始化HASH结构体和hash-&gt;array（DYNAMIC_ARRAY结构体）。&lt;/font&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<ul>
<li>
<h1><span style="font-size: small;"><strong>动态HASH函数</strong></span></h1>
</li>
</ul>
<div>
<div id="highlighter_728621">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>&lt;font size=</code><code>"3"</code><code>&gt;   我们首先来看下hash函数的定义：&lt;/font&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div id="highlighter_7662">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>static</code> <code>inline</code> <code>char</code><code>*</code></div>
<div><code>my_hash_key(</code><code>const</code> <code>HASH *hash, </code><code>const</code> <code>uchar *record, </code><code>size_t</code> <code>*length,</code></div>
<div><code>            </code><code>my_bool first)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>if</code> <code>(hash-&gt;get_key)</code></div>
<div><code>    </code><code>return</code> <code>(</code><code>char</code><code>*) (*hash-&gt;get_key)(record,length,first);</code></div>
<div><code>  </code><code>*length=hash-&gt;key_length;</code></div>
<div><code>  </code><code>return</code> <code>(</code><code>char</code><code>*) record+hash-&gt;key_offset;</code></div>
<div><code>}</code></div>
<div></div>
<div><code>static</code> <code>uint my_hash_mask(my_hash_value_type hashnr, </code><code>size_t</code> <code>buffmax,</code></div>
<div><code>                         </code><code>size_t</code> <code>maxlength)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>if</code> <code>((hashnr &amp; (buffmax-1)) &lt; maxlength) </code><code>return</code> <code>(hashnr &amp; (buffmax-1));</code></div>
<div><code>  </code><code>return</code> <code>(hashnr &amp; ((buffmax &gt;&gt; 1) -1));</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div align="center">
<table width="400" border="1" cellspacing="0" cellpadding="2" align="center">
<tbody>
<tr>
<td align="center" width="199">my_hash_key参数</td>
<td align="center" width="199">说明</td>
</tr>
<tr>
<td align="center" width="199">hash</td>
<td align="center" width="199">HASH链表结构</td>
</tr>
<tr>
<td align="center" width="199">record</td>
<td align="center" width="199">带插入的元素的值</td>
</tr>
<tr>
<td align="center" width="199">length</td>
<td align="center" width="199">带插入元素的值长度</td>
</tr>
<tr>
<td align="center" width="199">first</td>
<td align="center" width="199">辅助参数</td>
</tr>
</tbody>
</table>
</div>
<div>
<div id="highlighter_844892">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>&lt;font size=</code><code>"3"</code><code>&gt;   &lt;/font&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div align="center">
<table width="400" border="1" cellspacing="0" cellpadding="2" align="center">
<tbody>
<tr>
<td align="center" width="199">my_hash_mask参数</td>
<td align="center" width="199">说明</td>
</tr>
<tr>
<td align="center" width="199">hashnr</td>
<td align="center" width="199">my_hash_key的计算结果</td>
</tr>
<tr>
<td align="center" width="199">buffmax</td>
<td align="center" width="199">hash结构体中的blength</td>
</tr>
<tr>
<td align="center" width="199">maxlength</td>
<td align="center" width="199">实际桶的个数</td>
</tr>
</tbody>
</table>
</div>
<p><span style="font-size: small;">    你可能要问我怎么有两个？其实这和我们平时使用的差不多，第一个函数my_hash_key是根据我们的值进行Hash </span><span style="font-size: small;">Key计算，一般我们在</span><span style="font-size: small;">计算后，会对hash key进行一次模运算，以便计算结果在我们的bucket中。即my_hash_key的结果作为my_hash_mask的第一个输入参数。</span><span style="font-size: small;">其实到这里都是非常好理解的，唯一让我蛋疼的是my_hash_mask的实现，<span style="color: #0000ff;">其计算结果是和第二和第三个参数有关，即Hash结构体中的blength</span></span><span style="font-size: small;"><span style="color: #0000ff;">和records有关</span>。动态变化的，我去..</span></p>
<p><span style="font-family: Verdana; font-size: small;">     看到这里我迷惑了，我上网经过各种百度，谷歌，终于让我找到了一封Mysql Expert的回信：</span></p>
<div>
<div id="highlighter_180496">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>Hi!</code></div>
<div><code>"Yan"</code> <code>== Yan Yu &lt;yan2...@facebook.com&gt; writes:</code></div>
<div></div>
<div><code>Yan&gt; Dear MySQL experts:</code></div>
<div><code>Yan&gt;      Thank you so much </code><code>for</code> <code>your reply to my previous Qs, they are very  </code></div>
<div><code>Yan&gt; helpful!</code></div>
<div><code>Yan&gt;      Could someone please help me understand function my_hash_insert()  </code></div>
<div><code>Yan&gt; in mysys/hash.cc?</code></div>
<div><code>Yan&gt; what are lines 352 -429 trying to achieve?  Are they just some  </code></div>
<div><code>Yan&gt; optimization to shuffle existing</code></div>
<div><code>Yan&gt; hash entries in the table (since the existing hash entries may be in  </code></div>
<div><code>Yan&gt; the wrong slot due to chaining</code></div>
<div><code>Yan&gt; in the </code><code>case</code> <code>of hash collision)?</code></div>
<div></div>
<div><code>&lt;strong&gt;&lt;font color=</code><code>"#ff0000"</code><code>&gt;The hash algorithm is based on dynamic hashing without empty slots.&lt;/font&gt;&lt;/strong&gt;</code></div>
<div><code>This means that when you insert a </code><code>new</code> <code>key, in some cases a small set</code></div>
<div><code>of old keys needs to be moved to other buckets.  This is what the code</code></div>
<div><code>does.</code></div>
<div></div>
<div><code>Regards,</code></div>
<div><code>Monty</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p><span style="font-size: small;">      红色注释的地方是重点，dynamic hash，原来如此，动态hash，第一次听说，在网上下了个《Dynamic Hash Tables》的论文，</span><span style="font-size: small;">下面图解下基本原理。</span></p>
<div>
<div id="highlighter_847480">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>&lt;a href=</code><code>"<a href="http://images.cnblogs.com/cnblogs_com/nocode/201110/201110142107072298.png">http://images.cnblogs.com/cnblogs_com/nocode/201110/201110142107072298.png</a>"</code><code>&gt;&lt;img title=</code><code>"image"</code> <code>style=</code><code>"border-width: 0px; display: block; float: none; margin-left: auto; margin-right: auto;"</code> <code>alt=</code><code>"image"</code> <code>src=</code><code>"<a href="http://images.cnblogs.com/cnblogs_com/nocode/201110/201110142107114127.png">http://images.cnblogs.com/cnblogs_com/nocode/201110/201110142107114127.png</a>"</code> <code>border=</code><code>"0"</code> <code>height=</code><code>"910"</code> <code>width=</code><code>"570"</code><code>&gt;&lt;</code><code>/a</code><code>&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div id="highlighter_729360">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>&lt;font size=</code><code>"3"</code><code>&gt;&lt;</code><code>/font</code><code>&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p><span style="font-size: small;">动态Hash的本质是Hash函数的设计，图中给出的动态hash函数只是论文中提到的一个例子。下面就具体解读下Mysql中的hash插入——my_hash_insert</span></p>
<ul>
<li>
<h1><span style="font-size: small;"><strong>my_hash_insert非深入解析</strong></span></h1>
</li>
</ul>
<p><span style="font-size: small;"><strong>  </strong>  首先给出my_hash_insert的源代码,代码在<strong><em>mysys/hash.c</em></strong>中。</span></p>
<div>
<div id="highlighter_709223">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>my_bool my_hash_insert(HASH *info, </code><code>const</code> <code>uchar *record)</code></div>
<div><code>{</code></div>
<div><code>    </code><code>int</code> <code>flag;</code></div>
<div><code>    </code><code>size_t</code> <code>idx,halfbuff,first_<a href="http://ourmysql.com/archives/tag/index" class="st_tag internal_tag" rel="tag" title="标签 index 下的日志">index</a>;</code></div>
<div><code>    </code><code>my_hash_value_type hash_nr;</code></div>
<div><code>    </code><code>uchar *UNINIT_VAR(ptr_to_rec),*UNINIT_VAR(ptr_to_rec2);</code></div>
<div><code>    </code><code>HASH_LINK *data,*empty,*UNINIT_VAR(gpos),*UNINIT_VAR(gpos2),*pos;</code></div>
<div></div>
<div><code>    </code><code>if</code> <code>(HASH_UNIQUE &amp; info-&gt;flags)</code></div>
<div><code>    </code><code>{</code></div>
<div><code>        </code><code>uchar *key= (uchar*) my_hash_key(info, record, &amp;idx, 1);</code></div>
<div><code>        </code><code>if</code> <code>(my_hash_search(info, key, idx))</code></div>
<div><code>            </code><code>return</code><code>(TRUE);               </code><code>/* Duplicate entry */</code></div>
<div><code>    </code><code>}</code></div>
<div></div>
<div><code>    </code><code>flag=0;</code></div>
<div><code>    </code><code>if</code> <code>(!(empty=(HASH_LINK*) alloc_dynamic(&amp;info-&gt;array)))</code></div>
<div><code>        </code><code>return</code><code>(TRUE);               </code><code>/* No more memory */</code></div>
<div></div>
<div><code>    </code><code>data=dynamic_element(&amp;info-&gt;array,0,HASH_LINK*);</code></div>
<div><code>    </code><code>halfbuff= info-&gt;blength &gt;&gt; 1;</code></div>
<div></div>
<div><code>    </code><code>idx=first_index=info-&gt;records-halfbuff;</code></div>
<div><code>    </code><code>if</code> <code>(idx != info-&gt;records)                </code><code>/* If some records */</code></div>
<div><code>    </code><code>{</code></div>
<div><code>        </code><code>do</code></div>
<div><code>        </code><code>{</code></div>
<div><code>            </code><code>pos=data+idx;</code></div>
<div><code>            </code><code>hash_nr=rec_hashnr(info,pos-&gt;data);</code></div>
<div><code>            </code><code>if</code> <code>(flag == 0)              </code><code>/* First loop; Check if ok */</code></div>
<div><code>                </code><code>if</code> <code>(my_hash_mask(hash_nr, info-&gt;blength, info-&gt;records) != first_index)</code></div>
<div><code>                    </code><code>break</code><code>;</code></div>
<div><code>            </code><code>if</code> <code>(!(hash_nr &amp; halfbuff))</code></div>
<div><code>            </code><code>{                       </code><code>/* Key will not move */</code></div>
<div><code>                </code><code>if</code> <code>(!(flag &amp; LOWFIND))</code></div>
<div><code>                </code><code>{</code></div>
<div><code>                    </code><code>if</code> <code>(flag &amp; HIGHFIND)</code></div>
<div><code>                    </code><code>{</code></div>
<div><code>                        </code><code>flag=LOWFIND | HIGHFIND;</code></div>
<div><code>                        </code><code>/* key shall be moved to the current empty position */</code></div>
<div><code>                        </code><code>gpos=empty;</code></div>
<div><code>                        </code><code>ptr_to_rec=pos-&gt;data;</code></div>
<div><code>                        </code><code>empty=pos;              </code><code>/* This place is now free */</code></div>
<div><code>                    </code><code>}</code></div>
<div><code>                    </code><code>else</code></div>
<div><code>                    </code><code>{</code></div>
<div><code>                        </code><code>flag=LOWFIND | LOWUSED;     </code><code>/* key isn't changed */</code></div>
<div><code>                        </code><code>gpos=pos;</code></div>
<div><code>                        </code><code>ptr_to_rec=pos-&gt;data;</code></div>
<div><code>                    </code><code>}</code></div>
<div><code>                </code><code>}</code></div>
<div><code>                </code><code>else</code></div>
<div><code>                </code><code>{</code></div>
<div><code>                    </code><code>if</code> <code>(!(flag &amp; LOWUSED))</code></div>
<div><code>                    </code><code>{</code></div>
<div><code>                        </code><code>/* Change link of previous LOW-key */</code></div>
<div><code>                        </code><code>gpos-&gt;data=ptr_to_rec;</code></div>
<div><code>                        </code><code>gpos-&gt;next= (uint) (pos-data);</code></div>
<div><code>                        </code><code>flag= (flag &amp; HIGHFIND) | (LOWFIND | LOWUSED);</code></div>
<div><code>                    </code><code>}</code></div>
<div><code>                    </code><code>gpos=pos;</code></div>
<div><code>                    </code><code>ptr_to_rec=pos-&gt;data;</code></div>
<div><code>                </code><code>}</code></div>
<div><code>            </code><code>}</code></div>
<div><code>            </code><code>else</code></div>
<div><code>            </code><code>{                       </code><code>/* key will be moved */</code></div>
<div><code>                </code><code>if</code> <code>(!(flag &amp; HIGHFIND))</code></div>
<div><code>                </code><code>{</code></div>
<div><code>                    </code><code>flag= (flag &amp; LOWFIND) | HIGHFIND;</code></div>
<div><code>                    </code><code>/* key shall be moved to the last (empty) position */</code></div>
<div><code>                    </code><code>gpos2 = empty; empty=pos;</code></div>
<div><code>                    </code><code>ptr_to_rec2=pos-&gt;data;</code></div>
<div><code>                </code><code>}</code></div>
<div><code>                </code><code>else</code></div>
<div><code>                </code><code>{</code></div>
<div><code>                    </code><code>if</code> <code>(!(flag &amp; HIGHUSED))</code></div>
<div><code>                    </code><code>{</code></div>
<div><code>                        </code><code>/* Change link of previous hash-key and save */</code></div>
<div><code>                        </code><code>gpos2-&gt;data=ptr_to_rec2;</code></div>
<div><code>                        </code><code>gpos2-&gt;next=(uint) (pos-data);</code></div>
<div><code>                        </code><code>flag= (flag &amp; LOWFIND) | (HIGHFIND | HIGHUSED);</code></div>
<div><code>                    </code><code>}</code></div>
<div><code>                    </code><code>gpos2=pos;</code></div>
<div><code>                    </code><code>ptr_to_rec2=pos-&gt;data;</code></div>
<div><code>                </code><code>}</code></div>
<div><code>            </code><code>}</code></div>
<div><code>        </code><code>}</code></div>
<div><code>        </code><code>while</code> <code>((idx=pos-&gt;next) != NO_RECORD);</code></div>
<div></div>
<div><code>        </code><code>if</code> <code>((flag &amp; (LOWFIND | LOWUSED)) == LOWFIND)</code></div>
<div><code>        </code><code>{</code></div>
<div><code>            </code><code>gpos-&gt;data=ptr_to_rec;</code></div>
<div><code>            </code><code>gpos-&gt;next=NO_RECORD;</code></div>
<div><code>        </code><code>}</code></div>
<div><code>        </code><code>if</code> <code>((flag &amp; (HIGHFIND | HIGHUSED)) == HIGHFIND)</code></div>
<div><code>        </code><code>{</code></div>
<div><code>            </code><code>gpos2-&gt;data=ptr_to_rec2;</code></div>
<div><code>            </code><code>gpos2-&gt;next=NO_RECORD;</code></div>
<div><code>        </code><code>}</code></div>
<div><code>    </code><code>}</code></div>
<div><code>  </code><code>/* Check if we are at the empty position */</code></div>
<div></div>
<div><code>  </code><code>idx= my_hash_mask(rec_hashnr(info, record), info-&gt;blength, info-&gt;records + 1);</code></div>
<div><code>  </code><code>pos=data+idx;</code></div>
<div><code>  </code><code>if</code> <code>(pos == empty)</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>pos-&gt;data=(uchar*) record;</code></div>
<div><code>    </code><code>pos-&gt;next=NO_RECORD;</code></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>else</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>/* Check if more records in same hash-nr family */</code></div>
<div><code>    </code><code>empty[0]=pos[0];</code></div>
<div><code>    </code><code>gpos= data + my_hash_rec_mask(info, pos, info-&gt;blength, info-&gt;records + 1);</code></div>
<div><code>    </code><code>if</code> <code>(pos == gpos)</code></div>
<div><code>    </code><code>{</code></div>
<div><code>      </code><code>pos-&gt;data=(uchar*) record;</code></div>
<div><code>      </code><code>pos-&gt;next=(uint) (empty - data);</code></div>
<div><code>    </code><code>}</code></div>
<div><code>    </code><code>else</code></div>
<div><code>    </code><code>{</code></div>
<div><code>      </code><code>pos-&gt;data=(uchar*) record;</code></div>
<div><code>      </code><code>pos-&gt;next=NO_RECORD;</code></div>
<div><code>      </code><code>movelink(data,(uint) (pos-data),(uint) (gpos-data),(uint) (empty-data));</code></div>
<div><code>    </code><code>}</code></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>if</code> <code>(++info-&gt;records == info-&gt;blength)</code></div>
<div><code>    </code><code>info-&gt;blength+= info-&gt;blength;</code></div>
<div><code>  </code><code>return</code><code>(0);</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p><span style="font-size: small;">    同时给出动态hash函数如下：</span></p>
<div>
<div id="highlighter_70525">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>static</code> <code>uint my_hash_mask(my_hash_value_type hashnr, </code><code>size_t</code> <code>buffmax,</code></div>
<div><code>                         </code><code>size_t</code> <code>maxlength)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>if</code> <code>((hashnr &amp; (buffmax-1)) &lt; maxlength) </code><code>return</code> <code>(hashnr &amp; (buffmax-1));</code></div>
<div><code>  </code><code>return</code> <code>(hashnr &amp; ((buffmax &gt;&gt; 1) -1));</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div id="highlighter_5088">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>&lt;font size=</code><code>"3"</code><code>&gt;  &lt;/font&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p><span style="font-size: small;">   可以看出，hash函数是hash key与buffmax的模运算，buffmax即HASH结构中的blength，由my_hash_insert中最后几行</span><span style="font-size: small;">代码可知：info-&gt;blength+= info-&gt;blength; 其初始值为1，<strong>即blength = 2^n</strong>，而且blengh始终是大于records。这个动态hash函数的基本意思是<strong>key%（2^n）。</strong>依然用图解这个动态hash函数。</span></p>
<p><a href="http://images.cnblogs.com/cnblogs_com/nocode/201110/201110142107218893.png"><img title="image" src="http://images.cnblogs.com/cnblogs_com/nocode/201110/201110142107237300.png" alt="image" width="490" height="597" border="0" /></a></p>
<p><span style="font-size: small;">hash函数基本清楚了，但是mysql的具体实现还是比较值得探讨的。那封回信中也提到了without empty slots，是的，它这种实现方式是根据实际的数据量进行桶数的分配。我这里大概说下代码的流程（有兴趣的还需要大家自己仔细琢磨）。</span></p>
<ol>
<li><span style="font-size: small;">根据flag去判断是否是否唯一Hash，如果是唯一Hash，去查找Hash表中是否存在重复值(dupliacate entry)，存在则报错。</span></li>
<li><span style="font-size: small;">进行桶分裂，对应代码中的if (idx != info-&gt;records)分支。这个分支有些费解，稍微提示下：gpos和ptr_to_rec指示了低位需要移动的数据，gpos2和 ptr_to_rec2只是了高位需要移动的数据。LOWFIND表示低位存在值，LOWUSED表示低位是否进行了调整。HIGH的宏意义基本相同。 if (!(hash_nr &amp; halfbuff)) 用于判断hash值存在高位还是低位。</span></li>
<li><span style="font-size: small;">计算新值对应的bucket号，插入。如果此位置上存在元素(一般情况都存在，除非是empty，概率比较小)，调整原始元素的位置。</span></li>
</ol>
</div>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1112" title="mysql hash 破解提权">mysql hash 破解提权</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/hash" title="Hash" rel="tag">Hash</a>, <a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/4tmiMghCh-emP27HJnzi_XLjxGg/0/da"><img src="http://feedads.g.doubleclick.net/~a/4tmiMghCh-emP27HJnzi_XLjxGg/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/4tmiMghCh-emP27HJnzi_XLjxGg/1/da"><img src="http://feedads.g.doubleclick.net/~a/4tmiMghCh-emP27HJnzi_XLjxGg/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1107/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习——用户认证原理与实现</title>
		<link>http://ourmysql.com/archives/1105</link>
		<comments>http://ourmysql.com/archives/1105#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:48:33 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[源码]]></category>
		<category><![CDATA[认证]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1105</guid>
		<description><![CDATA[Mysql中权限系统表都是在系统启动时，载入内存的（当然User表也是这样），一般情况下，不需要进行频繁的授权和回收操作，这中情况下，权限表基本保持不变，将其在系统启动的时候载入内存的好处自然是快速的进行权限判断，减少磁盘的I/O，你懂的^_^。有好处自然有坏处，就是在频繁进行授权和回收相关操作时，权限表需要重新载入内存，Mysql为了避免这种情况，在手册中已经说的很清楚了，授权和回收只会反应到磁盘中，内存的数据字典信息是不会改变的，如果想立即生效，需要调用FLUSH PRIVILEGES系统函数，这个系统函数的工作应该就是对权限系统表的RELOAD。]]></description>
			<content:encoded><![CDATA[<p>前几节跟踪了<a href="http://ourmysql.com/archives/1100" target="_blank">Connection Manager</a>和<a href="http://ourmysql.com/archives/1102" target="_blank">Thread Manager</a>，在连接的过程中，还有一个身份认证的过程，就是大家所熟悉的</p>
<p>验证用户名和密码的过程，我们平时做一个系统的时候，很多时候都会涉及到身份验证。今天我们就来看下Mysql是如何进</p>
<p>行验证的。（注意是登录，不是登陆^_^）</p>
<p><strong>一、用户认证原理</strong></p>
<p>我们在应用程序中实现验证的方式基本上都是创建一张用户表，里面至少包含username和password两个字段，</p>
<p>password基本上都是加密后进行存储的。作为数据库，对用户的限制较多，不是像我说的仅仅只有username和password</p>
<p>这么简单了。首先粗略的讲下访问控制。</p>
<p>信息系统中，访问控制分为<strong>自主访问控制(DAC)</strong>和<strong>强制访问控制(MAC)</strong>。具体到DBMS，自主访问控制就是我们所熟悉</p>
<p>的GRANT，REVOKE，大多数数据库都支持自助的访问控制。强制访问控制就是ORACLE中的LABEL，只有很少的一些系统支持MAC。</p>
<p>严格来说，登录并不属于访问控制机制，而应该属于用户身份识别和认证。在Mysql中，将登录和DAC的相关接口都实现在了</p>
<p>sql_acl.cc中(其实说登录是用户拥有的一种权限也未尝不可，正如ORACLE中的CREATE SESSION，不过登录并不仅仅是一种权</p>
<p>限，还包含很多其他的属性)，从文件名大家可以看出来，ACL即ACCESS CONTROL LIST，访问控制列表，这是实现访问控制的</p>
<p>基本方法。<strong>下图是Mysql的整个访问控制的流程</strong>。</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/nocode/201108/201108302046147054.png"><img title="image001" src="http://images.cnblogs.com/cnblogs_com/nocode/201108/201108302048231497.png" alt="image001" width="443" height="713" border="0" /></a></p>
<p>Mysql中用户管理模块的信息存储在系统表<strong>mysql.User</strong>中，这个表不仅仅存放了授权用户的基本信息，还存放一些权限</p>
<p>信息。我们首先大概看一下这个表的结构。</p>
<div>
<pre>+-----------------------+-----------------------------------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+-----------------------+-----------------------------------+------+-----+---------+-------+

| Host | char(60) | NO | PRI | | |

| User | char(16) | NO | PRI | | |

| Password | char(41) | NO | | | |

| Select_priv | enum('N','Y') | NO | | N | |

| Insert_priv | enum('N','Y') | NO | | N | |

| Update_priv | enum('N','Y') | NO | | N | |

| Delete_priv | enum('N','Y') | NO | | N | |

| Create_priv | enum('N','Y') | NO | | N | |

| Drop_priv | enum('N','Y') | NO | | N | |

| Reload_priv | enum('N','Y') | NO | | N | |

| Shutdown_priv | enum('N','Y') | NO | | N | |

| Process_priv | enum('N','Y') | NO | | N | |

| File_priv | enum('N','Y') | NO | | N | |

| Grant_priv | enum('N','Y') | NO | | N | |

| References_priv | enum('N','Y') | NO | | N | |

| <a href="http://ourmysql.com/archives/tag/index" class="st_tag internal_tag" rel="tag" title="标签 index 下的日志">Index</a>_priv | enum('N','Y') | NO | | N | |

| Alter_priv | enum('N','Y') | NO | | N | |

| Show_db_priv | enum('N','Y') | NO | | N | |

| Super_priv | enum('N','Y') | NO | | N | |

| Create_tmp_table_priv | enum('N','Y') | NO | | N | |

| Lock_tables_priv | enum('N','Y') | NO | | N | |

| Execute_priv | enum('N','Y') | NO | | N | |

| Repl_slave_priv | enum('N','Y') | NO | | N | |

| Repl_client_priv | enum('N','Y') | NO | | N | |

| Create_view_priv | enum('N','Y') | NO | | N | |

| Show_view_priv | enum('N','Y') | NO | | N | |

| Create_routine_priv | enum('N','Y') | NO | | N | |

| Alter_routine_priv | enum('N','Y') | NO | | N | |

| Create_user_priv | enum('N','Y') | NO | | N | |

| Event_priv | enum('N','Y') | NO | | N | |

| Trigger_priv | enum('N','Y') | NO | | N | |

| ssl_type | enum('','ANY','X509','SPECIFIED') | NO | | | |

| ssl_cipher | blob | NO | | NULL | |

| x509_issuer | blob | NO | | NULL | |

| x509_subject | blob | NO | | NULL | |

| max_questions | int(11) unsigned | NO | | 0 | |

| max_updates | int(11) unsigned | NO | | 0 | |

| max_connections | int(11) unsigned | NO | | 0 | |

| max_user_connections | int(11) unsigned | NO | | 0 | |

+-----------------------+-----------------------------------+------+-----+---------+-------+

39 rows in set (0.01 sec)</pre>
<div><a>复制代码</a></div>
</div>
<p>这个表包含了39个字段，对于我们登录来说，应该主要是使用前三个字段，即Host，User，Password。</p>
<p>&nbsp;</p>
<div>
<pre>mysql&gt; select Host,User,Password from user;

+-----------+------+----------+

| Host | User | Password |

+-----------+------+----------+

| localhost | root | |

| 127.0.0.1 | root | |

| localhost | | |

+-----------+------+----------+

3 rows in set (0.00 sec)</pre>
<div><a>复制代码</a></div>
</div>
<p>这里比我们预想的只需要用户名和密码的方式有所出入，<strong>多了一个Host字段</strong>，这个字段起到什么作用呢？！原来Mysql的登录认证不仅需要验证用户名和密码，还需要验证连接的主机地址，这样也是为了提高安全性吧。那如果我想一个用户在任何地址都可以进行登录岂不是要设置很多地址？Mysql提供了通配符，可以设置Host字段为*，这就代表可以匹配任何Host。具体看下这三行的意思，这三行的密码均为空。针对root用户，不需要输入密码，客户端的地址为本机。第三行的用户名为空，Host为localhost，说明本地的任何用户均可以进行登录，即使是个不存在的用户也可以登录成功，但是仅限于登录，没有其他相关的权限，无法进行实际操作。</p>
<p><strong>二、源码跟踪</strong></p>
<p>在Connection Manager中提到了login_connection函数用于检查用户名和密码等相关信息，其源码如下（<strong>重点的函数代码</strong></p>
<p><strong>会着色</strong>）：</p>
<div>
<pre>static bool login_connection(THD *thd)
{
  NET *net= &amp;thd-&gt;net;
  int error;
  DBUG_ENTER("login_connection");
  DBUG_PRINT("info", ("login_connection called by thread %lu",
                      thd-&gt;thread_id));

  /* Use "connect_timeout" value during connection phase */
  my_net_set_read_timeout(net, connect_timeout);
  my_net_set_write_timeout(net, connect_timeout);
error= check_connection(thd); //此处是验证的具体函数

  net_end_statement(thd);

  if (error)
  {                        // Wrong permissions
#ifdef __NT__
    if (vio_type(net-&gt;vio) == VIO_TYPE_NAMEDPIPE)
      my_sleep(1000);                /* must wait after eof() */
#endif
    statistic_increment(aborted_connects,&amp;LOCK_status);
    DBUG_RETURN(1);
  }
  /* Connect completed, set read/write timeouts back to default */
  my_net_set_read_timeout(net, thd-&gt;variables.net_read_timeout);
  my_net_set_write_timeout(net, thd-&gt;variables.net_write_timeout);
  DBUG_RETURN(0);
}</pre>
<div><a>复制代码</a></div>
</div>
<p>此函数主要是功能是调用函数check_connection进行用户认证,由于函数check_connection过长，对其进行简化，如下所示：</p>
<div>
<pre> static int check_connection(THD *thd)

{
  uint connect_errors= 0;
  NET *net= &amp;thd-&gt;net;
  ulong pkt_len= 0;
  char *end;

  DBUG_PRINT("info",
             ("New connection received on %s", vio_description(net-&gt;vio)));
#ifdef SIGNAL_WITH_VIO_CLOSE
  thd-&gt;set_active_vio(net-&gt;vio);
#endif

  if (!thd-&gt;main_security_ctx.host)         // If TCP/IP connection
  {
    char ip[30];

    if (vio_peer_addr(net-&gt;vio, ip, &amp;thd-&gt;peer_port))
    {
      my_error(ER_BAD_HOST_ERROR, MYF(0), thd-&gt;main_security_ctx.host_or_ip);
      return 1;
    }
    if (!(thd-&gt;main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
      return 1; /* The error is set by my_strdup(). */
    thd-&gt;main_security_ctx.host_or_ip= thd-&gt;main_security_ctx.ip;
    vio_in_addr(net-&gt;vio,&amp;thd-&gt;remote.sin_addr);
    if (!(specialflag &amp; SPECIAL_NO_RESOLVE))
    {
      vio_in_addr(net-&gt;vio,&amp;thd-&gt;remote.sin_addr);
      thd-&gt;main_security_ctx.host=
        ip_to_hostname(&amp;thd-&gt;remote.sin_addr, &amp;connect_errors);
      /* Cut very long hostnames to avoid possible overflows */
      if (thd-&gt;main_security_ctx.host)
      {
        if (thd-&gt;main_security_ctx.host != my_localhost)
          thd-&gt;main_security_ctx.host[min(strlen(thd-&gt;main_security_ctx.host),
                                          HOSTNAME_LENGTH)]= 0;
        thd-&gt;main_security_ctx.host_or_ip= thd-&gt;main_security_ctx.host;
      }
      if (connect_errors &gt; max_connect_errors)
      {
        my_error(ER_HOST_IS_BLOCKED, MYF(0), thd-&gt;main_security_ctx.host_or_ip);
        return 1;
      }
    }
    ...

if (acl_check_host(thd-&gt;main_security_ctx.host, thd-&gt;main_security_ctx.ip))//此处验证主机名或IP是否存在

    {
      my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
               thd-&gt;main_security_ctx.host_or_ip);
      return 1;
    }
  }
  else /* Hostname given means that the connection was on a socket */
  {
   ...
  }
  vio_keepalive(net-&gt;vio, TRUE);

  ...

  char *user= end;
  char *passwd= strend(user)+1;
  uint user_len= passwd - user - 1;
  char *db= passwd;
  char db_buff[NAME_LEN + 1];           // buffer to store db in utf8
  char user_buff[USERNAME_LENGTH + 1];    // buffer to store user in utf8
  uint dummy_errors;

  uint passwd_len= thd-&gt;client_capabilities &amp; CLIENT_SECURE_CONNECTION ?
    (uchar)(*passwd++) : strlen(passwd);
  db= thd-&gt;client_capabilities &amp; CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
  uint db_len= db ? strlen(db) : 0;

  if (passwd + passwd_len + db_len &gt; (char *)net-&gt;read_pos + pkt_len)
  {
    inc_host_errors(&amp;thd-&gt;remote.sin_addr);
    my_error(ER_HANDSHAKE_ERROR, MYF(0), thd-&gt;main_security_ctx.host_or_ip);
    return 1;
  }

...
  /* If username starts and ends in "'", chop them off */
  if (user_len &gt; 1 &amp;&amp; user[0] == '\'' &amp;&amp; user[user_len - 1] == '\'')
  {
    user[user_len-1]= 0;
    user++;
    user_len-= 2;
  }

  if (thd-&gt;main_security_ctx.user)
    x_free(thd-&gt;main_security_ctx.user);
  if (!(thd-&gt;main_security_ctx.user= my_strdup(user, MYF(MY_WME))))
    return 1; /* The error is set by my_strdup(). */
  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);//验证用户名和密码

}</pre>
<div><a>复制代码</a></div>
</div>
<p>上面的源码主要做了如下几件事情：</p>
<ol>
<li>获取客户端的IP和主机名</li>
<li><strong>acl_check_host</strong>函数验证USER表中是否存在相应的IP或HOST，如果不存在直接报错</li>
<li>获取用户名和密码</li>
<li><strong>check_user</strong>函数验证用户名和密码(不输入用户名默认为ODBC)，如果系统表中不存在匹配的报错返回</li>
<li>获取用户的权限列表，<strong>验证用户的相关属性是否合法</strong>，如连接数是否超过上限，连接是否超时，操作是否超过限制等信息，如果不合法，则报错返回。</li>
</ol>
<p>由于在一个认证的过程中涉及到的东西比较多，各个方面吧，我不能一一跟踪，只能大概了解其中的实现流程，捡重点进行</p>
<p>跟踪，有兴趣的童鞋自己具体跟踪吧</p>
<p><strong>题外话：</strong></p>
<p>Mysql中权限系统表都是在系统启动时，载入内存的（当然User表也是这样），一般情况下，不需要进行频繁的授权和回收操作，这中情况下，权限表基本保持不变，将其在系统启动的时候载入内存的好处自然是快速的进行权限判断，减少磁盘的I/O，你懂的^_^。有好处自然有坏处，就是在频繁进行授权和回收相关操作时，权限表需要重新载入内存，Mysql为了避免这种情况，在手册中已经说的很清楚了，授权和回收只会反应到磁盘中，内存的数据字典信息是不会改变的，如果想立即生效，需要调用FLUSH PRIVILEGES系统函数，这个系统函数的工作应该就是对权限系统表的RELOAD。</p>
<p>下篇进入实质性的介绍，通过跟踪一个建表语句，来学习Mysql是如何存储表的元数据的，即<strong>frm格式文件的剖析</strong>。</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a>, <a href="http://ourmysql.com/archives/tag/%e8%ae%a4%e8%af%81" title="认证" rel="tag">认证</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/fHj6v09dOdJT-NBNU5QZNB-Fx4s/0/da"><img src="http://feedads.g.doubleclick.net/~a/fHj6v09dOdJT-NBNU5QZNB-Fx4s/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/fHj6v09dOdJT-NBNU5QZNB-Fx4s/1/da"><img src="http://feedads.g.doubleclick.net/~a/fHj6v09dOdJT-NBNU5QZNB-Fx4s/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1105/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习——Thread Manager</title>
		<link>http://ourmysql.com/archives/1102</link>
		<comments>http://ourmysql.com/archives/1102#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:42:04 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[Thread Manager]]></category>
		<category><![CDATA[源码]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1102</guid>
		<description><![CDATA[为了提高系统效率，减少频繁创建线程和中止线程的系统消耗，Mysql使用了线程缓冲区的概念，即如果 一个连接断开，则并不销毁承载其的线程，而是将此线程放入线程缓冲区，并处于挂起状态，当下一个新的Connection到来 时，首先去线程缓冲区去查找是否有空闲的线程，如果有，则使用之，如果没有则新建线程。本问主要介绍这个线程缓冲区， 首先介绍下基本的概念]]></description>
			<content:encoded><![CDATA[<div id="cnblogs_post_body">
<p><strong>一、前言</strong></p>
<p>上篇的<a href="http://www.cnblogs.com/nocode/archive/2011/08/21/2147781.html">Connection Manager</a>中，曾提及对于一个新到来的Connection，服务器会创建一个新的线程来处理这个连接。</p>
<p>其实没那么简单，为了提高系统效率，减少频繁创建线程和中止线程的系统消耗，Mysql使用了线程缓冲区的概念，即如果</p>
<p>一个连接断开，则并不销毁承载其的线程，而是将此线程放入线程缓冲区，并处于挂起状态，当下一个新的Connection到来</p>
<p>时，首先去线程缓冲区去查找是否有空闲的线程，如果有，则使用之，如果没有则新建线程。本问主要介绍这个线程缓冲区，</p>
<p>首先介绍下基本的概念。</p>
<p><strong>二、基本概念</strong></p>
<p><strong>1.线程创建函数</strong></p>
<p>大家知道，Mysql现在是插件式的存储引擎，只要实现规定的接口，就可实现自己的存储引擎。故Mysql的线程创建除了</p>
<p>出现在主服务器框架外，存储引擎也可能会进行线程的创建。通过设置断点，在我调试的版本中，发现了两个创建线程的函数。</p>
<div>
<div id="highlighter_418463">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>pthread_create：Mysql自用的创建线程函数</code></div>
<div><code>os_thread_create：存储引擎innobase的创建线程的函数</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>os_thread_create是存储引擎innobase的线程函数，先搁浅不研究了，重点看下pthread_create，首先看下其源码。</p>
<p>&nbsp;</p>
<div>
<div id="highlighter_528910">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>int</code> <code>pthread_create(pthread_t *thread_id, pthread_attr_t *attr,</code></div>
<div><code>           </code><code>pthread_handler func, </code><code>void</code> <code>*param)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>HANDLE</code> <code>hThread;</code></div>
<div><code>  </code><code>struct</code> <code>pthread_map *map;</code></div>
<div><code>  </code><code><a href="http://ourmysql.com/archives/tag/dbug" class="st_tag internal_tag" rel="tag" title="标签 DBUG 下的日志">DBUG</a>_ENTER(</code><code>"pthread_create"</code><code>);</code></div>
<div></div>
<div><code>  </code><code>if</code> <code>(!(map=</code><code>malloc</code><code>(</code><code>sizeof</code><code>(*map))))</code></div>
<div><code>    </code><code>DBUG_RETURN(-1);</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>map-&gt;func=func; map-&gt;param=param;</p>
<div>
<div id="highlighter_274572">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>  </code><code>pthread_mutex_lock(&amp;THR_LOCK_thread);</code></div>
<div><code>#ifdef __BORLANDC__</code></div>
<div><code>  </code><code>hThread=(</code><code>HANDLE</code><code>)_beginthread((</code><code>void</code><code>(_USERENTRY *)(</code><code>void</code> <code>*)) pthread_start,</code></div>
<div><code>                   </code><code>attr-&gt;dwStackSize ? attr-&gt;dwStackSize :</code></div>
<div><code>                   </code><code>65535, (</code><code>void</code><code>*) map);</code></div>
<div><code>#else</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>hThread=(HANDLE)_beginthread((void( __cdecl *)(void *)) pthread_start, attr-&gt;dwStackSize ? attr-&gt;dwStackSize : 65535, (void*) map);</p>
<div>
<div id="highlighter_227785">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>#endif</code></div>
<div><code>  </code><code>DBUG_PRINT(</code><code>"info"</code><code>, (</code><code>"hThread=%lu"</code><code>,(</code><code>long</code><code>) hThread));</code></div>
<div><code>  </code><code>*thread_id=map-&gt;pthreadself=hThread;</code></div>
<div><code>  </code><code>pthread_mutex_unlock(&amp;THR_LOCK_thread);</code></div>
<div></div>
<div><code>  </code><code>if</code> <code>(hThread == (</code><code>HANDLE</code><code>) -1)</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>int</code> <code>error=</code><code>errno</code><code>;</code></div>
<div><code>    </code><code>DBUG_PRINT(</code><code>"error"</code><code>,</code></div>
<div><code>           </code><code>(</code><code>"Can't create thread to handle request (error %d)"</code><code>,error));</code></div>
<div><code>    </code><code>DBUG_RETURN(error ? error : -1);</code></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>VOID</code><code>(SetThreadPriority(hThread, attr-&gt;priority)) ;</code></div>
<div><code>  </code><code>DBUG_RETURN(0);</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>上面代码首先构造了一个map结构体，成员分别是函数地址和传入参数。然后调用操作系统的接口，_beginthread,但是执行函数并不是传入的函数——func，而是pthread_start,参数为map。继续跟踪pthread_start。</p>
<div>
<div id="highlighter_114041">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>pthread_handler_t pthread_start(</code><code>void</code> <code>*param)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>pthread_handler</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>func=((struct pthread_map *) param)-&gt;func</p>
<div>
<div id="highlighter_805932">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>;</code></div>
<div><code>  </code><code>void</code> <code>*func_param=((</code><code>struct</code> <code>pthread_map *) param)-&gt;param;</code></div>
<div><code>  </code><code>my_thread_init();         </code><code>/* Will always succeed in windows */</code></div>
<div><code>  </code><code>pthread_mutex_lock(&amp;THR_LOCK_thread);   </code><code>/* Wait for beginthread to return */</code></div>
<div><code>  </code><code>win_pthread_self=((</code><code>struct</code> <code>pthread_map *) param)-&gt;pthreadself;</code></div>
<div><code>  </code><code>pthread_mutex_unlock(&amp;THR_LOCK_thread);</code></div>
<div><code>  </code><code>free</code><code>((</code><code>char</code><code>*) param);            </code><code>/* Free param from create */</code></div>
<div><code>  </code><code>pthread_exit((</code><code>void</code><code>*) (*func)(func_param));</code></div>
<div><code>  </code><code>return</code> <code>0;               </code><code>/* Safety */</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>可以看出，pthread_start中调用了map的func元素，作为真正执行的函数体。OK,创建线程的函数跟踪到此！</p>
<p><strong>2.服务器启动时创建了哪些函数？</strong></p>
<p>通过在两个创建线程的地方设置断点，总结了下，在服务器启动时，创建了如下的线程。</p>
<p><strong>pthread_create创建的线程</strong>：</p>
<table width="428" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="217">创建线程函数</td>
<td valign="top" width="209">线程执行函数</td>
</tr>
<tr>
<td valign="top" width="217">create_shutdown_thread</td>
<td valign="top" width="209">handle_shutdown</td>
</tr>
<tr>
<td valign="top" width="217">start_handle_manager</td>
<td valign="top" width="209">handle_manager</td>
</tr>
<tr>
<td valign="top" width="217">handle_connections_methods</td>
<td valign="top" width="209">handle_connections_sockets</td>
</tr>
</tbody>
</table>
<p><strong>innobase的os_thread_create创建的线程：</strong></p>
<p>&nbsp;</p>
<table width="563" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="277">创建线程函数</td>
<td valign="top" width="284">线程执行函数</td>
</tr>
<tr>
<td valign="top" width="277">innobase_start_or_create_for_mysql</td>
<td valign="top" width="284">io_handler_thread（4个）</td>
</tr>
<tr>
<td valign="top" width="277">recv_recovery_from_checkpoint_finish</td>
<td valign="top" width="284">trx_rollback_or_clean_all_without_sess</td>
</tr>
<tr>
<td valign="top" width="277">innobase_start_or_create_for_mysql</td>
<td valign="top" width="284">srv_lock_timeout_thread</td>
</tr>
<tr>
<td valign="top" width="277"></td>
<td valign="top" width="284">srv_error_monitor_thread</td>
</tr>
<tr>
<td valign="top" width="277"></td>
<td valign="top" width="284">srv_monitor_thread</td>
</tr>
<tr>
<td valign="top" width="277"></td>
<td valign="top" width="284">srv_master_thread</td>
</tr>
</tbody>
</table>
<p>还可以在调试过程中，通过暂停来看此时服务器中的线程，如下图：</p>
<p><a href="http://images.cnblogs.com/cnblogs_com/nocode/201108/201108271517568307.jpg"><img title="1" src="http://images.cnblogs.com/cnblogs_com/nocode/201108/201108271517561829.jpg" alt="1" width="530" height="281" border="0" /></a></p>
<p><strong>三、线程缓冲池</strong></p>
<p>Mysql支持线程缓存，在多线程连接模式下，如果连接断开后，将这个线程放入空闲线程缓冲区，在下次有连接到来时，</p>
<p>先去缓冲池中查找是否有空闲线程，有则用之，无则创建。启动时可以设置线程缓冲池的数目：</p>
<div>
<div id="highlighter_41496">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>Mysqld.exe --thread_cache_size=10</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>在一个连接断开时，会调用cache_thread函数，将空闲的线程加入到cache中，以备后用。源码如下：</p>
<div>
<div id="highlighter_542247">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>static</code> <code>bool</code> <code>cache_thread()</code></div>
<div><code>{</code></div>
<div><code>  </code><code>safe_mutex_assert_owner(&amp;LOCK_thread_count);</code></div>
<div><code>  </code><code>if</code> <code>(</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>cached_thread_count &lt; thread_cache_size</p>
<div>
<div id="highlighter_835008">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>&amp;&amp;</code></div>
<div><code>     </code><code>! abort_loop &amp;&amp; !kill_cached_threads)</code></div>
<div><code> </code><code>{</code></div>
<div><code>   </code><code>/* Don't kill the thread, just put it in cache for reuse */</code></div>
<div><code>   </code><code>DBUG_PRINT(</code><code>"info"</code><code>, (</code><code>"Adding thread to cache"</code><code>));</code></div>
<div><code>   </code><code>cached_thread_count++;</code></div>
<div><code>   </code><code>while</code> <code>(!abort_loop &amp;&amp; ! wake_thread &amp;&amp; ! kill_cached_threads)</code></div>
<div><code>     </code><code>(</code><code>void</code><code>) pthread_cond_wait(&amp;COND_thread_cache, &amp;LOCK_thread_count);</code></div>
<div><code>   </code><code>cached_thread_count--;</code></div>
<div><code>   </code><code>if</code> <code>(kill_cached_threads)</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>pthread_cond_signal(&amp;COND_flush_thread_cache);</p>
<div>
<div id="highlighter_463063">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>  </code><code>if</code> <code>(wake_thread)</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>THD *thd;</code></div>
<div><code>    </code><code>wake_thread--;</code></div>
<div><code>    </code><code>thd= thread_cache.get();</code></div>
<div><code>    </code><code>thd-&gt;thread_stack= (</code><code>char</code><code>*) &amp;thd;          </code><code>// For store_globals</code></div>
<div><code>    </code><code>(</code><code>void</code><code>) thd-&gt;store_globals();</code></div>
<div><code>    </code><code>/*</code></div>
<div><code>      </code><code>THD::mysys_var::abort is associated with physical thread rather</code></div>
<div><code>      </code><code>than with THD object. So we need to reset this flag before using</code></div>
<div><code>      </code><code>this thread for handling of new THD object/connection.</code></div>
<div><code>    </code><code>*/</code></div>
<div><code>    </code><code>thd-&gt;mysys_var-&gt;</code><code>abort</code><code>= 0;</code></div>
<div><code>    </code><code>thd-&gt;thr_create_utime= my_micro_time();</code></div>
<div><code>    </code><code>threads.append(thd);</code></div>
<div><code>    </code><code>return</code><code>(1);</code></div>
<div><code>  </code><code>}</code></div>
<div><code>}</code></div>
<div><code>return</code><code>(0);</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div id="highlighter_308397">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>上面我们的启动参数设置线程缓冲区为10，此时对应代码里面的thread_cache_size = 10，cached_thread_count记录</p>
<p>了此刻cache中的空闲线程数目，只有在cache未满的情况下，才会将新的空闲线程加入缓冲池中。加入到缓冲区其实就是将线</p>
<p>程挂起，pthread_cond_wait函数便是线程等待函数，在此函数中，会调用WaitForMultipleObjects进行事件等待。具体源码</p>
<p>如下：</p>
<div>
<div id="highlighter_464553">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>int</code> <code>pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,</code></div>
<div><code>                           </code><code>struct</code> <code>timespec *abstime)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>int</code> <code>result;</code></div>
<div><code>  </code><code>long</code> <code>timeout; </code></div>
<div><code>  </code><code>union</code> <code>ft64 now;</code></div>
<div></div>
<div><code>  </code><code>if</code><code>( abstime != NULL )</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>GetSystemTimeAsFileTime(&amp;now.ft);</code></div>
<div></div>
<div><code>    </code><code>/*</code></div>
<div><code>      </code><code>Calculate time left to abstime</code></div>
<div><code>      </code><code>- subtract start time from current time(values are in 100ns units)</code></div>
<div><code>      </code><code>- convert to millisec by dividing with 10000</code></div>
<div><code>    </code><code>*/</code></div>
<div><code>    </code><code>timeout= (</code><code>long</code><code>)((abstime-&gt;tv.i64 - now.i64) / 10000);</code></div>
<div><code>    </code></div>
<div><code>    </code><code>/* Don't allow the timeout to be negative */</code></div>
<div><code>    </code><code>if</code> <code>(timeout &lt; 0)</code></div>
<div><code>      </code><code>timeout= 0L;</code></div>
<div></div>
<div><code>    </code><code>/*</code></div>
<div><code>      </code><code>Make sure the calucated timeout does not exceed original timeout</code></div>
<div><code>      </code><code>value which could cause "wait for ever" if system time changes</code></div>
<div><code>    </code><code>*/</code></div>
<div><code>    </code><code>if</code> <code>(timeout &gt; abstime-&gt;max_timeout_msec)</code></div>
<div><code>      </code><code>timeout= abstime-&gt;max_timeout_msec;</code></div>
<div></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>else</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>/* No time specified; don't expire */</code></div>
<div><code>    </code><code>timeout= INFINITE;</code></div>
<div><code>  </code><code>}</code></div>
<div></div>
<div><code>  </code><code>/* </code></div>
<div><code>    </code><code>Block access if previous broadcast hasn't finished.</code></div>
<div><code>    </code><code>This is just for safety and should normally not</code></div>
<div><code>    </code><code>affect the total time spent in this function.</code></div>
<div><code>  </code><code>*/</code></div>
<div><code>  </code><code>WaitForSingleObject(cond-&gt;broadcast_block_event, INFINITE);</code></div>
<div></div>
<div><code>  </code><code>EnterCriticalSection(&amp;cond-&gt;lock_waiting);</code></div>
<div><code>  </code><code>cond-&gt;waiting++;</code></div>
<div><code>  </code><code>LeaveCriticalSection(&amp;cond-&gt;lock_waiting);</code></div>
<div></div>
<div><code>  </code><code>LeaveCriticalSection(mutex);</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>result= WaitForMultipleObjects(2, cond-&gt;events, FALSE, timeout);</p>
<div>
<div id="highlighter_20624">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>  </code><code>EnterCriticalSection(&amp;cond-&gt;lock_waiting);</code></div>
<div><code>  </code><code>cond-&gt;waiting--;</code></div>
<div><code>  </code></div>
<div><code>  </code><code>if</code> <code>(cond-&gt;waiting == 0)</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>/*</code></div>
<div><code>      </code><code>We're the last waiter to be notified or to stop waiting, so</code></div>
<div><code>      </code><code>reset the manual event. </code></div>
<div><code>    </code><code>*/</code></div>
<div><code>    </code><code>/* Close broadcast gate */</code></div>
<div><code>    </code><code>ResetEvent(cond-&gt;events[BROADCAST]);</code></div>
<div><code>    </code><code>/* Open block gate */</code></div>
<div><code>    </code><code>SetEvent(cond-&gt;broadcast_block_event);</code></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>LeaveCriticalSection(&amp;cond-&gt;lock_waiting);</code></div>
<div><code>  </code></div>
<div><code>  </code><code>EnterCriticalSection(mutex);</code></div>
<div></div>
<div><code>  </code><code>return</code> <code>result == WAIT_TIMEOUT ? ETIMEDOUT : 0;</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>此处是等待时间，何处进行事件通知呢？我们再次来到上篇所提及的为新的连接创建线程的代码中：</p>
<div>
<div id="highlighter_131475">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>void</code> <code>create_thread_to_handle_connection(THD *thd)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>if</code> <code>(cached_thread_count &gt; wake_thread)</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>/* Get thread from cache */</code></div>
<div><code>    </code><code>thread_cache.append(thd);</code></div>
<div><code>    </code><code>wake_thread++;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>pthread_cond_signal(&amp;COND_thread_cache);</p>
<div>
<div id="highlighter_202994">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>Else</code></div>
<div><code>...</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>上篇文章我们其实只讲了ELSE分支，而忽略了IF分支。wake_thread代表了唤醒的线程数，即在线程缓冲区中被再次使用的</p>
<p><a href="http://ourmysql.com/archives/tag/%e7%ba%bf%e7%a8%8b" class="st_tag internal_tag" rel="tag" title="标签 线程 下的日志">线程</a>，如果cache中的总数&gt;被重新使用的数目，说明还有空闲的线程，此时进入if分支，调用phtread_cond_signal唤醒上面挂起</p>
<p>的空闲线程。</p>
<blockquote><p>线程管理就到此为止了，这里只是介绍了下线程缓冲区的工作原理，并没有具体去介绍如何利用EVENT进行线程的挂起和唤醒，这些都是借助了操作系统的特性，有兴趣的可以自己研究下。这篇就到此为止，下节会介绍Mysql的用户身份认证原理和实现。</p></blockquote>
<p>&nbsp;</p>
<p><strong>PS. 男怕入错行，夜半三更忙，一行又一行</strong></p>
</div>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/thread-manager" title="Thread Manager" rel="tag">Thread Manager</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/BoDzQ4PXcs6LYzeg-ioFp4F6xjY/0/da"><img src="http://feedads.g.doubleclick.net/~a/BoDzQ4PXcs6LYzeg-ioFp4F6xjY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/BoDzQ4PXcs6LYzeg-ioFp4F6xjY/1/da"><img src="http://feedads.g.doubleclick.net/~a/BoDzQ4PXcs6LYzeg-ioFp4F6xjY/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1102/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习——Connection Manager</title>
		<link>http://ourmysql.com/archives/1100</link>
		<comments>http://ourmysql.com/archives/1100#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:39:39 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[Connection Manager]]></category>
		<category><![CDATA[源码]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1100</guid>
		<description><![CDATA[Mysql支持单线程和多线程两种连接线程数。如果是单线程，则在同一时刻，只能有一个connection连接到Mysql，其他的连接会被挂起。如果是多线程，则同一时刻可以支持多个connection同时连接到服务器。]]></description>
			<content:encoded><![CDATA[<p><strong>1.连接的线程数</strong></p>
<p>Mysql支持单线程和多线程两种连接线程数。如果是单线程，则在同一时刻，只能有一个connection连接到Mysql，</p>
<p>其他的连接会被挂起。如果是多线程，则同一时刻可以支持多个connection同时连接到服务器。</p>
<p>可以通过<strong>设置服务器的启动参数</strong>来设定连接的线程数：</p>
<div>
<pre>mysqld.exe --thread-handling=no-threads
mysqld.exe --thread-handling=one-thread-per-connection</pre>
<div><a>复制代码</a></div>
</div>
<p>服务器如何通过参数来选择使用哪种方式的呢？且看服务器中的分支代码：</p>
<div>
<pre>#ifdef EMBEDDED_LIBRARY
  one_thread_scheduler(&amp;thread_scheduler);
#else
  if (global_system_variables.thread_handling &lt;=
      SCHEDULER_ONE_THREAD_PER_CONNECTION)
    one_thread_per_connection_scheduler(&amp;thread_scheduler);
  else if (global_system_variables.thread_handling == SCHEDULER_NO_THREADS)
    one_thread_scheduler(&amp;thread_scheduler);
  else
    pool_of_threads_scheduler(&amp;thread_scheduler);  /* purecov: tested */
#endif</pre>
<div><a>复制代码</a></div>
</div>
<p>这段代码出现在get_options函数中，此函数是根据传入的服务器参数，设置相应参数的模式。由这段代码可以看出，如果定义了EMBEDDED_LIBRARY宏定义(估计应该是嵌入式使用)，则调用one_thread_scheduler，即使用单线程。如果参数小于等于SCHEDULER_ONE_THREAD_PER_CONNECTION，则调用one_thread_per_connection_scheduler，即每个连接一个线程，即多线程。 至于global_system_variables.thread_handling是如何进行设置的呢？其实就是根据我们传递给服务器的参数&#8211;thread-handling进行设置的，参数的设置统一在函数get_options中，其调用mysqld_get_one_option，其中有个分支，代码如下：</p>
<div>
<pre>case OPT_THREAD_HANDLING:
  {
    global_system_variables.thread_handling=
      find_type_or_exit(argument, &amp;thread_handling_typelib, opt-&gt;name)-1;
    break;
  }</pre>
<div><a>复制代码</a></div>
</div>
<p>对参数初始化有兴趣的可以具体的看下get_options这个函数，这里就不详细讲解了。 我们来看下one_thread_scheduler和one_thread_per_connection_scheduler的源代码，看下他们都做了些什么？</p>
<div>
<pre>void one_thread_scheduler(scheduler_functions* func)
{
  func-&gt;max_threads= 1;
#ifndef EMBEDDED_LIBRARY
  func-&gt;add_connection= handle_connection_in_main_thread;
#endif
  func-&gt;init_new_connection_thread= init_dummy;
  func-&gt;end_thread= no_threads_end;
}

void one_thread_per_connection_scheduler(scheduler_functions* func)
{
  func-&gt;max_threads= max_connections;
  func-&gt;add_connection= create_thread_to_handle_connection;
  func-&gt;end_thread= one_thread_per_connection_end;
}</pre>
<div><a>复制代码</a></div>
</div>
<p>原来就是设置了一个结构体中scheduler_functions的参数，只不过这些参数是一些函数指针罢了，也就是说在具体的调用中，</p>
<p>只需要调用add_connection或end_thread即可，不需要知道到底是调用了哪个函数，这大概就是一种变形的多态性吧。</p>
<p><strong>2.初始化网络配置</strong></p>
<p>网络配置比较简单，就是设置端口，创建套接字，绑定端口，监听端口。实现全部集中在network_init函数中，由于这个函数确</p>
<p>实没什么好讲的，如果对socket不熟悉的话，可以去网上搜索下相关知识。这里直接给出相应的伪代码：</p>
<div>
<pre>network_init
{
    set_ports; //设置端口号，#define MYSQL_PORT            3306
    socket； //创建套接字
    bind;       //绑定端口号
    listen； //监听端口号
}</pre>
<div><a>复制代码</a></div>
</div>
<p><strong>3.连接的方式</strong></p>
<p>进程间通信的方式不止是SOCKET，还有其他很多方式。Mysql支持三种连接方式：namepipe、socket和shared memory，</p>
<p>即命名管道、套接字和共享内存的方式。这三种方式是可以共存的。默认只使用套接字。</p>
<p>TCP/IP套接字方式是MySQL在任何平台下都提供的连接方式，也是网络中使用得最多的一种方式。这种方式在TCP/IP连接上建立</p>
<p>一个基于网络的连接请求，一般情况下客户端在一台服务器上，而MySQL实例在另一台服务器上，这两台机器通过一个TCP/IP网络连接。</p>
<p>例如，我可以在Windows服务器下请求一台远程Linux服务器下的MySQL实例。</p>
<p>在Windows 2000、Windows XP、Windows 2003和Windows Vista以及在此之后的 Windows操作系统中，如果两个需要通</p>
<p>信的进程在同一台服务器上，那么可以使用命名管道，SQL Server数据库默认安装后的本地连接也使用命名管道。在MySQL数据库中，</p>
<p>需在配置文件中启用&#8211;enable-named-pipe选项。在MySQL 4.1之后的版本中，MySQL还提供了共享内存的连接方式，在配置文件中</p>
<p>添加&#8211;shared-memory。如果想使用共享内存的方式，在连接时，Mysql客户端还必须使用-protocol=memory选项。</p>
<p>启动时可以通过下面的参数进行设置。</p>
<div>
<pre>mysqld.exe --enable-named-pipe
mysqld.exe --shared-memory</pre>
<div><a>复制代码</a></div>
</div>
<p>除了在启动时进行参数设置外，也可以通过修改MY.INI文件进行设置。我们来看下源码中选择连接方式的分支函数handle_connections_methods：</p>
<div>
<pre>handle_connections_methods()
{
if (hPipe != INVALID_HANDLE_VALUE)
{
       handler_count++;
       if (pthread_create(&amp;hThread,&amp;connection_attrib,
               handle_connections_namedpipes, 0))
       {
      sql_print_warning("Can't create thread to handle named pipes");
      handler_count--;
    }
}
    if (have_tcpip &amp;&amp; !opt_disable_networking)
  {
    handler_count++;
    if (pthread_create(&amp;hThread,&amp;connection_attrib,
               handle_connections_sockets, 0))
    {
      sql_print_warning("Can't create thread to handle TCP/IP");
      handler_count--;
    }
  }

if (opt_enable_shared_memory)
  {
    handler_count++;
    if (pthread_create(&amp;hThread,&amp;connection_attrib,
               handle_connections_shared_memory, 0))
    {
      sql_print_warning("Can't create thread to handle shared memory");
      handler_count--;
    }
  }
}</pre>
<div><a>复制代码</a></div>
</div>
<p>由于对于namepipe和memory share的通信方式不太了解，这里只研究socket的通信方式。从代码中可以看出，handle_connections_sockets便是socket的设置，我们就来看下它。</p>
<p><strong>4.socket管理创建新线程</strong> socket管理其实比较简单，直接给出其伪代码：</p>
<div>
<pre>handle_connections_sockets
{
    select; //监视socket文件描述符
    new_socket = accept;//处理到来的客户端连接
    thd = new THD；创建THD类
    vio_tmp = vio_new(new_socket,VIO_TYPE_TCPIP, 0);   //初始化VIO结构体
    my_net_init(&amp;thd-&gt;net, vio_tmp);//初始化thd的net结构体
    create_new_thread(thd);//为这个连接创建一个新的线程，如果是单线程模式的话，就不会创建一个新线程
}</pre>
<div><a>复制代码</a></div>
</div>
<p>首先是select函数进行监视socket端口，如果监控到有连接，则通过accept函数接受客户端的连接，然后新建一个THD类，将连接参数全部设置到THD类的参数上，<strong>最后调用create_new_thread函数，这个函数便是重点</strong>。 我们进入这个函数，看下做了啥。</p>
<div>
<pre>create_new_thread
{
    ++connection_count;//全局连接数自增
    thread_count++;    //全局线程数自增
    thread_scheduler.add_connection(thd);//真正创建线程
}</pre>
<div><a>复制代码</a></div>
</div>
<p>So easy,首先将全局连接数+1，全局线程数+1，然后调用add_connection函数，这个函数就是我们在上面第一步设置连接的</p>
<p>线程数中，one_thread_scheduler和one_thread_per_connection_scheduler中设置的一个参数。这两者的区别便是是否创建了</p>
<p>一个新的线程来处理到来的连接。 one_thread_scheduler是单线程方式，木有新建线程。我们重点研究 one_thread_per_connection_scheduler，其设置的add_connection函数为 create_thread_to_handle_connection：</p>
<div>
<pre>create_thread_to_handle_connection(THD *thd)
{
    thread_created++;
    threads.append(thd); // 创建线程数自增，并加入到threads链表上
    pthread_create(&amp;thd-&gt;real_id,&amp;connection_attrib,
                              handle_one_connection,
                              (void*) thd);//这就是真正创建线程的地方了，函数便是handle_one_connection
}</pre>
<div><a>复制代码</a></div>
</div>
<p>可见，最后调用了pthread_create函数，这个函数便是创建一个新的线程，新线程的处理函数为handle_one_connection.</p>
<p><strong>5.新线程处理流程</strong></p>
<p>新线程处理函数为handle_one_connection，到此位置，一个新的connection被一个新创建的线程所单独处理。我们看下其中</p>
<p>是如何进行处理的。</p>
<div>
<pre>handle_one_connection(void *arg)
{
    for (;;)
    {
        lex_start(thd); //初始化词法分析结构体
        login_connection(thd); //用户认证，失败报错
        prepare_new_connection_state(THD* thd)；//Initialize THD to handle queries
         while (!net-&gt;error &amp;&amp; net-&gt;vio != 0 &amp;&amp;   //循环处理command
           !(thd-&gt;killed == THD::KILL_CONNECTION))
        {
            if (do_command(thd))
              break;                   //处理失败跳出
          }
          end_connection(thd);         //关闭连接
          close_connection(thd, 0, 1);
         thread_scheduler.end_thread(thd,1);//结束线程
                  return 0;
    }
}</pre>
<div><a>复制代码</a></div>
</div>
<p>首先进行了词法分析结构体的初始化，然后进行用户认证，认证成功后通过do_command循环执行客户端发过来的命令。</p>
<p><strong>6.总结</strong></p>
<p>整个connection manager的流程十分清晰，单线程的连接一般很少使用，大多使用多线程方式。多线程连接中其实还涉及到线程缓冲</p>
<p>池的概念，即如果一个连接断开后，其所创建的线程不会被销毁掉，而是放到缓冲池中，等待下一个新的connection到来时，首先去线程</p>
<p>缓冲池查找是否有空闲的线程，有的话直接使用，木有的话才去创建新的线程来管理这个connection。这些属于Thread Manage的内容，</p>
<p>下节进行Thread Manage的学习。</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/connection-manager" title="Connection Manager" rel="tag">Connection Manager</a>, <a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/EzPgkKLrN6ReFgqQWl7uynzcl-4/0/da"><img src="http://feedads.g.doubleclick.net/~a/EzPgkKLrN6ReFgqQWl7uynzcl-4/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/EzPgkKLrN6ReFgqQWl7uynzcl-4/1/da"><img src="http://feedads.g.doubleclick.net/~a/EzPgkKLrN6ReFgqQWl7uynzcl-4/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1100/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习——八度空间</title>
		<link>http://ourmysql.com/archives/1098</link>
		<comments>http://ourmysql.com/archives/1098#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:37:49 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[八度空间]]></category>
		<category><![CDATA[源码]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1098</guid>
		<description><![CDATA[我们从上至下来看各个模块的划分，首先客户端发送请求与服务器连接，通过connection manager模块，连接管理模块会调用Thread Manager模块，即线程管理模块，这里会为一个连接创建一个新的线程，专门为这个连接服务，这就保证了每个连接都有一个独立的线程为之工作，当然连接数一般也会有个限制，不然无限制的创建新的线程，操作系统也顶不住啊。接着进入了User Module，用户模块，这个模块应该是身份识别认证阶段，说白了就是检查用户名密码，当然应该还包括权限检查(只有自主访问控制，Mysql不像 Oracle，不支持role，更不用说label了。这就是简约而不简单吧，^_^)。]]></description>
			<content:encoded><![CDATA[<div id="cnblogs_post_body">
<p>      学习完词法分析和语法分析后，开始进入Mysql源码的正式学习之旅了。这么多模块，肿么入手呢？！还好从网上搜到了一个模块划分，以后就尽可能根据这个模块划分一步一步的跟踪源码，揭开Mysql的面纱。</p>
<blockquote><p><a href="http://images.cnblogs.com/cnblogs_com/nocode/201108/20110815222255206.png"><img title="01-1" src="http://images.cnblogs.com/cnblogs_com/nocode/201108/20110815222333572.png" alt="01-1" width="711" height="604" border="0" /></a></p></blockquote>
<p>我们从上至下来看各个模块的划分，首先客户端发送请求与服务器连接，通过connection manager模块，连接管理模块会调用Thread Manager模块，即线程管理模块，这里会为一个连接创建一个新的线程，专门为这个连接服务，这就保证了每个连接都有一个独立的线程为之工作，当然连接 数一般也会有个限制，不然无限制的创建新的线程，操作系统也顶不住啊。接着进入了User Module，用户模块，这个模块应该是身份识别认证阶段，说白了就是检查用户名密码，当然应该还包括权限检查(只有自主访问控制，Mysql不像 Oracle，不支持role，更不用说label了。这就是简约而不简单吧，^_^)。</p>
<p>上面的三个模块就是登录过程中必须经历的阶段。connection manager为客户端和服务器创建连接，thread manager为新建的连接分配一个独立的线程，user Module进行身份认证。oh yeah～～</p>
<p>接着进入Commander Dispatcher模块，命令分发模块，大概就是一个switch case的过程，根据不同的命令，进行不同的操作。这个模块又会间接关联到Query Cache模块和Logging Module，即查询缓存模块和日志模块。Query Cache模块个人感觉至少包含结果集缓存模块，至于有木有执行计划缓存，这个就不清楚了，还需要进入源码慢慢看来。Logging Module就是传说中的日志了，日志包括redo和undo两方面吧，日志系统是一个database必备的，是实现事务特性的重要手段，而事务便是 database和file system的根本区别，不要小瞧了Logging Module啊！！</p>
<p>命令分发模块，根据命令的性质，将命令分给不同的子模块。SELECT分配给Optimizer即优化模块，一条select语句最重要的就是执行计划 了，一个好的执行计划比一个坏的执行计划不知道要快多少倍，这就是为什么我们要建立索引，其实在经常查询的列上建立二级索引，就是为了改变执行计划，让执 行计划可以选择二级索引，而抛弃聚簇索引(当然不是完全抛弃，聚簇索引是根本)。UPDATE、INSERT、DELETE交给Table Modification Module，不看也知道，这个模块主要是处理数据更新了（这里所说的更新是指数据的改变，即UID）。Repairs分配给了Table Maintenance Module，字面意义是说表维护模块，我OUT了，默认表坏了，支持修复？是何种坏了？不太了解啊…。Replication分配给了 Replication Module，就是复制模块。最后一个是Status分配给了Status Reporting Module，这个我貌似接触过，是不是所谓的那些动态试图？比如查看系统当前状态下的锁资源的占用情况等，这个模块应该是性能分析者居家旅行必备的模块 吧，通过这个模块可能很快找到系统性能的瓶颈。</p>
<p>上面的模块又统一走到Access Control Module，访问控制模块啊，原来上面所说的User Module不包含访问控制…，这个访问控制就是简单的DAC,即检查用户是否对要访问的对象具有增、删、改、查的权限。接着进入Table Manager模块，因为Mysql支持不同的存储引擎，而这个模块是个统一的模块，个人感觉这个应该是表的字典对象管理模块，即表的一些元数据，比如这 个表包含哪些列啊，各个列的类型，表的创建者，创建日期，表的存储引擎之类的信息。最下面是Abstract Storage Engine Module，即抽象存储引擎模块，Mysql的保持这么多存储引擎同时在服务器中，是通过一个handler抽象类实现的(不知道有木有抽象类的概念， 小弟C++、JAVA丢了很久了…)，每个存储引擎按照规定的接口，实现一个具体的handler类，然后再调用的时候，根据上面的Table Manager模块中的存储引擎的信息，选择对应的handler类进行相应表的物理操作。</p>
<p>看了这个模块图，感觉思路清晰了很多。下面就一步一步的学习每个模块，当然这些模块之间也是一个执行流程顺序执行的过程。今天就写到这了，下次开始学习第一个模块：<a href="http://ourmysql.com/archives/tag/connection-manager" class="st_tag internal_tag" rel="tag" title="标签 Connection Manager 下的日志">Connection Manager</a></p>
<p>PS. 貌似最近没这么忙了，加班加了好几个月终于算是稍微轻松了一点。以后把更多的业余时间放在读书学习上。</p>
<p>PS again. 每天下班脑子都一锅粥了，回来写点博客权当放松啦，标题的八度空间是JAY的专辑名啦，借此来比喻Mysql的各个模块^_^</p>
</div>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e5%85%ab%e5%ba%a6%e7%a9%ba%e9%97%b4" title="八度空间" rel="tag">八度空间</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/n5TcLf7xJxfKGpzIbiVnK_iCtig/0/da"><img src="http://feedads.g.doubleclick.net/~a/n5TcLf7xJxfKGpzIbiVnK_iCtig/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/n5TcLf7xJxfKGpzIbiVnK_iCtig/1/da"><img src="http://feedads.g.doubleclick.net/~a/n5TcLf7xJxfKGpzIbiVnK_iCtig/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1098/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习——源码目录结构</title>
		<link>http://ourmysql.com/archives/1095</link>
		<comments>http://ourmysql.com/archives/1095#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:36:24 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[源码]]></category>
		<category><![CDATA[目录结构]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1095</guid>
		<description><![CDATA[Mysql源码学习——源码目录结构]]></description>
			<content:encoded><![CDATA[<p>目录清单</p>
<p>目录名 注释</p>
<p>Bdb 伯克利DB表引擎</p>
<p>BUILD 构建工程的脚本</p>
<p>Client 客户端</p>
<p>Cmd-line-utils 命令行工具</p>
<p>Config 构建工程所需的一些文件</p>
<p><a href="http://ourmysql.com/archives/tag/dbug" class="st_tag internal_tag" rel="tag" title="标签 DBUG 下的日志">Dbug</a> Fred Fish的调试库</p>
<p>Docs 文档文件夹</p>
<p>Extra 一些相对独立的次要的工具</p>
<p>Heap HEAP表引擎</p>
<p>Include 头文件</p>
<p>Innobase INNODB表引擎</p>
<p>Libmysql 动态库</p>
<p>Libmysql_r 为了构建线程安全的libmysql库</p>
<p>Libmysqld 服务器作为一个嵌入式的库</p>
<p>Man 用户手册</p>
<p><a href="http://ourmysql.com/archives/tag/myisam" class="st_tag internal_tag" rel="tag" title="标签 MyISAM 下的日志">Myisam</a> MyISAM表引擎</p>
<p>Myisammrg MyISAM Merge表引擎</p>
<p>Mysql-test mysqld的测试单元</p>
<p>Mysys MySQL的系统库</p>
<p>Ndb Mysql集群</p>
<p>Netware Mysql网络版本相关文件</p>
<p>NEW-RPM 部署时存放RPM</p>
<p>Os2 针对OS/2操作系统的底层函数</p>
<p>Pstack 进行堆栈</p>
<p>Regex 正则表达式库（包括扩展的正则表达式函数）</p>
<p>SCCS 源码控制系统（不是源码的一部分）</p>
<p>Scripts 批量SQL脚本，如初始化库脚本</p>
<p>Server-tools 管理工具</p>
<p>Sql 处理SQL命令；Mysql的核心</p>
<p>Sql-bench Mysql的标准检查程序</p>
<p>Sql-common 一些sql文件夹相关的C文件</p>
<p>SSL 安全套接字层</p>
<p>Strings 字符串函数库</p>
<p>Support-files 用于在不同系统上构建Mysql的文件</p>
<p>Tests 包含Perl和C的测试</p>
<p>Tools</p>
<p>Vio 虚拟I/O库</p>
<p>Zlib 数据压缩库，用于WINDOWS</p>
<p>下面给出几个比较重要的目录清单：</p>
<p>文件清单</p>
<p>目录名 文件名 注释</p>
<p>Client</p>
<p>get_password.c 命令行输入密码</p>
<p>Mysql.cc MySQL命令行工具</p>
<p>Mysqladmin.cc 数据库weihu</p>
<p>Mysqldump.c 将表的内容以SQL语句输出，即逻辑备份</p>
<p>Mysqlimport.c 文本文件数据导入表中</p>
<p>Mysqlmanager-pwgen.c 密码生成</p>
<p>Mysqlshow.c 显示数据库，表和列</p>
<p>Mysqltest.c 被mysql测试单元使用的测试程序</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
<p>MYSYS</p>
<p>Array.c 动态数组</p>
<p>Charset.c 动态字符集，默认字符集</p>
<p>Charset-def.c 包含客户端使用的字符集</p>
<p>Checksum.c 为内存块计算校验和，用于pack_isam</p>
<p>Default.c 从*.cnf和*.ini文件中查找默认配置项</p>
<p>Default_modify.c 编辑可选项</p>
<p>Errors.c 英文错误文本</p>
<p><a href="http://ourmysql.com/archives/tag/hash" class="st_tag internal_tag" rel="tag" title="标签 Hash 下的日志">Hash</a>.c hash查找、比较、释放函数</p>
<p>List.c 双向链表</p>
<p>Make-conf.c 创建*.conf文件</p>
<p>Md5.c MD5算法</p>
<p>Mf_brkhant.c</p>
<p>Mf_cache.c 打开临时文件,并使用io_cache进行缓存</p>
<p>Mf_driname.c 解析，转换路径名</p>
<p>Mf_fn_ext.c 获取文件名的后缀</p>
<p>Mf_format.c 格式化文件名</p>
<p>Mf_getdate 获取日期：</p>
<p>yyyy-mm-dd hh:mm:ss format</p>
<p>mf_iocache.c 缓存I/O</p>
<p>mf_iocaches.c 多键值缓存</p>
<p>mf_loadpath.c 获取全路径名</p>
<p>mf_pack.c 创建需要的压缩/非压缩文件名</p>
<p>mf_path.c 决定是否程序可以找到文件</p>
<p>mf_qsort.c 快速排序</p>
<p>mf_qsort2.c 快速排序2</p>
<p>mf_radix.c 基数排序</p>
<p>mf_soundex.c 探测算法（EDN NOV 14, 1985）</p>
<p>mf_strip.c 去字符串结尾空格</p>
<p>mf_tempdir.c 临时文件夹的创建、查找、删除</p>
<p>mf_tempfile.c 临时文件的创建</p>
<p>mf_unixpath.c 转化文件名为UNIX风格</p>
<p>mf_util.c 常用函数</p>
<p>mf_wcomp.c 使用通配符比较</p>
<p>mf_wfile.c 通配符查找文件</p>
<p>mulalloc.c 同时分配多个指针</p>
<p>my_access.c 检查文件或路径是否合法</p>
<p>my_aes.c AES加密算法</p>
<p>my_alarm.c 警报相关</p>
<p>my_alloc.c 同时分配临时结果集缓存</p>
<p>my_append.c 一个文件到另一个</p>
<p>my_bit.c 除法使用，位运算</p>
<p>my_bitmap.c 位图</p>
<p>my_chsize.c 填充或截断一个文件</p>
<p>my_clock.c 时钟函数</p>
<p>my_compress.c 压缩</p>
<p>my_copy.c 拷贝文件</p>
<p>my_crc32.c</p>
<p>my_create.c 创建文件</p>
<p>my_delete.c 删除文件</p>
<p>my_div.c 获取文件名</p>
<p>my_dup.c 打开复制文件</p>
<p>my_error.c 错误码</p>
<p>my_file.c</p>
<p>my_fopen.c 打开文件</p>
<p>my_fstream.c 文件流读/写</p>
<p>my_gethostbyname.c 获取主机名</p>
<p>my_gethwaddr.c 获取硬件地址</p>
<p>my_getopt.c 查找生效的选项</p>
<p>my_getsystime.c time of day</p>
<p>my_getwd.c 获取工作目录</p>
<p>my_handler.c</p>
<p>my_init.c 初始化变量和函数</p>
<p>my_largepage.c 获取OS的分页大小</p>
<p>my_lib.c 比较/转化目录名和文件名</p>
<p>my_lock.c 锁住文件</p>
<p>my_lockmem.c 分配一块被锁住的内存</p>
<p>my_lread.c 读取文件到内存</p>
<p>my_lwrite.c 内存写入文件</p>
<p>my_malloc.c 分配内存</p>
<p>my_messnc.c 标准输出上输出消息</p>
<p>my_mkdir.c 创建目录</p>
<p>my_mmap.c 内存映射</p>
<p>my_net.c net函数</p>
<p>my_netware.c Mysql网络版<br />
my_once.c 一次分配，永不free</p>
<p>my_open.c 打开一个文件</p>
<p>my_os2cond.c 操作系统cond的简单实现</p>
<p>my_os2dirsrch.c 模拟Win32目录查询</p>
<p>my_os2dlfcn.c 模拟UNIX动态装载</p>
<p>my_os2file64.c 文件64位设置</p>
<p>my_os2mutex.c 互斥量</p>
<p>my_os2thread.c <a href="http://ourmysql.com/archives/tag/%e7%ba%bf%e7%a8%8b" class="st_tag internal_tag" rel="tag" title="标签 线程 下的日志">线程</a></p>
<p>my_os2tls.c 线程本地存储</p>
<p>my_port.c</p>
<p>my_pthread.c 线程的封装</p>
<p>my_quick.c 读/写</p>
<p>my_read.c 从文件读bytes</p>
<p>my_realloc.c 重新分配内存</p>
<p>my_redel.c 重命名和删除文件</p>
<p>my_seek.c 查找</p>
<p>my_semaphore.c 信号量</p>
<p>my_sleep.c 睡眠等待</p>
<p>my_static.c 静态变量</p>
<p>my_symlink.c 读取符号链接</p>
<p>my_symlink2.c 2</p>
<p>my_sync.c 同步内存和文件</p>
<p>my_thr_init.c 初始化/分配线程变量</p>
<p>my_wincond.c</p>
<p>my_windac.c WINDOWS NT/2000自主访问控制</p>
<p>my_winsem.c 模拟线程</p>
<p>my_winthread.c 模拟线程</p>
<p>my_write.c 写文件</p>
<p>ptr_cmp.c 字节流比较函数</p>
<p>queue,c 优先级队列</p>
<p>raid2.c 支持RAID</p>
<p>rijndael.c AES加密算法</p>
<p>safemalloc.c 安全的malloc</p>
<p>sha1.c sha1哈希加密算法</p>
<p>string.c 字符串函数</p>
<p>testhash.c 测试哈希函数（独立程序）</p>
<p>test_charset 测试字符集(独立)</p>
<p>thr_lock.c 读写锁</p>
<p>thr_mutex.c 互斥量</p>
<p>thr_rwlock.c 同步读写锁</p>
<p>tree.c 二叉树</p>
<p>typelib.c 字符串中匹配字串</p>
<p>SQL<br />
derror.cc 读取独立于语言的信息文件</p>
<p>Des_key_file.cc 加载DES密钥</p>
<p>Discover.cc frm文件的查找</p>
<p>Field.cc 存储列信息</p>
<p>Filed_conv.cc 拷贝字段信息</p>
<p>Filesort.cc 结果集排序（内存或临时文件）</p>
<p>Frm_crypt.cc get_crypt_from_frm</p>
<p>Gen_<a href="http://ourmysql.com/archives/tag/lex" class="st_tag internal_tag" rel="tag" title="标签 LEX 下的日志">lex</a>_hash.cc 查找、排列SQL关键字</p>
<p>Gstream.c GIS</p>
<p>Handler.cc 函数句柄</p>
<p>Hash_filo.cc 静态大小HASH表，</p>
<p>以FIFO方式存储主机名、IP表</p>
<p>Ha_berkeley.cc BDB的句柄</p>
<p>Ha_<a href="http://ourmysql.com/archives/tag/innodb" class="st_tag internal_tag" rel="tag" title="标签 InnoDB 下的日志">innodb</a>.cc INNODB句柄</p>
<p>Hostname.cc 根据IP获取hostname</p>
<p>Init.cc 初始化和unireg相关的函数</p>
<p>item.cc  item函数</p>
<p>item_buff.cc item的保存和比较的缓存</p>
<p>item_cmpfunc.cc 比较函数的定义</p>
<p>item_create.cc 创建一个item</p>
<p>item_func.cc 数字函数</p>
<p>item_geofunc.cc 集合函数</p>
<p>item_row.cc 记录项比较</p>
<p>item_strfunc.cc 字符串函数</p>
<p>item_subselect.cc 子查询</p>
<p>item_sum.cc 集函数（SUM,AVG&#8230;）</p>
<p>item_timefunc.cc 时间日期函数</p>
<p>item_uniq.cc  空文件</p>
<p>Key.cc 创建KEY以及比较</p>
<p>Lock.cc 锁</p>
<p>Log.cc 日志</p>
<p>log_event.cc 日志事件</p>
<p>Matherr.c 处理溢出</p>
<p>mf_iocache.cc 顺序读写的缓存</p>
<p>Mysqld.cc main，处理信号和连接</p>
<p>mf_<a href="http://ourmysql.com/archives/tag/decimal" class="st_tag internal_tag" rel="tag" title="标签 DECIMAL 下的日志">decimal</a>.cc decimal类型</p>
<p>my_lock.c</p>
<p>net_serv.cc socket数据包的解析</p>
<p>nt_servc.cc NT服务</p>
<p>opt_range.cc KEY排序</p>
<p>opt_sum.cc 集函数优化</p>
<p>parse_file.cc frm解析</p>
<p>Password.c 密码检查</p>
<p>Procedure.cc</p>
<p>Protocol.cc 数据包打包发送给客户端</p>
<p>protocol_cursor.cc 存储返送数据</p>
<p>Records.cc 读取记录集</p>
<p>repl_failsafe.cc</p>
<p>set_var.cc 设置、读取用户变量</p>
<p>Slave.cc slave节点</p>
<p>Sp.cc 存储过程和存储函数</p>
<p>sp_cache.cc</p>
<p>sp_head.cc</p>
<p>sp_pcontext.cc</p>
<p>sp_rcontext.cc</p>
<p>Spatial.cc 集合函数，点线面</p>
<p>Sql_acl.cc ACL</p>
<p>sql_analyse.cc</p>
<p>sql_base.cc 基础函数</p>
<p>sql_cache.cc 查询缓存</p>
<p>sql_client.cc</p>
<p>sql_crypt.cc 加解密</p>
<p>sql_db.cc 创建、删除DB</p>
<p>sql_delete.cc DELETE语句</p>
<p>sql_derived.cc 派生表</p>
<p>sql_do.cc DO</p>
<p>sql_error.cc  错误和警告</p>
<p>sql_handler.cc</p>
<p>sql_help.cc HELP</p>
<p>sql_insert.cc INSERT</p>
<p>sql_lex.cc <a href="http://ourmysql.com/archives/tag/%e8%af%8d%e6%b3%95%e5%88%86%e6%9e%90" class="st_tag internal_tag" rel="tag" title="标签 词法分析 下的日志">词法分析</a></p>
<p>sql_list.cc</p>
<p>sql_load.cc LOAD DATA 语句</p>
<p>sql_manager.cc 维护工作</p>
<p>sql_map.cc  内存映射</p>
<p>sql_olap.cc</p>
<p>sql_parse.cc 解析语句</p>
<p>sql_prepare.cc</p>
<p>sql_rename.cc 重命名table名</p>
<p>sql_repl.cc 复制</p>
<p>sql_select.cc SELECT和JOIN优化</p>
<p>sql_show.cc SHOW</p>
<p>sql_state.c 错误号和状态的映射</p>
<p>sql_string.cc</p>
<p>sql_table.cc DROP TABLE、ALTER TABLE</p>
<p>sql_trigger.cc 触发器</p>
<p>sql_udf.cc 用户自定义函数</p>
<p>sql_union.cc UNION操作符</p>
<p>sql_update.cc UPDATE</p>
<p>sql_view.cc 视图</p>
<p>Stacktrace.c 显示堆栈（LINUX/INTEL ONLY）</p>
<p>Strfunc.cc</p>
<p>Table.cc 表元数据获取（FRM）</p>
<p>thr_malloc.cc</p>
<p>Time.cc</p>
<p>Uniques.cc 副本的快速删除</p>
<p>Unireg.cc 创建一个FRM</p>
<p>更多内容请参考：</p>
<p><a href="http://forge.mysql.com/wiki/MySQL_Internals_Files_In_MySQL_Sources#The_sql_Directory">http://forge.mysql.com/wiki/MySQL_Internals_Files_In_MySQL_Sources#The_sql_Directory</a></p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a>, <a href="http://ourmysql.com/archives/tag/%e7%9b%ae%e5%bd%95%e7%bb%93%e6%9e%84" title="目录结构" rel="tag">目录结构</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/8bBA1je4Dk8jL8hTjW4MtHx1YZ4/0/da"><img src="http://feedads.g.doubleclick.net/~a/8bBA1je4Dk8jL8hTjW4MtHx1YZ4/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/8bBA1je4Dk8jL8hTjW4MtHx1YZ4/1/da"><img src="http://feedads.g.doubleclick.net/~a/8bBA1je4Dk8jL8hTjW4MtHx1YZ4/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1095/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习——打造专属语法</title>
		<link>http://ourmysql.com/archives/1092</link>
		<comments>http://ourmysql.com/archives/1092#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:32:50 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[YACC]]></category>
		<category><![CDATA[源码]]></category>
		<category><![CDATA[语法]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1092</guid>
		<description><![CDATA[接触过SQL语句的人都会看过这家或者那家的SQL手册，其语法标准应该是从SQL92开始吧，在看SQL92标准的时候，你会发现里面定义的都是一些巴科斯范式(BNF)，就是一种语法定义的标准。不管是牛X哄哄的ORACLE，还是不幸被其收购的Mysql，都会遵循里面的标准语法，当然一些扩展的语法除外，比如今天我们就会扩展一个简单的语法^-^。]]></description>
			<content:encoded><![CDATA[<p>接触过SQL语句的人都会看过这家或者那家的SQL手册，其语法标准应该是从SQL92开始吧，在看SQL92标准的时候，你会发现里面定义的都是一些巴 科斯范式(BNF)，就是一种语法定义的标准。不管是牛X哄哄的ORACLE，还是不幸被其收购的Mysql，都会遵循里面的标准语法，当然一些扩展的语 法除外，比如今天我们就会扩展一个简单的语法^-^。</p>
<p align="left">         OK，大家知道了SQL语法的来源，那么如何进行语法解析呢？<a href="http://ourmysql.com/archives/tag/yacc" class="st_tag internal_tag" rel="tag" title="标签 YACC 下的日志">YACC</a>！！(Yet Another Compiler Compiler)，它的书写方式便是BNF，语法解析的利器。YACC接收来自词法分析阶段分解出来的token，然后去匹配那些BNF。今天哥就来揭 开它的面纱。（关于YACC的基本使用方法，大家可以看我上一篇中提到IBM的链接，一定要看懂那个先）</p>
<p align="left">         继续上一节的语句SELECT @@VERSION_COMMET,为了简单，这里省去后缀limit 1。Mysql的语法文件是sql_yacc.yy,首先给出这条语句涉及到的语法节点（大体浏览下即可）:</p>
<blockquote>
<div>
<div id="highlighter_672907">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
<div>21</div>
<div>22</div>
<div>23</div>
<div>24</div>
<div>25</div>
<div>26</div>
<div>27</div>
<div>28</div>
<div>29</div>
<div>30</div>
<div>31</div>
<div>32</div>
<div>33</div>
<div>34</div>
<div>35</div>
<div>36</div>
<div>37</div>
<div>38</div>
<div>39</div>
<div>40</div>
<div>41</div>
<div>42</div>
<div>43</div>
<div>44</div>
<div>45</div>
<div>46</div>
<div>47</div>
<div>48</div>
<div>49</div>
<div>50</div>
<div>51</div>
<div>52</div>
<div>53</div>
<div>54</div>
<div>55</div>
<div>56</div>
<div>57</div>
<div>58</div>
<div>59</div>
<div>60</div>
<div>61</div>
<div>62</div>
<div>63</div>
<div>64</div>
<div>65</div>
<div>66</div>
<div>67</div>
<div>68</div>
<div>69</div>
<div>70</div>
<div>71</div>
<div>72</div>
<div>73</div>
<div>74</div>
<div>75</div>
<div>76</div>
<div>77</div>
<div>78</div>
<div>79</div>
<div>80</div>
<div>81</div>
<div>82</div>
<div>83</div>
<div>84</div>
<div>85</div>
<div>86</div>
<div>87</div>
<div>88</div>
<div>89</div>
<div>90</div>
<div>91</div>
<div>92</div>
<div>93</div>
<div>94</div>
<div>95</div>
<div>96</div>
<div>97</div>
<div>98</div>
<div>99</div>
<div>100</div>
<div>101</div>
<div>102</div>
<div>103</div>
<div>104</div>
<div>105</div>
<div>106</div>
<div>107</div>
<div>108</div>
<div>109</div>
<div>110</div>
<div>111</div>
<div>112</div>
<div>113</div>
<div>114</div>
<div>115</div>
<div>116</div>
<div>117</div>
<div>118</div>
</td>
<td>
<div>
<div><code>query:</code></div>
<div><code>END_OF_INPUT</code></div>
<div><code>{...}</code></div>
<div><code>|| verb_clause</code></div>
<div><code>{...}</code></div>
<div><code>| verb_clause END_OF_INPUT</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>/* Single query, not terminated. */</code></div>
<div><code>            </code><code>YYLIP-&gt;found_semicolon= NULL;</code></div>
<div><code>          </code><code>}</code></div>
<div></div>
<div><code>verb_clause:</code></div>
<div><code>          </code><code>statement</code></div>
<div><code>        </code><code>| begin</code></div>
<div><code>        </code><code>;</code></div>
<div></div>
<div><code>statement:</code></div>
<div><code>          </code><code>alter</code></div>
<div><code>        </code><code>| analyze</code></div>
<div><code>        </code><code>| backup</code></div>
<div><code>        </code><code>| binlog_base64_event</code></div>
<div><code>        </code><code>| call</code></div>
<div><code>        </code><code>| change</code></div>
<div><code>        </code><code>| check</code></div>
<div><code>        </code><code>| checksum</code></div>
<div><code>        </code><code>| commit</code></div>
<div><code>        </code><code>| create</code></div>
<div><code>        </code><code>| deallocate</code></div>
<div><code>        </code><code>| </code><code>delete</code></div>
<div><code>        </code><code>| describe</code></div>
<div><code>        </code><code>| </code><code>do</code></div>
<div><code>        </code><code>| drop</code></div>
<div><code>        </code><code>| execute</code></div>
<div><code>        </code><code>| flush</code></div>
<div><code>        </code><code>| grant</code></div>
<div><code>        </code><code>| handler</code></div>
<div><code>        </code><code>| help</code></div>
<div><code>        </code><code>| insert</code></div>
<div><code>        </code><code>| install</code></div>
<div><code>        </code><code>| kill</code></div>
<div><code>        </code><code>| load</code></div>
<div><code>        </code><code>| lock</code></div>
<div><code>        </code><code>| optimize</code></div>
<div><code>        </code><code>| keycache</code></div>
<div><code>        </code><code>| partition_entry</code></div>
<div><code>        </code><code>| preload</code></div>
<div><code>        </code><code>| prepare</code></div>
<div><code>        </code><code>| purge</code></div>
<div><code>        </code><code>| release</code></div>
<div><code>        </code><code>| </code><code>rename</code></div>
<div><code>        </code><code>| repair</code></div>
<div><code>        </code><code>| replace</code></div>
<div><code>        </code><code>| reset</code></div>
<div><code>        </code><code>| restore</code></div>
<div><code>        </code><code>| revoke</code></div>
<div><code>        </code><code>| rollback</code></div>
<div><code>        </code><code>| savepoint</code></div>
<div><code>        </code><code>| select</code></div>
<div><code>        </code><code>| set</code></div>
<div><code>        </code><code>| show</code></div>
<div><code>        </code><code>| slave</code></div>
<div><code>        </code><code>| start</code></div>
<div><code>        </code><code>| truncate</code></div>
<div><code>        </code><code>| uninstall</code></div>
<div><code>        </code><code>| unlock</code></div>
<div><code>        </code><code>| update</code></div>
<div><code>        </code><code>| use</code></div>
<div><code>        </code><code>| xa</code></div>
<div><code>        </code><code>;</code></div>
<div></div>
<div><code>select:</code></div>
<div><code>          </code><code>select_init</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code><a href="http://ourmysql.com/archives/tag/lex" class="st_tag internal_tag" rel="tag" title="标签 LEX 下的日志">LEX</a> *<a href="http://ourmysql.com/archives/tag/lex" class="st_tag internal_tag" rel="tag" title="标签 LEX 下的日志">lex</a>= <a href="http://ourmysql.com/archives/tag/lex" class="st_tag internal_tag" rel="tag" title="标签 LEX 下的日志">Lex</a>;</code></div>
<div><code>            </code><code>lex-&gt;sql_command= SQLCOM_SELECT;</code></div>
<div><code>          </code><code>}</code></div>
<div><code>        </code><code>;</code></div>
<div></div>
<div><code>select_init:</code></div>
<div><code>          </code><code>SELECT_SYM select_init2</code></div>
<div><code>        </code><code>| </code><code>'('</code> <code>select_paren </code><code>')'</code> <code>union_opt</code></div>
<div><code>        </code><code>;</code></div>
<div></div>
<div></div>
<div><code>select_init2:</code></div>
<div><code>          </code><code>select_part2</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>LEX *lex= Lex;</code></div>
<div><code>            </code><code>SELECT_LEX * sel= lex-&gt;current_select;</code></div>
<div><code>            </code><code>if</code> <code>(lex-&gt;current_select-&gt;set_braces(0))</code></div>
<div><code>            </code><code>{</code></div>
<div><code>              </code><code>my_parse_error(ER(ER_SYNTAX_ERROR));</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>}</code></div>
<div><code>            </code><code>if</code> <code>(sel-&gt;linkage == UNION_TYPE &amp;&amp;</code></div>
<div><code>                </code><code>sel-&gt;master_unit()-&gt;first_select()-&gt;braces)</code></div>
<div><code>            </code><code>{</code></div>
<div><code>              </code><code>my_parse_error(ER(ER_SYNTAX_ERROR));</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>}</code></div>
<div><code>          </code><code>}</code></div>
<div><code>          </code><code>union_clause</code></div>
<div><code>        </code><code>;</code></div>
<div></div>
<div><code>select_part2:</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>LEX *lex= Lex;</code></div>
<div><code>            </code><code>SELECT_LEX *sel= lex-&gt;current_select;</code></div>
<div><code>            </code><code>if</code> <code>(sel-&gt;linkage != UNION_TYPE)</code></div>
<div><code>              </code><code>mysql_init_select(lex);</code></div>
<div><code>            </code><code>lex-&gt;current_select-&gt;parsing_place= SELECT_LIST;</code></div>
<div><code>          </code><code>}</code></div>
<div><code>          </code><code>select_options select_item_list</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>Select-&gt;parsing_place= NO_MATTER;</code></div>
<div><code>          </code><code>}</code></div>
<div><code>          </code><code>select_into select_lock_type</code></div>
<div><code>        </code><code>;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</blockquote>
<blockquote>
<div>
<div id="highlighter_892832">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
<div>21</div>
<div>22</div>
<div>23</div>
<div>24</div>
<div>25</div>
<div>26</div>
<div>27</div>
<div>28</div>
<div>29</div>
<div>30</div>
<div>31</div>
<div>32</div>
<div>33</div>
<div>34</div>
<div>35</div>
<div>36</div>
<div>37</div>
<div>38</div>
<div>39</div>
<div>40</div>
<div>41</div>
<div>42</div>
<div>43</div>
<div>44</div>
<div>45</div>
<div>46</div>
<div>47</div>
<div>48</div>
<div>49</div>
<div>50</div>
<div>51</div>
<div>52</div>
<div>53</div>
<div>54</div>
<div>55</div>
<div>56</div>
<div>57</div>
<div>58</div>
<div>59</div>
<div>60</div>
<div>61</div>
<div>62</div>
<div>63</div>
<div>64</div>
<div>65</div>
<div>66</div>
<div>67</div>
<div>68</div>
<div>69</div>
<div>70</div>
<div>71</div>
<div>72</div>
<div>73</div>
<div>74</div>
<div>75</div>
<div>76</div>
<div>77</div>
<div>78</div>
<div>79</div>
<div>80</div>
<div>81</div>
<div>82</div>
<div>83</div>
<div>84</div>
<div>85</div>
<div>86</div>
<div>87</div>
<div>88</div>
<div>89</div>
<div>90</div>
<div>91</div>
</td>
<td>
<div>
<div><code>select_item_list:</code></div>
<div><code>          </code><code>select_item_list </code><code>','</code> <code>select_item</code></div>
<div><code>        </code><code>| select_item</code></div>
<div><code>        </code><code>| </code><code>'*'</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>THD *thd= YYTHD;</code></div>
<div><code>            </code><code>Item *item= </code><code>new</code> <code>(thd-&gt;mem_root)</code></div>
<div><code>                          </code><code>Item_field(&amp;thd-&gt;lex-&gt;current_select-&gt;context,</code></div>
<div><code>                                     </code><code>NULL, NULL, </code><code>"*"</code><code>);</code></div>
<div><code>            </code><code>if</code> <code>(item == NULL)</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>if</code> <code>(add_item_to_list(thd, item))</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>(thd-&gt;lex-&gt;current_select-&gt;with_wild)++;</code></div>
<div><code>          </code><code>}</code></div>
<div><code>        </code><code>;</code></div>
<div></div>
<div><code>select_item:</code></div>
<div><code>          </code><code>remember_name select_item2 remember_end select_alias</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>THD *thd= YYTHD;</code></div>
<div><code>            </code><code><a href="http://ourmysql.com/archives/tag/dbug" class="st_tag internal_tag" rel="tag" title="标签 DBUG 下的日志">DBUG</a>_ASSERT($1 &lt; $3);</code></div>
<div></div>
<div><code>            </code><code>if</code> <code>(add_item_to_list(thd, $2))</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>if</code> <code>($4.str)</code></div>
<div><code>            </code><code>{</code></div>
<div><code>              </code><code>if</code> <code>(Lex-&gt;sql_command == SQLCOM_CREATE_VIEW &amp;&amp;</code></div>
<div><code>                  </code><code>check_column_name($4.str))</code></div>
<div><code>              </code><code>{</code></div>
<div><code>                </code><code>my_error(ER_WRONG_COLUMN_NAME, MYF(0), $4.str);</code></div>
<div><code>                </code><code>MYSQL_YYABORT;</code></div>
<div><code>              </code><code>}</code></div>
<div><code>              </code><code>$2-&gt;is_autogenerated_name= FALSE;</code></div>
<div><code>              </code><code>$2-&gt;set_name($4.str, $4.length, system_charset_info);</code></div>
<div><code>            </code><code>}</code></div>
<div><code>            </code><code>else</code> <code>if</code> <code>(!$2-&gt;name)</code></div>
<div><code>            </code><code>{</code></div>
<div><code>              </code><code>$2-&gt;set_name($1, (uint) ($3 - $1), thd-&gt;charset());</code></div>
<div><code>            </code><code>}</code></div>
<div><code>          </code><code>}</code></div>
<div><code>        </code><code>;</code></div>
<div></div>
<div><code>variable:</code></div>
<div><code>          </code><code>'@'</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>if</code> <code>(! Lex-&gt;parsing_options.allows_variable)</code></div>
<div><code>            </code><code>{</code></div>
<div><code>              </code><code>my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>}</code></div>
<div><code>          </code><code>}</code></div>
<div><code>          </code><code>variable_aux</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>$$= $3;</code></div>
<div><code>          </code><code>}</code></div>
<div><code>        </code><code>;</code></div>
<div></div>
<div><code>variable_aux:</code></div>
<div><code>          </code><code>ident_or_text SET_VAR expr</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>Item_func_set_user_var *item;</code></div>
<div><code>            </code><code>$$= item= </code><code>new</code> <code>(YYTHD-&gt;mem_root) Item_func_set_user_var($1, $3);</code></div>
<div><code>            </code><code>if</code> <code>($$ == NULL)</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>LEX *lex= Lex;</code></div>
<div><code>            </code><code>lex-&gt;uncacheable(UNCACHEABLE_RAND);</code></div>
<div><code>            </code><code>lex-&gt;set_var_list.push_back(item);</code></div>
<div><code>          </code><code>}</code></div>
<div><code>        </code><code>| ident_or_text</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>$$= </code><code>new</code> <code>(YYTHD-&gt;mem_root) Item_func_get_user_var($1);</code></div>
<div><code>            </code><code>if</code> <code>($$ == NULL)</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>LEX *lex= Lex;</code></div>
<div><code>            </code><code>lex-&gt;uncacheable(UNCACHEABLE_RAND);</code></div>
<div><code>          </code><code>}</code></div>
<div><code>        </code><code>| </code><code>'@'</code> <code>opt_var_ident_type ident_or_text opt_component</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>/* disallow "SELECT @@global.global.variable" */</code></div>
<div><code>            </code><code>if</code> <code>($3.str &amp;&amp; $4.str &amp;&amp; check_reserved_words(&amp;$3))</code></div>
<div><code>            </code><code>{</code></div>
<div><code>              </code><code>my_parse_error(ER(ER_SYNTAX_ERROR));</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>}</code></div>
<div><code>            </code><code>if</code> <code>(!($$= get_system_var(YYTHD, $2, $3, $4)))</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>if</code> <code>(!((Item_func_get_system_var*) $$)-&gt;is_written_to_binlog())</code></div>
<div><code>              </code><code>Lex-&gt;set_stmt_unsafe();</code></div>
<div><code>          </code><code>}</code></div>
<div><code>        </code><code>;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</blockquote>
<blockquote><p>下面我们仔细的来看一下整个SELECT语法节点的执行流程：</p></blockquote>
<blockquote>
<div>
<div id="highlighter_182877">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div><code>query-&gt;verb_clause-&gt;statement-&gt;select-&gt;select_init-&gt;select_init2-&gt;select_part2-&gt;select_item_list-&gt;select_item…-&gt;variable</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</blockquote>
<blockquote><p>语法是自上而下的，实际的解析过程是自下而上的匹配过程。词法分析首先yacc送来SELECT关键字，上一节说过为什么SELECT是关键字呢？</p></blockquote>
<blockquote><p>我们看下sql_yacc.yy，可以找到如下一个定义：</p>
<div>
<div id="highlighter_367345">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div><code>%token  SELECT_SYM                    </code><code>/* SQL-2003-R */</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>这里其实是定义了一个宏SELECT_SYM，代表一个关键字，宏定义如下：</p>
<div>
<div id="highlighter_465497">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div><code>#define SELECT_SYM 687</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>那么字符串”SELECT”和SELECT_SYM是如何联系在一起的呢？我们回头看下MYSQLlex中的find_keyword这个函数：</p>
<div>
<div id="highlighter_269430">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
<div>21</div>
<div>22</div>
<div>23</div>
<div>24</div>
<div>25</div>
<div>26</div>
<div>27</div>
<div>28</div>
<div>29</div>
<div>30</div>
<div>31</div>
</td>
<td>
<div>
<div><code>static</code> <code>int</code> <code>find_keyword(Lex_input_stream *lip, uint len, </code><code>bool</code> <code>function)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>const</code> <code>char</code> <code>*tok= lip-&gt;get_tok_start();</code></div>
<div></div>
<div><code>  </code><code>SYMBOL *symbol= get_<a href="http://ourmysql.com/archives/tag/hash" class="st_tag internal_tag" rel="tag" title="标签 Hash 下的日志">hash</a>_symbol(tok, len, function);</code></div>
<div><code>  </code><code>if</code> <code>(symbol)</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>lip-&gt;yylval-&gt;symbol.symbol=symbol;</code></div>
<div><code>    </code><code>lip-&gt;yylval-&gt;symbol.str= (</code><code>char</code><code>*) tok;</code></div>
<div><code>    </code><code>lip-&gt;yylval-&gt;symbol.length=len;</code></div>
<div></div>
<div><code>    </code><code>if</code> <code>((symbol-&gt;tok == NOT_SYM) &amp;&amp;</code></div>
<div><code>        </code><code>(lip-&gt;m_thd-&gt;variables.sql_mode &amp; MODE_HIGH_NOT_PRECEDENCE))</code></div>
<div><code>      </code><code>return</code> <code>NOT2_SYM;</code></div>
<div><code>    </code><code>if</code> <code>((symbol-&gt;tok == OR_OR_SYM) &amp;&amp;</code></div>
<div><code>    </code><code>!(lip-&gt;m_thd-&gt;variables.sql_mode &amp; MODE_PIPES_AS_CONCAT))</code></div>
<div><code>      </code><code>return</code> <code>OR2_SYM;</code></div>
<div></div>
<div><code>    </code><code>return</code> <code>symbol-&gt;tok;</code></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>return</code> <code>0;</code></div>
<div><code>}</code></div>
<div></div>
<div><code>static</code> <code>SYMBOL *get_hash_symbol(</code><code>const</code> <code>char</code> <code>*s,</code></div>
<div><code>                               </code><code>unsigned </code><code>int</code> <code>len,</code><code>bool</code> <code>function)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>register</code> <code>uchar *hash_map;</code></div>
<div><code>  </code><code>register</code> <code>const</code> <code>char</code> <code>*cur_str= s;</code></div>
<div></div>
<div><code>  </code><code>if</code> <code>(len == 0) {</code></div>
<div><code>    </code><code>DBUG_PRINT(</code><code>"warning"</code><code>, (</code><code>"get_hash_symbol() received a request for a zero-length symbol, which is probably a mistake."</code><code>));</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div id="highlighter_300472">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
<div>21</div>
<div>22</div>
<div>23</div>
<div>24</div>
<div>25</div>
<div>26</div>
<div>27</div>
<div>28</div>
<div>29</div>
<div>30</div>
<div>31</div>
<div>32</div>
<div>33</div>
<div>34</div>
<div>35</div>
<div>36</div>
<div>37</div>
<div>38</div>
<div>39</div>
<div>40</div>
<div>41</div>
<div>42</div>
<div>43</div>
<div>44</div>
<div>45</div>
<div>46</div>
<div>47</div>
<div>48</div>
<div>49</div>
<div>50</div>
<div>51</div>
<div>52</div>
<div>53</div>
<div>54</div>
<div>55</div>
<div>56</div>
<div>57</div>
<div>58</div>
<div>59</div>
<div>60</div>
<div>61</div>
</td>
<td>
<div>
<div><code>    </code><code>return</code><code>(NULL);</code></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>if</code> <code>(function){</code></div>
<div><code>    </code><code>if</code> <code>(len&gt;sql_functions_max_len) </code><code>return</code> <code>0;</code></div>
<div><code>    </code><code>hash_map= sql_functions_map;</code></div>
<div><code>    </code><code>register</code> <code>uint32 cur_struct= uint4korr(hash_map+((len-1)*4));</code></div>
<div></div>
<div><code>    </code><code>for</code> <code>(;;){</code></div>
<div><code>      </code><code>register</code> <code>uchar first_char= (uchar)cur_struct;</code></div>
<div></div>
<div><code>      </code><code>if</code> <code>(first_char == 0)</code></div>
<div><code>      </code><code>{</code></div>
<div><code>        </code><code>register</code> <code>int16 ires= (int16)(cur_struct&gt;&gt;16);</code></div>
<div><code>        </code><code>if</code> <code>(ires==array_elements(symbols)) </code><code>return</code> <code>0;</code></div>
<div><code>        </code><code>register</code> <code>SYMBOL *res;</code></div>
<div><code>        </code><code>if</code> <code>(ires&gt;=0) </code></div>
<div><code>          </code><code>res= symbols+ires;</code></div>
<div><code>        </code><code>else</code></div>
<div><code>          </code><code>res= sql_functions-ires-1;</code></div>
<div><code>          </code><code>register</code> <code>uint count= (uint) (cur_str - s);</code></div>
<div><code>        </code><code>return</code> <code>lex_casecmp(cur_str,res-&gt;name+count,len-count) ? 0 : res;</code></div>
<div><code>      </code><code>}</code></div>
<div></div>
<div><code>      </code><code>register</code> <code>uchar cur_char= (uchar)to_upper_lex[(uchar)*cur_str];</code></div>
<div><code>      </code><code>if</code> <code>(cur_char&lt;first_char) </code><code>return</code> <code>0;</code></div>
<div><code>      </code><code>cur_struct&gt;&gt;=8;</code></div>
<div><code>      </code><code>if</code> <code>(cur_char&gt;(uchar)cur_struct) </code><code>return</code> <code>0;</code></div>
<div></div>
<div><code>      </code><code>cur_struct&gt;&gt;=8;</code></div>
<div><code>      </code><code>cur_struct= uint4korr(hash_map+</code></div>
<div><code>                        </code><code>(((uint16)cur_struct + cur_char - first_char)*4));</code></div>
<div><code>      </code><code>cur_str++;</code></div>
<div><code>    </code><code>}</code></div>
<div><code>  </code><code>}</code><code>else</code><code>{</code></div>
<div><code>    </code><code>if</code> <code>(len&gt;symbols_max_len) </code><code>return</code> <code>0;</code></div>
<div><code>    </code><code>hash_map= symbols_map;</code></div>
<div><code>    </code><code>register</code> <code>uint32 cur_struct= uint4korr(hash_map+((len-1)*4));</code></div>
<div></div>
<div><code>    </code><code>for</code> <code>(;;){</code></div>
<div><code>      </code><code>register</code> <code>uchar first_char= (uchar)cur_struct;</code></div>
<div></div>
<div><code>      </code><code>if</code> <code>(first_char==0){</code></div>
<div><code>        </code><code>register</code> <code>int16 ires= (int16)(cur_struct&gt;&gt;16);</code></div>
<div><code>        </code><code>if</code> <code>(ires==array_elements(symbols)) </code><code>return</code> <code>0;</code></div>
<div><code>        </code><code>register</code> <code>SYMBOL *res= symbols+ires;</code></div>
<div><code>        </code><code>register</code> <code>uint count= (uint) (cur_str - s);</code></div>
<div><code>        </code><code>return</code> <code>lex_casecmp(cur_str,res-&gt;name+count,len-count)!=0 ? 0 : res;</code></div>
<div><code>      </code><code>}</code></div>
<div></div>
<div><code>      </code><code>register</code> <code>uchar cur_char= (uchar)to_upper_lex[(uchar)*cur_str];</code></div>
<div><code>      </code><code>if</code> <code>(cur_char&lt;first_char) </code><code>return</code> <code>0;</code></div>
<div><code>      </code><code>cur_struct&gt;&gt;=8;</code></div>
<div><code>      </code><code>if</code> <code>(cur_char&gt;(uchar)cur_struct) </code><code>return</code> <code>0;</code></div>
<div></div>
<div><code>      </code><code>cur_struct&gt;&gt;=8;</code></div>
<div><code>      </code><code>cur_struct= uint4korr(hash_map+</code></div>
<div><code>                        </code><code>(((uint16)cur_struct + cur_char - first_char)*4));</code></div>
<div><code>      </code><code>cur_str++;</code></div>
<div><code>    </code><code>}</code></div>
<div><code>  </code><code>}</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>其中的get_hash_symbol便是去系统中查找关键字，第三个参数function代表是否去查找系统函数，我们这里是系统变量，不是函数，故为FALSE。所有的关键字都挂在了hash_map上，即symbols_map上。symbols_maps又是一堆处理过的数据：</p>
<div>
<div id="highlighter_667470">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
</td>
<td>
<div>
<div><code>static</code> <code>uchar symbols_map[11828]= {</code></div>
<div><code>'&lt;'</code><code>, </code><code>'&gt;'</code><code>, 29, 0,</code></div>
<div><code>'!'</code><code>, </code><code>'|'</code><code>, 32, 0,</code></div>
<div><code>'&lt;'</code><code>, </code><code>'X'</code><code>, 150, 0,</code></div>
<div><code>'B'</code><code>, </code><code>'Y'</code><code>, 11, 1,</code></div>
<div><code>'A'</code><code>, </code><code>'W'</code><code>, 147, 2,</code></div>
<div><code>'A'</code><code>, </code><code>'V'</code><code>, 0, 4,</code></div>
<div><code>...</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>看一下这个文件的最上面的注释吧，看看有啥有用的信息，果然被找到了：</p>
<div>
<div id="highlighter_632445">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
</td>
<td>
<div>
<div><code>/* Do not edit this file!  This is generated by gen_lex_hash.cc</code></div>
<div><code>that seeks for a perfect hash function */</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>看到了这个注释，心中豁然开朗，原来lex_hash.h是由gen_lex_hash.cc进行生成的，大家千万不要自己进行编辑此文件啊！！</p>
<p>来gen_lex_hash.cc看下吧，看到了个main函数，里面是一些生成文件的操作，在generate_find_structs函数中找到了insert_symbols，</p>
<p>这应该是初始化我们的symbols_map数组了吧。</p>
<div>
<div id="highlighter_994853">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
</td>
<td>
<div>
<div><code>void</code> <code>insert_symbols()</code></div>
<div><code>{</code></div>
<div><code>  </code><code>size_t</code> <code>i= 0;</code></div>
<div><code>  </code><code>SYMBOL *cur;</code></div>
<div><code>  </code><code>for</code> <code>(cur= symbols; i&lt;array_elements(symbols); cur++, i++){</code></div>
<div><code>    </code><code>hash_lex_struct *root= </code></div>
<div><code>      </code><code>get_hash_struct_by_len(&amp;root_by_len,cur-&gt;length,&amp;max_len);</code></div>
<div><code>    </code><code>insert_into_hash(root,cur-&gt;name,0,(uint) i,0);</code></div>
<div><code>  </code><code>}</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>看到函数的实现是循环取数组symbols,找到symbols定义，在文件lex.h中，看到这个数组，我想大家就会了然了:</p>
<div>
<div id="highlighter_208548">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div><code>{ </code><code>"SELECT"</code><code>,     SYM(SELECT_SYM)},</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>这就是将SELECT字符串与SELECT_SYM关联的地方了，bingo！</p>
<p>我们再来捋一下SELECT解析的思路，词法分析解析到SELECT后，执行find_keyword去找是否是关键字，发现SELECT是关键字，</p>
<p>于是给yacc返回SELECT_SYM用于语法分析。note：如果我们想要加关键字，只需在sql_yacc.yy上面添加一个%token xxx,</p>
<p>然后在lex.h里面加入相应的字符串和SYM的对应即可。</p>
<p>下面看下@@version_comment这个系统变量如何解析的，首先给出其语法节点：</p>
<div>
<div id="highlighter_679580">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
</td>
<td>
<div>
<div><code>variable_aux:</code></div>
<div><code>...</code></div>
<div><code>  </code><code>| </code><code>'@'</code> <code>opt_var_ident_type ident_or_text opt_component</code></div>
<div><code>          </code><code>{</code></div>
<div><code>            </code><code>/* disallow "SELECT @@global.global.variable" */</code></div>
<div><code>            </code><code>if</code> <code>($3.str &amp;&amp; $4.str &amp;&amp; check_reserved_words(&amp;$3))</code></div>
<div><code>            </code><code>{</code></div>
<div><code>              </code><code>my_parse_error(ER(ER_SYNTAX_ERROR));</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>}</code></div>
<div><code>            </code><code>if</code> <code>(!($$= get_system_var(YYTHD, $2, $3, $4)))</code></div>
<div><code>              </code><code>MYSQL_YYABORT;</code></div>
<div><code>            </code><code>if</code> <code>(!((Item_func_get_system_var*) $$)-&gt;is_written_to_binlog())</code></div>
<div><code>              </code><code>Lex-&gt;set_stmt_unsafe();</code></div>
<div><code>          </code><code>}</code></div>
<div><code>        </code><code>;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>这里便是查找系统变量的地方了：get_system_var，我们跟进去看下：</p>
<div>
<div id="highlighter_533199">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
<div>21</div>
<div>22</div>
<div>23</div>
<div>24</div>
<div>25</div>
<div>26</div>
<div>27</div>
<div>28</div>
<div>29</div>
<div>30</div>
<div>31</div>
<div>32</div>
<div>33</div>
<div>34</div>
</td>
<td>
<div>
<div><code>Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,</code></div>
<div><code>             </code><code>LEX_STRING component)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>sys_var *var;</code></div>
<div><code>  </code><code>LEX_STRING *base_name, *component_name;</code></div>
<div></div>
<div><code>  </code><code>if</code> <code>(component.str)</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>base_name= &amp;component;</code></div>
<div><code>    </code><code>component_name= &amp;name;</code></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>else</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>base_name= &amp;name;</code></div>
<div><code>    </code><code>component_name= &amp;component;         </code><code>// Empty string</code></div>
<div><code>  </code><code>}</code></div>
<div></div>
<div><code>  </code><code>if</code> <code>(!(var= find_sys_var(thd, base_name-&gt;str, base_name-&gt;length)))</code></div>
<div><code>    </code><code>return</code> <code>0;</code></div>
<div><code>  </code><code>if</code> <code>(component.str)</code></div>
<div><code>  </code><code>{</code></div>
<div><code>    </code><code>if</code> <code>(!var-&gt;is_struct())</code></div>
<div><code>    </code><code>{</code></div>
<div><code>      </code><code>my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name-&gt;str);</code></div>
<div><code>      </code><code>return</code> <code>0;</code></div>
<div><code>    </code><code>}</code></div>
<div><code>  </code><code>}</code></div>
<div><code>  </code><code>thd-&gt;lex-&gt;uncacheable(UNCACHEABLE_SIDEEFFECT);</code></div>
<div></div>
<div><code>  </code><code>set_if_smaller(component_name-&gt;length, MAX_SYS_VAR_LENGTH);</code></div>
<div></div>
<div><code>  </code><code>return</code> <code>new</code> <code>Item_func_get_system_var(var, var_type, component_name,</code></div>
<div><code>                                      </code><code>NULL, 0);</code></div>
<div><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>由find_sys_var函数不断跟进去，我们跟到了set_var.cc，找到了如下定义：</p>
<div>
<div id="highlighter_572753">
<div><a href="http://www.cnblogs.com/nocode/archive/2011/08/09/2132814.html#">?</a></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div><code>static</code> <code>sys_var_chain vars = { NULL, NULL };</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>系统变量都会挂载在次链上。在文件中，搜索到了version_comment:</p>
<div>
<div id="highlighter_913095">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
</td>
<td>
<div>
<div><code>static</code> <code>sys_var_const_str    sys_version_comment(&amp;vars, </code><code>"version_comment"</code><code>,</code></div>
<div><code>                                            </code><code>MYSQL_COMPILATION_COMMENT);</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div id="highlighter_591320">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div><code>#define MYSQL_COMPILATION_COMMENT   "Source distribution"</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>这便是将version_comment加载到vars的链表上。</p>
<p>OK，我们也来加一个自己的系统变量：</p>
<div>
<div id="highlighter_97256">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</td>
<td>
<div>
<div><code>static</code> <code>sys_var_const_str    sys_version_comment(&amp;vars, </code><code>"version_comment"</code><code>,</code></div>
<div><code>                                            </code><code>MYSQL_COMPILATION_COMMENT);</code></div>
<div></div>
<div><code>/**add by nocode */</code></div>
<div><code>static</code> <code>sys_var_const_str    sys_version_comment_test(&amp;vars, </code><code>"nocode_test_sysvar"</code><code>,</code></div>
<div><code>                                            </code><code>MYSQL_COMPILATION_NOCODE_TEST_SYSVAR);</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>#define MYSQL_COMPILATION_COMMENT    “Source distribution”<br />
#define MYSQL_COMPILATION_NOCODE_TEST_SYSVAR  “No code in heart”    /*add by nocode*/</p>
<div>
<div id="highlighter_522330">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div><code> </code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>注释add by nocode的地方，即是新添加的系统变量和宏定义，我们的系统变量叫@@nocode_test_sysvar,其值为No code in heartOK，重新编译代码，执行SELECT语句，OK了。</p>
<div>
<div id="highlighter_434984">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
</td>
<td>
<div>
<div><code>mysql&gt; </code><code>select</code> <code>@@nocode_test_sysvar;</code></div>
<div><code>+</code><code>----------------------+</code></div>
<div><code>| @@nocode_test_sysvar |</code></div>
<div><code>+</code><code>----------------------+</code></div>
<div><code>| </code><code>No</code> <code>code </code><code>in</code> <code>heart     |</code></div>
<div><code>+</code><code>----------------------+</code></div>
<div><code>1 row </code><code>in</code> <code>set</code> <code>(0.01 sec)</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>上面添加了一个系统变量，并没有修改语法文件sql_yacc.yy,为了加深理解，我们添加一个属于自己的语法：nocode语法，为了简单化实现，我们的目标很简单，在客户端输入no_code后显示字符串”MAKE BY NOCODE”。</p>
<ul>
<li><strong>定义关键字</strong></li>
</ul>
<p>首先在sql_yacc.yy文件中添加相应的SYMBOL</p>
<div>
<div id="highlighter_914162">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
</td>
<td>
<div>
<div><code>%token  NO_SYM                        /* SQL-2003-R */</code></div>
<div><code>%token  NO_CODE_SYM                   /* </code><code>add</code> <code>by</code> <code>nocode*/</code></div>
<div><code>%token  NO_WAIT_SYM</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>然后在lex.h中的symblos数组中添加nocode的字符串和符号的对应关系：</p>
<div>
<div id="highlighter_415614">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
</td>
<td>
<div>
<div><code>{ </code><code>"NO"</code><code>,       SYM(NO_SYM)},</code></div>
<div><code>{ </code><code>"NO_CODE"</code><code>,      SYM(NO_CODE_SYM)}, /*</code><code>add</code> <code>by</code> <code>nocode*/</code></div>
<div><code>{ </code><code>"NO_WAIT"</code><code>,      SYM(NO_WAIT_SYM)},</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>ok,至此我们关键字已经添加进去了</p>
<ul>
<li><strong>添加语法节点</strong></li>
</ul>
<p>我们给语法分支节点起名叫nocode，定义如下：</p>
<div>
<div id="highlighter_780738">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
<div>21</div>
<div>22</div>
<div>23</div>
<div>24</div>
<div>25</div>
<div>26</div>
<div>27</div>
<div>28</div>
<div>29</div>
<div>30</div>
<div>31</div>
<div>32</div>
<div>33</div>
<div>34</div>
<div>35</div>
</td>
<td>
<div>
<div><code>/**</code><code>add</code> <code>by</code> <code>nocode*/</code></div>
<div><code>nocode:</code></div>
<div><code>        </code><code>NO_CODE_SYM</code></div>
<div><code>        </code><code>{</code></div>
<div><code>            </code><code>THD *thd= YYTHD;</code></div>
<div><code>            </code><code>LEX *lex= Lex;</code></div>
<div><code>            </code><code>SELECT_LEX *sel= lex-&gt;current_select;</code></div>
<div><code>            </code><code>Item_string* field;</code></div>
<div><code>            </code><code>LEX_STRING tmp;</code></div>
<div><code>            </code><code>CHARSET_INFO *cs_con= thd-&gt;variables.collation_connection;</code></div>
<div><code>            </code><code>CHARSET_INFO *cs_cli= thd-&gt;variables.character_set_client;</code></div>
<div></div>
<div><code>            </code><code>if (sel-&gt;linkage != UNION_TYPE)</code></div>
<div><code>                </code><code>mysql_init_select(lex);</code></div>
<div><code>            </code><code>lex-&gt;current_select-&gt;parsing_place= SELECT_LIST;</code></div>
<div></div>
<div><code>            </code><code>uint repertoire= thd-&gt;lex-&gt;text_string_is_7bit &amp;&amp;</code></div>
<div><code>                </code><code>my_charset_is_ascii_based(cs_cli) ? MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;</code></div>
<div></div>
<div><code>            </code><code>tmp.str = </code><code>"MAKE BY NOCODE"</code><code>;</code></div>
<div><code>            </code><code>tmp.length = strlen(tmp.str);</code></div>
<div></div>
<div><code>            </code><code>field= new (thd-&gt;mem_root) Item_string(tmp.str, tmp.length, cs_con,</code></div>
<div><code>                </code><code>DERIVATION_COERCIBLE,</code></div>
<div><code>                </code><code>repertoire);</code></div>
<div><code>            </code><code>if (field== </code><code>NULL</code><code>)</code></div>
<div><code>                </code><code>MYSQL_YYABORT;</code></div>
<div></div>
<div><code>            </code><code>if (add_item_to_list(thd, field))</code></div>
<div><code>                </code><code>MYSQL_YYABORT;</code></div>
<div></div>
<div><code>            </code><code>Select</code><code>-&gt;parsing_place= NO_MATTER;</code></div>
<div><code>            </code><code>lex-&gt;sql_command= SQLCOM_SELECT;</code></div>
<div><code>        </code><code>}</code></div>
<div><code>        </code><code>;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>最后要在statement的语法节点上加入nocode分支，我就不贴不来了。只要读到”no_code”便会进行进入这个语法分支。在这个分支里，做了一些操作，首先构造了一个SELECT类型的语句，然后对其添加了一列，这列的名称就是”MAKE BY NOCODE”…具体的细节大家自己研究吧，这都不是本文的重点。</p>
<p>语法添加完之后，我们重新编译项目，值得说明的是，Mysql还是项目组织还是非常好的，修改了语法文件之后，不需要我们自己去用bison编译，项目自动就帮我们编译好了，真是不错。重启服务器，在客户端输入no_code,结果如下：</p>
<div>
<div id="highlighter_895491">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
</td>
<td>
<div>
<div><code>mysql&gt; no_code;</code></div>
<div><code>+</code><code>----------------+</code></div>
<div><code>| MAKE </code><code>BY</code> <code>NOCODE |</code></div>
<div><code>+</code><code>----------------+</code></div>
<div><code>| MAKE </code><code>BY</code> <code>NOCODE |</code></div>
<div><code>+</code><code>----------------+</code></div>
<div><code>1 row </code><code>in</code> <code>set</code> <code>(3.02 sec)</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>语法分析到此结束。这里只添加了一个很简单的语法分支，没啥用处，主要是介绍下添加分支的步骤，大家添加分支的时候要尽量使用已有的分支，既减少劳动量，同时也会减少语法冲突。 唠叨两句，最近项目太紧张，压力山大，每晚都被噩梦惊醒，噩梦中总会想到算法的各种BUG，写个代码都提心吊胆的，哎，搞IT的真是悲催啊。PS 终于又更新了一篇，oh yeah，-_-ps again: 第一次用windows live writer写博客，感觉比网页方便多了～～，赞一个</p></blockquote>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/yacc" title="YACC" rel="tag">YACC</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a>, <a href="http://ourmysql.com/archives/tag/%e8%af%ad%e6%b3%95" title="语法" rel="tag">语法</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/82nLaJRpSNZmGH0rPAGFJXFha0Y/0/da"><img src="http://feedads.g.doubleclick.net/~a/82nLaJRpSNZmGH0rPAGFJXFha0Y/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/82nLaJRpSNZmGH0rPAGFJXFha0Y/1/da"><img src="http://feedads.g.doubleclick.net/~a/82nLaJRpSNZmGH0rPAGFJXFha0Y/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1092/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习——词法分析MYSQLlex</title>
		<link>http://ourmysql.com/archives/1090</link>
		<comments>http://ourmysql.com/archives/1090#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:30:21 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[LEX]]></category>
		<category><![CDATA[源码]]></category>
		<category><![CDATA[词法分析]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1090</guid>
		<description><![CDATA[词法分析即将输入的语句进行分词(token)，解析出每个token的意义。分词的本质便是正则表达式的匹配过程，比较流行的分词工具应该是lex，通过简单的规则制定，来实现分词。Lex一般和yacc结合使用。]]></description>
			<content:encoded><![CDATA[<p><strong>       </strong>客户端向服务器发送过来SQL语句后，服务器首先要进行词法分析，而后进行语法分析，语义分析，构造执行树，生成执行计划。词法分析是第一阶段，虽然在理解Mysql实现上意义不是很大，但作为基础还是学习下比较好。</p>
<p>词法分析即将输入的语句进行分词(token)，解析出每个token的意义。分词的本质便是正则表达式的匹配过程，比较流行的分词工具应该是lex，通过简单的规则制定，来实现分词。Lex一般和yacc结合使用。关于lex和yacc的基础知识请参考<a href="http://www.google.com.hk/url?q=http://www.ibm.com/developerworks/cn/linux/sdk/lex/index.html&amp;sa=U&amp;ei=7eo4TsHbAsHsrAfn8onkDw&amp;ved=0CA4QFjAA&amp;usg=AFQjCNE7rsDghtWnWwnlgsyVmisTZk59Lg">Yacc 与Lex 快速入门 - IBM</a>。如果想深入学习的话，可以看下《LEX与YACC》。</p>
<p>然而Mysql并没有使用lex来实现词法分析，但是语法分析却用了yacc，而yacc需要词法分析函数yylex，故在sql_<a href="http://ourmysql.com/archives/tag/yacc" class="st_tag internal_tag" rel="tag" title="标签 YACC 下的日志">yacc</a>.cc文件最前面我们可以看到如下的宏定义:</p>
<div>
<pre>/* Substitute the variable and function names.  */
#define yyparse         MYSQLparse
#define yylex           MYSQLlex</pre>
<div>复制代码</div>
</div>
<p>这里的MYSQLlex也就是本文的重点，即MYSQL自己的词法分析程序。源码版本5.1.48。源码太长，贴不上来，算啦..在sql_<a href="http://ourmysql.com/archives/tag/lex" class="st_tag internal_tag" rel="tag" title="标签 LEX 下的日志">lex</a>.cc里面。</p>
<p>我们第一次进入词法分析，state默认值为MY_LEX_START，就是开始状态了，其实state的宏的意义可以从名称上猜个差不多，再比如MY_LEX_IDEN便是标识符。对START状态的处理伪代码如下：</p>
<div>
<pre>case MY_LEX_START:
{
Skip空格
获取第一个有效字符c
state = state_map[c];
Break;
}</pre>
<div>复制代码</div>
</div>
<p>我困惑了，这尼玛肿么出来个state_map？找到了在函数开始出有个赋值的地方：</p>
<div>
<pre>uchar *state_map= cs-&gt;state_map;</pre>
</div>
<pre></pre>
<p>cs？！不会是反恐精英吧!!快速监视下cs为my_charset_latin1,哥了然了，原来cs是latin字符集，character set的缩写吧。那么为神马state_map可以直接决定状态？找到其赋值的地方，在init_state_maps函数中，代码如下所示：</p>
<pre></pre>
<div>
<pre>/* Fill state_map with states to get a faster parser */
  for (i=0; i &lt; 256 ; i++)
  {
    if (my_isalpha(cs,i))
      state_map[i]=(uchar) MY_LEX_IDENT;
    else if (my_isdigit(cs,i))
      state_map[i]=(uchar) MY_LEX_NUMBER_IDENT;
#if defined(USE_MB) &amp;&amp; defined(USE_MB_IDENT)
    else if (my_mbcharlen(cs, i)&gt;1)
      state_map[i]=(uchar) MY_LEX_IDENT;
#endif
    else if (my_isspace(cs,i))
      state_map[i]=(uchar) MY_LEX_SKIP;
    else
      state_map[i]=(uchar) MY_LEX_CHAR;
  }
  state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) MY_LEX_IDENT;
  state_map[(uchar)'\'']=(uchar) MY_LEX_STRING;
  state_map[(uchar)'.']=(uchar) MY_LEX_REAL_OR_POINT;
  state_map[(uchar)'&gt;']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) MY_LEX_CMP_OP;
  state_map[(uchar)'&lt;']= (uchar) MY_LEX_LONG_CMP_OP;
  state_map[(uchar)'&amp;']=state_map[(uchar)'|']=(uchar) MY_LEX_BOOL;
  state_map[(uchar)'#']=(uchar) MY_LEX_COMMENT;
  state_map[(uchar)';']=(uchar) MY_LEX_SEMICOLON;
  state_map[(uchar)':']=(uchar) MY_LEX_SET_VAR;
  state_map[0]=(uchar) MY_LEX_EOL;
  state_map[(uchar)'\\']= (uchar) MY_LEX_ESCAPE;
  state_map[(uchar)'/']= (uchar) MY_LEX_LONG_COMMENT;
  state_map[(uchar)'*']= (uchar) MY_LEX_END_LONG_COMMENT;
  state_map[(uchar)'@']= (uchar) MY_LEX_USER_END;
  state_map[(uchar) '`']= (uchar) MY_LEX_USER_VARIABLE_DELIMITER;
  state_map[(uchar)'"']= (uchar) MY_LEX_STRING_OR_DELIMITER;</pre>
<div>复制代码</div>
</div>
<pre>　　先来看这个for循环，256应该是256个字符了，每个字符的处理应该如下规则：如果是字母，则state = MY_LEX_IDENT；如果是数字，则state = MY_LEX_NUMBER_IDENT，如果是空格，则state = MY_LEX_SKIP，剩下的全为MY_LEX_CHAR。</pre>
<p>for循环之后，又对一些特殊字符进行了处理，由于我们的语句“select @@version_comment limit 1”中有个特殊字符@，这个字符的state进行了特殊处理，为MY_LEX_USER_END。</p>
<p>对于my_isalpha等这几个函数是如何进行判断一个字符属于什么范畴的呢？跟进去看下，发现是宏定义：</p>
<div>
<pre>#define    my_isalpha(s, c)  (((s)-&gt;ctype+1)[(uchar) (c)] &amp; (_MY_U | _MY_L))</pre>
</div>
<p>Wtf，肿么又来了个ctype，c作为ctype的下标，_MY_U | _MY_L如下所示，</p>
<div>
<pre>#define    _MY_U   01    /* Upper case */
#define    _MY_L   02    /* Lower case */</pre>
<div>复制代码</div>
</div>
<p>ctype里面到底存放了什么？在ctype-latin1.c源文件里面，我们找到了my_charset_latin1字符集的初始值：</p>
<div>
<pre>CHARSET_INFO my_charset_latin1=
{
    8,0,0,                           /* number    */
    MY_CS_COMPILED | MY_CS_PRIMARY, /* state     */
    "latin1",                        /* cs name    */
    "latin1_swedish_ci",              /* name      */
    "",                                /* comment   */
    NULL,                         /* tailoring */
    ctype_latin1,
    to_lower_latin1,
    to_upper_latin1,
    sort_order_latin1,
    NULL,           /* contractions */
    NULL,           /* sort_order_big*/
    cs_to_uni,             /* tab_to_uni   */
    NULL,           /* tab_from_uni */
    my_unicase_default, /* caseinfo     */
    NULL,           /* state_map    */
    NULL,           /* ident_map    */
    1,                  /* strxfrm_multiply */
    1,                  /* caseup_multiply  */
    1,                  /* casedn_multiply  */
    1,                  /* mbminlen   */
    1,                  /* mbmaxlen  */
    0,                  /* min_sort_char */
    255,        /* max_sort_char */
    ' ',                /* pad char      */
    0,                  /* escape_with_backslash_is_dangerous */
    &amp;my_charset_handler,
    &amp;my_collation_8bit_simple_ci_handler
};</pre>
<div>复制代码</div>
</div>
<p>可以看出ctype = ctype_latin1；而ctype_latin1值为：</p>
<div>
<pre>static uchar ctype_latin1[] = {
    0,
   32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 32, 32,
   32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
   72, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
  132,132,132,132,132,132,132,132,132,132, 16, 16, 16, 16, 16, 16,
   16,129,129,129,129,129,129,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, 16, 16, 16, 16, 16,
   16,130,130,130,130,130,130,  2,  2,  2,  2,  2,  2,  2,  2,  2,
    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 16, 16, 16, 16, 32,
   16,  0, 16,  2, 16, 16, 16, 16, 16, 16,  1, 16,  1,  0,  1,  0,
    0, 16, 16, 16, 16, 16, 16, 16, 16, 16,  2, 16,  2,  0,  2,  1,
   72, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
   16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
    1,  1,  1,  1,  1,  1,  1, 16,  1,  1,  1,  1,  1,  1,  1,  2,
    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
    2,  2,  2,  2,  2,  2,  2, 16,  2,  2,  2,  2,  2,  2,  2,  2
};</pre>
<div>复制代码</div>
</div>
<p>看到这里哥再一次了然了，这些值都是经过预计算的，第一个0是无效的，这也是为什么my_isalpha(s, c)定义里面ctype要先+1的原因。通过_MY_U和_MY_L的定义，可以知道，这些值肯定是按照相应的ASCII码的具体意义进行置位的。比如字符&#8217;A'，其ASCII码为65，其实大写字母，故必然具有_MY_U，即第0位必然为1，找到ctype里面第66个（略过第一个无意义的0）元素，为129 = 10000001，显然第0位为1(右边起)，说明为大写字母。写代码的人确实比较牛X，如此运用位，哥估计这辈子也想不到了，小小佩服下。State的问题点到为止了。</p>
<p>继续进行词法分析，第一个字母为s，其state = MY_LEX_IDENT（IDENTIFIER:标识符的意思）,break出来，继续循环，case进入MY_LEX_IDENT分支：</p>
<div>
<pre>Case MY_LEX_IDENT：
{
由s开始读，直到空格为止
If（读入的单词为关键字）
{
nextstate = MY_LEX_START；
Return tokval;        //关键字的唯一标识
}
Else
{
return IDENT_QUOTED 或者 IDENT；表示为一般标识符
}
}</pre>
<div>复制代码</div>
</div>
<p>这里SELECT肯定为关键字，至于为什么呢？下节的语法分析会讲。</p>
<p>解析完SELECT后，需要解析@@version_comment,第一个字符为@,进入START分支，state = MY_LEX_USER_END；</p>
<p>进入MY_LEX_USER_END分支，如下：</p>
<div>
<pre>case MY_LEX_USER_END:        // end '@' of user@hostname
      switch (state_map[lip-&gt;yyPeek()]) {
      case MY_LEX_STRING:
      case MY_LEX_USER_VARIABLE_DELIMITER:
      case MY_LEX_STRING_OR_DELIMITER:
    break;
      case MY_LEX_USER_END:
    lip-&gt;next_state=MY_LEX_SYSTEM_VAR;
    break;
      default:
    lip-&gt;next_state=MY_LEX_HOSTNAME;
    break;</pre>
<div>复制代码</div>
</div>
<p>哥会心的笑了，两个@符号就是系统变量吧～～,下面进入MY_LEX_SYSTEM_VAR分支</p>
<div>
<pre>case MY_LEX_SYSTEM_VAR:
      yylval-&gt;lex_str.str=(char*) lip-&gt;get_ptr();
      yylval-&gt;lex_str.length=1;
      lip-&gt;yySkip();                                    // Skip '@'
      lip-&gt;next_state= (state_map[lip-&gt;yyPeek()] ==
            MY_LEX_USER_VARIABLE_DELIMITER ?
            MY_LEX_OPERATOR_OR_IDENT :
            MY_LEX_IDENT_OR_KEYWORD);
      return((int) '@');</pre>
<div>复制代码</div>
</div>
<p>所作的操作是略过@，next_state设置为MY_LEX_IDENT_OR_KEYWORD，再之后便是解析MY_LEX_IDENT_OR_KEYWORD了，也就是version_comment了，此解析应该和SELECT解析路径一致，但不是KEYWORD。剩下的留给有心的读者了（想起了歌手经常说的一句话：大家一起来，哈哈）。</p>
<p>Mysql的词法解析的状态还是比较多的，如果细究还是需要点时间的，但这不是Mysql的重点，我就浅尝辄止了。下节会针对上面的SQL语句讲解下语法分析。</p>
<p>PS: 一直想好好学习下Mysql，总是被这样或那样的事耽误，当然都是自己的原因，希望这次能走的远点&#8230;..</p>
<p>PS again：本文只代表本人的学习感悟，如有异议，欢迎指正。</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/lex" title="LEX" rel="tag">LEX</a>, <a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a>, <a href="http://ourmysql.com/archives/tag/%e8%af%8d%e6%b3%95%e5%88%86%e6%9e%90" title="词法分析" rel="tag">词法分析</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/MoF37KgagjIAiYCOK2iEKTtPAfI/0/da"><img src="http://feedads.g.doubleclick.net/~a/MoF37KgagjIAiYCOK2iEKTtPAfI/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/MoF37KgagjIAiYCOK2iEKTtPAfI/1/da"><img src="http://feedads.g.doubleclick.net/~a/MoF37KgagjIAiYCOK2iEKTtPAfI/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1090/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql源码学习笔记——偷窥线程</title>
		<link>http://ourmysql.com/archives/1088</link>
		<comments>http://ourmysql.com/archives/1088#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:28:33 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[源码]]></category>
		<category><![CDATA[线程]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1088</guid>
		<description><![CDATA[安装完Mysql后，使用VS打开源码开开眼，我嘞个去，这代码和想象中怎么差别这么大呢？感觉代码有些凌乱，注释代码都写的比较随意，好像没有什么统一的规范，不同的文件中代码风格也有差异，可能Mysql经过了很多牛人的手之后，集众牛人之长吧。也可能是我见识比较浅薄，适应了自己的代码风格，井底之蛙了，总之还是怀着敬畏的心情开始咱的源码之旅吧。]]></description>
			<content:encoded><![CDATA[<div id="cnblogs_post_body">
<p>　　安装完Mysql 后，使用VS打开源码开开眼，我嘞个去，这代码和想象中怎么差别这么大呢？感觉代码有些凌乱，注释代码都写的比较随意，好像没有什么统一的规范，不同的文 件中代码风格也有差异，可能Mysql经过了很多牛人的手之后，集众牛人之长吧。也可能是我见识比较浅薄，适应了自己的代码风格，井底之蛙了，总之还是怀 着敬畏的心情开始咱的源码之旅吧。本人菜鸟，大神轻拍。</p>
<p>Mysql可以启动起来了，应该怎么学习呢？总不能从main开始一步一步的 看吧，Mysql作为比较底层的大型软件，涉及到数据库实现的方方面面，没有厚实的数据库理论基础和对Mysql各个模块相当的熟悉，从main开始势必 会把自己引入某个死胡同啊，什么都看，最后啥也不会，咱伤不起。</p>
<p>经过思考后，我想还是通过客户端来调试服务器，从而学习服务器代码比较现实。 也就是通过客户端的动作，看服务器的反应。比如从客户端的登录动作来看SERVER如何进行通信、用户识别、鉴定以及任务分配的，通过CREATE TABLE，来看SERVER如何解析DDL语句以及针对不同的存储引擎采取的不同的物理存储方式，通过INSERT语句，来看SERVER如何进行 Btree的操作。通过SELECT语句来看如何进行SQL语句语法树的创建和优化的，通过ROLL BACK，来看SERVER事务是如何实现的。这里主要是通过跟踪代码学习Mysql数据库实现的思想，对于具体的代码不去做过多的追究(主要是我对 C++不是很熟悉)，好读书，不求甚解，呵呵。</p>
<p>由此，暂时准备了以下几条SQL语句，来有针对的进行SERVER的分析</p>
<div>
<div>1、 LOGIN(登录)<br />
mysql.exe –uroot –p<br />
2、 DDL(建表语句)<br />
create table tb_<a href="http://ourmysql.com/archives/tag/myisam" class="st_tag internal_tag" rel="tag" title="标签 MyISAM 下的日志">myisam</a>(c1 int, c2 varchar(256)) engine = <a href="http://ourmysql.com/archives/tag/myisam" class="st_tag internal_tag" rel="tag" title="标签 MyISAM 下的日志">myisam</a>;<br />
create table tb_<a href="http://ourmysql.com/archives/tag/innodb" class="st_tag internal_tag" rel="tag" title="标签 InnoDB 下的日志">innodb</a>(c1 int, c2 varchar(256)) engine = <a href="http://ourmysql.com/archives/tag/innodb" class="st_tag internal_tag" rel="tag" title="标签 InnoDB 下的日志">innodb</a>;<br />
3、 INSERT<br />
Insert into tb_myisam values(1 , ’寂寞的肥肉’);<br />
Insert into tb_innodb values(1 , ’寂寞的肥肉’);<br />
4、 SELECT<br />
Select c1 from tb_myisam;<br />
Select * from tb_innodb;<br />
5、 ROLLBACK</div>
<div><a>复制代码</a></div>
</div>
<p>大家都知道，mysql可以通过多个客户端，进行并发操作，当然也包括登录 了。在别人登录的时候，其他的用户可能正在进行一些其它的操作，因此对于登录我们猜测应该有专门的线程负责客户端和服务器的连接的创建，以保证登录的及时 性，对于每个连接的用户，应该用一个独立的线程进行任务的执行。</p>
<p>首先介绍下mysql中创建线程的函数，创建线程的函数貌似就是 _begin_thread，CreateThread，我们通过VS在整个解决方案中进行查找，bingo！在my_winthread.c中找到了调 用_begin_thread的函数pthread_create，在os0thread.c中找到了调用CreateThread的函数 os_thread_create，一个系统怎么封装两个系统函数呢？？再仔细看下，发现my_winthread.c是在项目mysys下，而 os0thread.c是在项目innobase下。innobase！！这不就是innodb的插件式存储引擎么，原来这是存储引擎自己的封装的底层函 数，哥心中豁然开朗了。我想Mysql应用范围如此之广，除了开源之外，插件式的存储引擎功不可没啊，用户可以根据自己的实际应用采取不同的存储引擎，对于大公司，估计会开发自己的存储引擎。</p>
<p>下面分析下pthread_create是如何调用_begin_thread的，先粗略看下源码。&nbsp;</p>
<div>
<div>int pthread_create(pthread_t *thread_id, pthread_attr_t *attr,<br />
pthread_handler func, void *param)<br />
{<br />
HANDLE hThread;<br />
struct pthread_map *map;<br />
DBUG_ENTER(“pthread_create”);</p>
<p>if (!(map=malloc(sizeof(*map))))<br />
DBUG_RETURN(-1);<br />
map-&gt;func=func;<br />
map-&gt;param=param;<br />
pthread_mutex_lock(&amp;THR_LOCK_thread);<br />
#ifdef __BORLANDC__<br />
hThread=(HANDLE)_beginthread((void(_USERENTRY *)(void *)) pthread_start,<br />
attr-&gt;dwStackSize ? attr-&gt;dwStackSize :<br />
65535, (void*) map);<br />
#else<br />
hThread=(HANDLE)_beginthread((void( __cdecl *)(void *)) pthread_start,<br />
attr-&gt;dwStackSize ? attr-&gt;dwStackSize :<br />
65535, (void*) map);<br />
#endif<br />
DBUG_PRINT(“info”, (“hThread=%lu”,(long) hThread));<br />
*thread_id=map-&gt;pthreadself=hThread;<br />
pthread_mutex_unlock(&amp;THR_LOCK_thread);</p>
<p>if (hThread == (HANDLE) -1)<br />
{<br />
int error=errno;<br />
DBUG_PRINT(“error”,<br />
(“Can&#8217;t create thread to handle request (error %d)”,error));<br />
DBUG_RETURN(error ? error : -1);<br />
}<br />
VOID(SetThreadPriority(hThread, attr-&gt;priority)) ;<br />
DBUG_RETURN(0);<br />
}</p></div>
<div><a>复制代码</a></div>
</div>
<p><strong>关键的代码是下面三句：</strong></p>
<div>
<div>map-&gt;func=func;<br />
map-&gt;param=param;<br />
_beginthread((void( __cdecl *)(void *)) pthread_start,<br />
attr-&gt;dwStackSize ? attr-&gt;dwStackSize :<br />
65535, (void*) map);</div>
<div><a>复制代码</a></div>
</div>
<p>&nbsp;</p>
<p>从这可以看出，创建的新线程的名字是个固定的函数——pthread_start,而我们传进来的想创建的函数func是挂载在了map上了，函数的参数同样的挂载在map上了，这样我们就可以推理出在pthread_start函数中，肯定会出现这样的代码：</p>
<p>map-&gt;func(map-&gt;param)；</p>
<p>mysql没有选择直接_beginthread(func, stack_size, param)的形式，而是进行了一次封装，不知道这样的好处是什么，可能牛人的思想不是我这样小菜鸟能顿悟的，跑题了～～</p>
<p>至此，我们只在pthread_create函数上设置断点，调试启动mysqld，断点停下来，看下系统的线程状况：<img src="http://pic002.cnblogs.com/images/2011/162828/2011042122290872.png" alt="" /></p>
<p>我们第一次进入pthread_create，任何线程都没开始创建呢，按理说系统线程应该就只有一个主线程，可现在多了这么多，这些应该是innodb存储引擎创建的线程了（具体是在plugin_init）。根据线程的名称，结合注释，猜测了下这些线程的作用。</p>
<p>Io_handler_thread：从名称可以知道这些是I/O线程，负责进行磁盘I/O。</p>
<p>Svr_error_monitor_thread：应该是服务器出错监控线程。</p>
<p>Svr_lock_timeout_thread：应该是和上锁相关的线程。</p>
<p>Svr_master_thread：</p>
<p align="left">/*************************************************************************</p>
<p>The master thread controlling the server. */</p>
<p>服务器控制线程，应该是具体进行作业的线程。</p>
<p>Svr_monitor_thread:</p>
<p align="left">/*************************************************************************</p>
<p>A thread prints the info output by various InnoDB monitors. */</p>
<p>监控线程，负责打印信息。</p>
<p>&nbsp;</p>
<p>淡然飘过吧，不去细究了，我们只关心pthread_create创建的线程。根据调试，发现多了几个线程同名的线程_threadstart，如下所示：</p>
<p><img src="http://pic002.cnblogs.com/images/2011/162828/2011042122394472.jpg" alt="" /></p>
<p>调试时看堆栈可以知道这三个线程的创建者和作用，如下所示</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="284">创建者</td>
<td valign="top" width="284">处理函数</td>
</tr>
<tr>
<td valign="top" width="284">create_shutdown_thread</td>
<td valign="top" width="284">handle_shutdown</td>
</tr>
<tr>
<td valign="top" width="284">start_handle_manager</td>
<td valign="top" width="284">handle_manager</td>
</tr>
<tr>
<td valign="top" width="284">handle_connections_methods</td>
<td valign="top" width="284">handle_connections_sockets</td>
</tr>
</tbody>
</table>
<p>创建者：调用pthread_create进行创建线程的函数。</p>
<p>处理函数：调用pthread_create所创建的线程的具体的线程函数。</p>
<p>&nbsp;</p>
<p>由名称我们就可以看出，handle_connections_sockets应该是处理连接的线程了，从顺序上看，也应该是这样，只有系统中所有的其他必须的线程创建完毕后，才能创建监听线程(连接线程)，即监听线程应该是系统最后创建的。</p>
<p align="left">    找到了我们LOGIN需要的线程了，下次针对这个线程，分析下如何进行登录的，以及登录后为用户分配哪些资源。时间不早啦，洗洗睡了</p>
</div>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a>, <a href="http://ourmysql.com/archives/tag/%e7%ba%bf%e7%a8%8b" title="线程" rel="tag">线程</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/X_alNcXY6pyfyrmFGlTUgYFrZSE/0/da"><img src="http://feedads.g.doubleclick.net/~a/X_alNcXY6pyfyrmFGlTUgYFrZSE/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/X_alNcXY6pyfyrmFGlTUgYFrZSE/1/da"><img src="http://feedads.g.doubleclick.net/~a/X_alNcXY6pyfyrmFGlTUgYFrZSE/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1088/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mysql的源码安装</title>
		<link>http://ourmysql.com/archives/1086</link>
		<comments>http://ourmysql.com/archives/1086#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:27:12 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[源码]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1086</guid>
		<description><![CDATA[闲来无事，准备学习下Mysql的源代码，花了1个小时的时间，终于在VS2008中运行起来Mysql了，有些地方也是知之甚少，理解错了希望大家及时纠正,下面介绍下如何进行配置。]]></description>
			<content:encoded><![CDATA[<p>闲来无事，准备学习下Mysql的源代码，花了1个小时的时间，终于在VS2008中运行起来Mysql了，有些地方也是知之甚少，理解错了希望大家及时纠正,下面介绍下如何进行配置。</p>
<p align="left"><strong>Step 1:</strong></p>
<p align="left">　　下载Mysql源代码，在<a href="http://dev.mysql.com/downloads/mysql/">http://dev.mysql.com/downloads/mysql/</a> 网站中，包含两种格式的Mysql，<strong>MSI Installer </strong>和<strong> </strong><strong>ZIP Archive</strong>，<strong>MSI Installer</strong>是直接安装程序，<strong>ZIP Archive</strong>中包含两种大小的ZIP，从大小可以看出，较小的是源码。Install和ZIP的具体区别请参考：<a href="http://dev.mysql.com/doc/refman/5.1/en/windows-choosing-package.html">http://dev.mysql.com/doc/refman/5.1/en/windows-choosing-package.html</a>。</p>
<p align="left"><strong>Step 2：</strong></p>
<p align="left">　　别告诉我你没有VS，呃，好吧，你是外星人。</p>
<p align="left"><strong>Step 3：</strong></p>
<p align="left">　　下载CMAKE，安装后，在CMD中测试是否安装成功。至于是干啥的，后面再讲吧。下载地址<a href="http://www.cmake.org/">http://www.cmake.org</a></p>
<p align="left"><strong>Step4：</strong></p>
<p align="left">　　下载bision，因为Mysql的词法和语法分析是使用的lex和yacc，下载地址：<a href="http://gnuwin32.sourceforge.net/packages/bison.htm">http://gnuwin32.sourceforge.net/packages/bison.htm</a></p>
<p align="left"><strong>Step 5：</strong></p>
<p>　　将下载下来的ZIP文件解压，我解压到了D:\open_src\mysql 中，CMD进入mysql目录下，执行脚本文件：</p>
<div>
<div>C:\&gt;d:</p>
<p>D:\&gt;cd open_src</p>
<p>D:\open_src&gt;cd mysql</p>
<p>D:\open_src\mysql&gt;wscript win\configure.js WITH_INNOBASE_STORAGE_ENGINE WITH_PARTITION_STORAGE_ENGINE MYSQL_SERVER_SUFFIX=-pro</p></div>
<div><a>复制代码</a></div>
</div>
<p>脚本后面带有三个参数，具体的参数根据自己的需要进行设置，这里选择了INNODB的存储引擎，</p>
<p>貌似这个很流行，提供的可选参数如下：</p>
<div>
<div>　　 WITH_INNOBASE_STORAGE_ENGINE Enable particular storage engines<br />
WITH_PARTITION_STORAGE_ENGINE<br />
WITH_ARCHIVE_STORAGE_ENGINE<br />
WITH_BLACKHOLE_STORAGE_ENGINE<br />
WITH_EXAMPLE_STORAGE_ENGINE<br />
WITH_FEDERATED_STORAGE_ENGINE<br />
__NT__ Enable named pipe support<br />
MYSQL_SERVER_SUFFIX=&lt;suffix&gt; Server suffix, default none<br />
COMPILATION_COMMENT=&lt;comment&gt; Server comment, default “Source distribution”<br />
MYSQL_TCP_PORT=&lt;port&gt; Server port, default 3306<br />
CYBOZU Default character set is UTF8<br />
EMBED_MANIFESTS Embed custom manifests into final exes, otherwise VS<br />
default will be used. (Note &#8211; This option should only be<br />
used by MySQL AB.)<br />
WITH_EMBEDDED_SERVER Configure solution to produce libmysqld.dll<br />
and the static mysqlserver.lib</div>
<div><a>复制代码</a></div>
</div>
<p align="left">打开configure.js脚本文件，如下所示：</p>
<p align="left">
<div>
<div>try<br />
{<br />
var fso = new ActiveXObject(“Scripting.FileSystemObject”);</p>
<p>var args = WScript.Arguments</p>
<p>// read in the Unix configure.in file</p>
</div>
<div>/**&lt;p&gt;打开文件mysql/configure.ini ,此文件是一些默认配置值，</div>
<div>　　　　　　　　如端口号MYSQL_TCP_PORT_DEFAULT&lt;/p&gt;*/<br />
var configureInTS = fso.OpenTextFile(“configure.in”, ForReading);<br />
var configureIn = configureInTS.ReadAll();<br />
configureInTS.Close();<br />
var default_comment = “Source distribution”;<br />
var default_port = GetValue(configureIn, “MYSQL_TCP_PORT_DEFAULT”);<br />
var actual_port = 0;</p>
<p>/**&lt;p&gt;输出文件mysql/win/configure.data ,此文件是运行js脚本产生的一些参数&lt;/p&gt;*/<br />
var configfile = fso.CreateTextFile(“win\\configure.data”, true);<br />
for (i=0; i &lt; args.Count(); i++)<br />
{<br />
var parts = args.Item(i).split(&#8216;=&#8217;);<br />
switch (parts[0])<br />
{<br />
case “CYBOZU”:<br />
case “EMBED_MANIFESTS”:<br />
case “EXTRA_DEBUG”:<br />
case “WITH_EMBEDDED_SERVER”:<br />
case “WITHOUT_MARIA_TEMP_TABLES”:<br />
configfile.WriteLine(“SET (” + args.Item(i) + ” TRUE)”);<br />
break;<br />
case “MYSQL_SERVER_SUFFIX”:<br />
case “MYSQLD_EXE_SUFFIX”:<br />
configfile.WriteLine(“SET (” + parts[0] + ” \”"<br />
+ parts[1] + “\”)”);<br />
break;<br />
case “COMPILATION_COMMENT”:<br />
default_comment = parts[1];<br />
break;<br />
case “MYSQL_TCP_PORT”:<br />
actual_port = parts[1];<br />
break;<br />
}<br />
}</p></div>
<div>&#8230;..</div>
<div>&#8230;..</div>
<div><a>复制代码</a></div>
</div>
<p align="left">由此可见configure.js是利用我们传入的参数和configure.in文件，生成一个configure.data文件，其内容如下所示：</p>
<p align="left">
<div>
<div>SET (MYSQL_SERVER_SUFFIX “-pro”)<br />
SET (COMPILATION_COMMENT “Source distribution”)<br />
SET (PROTOCOL_VERSION “10&#8243;)<br />
SET (DOT_FRM_VERSION “6&#8243;)<br />
SET (MYSQL_TCP_PORT_DEFAULT “0&#8243;)<br />
SET (MYSQL_TCP_PORT “3306&#8243;)<br />
SET (MYSQL_UNIX_ADDR “/tmp/mysql.sock”)<br />
SET (VERSION “5.1.48&#8243;)<br />
SET (MYSQL_BASE_VERSION “5.1&#8243;)<br />
SET (MYSQL_VERSION_ID “50148&#8243;)<br />
SET(WITH_INNOBASE_STORAGE_ENGINE TRUE)<br />
SET(WITH_PARTITION_STORAGE_ENGINE TRUE)</div>
<div><a>复制代码</a></div>
</div>
<p>&nbsp;</p>
<p><strong>Step 6：</strong></p>
<p align="left">执行win目录下的build-vs9.bat批处理文件，打开这个文件，只有一条语句：</p>
<p align="left">cmake -G “Visual Studio 9 2008&#8243;</p>
<p align="left">cmake是一个跨平台的自动化构建系统，即根据平台和编译器生成不同的make文件(UNIX下的叫法)。</p>
<div>
<div>-G &lt;generator-name&gt; = Specify a makefile generator.</p>
<p>Generators</p>
<p>The following generators are available on this platform:<br />
Borland Makefiles = Generates Borland makefiles.<br />
MSYS Makefiles = Generates MSYS makefiles.<br />
MinGW Makefiles = Generates a make file for use with<br />
mingw32-make.<br />
NMake Makefiles = Generates NMake makefiles.<br />
NMake Makefiles JOM = Generates JOM makefiles.<br />
Unix Makefiles = Generates standard UNIX makefiles.<br />
Visual Studio 10 = Generates Visual Studio 10 project files.<br />
Visual Studio 10 Win64 = Generates Visual Studio 10 Win64 project<br />
files.<br />
Visual Studio 6 = Generates Visual Studio 6 project files.<br />
Visual Studio 7 = Generates Visual Studio .NET 2002 project<br />
files.<br />
Visual Studio 7 .NET 2003 = Generates Visual Studio .NET 2003 project<br />
files.<br />
Visual Studio 8 2005 = Generates Visual Studio .NET 2005 project<br />
files.<br />
Visual Studio 8 2005 Win64 = Generates Visual Studio .NET 2005 Win64<br />
project files.<br />
Visual Studio 9 2008 = Generates Visual Studio 9 2008 project files.<br />
Visual Studio 9 2008 Win64 = Generates Visual Studio 9 2008 Win64 project</p></div>
<div><a>复制代码</a></div>
</div>
<p align="left">　　build-vs9.bat中CMAKE的参数是 Visual Studio 9 2008，即Generates Visual Studio 9 2008 project files。Cmake执行时，会使用step 5中生成的configure.data文件，具体咋使用的我也小白了，咱就不研究了。最后会在mysql根目录下生成一个VS的解决方案文件 MySql.sln。</p>
<p align="left"><strong>Step 7：</strong></p>
<p align="left">　　设置数据目录，Mysqld启动时，首先去WINDIR中找my.ini配置文件，可以通过如下命令获取WINDIR的路径：</p>
<p align="left">
<div>
<div>C:\&gt;echo %WINDIR%</p>
<p>C:\WINDOWS</p></div>
<div><a>复制代码</a></div>
</div>
<p align="left">
<p align="left">如果没有my.ini文件，可以自己创建一个，看到如下内容：</p>
<div>
<div>[mysqld]<br />
# set basedir to your installation path<br />
basedir=D:/mysql<br />
# set datadir to the location of your data directory<br />
datadir=D:/mysql/win/data</div>
<div><a>复制代码</a></div>
</div>
<p align="left">设置了两个参数，mysql的参数路径，我们通过修改这两个参数来指向我们具体的目录。</p>
<div>
<div>[mysqld]<br />
# set basedir to your installation path<br />
basedir=D:/open_src/mysql<br />
# set datadir to the location of your data directory<br />
datadir=D:/open_src/mysql/win/data</div>
<div><a>复制代码</a></div>
</div>
<p>如果不想自己创建的话，可以将win目录下的data目录整体拷贝到sql目录下。</p>
<p><strong>step 8：</strong></p>
<p>将sql目录下的share目录整体拷贝下mysql根目录下，因为 SERVER启动时需要在Mysql/share目录下找一个errmsg.txt，用于记录错误信息，当然你也可以不进行整体的目录拷贝，只将 errmsg.txt拷贝过去也行，但是确保是这样的路径mysql/share/errmsg.txt.<strong><br />
</strong></p>
<p><strong>step 9：</strong></p>
<p>启动mysql服务器，mysql解决方案中，服务器是mysqld项目，客户端是mysql项目，分别编译这两个项目。在编译 sql_locale.cc文件时，会出现ERROR，用ULTRA EDIT打开sql_locale.cc文件，另存为UTF-8格式，覆盖原文件即可。使用VS启动服务器mysqld，使用命令行启动客户端 mysql：</p>
<div>
<div>D:\open_src\mysql\client\Debug&gt;mysql -uroot -p<br />
Enter password:<br />
Welcome to the MySQL monitor. Commands end with ; or \g.<br />
Your MySQL connection id is 1<br />
Server version: 5.1.48-pro-debug Source distribution</p>
<p>mysql&gt; show databases;<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| Database |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
| information_schema |<br />
| mysql |<br />
| test |<br />
+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+<br />
3 rows in set (0.08 sec)</p></div>
<div><a>复制代码</a></div>
</div>
<p>服务器可以在VS下进行调试了，yeah～～</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1084" title="MySQL源码学习——DBUG调试">MySQL源码学习——DBUG调试</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/bKaE1P0tCLYxXYn8bDG53THh4yM/0/da"><img src="http://feedads.g.doubleclick.net/~a/bKaE1P0tCLYxXYn8bDG53THh4yM/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/bKaE1P0tCLYxXYn8bDG53THh4yM/1/da"><img src="http://feedads.g.doubleclick.net/~a/bKaE1P0tCLYxXYn8bDG53THh4yM/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1086/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MySQL源码学习——DBUG调试</title>
		<link>http://ourmysql.com/archives/1084</link>
		<comments>http://ourmysql.com/archives/1084#comments</comments>
		<pubDate>Mon, 26 Mar 2012 05:25:35 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[DBUG]]></category>
		<category><![CDATA[源码]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1084</guid>
		<description><![CDATA[在规模稍微大点的项目中，为了方便快速找到bug的所在，我们往往需要在代码中加入一些调试用的代码，比如加入一些printf，打印出一些重点的信息；加入assert，进行断言判断。这些比较随意的调试代码会使整个系统显得比较凌乱。于是Fred Fish开发了一套用于嵌入代码中的库，开发人员只需要调用相应的函数即可加入调试信息。

        对于MySQL这种多线程的程序来说，调试也是比较困难的，MySQL选择了DBUG作为其调试代码（在原始代码的基础上进行了略微的改造），充分说明了DBUG的实用性，本文就DBUG的原始代码进行介绍。]]></description>
			<content:encoded><![CDATA[<div id="cnblogs_post_body">
<h1>一、前言</h1>
<p>在规模稍微大点的项目中，为了方便快速找到bug的所在，我们往往需要在代码中加入一些调试用的代码，比如加入一些printf，打印出一些重点的信息； 加入assert，进行断言判断。这些比较随意的调试代码会使整个系统显得比较凌乱。于是Fred Fish开发了一套用于嵌入代码中的库，开发人员只需要调用相应的函数即可加入调试信息。</p>
<p>对于MySQL这种多线程的程序来说，调试也是比较困难的，MySQL选择了DBUG作为其调试代码（在原始代码的基础上进行了略微的改造），充分说明了DBUG的实用性，本文就DBUG的原始代码进行介绍。</p>
<h1>二、DBUG下载与使用</h1>
<p>这里以linux下的开发进行介绍。</p>
<p>首先需要下载DBUG源码，下载地址：<a href="http://sourceforge.net/projects/dbug/">http://sourceforge.net/projects/dbug/</a></p>
<p>解压后在dbug/src下即是debug所需的源码文件：<a href="http://ourmysql.com/archives/tag/dbug" class="st_tag internal_tag" rel="tag" title="标签 DBUG 下的日志">dbug</a>.h <a href="http://ourmysql.com/archives/tag/dbug" class="st_tag internal_tag" rel="tag" title="标签 DBUG 下的日志">dbug</a>.c</p>
<p>dbug/example.c是一个简单的例子，源码如下：</p>
<div>
<div>
<div id="highlighter_920514">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
<div>21</div>
<div>22</div>
<div>23</div>
<div>24</div>
<div>25</div>
<div>26</div>
<div>27</div>
<div>28</div>
<div>29</div>
<div>30</div>
<div>31</div>
<div>32</div>
<div>33</div>
<div>34</div>
<div>35</div>
<div>36</div>
<div>37</div>
<div>38</div>
<div>39</div>
<div>40</div>
<div>41</div>
<div>42</div>
<div>43</div>
<div>44</div>
<div>45</div>
<div>46</div>
<div>47</div>
<div>48</div>
<div>49</div>
<div>50</div>
</td>
<td>
<div>
<div><code>/*</code></div>
<div><code> </code><code>* This file is Public Domain</code></div>
<div><code> </code><code>*</code></div>
<div><code> </code><code>* Just short example, how Fred Fish's dbug package should be used</code></div>
<div><code> </code><code>*</code></div>
<div><code> </code><code>* Tonu Samuel &lt;tonu @spam.ee=""&gt;</code></div>
<div><code> </code><code>*</code></div>
<div><code> </code><code>*/</code></div>
<div><code>#include "dbug.h"</code></div>
<div></div>
<div><code>static</code> <code>int</code> <code>sub1 (</code><code>void</code><code>);</code></div>
<div><code>static</code> <code>void</code> <code>sub2 (</code><code>char</code> <code>*arg);</code></div>
<div></div>
<div><code>static</code> <code>int</code></div>
<div><code>sub1 (</code><code>void</code><code>)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>DBUG_ENTER (</code><code>"sub1"</code><code>);</code></div>
<div></div>
<div><code>  </code><code>sub2 (</code><code>"Hello world!"</code><code>);</code></div>
<div><code>  </code><code>sub2 (</code><code>"Hello earth!"</code><code>);</code></div>
<div><code>  </code><code>sub2 (</code><code>"Hello programmer!"</code><code>);</code></div>
<div></div>
<div><code>  </code><code>DBUG_RETURN (0);</code></div>
<div><code>}</code></div>
<div></div>
<div></div>
<div><code>static</code> <code>void</code></div>
<div><code>sub2 (</code><code>char</code> <code>*arg)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>DBUG_ENTER (</code><code>"sub2"</code><code>);</code></div>
<div><code>  </code><code>DBUG_PRINT (</code><code>"info"</code><code>, (</code><code>"Got argument: '%s'"</code><code>, arg));</code></div>
<div></div>
<div><code>  </code><code>printf</code> <code>(</code><code>"%s\n"</code><code>, arg);</code></div>
<div></div>
<div><code>  </code><code>DBUG_VOID_RETURN;</code></div>
<div><code>}</code></div>
<div></div>
<div></div>
<div><code>int</code></div>
<div><code>main (</code><code>void</code><code>)</code></div>
<div><code>{</code></div>
<div><code>  </code><code>int</code> <code>ret = 0;</code></div>
<div><code>  </code><code>DBUG_PUSH (</code><code>"d:t:O"</code><code>);</code></div>
<div><code>  </code><code>DBUG_PROCESS (</code><code>"example"</code><code>);</code></div>
<div></div>
<div><code>  </code><code>ret = sub1 ();</code></div>
<div><code>  </code><code>DBUG_PRINT (</code><code>"info"</code><code>, (</code><code>"Returned value: %d"</code><code>, ret));</code></div>
<div></div>
<div><code>  </code><code>return</code> <code>0;</code></div>
<div><code>}&lt;/tonu&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p>我们将example.c放到src目录下，然后编译example文件：</p>
<div>
<div>
<div id="highlighter_368973">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
</td>
<td>
<div>
<div><code>dbug</code><code>/src</code><code>$ gcc -Wall -g dbug.c example.c -o example</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p>运行example输出如下：</p>
<div>
<div>
<div id="highlighter_82351">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
</td>
<td>
<div>
<div><code>&gt;sub1</code></div>
<div><code>| &gt;sub2</code></div>
<div><code>| | info: Got argument: </code><code>'Hello world!'</code></div>
<div><code>Hello world!</code></div>
<div><code>| &lt;sub2 |=</code><code>""</code><code>&gt;sub2</code></div>
<div><code>| | info: Got argument: </code><code>'Hello earth!'</code></div>
<div><code>Hello earth!</code></div>
<div><code>| &lt;sub2 |=</code><code>""</code><code>&gt;sub2</code></div>
<div><code>| | info: Got argument: </code><code>'Hello programmer!'</code></div>
<div><code>Hello programmer!</code></div>
<div><code>| &lt;sub2 pre=</code><code>""</code> <code>0=</code><code>""</code><code>&gt;&lt; value: Returned info: &lt;sub1&gt;&lt;</code><code>/sub1</code><code>&gt;&lt;</code><code>/sub2</code><code>&gt;&lt;</code><code>/sub2</code><code>&gt;&lt;</code><code>/sub2</code><code>&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div>          结合源码，我们通过打印出来的信息，可以很容易的看出调试信息所表达的意思：</div>
<p>函数sub1调用了三次sub2，三次的参数分别是&#8217;Hello world!&#8217; ，&#8217;Hello earth!&#8217; ，&#8217;Hello programmer!&#8217; ，最后return 0.</p>
<p>下面初略介绍下上面用到的几个宏定义：</p>
<p>DBUG_PUSH (“d:t:O”);  设置调试的启动参数，参数列表在MySQL手册上也有：</p>
<table width="558" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="80">标记</td>
<td valign="top" width="476">描述</td>
</tr>
<tr>
<td valign="top" width="80">d</td>
<td valign="top" width="476">允许对当前状态从DBUG_&lt;N&gt;宏输出。可能跟着一列关键词，这些关键词仅对那些带有<br />
关键词的DBUG宏选择输出。一个空的关键词列意味着对所有宏输出。</td>
</tr>
<tr>
<td valign="top" width="80">D</td>
<td valign="top" width="476">在每个调试起输出行后延迟。参量一个十分之一秒为单位来延迟的数，它受限于机器的能<br />
力。比如 -#D,20 指定一个2秒的延迟。</td>
</tr>
<tr>
<td valign="top" width="80">f</td>
<td valign="top" width="476">限制调试和/或跟踪，以及简单设定于列出名字的函数。注意，空列将禁止所用函数。应<br />
该给出适当的d 或 t 标记，如果它们被允许了，这个标记仅限制它们的动作。</td>
</tr>
<tr>
<td valign="top" width="80">F</td>
<td valign="top" width="476">对调试或跟踪输出的每一行识别源文件名。</td>
</tr>
<tr>
<td valign="top" width="80">i</td>
<td valign="top" width="476">对调试或跟踪输出的每一行用PID或线程ID识别进程。</td>
</tr>
<tr>
<td valign="top" width="80">g</td>
<td valign="top" width="476">允许解析，创建名为的dbugmon.out文件，它包含可用来简单设定程序的信息。可能跟着<br />
一列关键词，它们是选择只对列中的函数做简单设定。一个空列意味着所有函数都要考虑<br />
到。</td>
</tr>
<tr>
<td valign="top" width="80">L</td>
<td valign="top" width="476">为调试或跟踪输出的每一行识别源文件行号。</td>
</tr>
<tr>
<td valign="top" width="80">n</td>
<td valign="top" width="476">为调试或跟踪输出的每一行打印当前函数嵌套深度。</td>
</tr>
<tr>
<td valign="top" width="80">N</td>
<td valign="top" width="476">给调试输出的每一行编号。</td>
</tr>
<tr>
<td valign="top" width="80">o</td>
<td valign="top" width="476">重定向调试器输出流到指定文件。默认输出是stderr 文件。</td>
</tr>
<tr>
<td valign="top" width="80">O</td>
<td valign="top" width="476">类似于 o，但是文件在每次写操作之间被冲刷。当需要之时，文件在每次写操作之间被关<br />
闭然后重新打开。</td>
</tr>
<tr>
<td valign="top" width="80">p</td>
<td valign="top" width="476">限制调试器作用于指定进程。为使调试器动作，一个进程必须用DBUG_PROCESS宏来识<br />
别，且匹配列表中的一个。</td>
</tr>
<tr>
<td valign="top" width="80">P</td>
<td valign="top" width="476">为调试或跟踪输出的每一行打印当前进程名字。</td>
</tr>
<tr>
<td valign="top" width="80">r</td>
<td valign="top" width="476">当推出一个新状态时，不继承前状态的操作嵌套深度级别。当输出在左边空白开始时有<br />
用。</td>
</tr>
<tr>
<td valign="top" width="80">S</td>
<td valign="top" width="476">在每个调试过的函数做_sanity(_file_,_line_)函数直到 _sanity() 返回不同于0的结果。（大多<br />
数的时候与safemalloc 一起用来找出内存漏洞）。</td>
</tr>
<tr>
<td valign="top" width="80">t</td>
<td valign="top" width="476">允许函数调用/退出跟踪行。可能跟着一个给出最大跟踪级别的数字列（只含一个修改<br />
量），超过这个数字，调试中或跟踪中的宏不能产生任何输出。 默认为一个编译时间选<br />
项。</td>
</tr>
</tbody>
</table>
<p>DBUG_PROCESS (“example”); 设置进程名为example</p>
<p>DBUG_ENTER (“sub1&#8243;); 表示进入函数sub1</p>
<p>DBUG_PRINT (“info”, (“Got argument: &#8216;%s&#8217;”, arg)); 和printf效果差不多，打印出需要的调试信息。</p>
<p>DBUG_RETURN (0);  在return 0 的基础上，打印出return 0的调试信息。</p>
<h1>三、小结</h1>
<p>本文简单介绍了DBUG的使用方法，没有深究，源码很少，却很实用，在源码的注释部分，我看到了是Fred Fish1987年写的，呃，我才刚出生&#8230;.，没想到现在还在看那时候的代码，可能这就是所谓的经典吧。</p>
<p>兄弟们，你们还在为复杂环境下的bug调试而苦恼么？还在为堆栈被写乱掉无从下手而凌乱么？淡定吧，一起来用DBUG吧～～</p>
</div>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1107" title="Mysql源码学习——没那么简单的Hash">Mysql源码学习——没那么简单的Hash</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1105" title="Mysql源码学习——用户认证原理与实现">Mysql源码学习——用户认证原理与实现</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1102" title="Mysql源码学习——Thread Manager">Mysql源码学习——Thread Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1100" title="Mysql源码学习——Connection Manager">Mysql源码学习——Connection Manager</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1098" title="Mysql源码学习——八度空间">Mysql源码学习——八度空间</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1095" title="Mysql源码学习——源码目录结构">Mysql源码学习——源码目录结构</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1092" title="Mysql源码学习——打造专属语法">Mysql源码学习——打造专属语法</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1090" title="Mysql源码学习——词法分析MYSQLlex">Mysql源码学习——词法分析MYSQLlex</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1088" title="Mysql源码学习笔记——偷窥线程">Mysql源码学习笔记——偷窥线程</a></li><li>2012-03-26 -- <a href="http://ourmysql.com/archives/1086" title="Mysql的源码安装">Mysql的源码安装</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/dbug" title="DBUG" rel="tag">DBUG</a>, <a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81" title="源码" rel="tag">源码</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/nJBR84JB-aCRjPvv01NeK2TW4nw/0/da"><img src="http://feedads.g.doubleclick.net/~a/nJBR84JB-aCRjPvv01NeK2TW4nw/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/nJBR84JB-aCRjPvv01NeK2TW4nw/1/da"><img src="http://feedads.g.doubleclick.net/~a/nJBR84JB-aCRjPvv01NeK2TW4nw/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1084/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>三种转换Mysql数据库数据编码的窍门-latin1转utf8</title>
		<link>http://ourmysql.com/archives/1080</link>
		<comments>http://ourmysql.com/archives/1080#comments</comments>
		<pubDate>Sun, 25 Mar 2012 14:31:15 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL基础知识]]></category>
		<category><![CDATA[编码]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1080</guid>
		<description><![CDATA[背景：某个操作系统的Mysql数据库数据库Databnsednname采用默认的latin1字符集，操作系统升级需求将所有数据转换成utf-8各式，目的数据库Databnse为newdbname(建库时应用utf8)]]></description>
			<content:encoded><![CDATA[<p>背景：某个操作系统的Mysql数据库数据库Databnsednname采用默认的latin1字符集，操作系统升级需求将所有数据转换成utf-8各式，目的数据库Databnse为newdbname(建库时应用utf8)<br />
<strong>方法一：</strong><br />
步骤一 命令行执行：Mysql数据库dump &#8211;opt -hlocalhost -uroot -p*** &#8211;default-character-set=lantin1 dbname &gt; /usr/local/dbname.sql<br />
步骤二 将 dbname.sql文档中的create table语句的CHARSET=latin1改为CHARSET=utf8<br />
步骤三 在dbname.sql文档中的insert语句之前加一条&#8217;set names utf8;&#8217;<br />
步骤四将dbname.sql转码为utf-8各式，意见应用UltraEditor，能够直接应用该编辑器的&#8217;转换-&gt;ASCII到UTF- 8(Unicode编辑)&#8217;，或者将文档另存为UTF-8(无BOM)各式<br />
步骤五 命令行执行：Mysql数据库 -hlocalhost -uroot -p*** &#8211;default-character-set=utf8 new_dbname &lt; /usr/local/dbname.sql<br />
总结：这种窍门有个致命之处就当数据中有大量汉文字符和更多有联系特殊符号字符时，很有估计导致在 [步骤五]时报错导致无法正常导入数据，假如数据库Databnse较量大能够分别对每张表执行上述步骤<br />
<strong>方法二（推荐大部份人应用）：</strong><br />
为 了解决NO·1种窍门中总结时说到的疑难问题，在网上苦苦查找了一天资源才东拼西凑的搞出唯一较量稳妥的解决窍门<br />
步骤一将待导出的数据表的表框架导出（能够用Phpmyadmin、Mysql数据库dump等，很简单就不说了），然后将导出的create table语句的CHARSET=latin1改为CHARSET=utf8，在目的库newdbname中执行该create table语句把表框架建好，接下来开端导出-导入数据<br />
步骤二 命令行：进入Mysql数据库命令行下，Mysql数据库 -hlocalhost -uroot -p*** dbname<br />
步骤三 执行SQL select * from tbname into outfile &#8216;/usr/local/tbname.sql&#8217;;<br />
步骤四将tbname.sql转码为utf-8各式，意见应用UltraEditor，能够直接应用该编辑器的&#8217;转换-&gt;ASCII到UTF- 8(Unicode编辑)&#8217;，或者将文档另存为UTF-8(无BOM)各式<br />
步骤五 在Mysql数据库命令行下执行语句 set character_set_database=utf8; 注：设置Mysql数据库的环境变量，那样Mysql数据库在下一步读取sql文档时将以utf8的形式去解释该文档内容<br />
步骤六 在Mysql数据库命令行下执行语句 load data infile &#8216;tbname.sql&#8217; into table newdbname.tbname;<br />
留意：千万不能忘了第四步<br />
采用第二种窍门，所有数据均正常导入，且各式转换胜利没有乱码</p>
<p>&nbsp;</p>
<p><strong>方法三：</strong></p>
<p>别人的今天问起，随手转了一下，很简单，这里截图说明一下<br />
用phpmyadmin导入库test中后发现，这个库中有UTF8和latin1两种编码，如下图</p>
<p>&nbsp;</p>
<p><a href="http://photo.blog.sina.com.cn/showpic.html#blogid=6fcb75890100x6o7&amp;url=http://s13.sinaimg.cn/orignal/6fcb7589hb4a671b6dd3c" target="_blank"><img src="http://hiphotos.baidu.com/ccc009/pic/item/ef5e9413632762d028797b06a0ec08fa503dc673.jpg" alt="" name="image_operate_68601324526869421" width="261" height="264" /></a></p>
<p>&nbsp;</p>
<p>先把test备份为test1库，在WIN下操作的话就是把：路径\MySQL\data下的test目录备份一下，改名为test1目录<br />
在phpmyadmin中把所有为UTF8的表删除，在命令行中运行<br />
<strong>D:\PHPServer\MySQL\bin&gt;mysqldump -uroot -p &#8211;quick &#8211;compatible=mysql40 &#8211;default-character-set=latin1 &#8211;extended-insert=FALSE test &gt; /root/test.sql</strong></p>
<p>&nbsp;</p>
<p>说明一下D:\PHPServer\MySQL\bin是我的安装目录，test为数据库，d:/test.sql为生成的数据文件，程序会提示你输入root的用户密码，输入后，等几分钟，test.sql就生成好了</p>
<p>&nbsp;</p>
<p>通过phpmyadmin建立一个新库，库名：test2 ，采集字符集设定为utf8-general-li</p>
<p>&nbsp;</p>
<p>再将刚才生成sql文件导入新数据库</p>
<p>&nbsp;</p>
<p><strong>D:\PHPServer\MySQL\bin&gt;mysql -uroot -p &#8211;default-character-set=utf8 test2 &lt; /root/test.sql</strong></p>
<p>&nbsp;</p>
<p>输入root密码，等几分钟，zxs.sql就导入成功了，到这还没完，因为这里只是把latin1编码的转换为utf8了<br />
最后，停止mysql，把test2目录下的所有文件覆盖到我们刚刚备份的test1目录下，这样，test1目录就是我们要得到了转换好了的UTF8数据库了，OK，收工<br />
当然，方法很简单，就是把latin1编码的表单独分离出来转为UTF8后再盖原来的库，这样多种编码也都可以完美的转换，这种情况也非常常见，换主机，升mysql版本等都会出现这种情况的。</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>目前没有相关的文章</li></ul>
	标签：<a href="http://ourmysql.com/archives/category/basic" title="MySQL基础知识" rel="tag">MySQL基础知识</a>, <a href="http://ourmysql.com/archives/tag/%e7%bc%96%e7%a0%81" title="编码" rel="tag">编码</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/wfZxRZXdM7W0E2M3AETgKY_eINw/0/da"><img src="http://feedads.g.doubleclick.net/~a/wfZxRZXdM7W0E2M3AETgKY_eINw/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/wfZxRZXdM7W0E2M3AETgKY_eINw/1/da"><img src="http://feedads.g.doubleclick.net/~a/wfZxRZXdM7W0E2M3AETgKY_eINw/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1080/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL源码学习——MDL字典锁</title>
		<link>http://ourmysql.com/archives/1077</link>
		<comments>http://ourmysql.com/archives/1077#comments</comments>
		<pubDate>Sun, 25 Mar 2012 14:26:02 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[MDL]]></category>
		<category><![CDATA[字典锁]]></category>
		<category><![CDATA[源码学习]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1077</guid>
		<description><![CDATA[MDL，Meta Data lock，元数据锁，一般称为字典锁。字典锁与数据锁相对应。字典锁是为了保护数据对象被改变，一般是一些DDL会对字典对象改变，如两个TX，TX1先查询表，然后TX2试图DROP，字典锁就会lock住TX2，知道TX1结束（提交或回滚）。数据锁是保护表中的数据，如两个TX同时更新一行时，先得到row lock的TX会先执行，后者只能等待。]]></description>
			<content:encoded><![CDATA[<ul>
<li>
<h1>什么是MDL</h1>
</li>
</ul>
<p><a href="http://ourmysql.com/archives/tag/mdl" class="st_tag internal_tag" rel="tag" title="标签 MDL 下的日志">MDL</a>，Meta Data lock，元数据锁，一般称为<span style="color: #008000;"><strong><a href="http://ourmysql.com/archives/tag/%e5%ad%97%e5%85%b8%e9%94%81" class="st_tag internal_tag" rel="tag" title="标签 字典锁 下的日志">字典锁</a></strong></span>。字典锁与数据锁相对应。字典锁是为了保护数据对象被改变，一般是一些DDL会对字典对象改变，如两个TX，TX1先查询表，然后TX2试图DROP，字典锁就会lock住TX2，知道TX1结束（提交或回滚）。数据锁是保护表中的数据，如两个TX同时更新一行时，先得到row lock的TX会先执行，后者只能等待。</p>
<ul>
<li>
<h1>MDL的设计目标</h1>
</li>
</ul>
<p>字典锁在设计的时候是为了数据库对象的元数据。到达以下3个目的。</p>
<p>1. 提供对并发访问内存中字典对象缓存(table definatin cache，TDC)的保护。这是系统的内部要求。</p>
<p>2. 确保DML的并发性。如TX1对表T1查询，TX2同是对表T1插入。</p>
<p>3. 确保一些操作的互斥性，如DML与大部分DDL(ALTER TABLE除外)的互斥性。如TX1对表T1执行插入，TX2执行DROP TABLE，这两种操作是不允许并发的，故需要将表对象保护起来，这样可以保证binlog逻辑的正确性。（貌似之前的版本存在字典锁是语句级的，导致binlog不合逻辑的bug。）</p>
<ul>
<li>
<h1>支持的锁类型</h1>
</li>
</ul>
<p>数据库理论中的基本锁类型是S、X，意向锁IS、IX是为了层次上锁而引入的。比如要修改表中的数据，可能先对表上一个表级IX锁，然后再对修改的数据上一个行级X锁，这样就可以保证其他试图修改表定义的事物因为获取不到表级的X锁而等待。</p>
<p>MySQL中将字典锁的类型根据不同语句的功能，进一步细分，<strong><span style="color: #ff0000;">细分的依据是对字典的操作和对数据的操作</span></strong>。细分的好处是能在一定程度上提高并发效率，因为如果只定义X和S两种锁，必然导致兼容性矩阵的局限性。MySQL不遗余力的定义了如下的锁类型。</p>
<table width="493" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="245">名称</td>
<td valign="top" width="246">意义</td>
</tr>
<tr>
<td valign="top" width="245">MDL_INTENTION_EXCLUSIVE</td>
<td valign="top" width="246">意向排他锁，只用于范围上锁</td>
</tr>
<tr>
<td valign="top" width="245">MDL_SHARED</td>
<td valign="top" width="246">共享锁，用于访问字典对象，而不访问数据。</td>
</tr>
<tr>
<td valign="top" width="245">MDL_SHARED_HIGH_PRIO</td>
<td valign="top" width="246">只访问字典对象（如DESC TABLE）</td>
</tr>
<tr>
<td valign="top" width="245">MDL_SHARED_READ</td>
<td valign="top" width="246">共享读锁，用于读取数据（如select）</td>
</tr>
<tr>
<td valign="top" width="245">MDL_SHARED_WRITE</td>
<td valign="top" width="246">共享写锁，用于修改数据（如update）</td>
</tr>
<tr>
<td valign="top" width="245">MDL_SHARED_NO_WRITE</td>
<td valign="top" width="246">共享非写锁，允许读取数据，阻塞其他TX修改数据（如alter table）</td>
</tr>
<tr>
<td valign="top" width="245">MDL_SHARED_NO_READ_WRITE</td>
<td valign="top" width="246">用于访问字典，读写数据</p>
<p>不允许其他TX读写数据</td>
</tr>
<tr>
<td valign="top" width="245">MDL_EXCLUSIVE</td>
<td valign="top" width="246">排他锁，可以修改字典和数据</td>
</tr>
</tbody>
</table>
<p>可以看到MySQL在ALTER TABLE的时候还是允许其他事务进行读表操作的。需要注意的是读操作的事物需要在ALTER TABLE获取MDL_SHARED_NO_WRITE锁之后，否则无法并发。这种应用场景应该是对一个较大的表进行ALTER时，其他事物仍然可以读，并发性得到了提高。</p>
<ul>
<li>
<h1>锁的兼容性</h1>
</li>
</ul>
<p>锁的兼容性就是我们经常看到的那些兼容性矩阵，X和S必然互斥，S和S兼容。MySQL根据锁的类型我们也可以知道其兼容矩阵如下：</p>
<table width="425" border="1" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="52"></td>
<td valign="top" width="36">IX</td>
<td valign="top" width="44">S</td>
<td valign="top" width="44">SH</td>
<td valign="top" width="44">SR</td>
<td valign="top" width="44">SW</td>
<td valign="top" width="44">SNW</td>
<td valign="top" width="61">SNRW</td>
<td valign="top" width="54">X</td>
</tr>
<tr>
<td valign="top" width="52">IX</td>
<td valign="top" width="36">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="61">1</td>
<td valign="top" width="54">1</td>
</tr>
<tr>
<td valign="top" width="52">S</td>
<td valign="top" width="36">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="61">1</td>
<td valign="top" width="54">0</td>
</tr>
<tr>
<td valign="top" width="52">SH</td>
<td valign="top" width="36">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="61">1</td>
<td valign="top" width="54">0</td>
</tr>
<tr>
<td valign="top" width="52">SR</td>
<td valign="top" width="36">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="61">0</td>
<td valign="top" width="54">0</td>
</tr>
<tr>
<td valign="top" width="52">SW</td>
<td valign="top" width="36">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">0</td>
<td valign="top" width="61">0</td>
<td valign="top" width="54">0</td>
</tr>
<tr>
<td valign="top" width="52">SNW</td>
<td valign="top" width="36">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">0</td>
<td valign="top" width="44">0</td>
<td valign="top" width="61">0</td>
<td valign="top" width="54">0</td>
</tr>
<tr>
<td valign="top" width="52">SNRW</td>
<td valign="top" width="36">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">1</td>
<td valign="top" width="44">0</td>
<td valign="top" width="44">0</td>
<td valign="top" width="44">0</td>
<td valign="top" width="61">0</td>
<td valign="top" width="54">0</td>
</tr>
<tr>
<td valign="top" width="52">X</td>
<td valign="top" width="36">1</td>
<td valign="top" width="44">0</td>
<td valign="top" width="44">0</td>
<td valign="top" width="44">0</td>
<td valign="top" width="44">0</td>
<td valign="top" width="44">0</td>
<td valign="top" width="61">0</td>
<td valign="top" width="54">0</td>
</tr>
</tbody>
</table>
<p>1代表兼容，0代表不兼容。你可能发现X和IX竟然兼容，没错，其实这里的IX已经不是传统意义上的IX，这个IX是用在范围锁上，所以和X锁不互斥。</p>
<ul>
<li>
<h1>数据结构</h1>
</li>
</ul>
<p>涉及到的和锁相关的数据结构主要是如下几个：</p>
<p>MDL_context：字典锁上下文。包含一个事物所有的字典锁请求。</p>
<p>MDL_request：字典锁请求。包含对某个对象的某种锁的请求。</p>
<p>MDL_ticket：字典锁排队。MDL_request就是为了获取一个ticket。</p>
<p>MDL_lock：锁资源。一个对象全局唯一。可以允许多个可以并发的事物同时获得。</p>
<p>涉及到的源码文件主要是sql/mdl.cc</p>
<ul>
<li>
<h1>锁资源</h1>
</li>
</ul>
<p>锁资源在系统中是<strong><span style="color: #ff0000;">共享的</span></strong>，即全局的，存放在static MDL_map mdl_locks;的hash链表中，对于数据库中的一个对象，其hashkey必然是唯一的，对应一个锁资源。多个事务同时对一张表操作时，申请的lock也是同一个内存对象。获取mdl_locks中的lock需要通过全局互斥量保护起来mysql_mutex_lock(&amp;m_mutex); m_mutex是MDL_map的成员。</p>
<ul>
<li>
<h1>上锁流程</h1>
</li>
</ul>
<p><span style="text-decoration: underline;">一个会话连接在实现中对应一个THD实体，一个THD对应一个MDL_CONTEXT，表示需要的mdl锁资源，一个MDL_CONTEXT中包含多个MDL_REQUEST，一个MDL_REQUEST即是对一个对象的某种类型的lock请求</span>。每个mdl_request上有一个ticket对象，ticket中包含lock。</p>
<p>上锁的也就是根据MDL_REQUEST进行上锁。</p>
<div>
<div id="highlighter_900599">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
</td>
<td>
<div>
<div><code>Acquire_lock:</code></div>
<div><code>    </code><code>if</code> <code>(mdl_request contains the needed ticket )</code></div>
<div><code>    </code><code>return</code> <code>ticket;</code></div>
<div><code>    </code><code>End </code><code>if</code><code>;</code></div>
<div><code>    </code><code>Create a ticket;</code></div>
<div><code>    </code><code>If (!find lock in lock_sys)</code></div>
<div><code>    </code><code>Create a lock;</code></div>
<div><code>    </code><code>End </code><code>if</code></div>
<div><code>    </code><code>If (lock can be granted to mdl_request)</code></div>
<div><code>    </code><code>Set lock to ticket;</code></div>
<div><code>    </code><code>Set ticket to mdl_request;</code></div>
<div><code>    </code><code>Else</code></div>
<div><code>    </code><code>Wait </code><code>for</code> <code>lock</code></div>
<div><code>End </code><code>if</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>&nbsp;</p>
<p>稍微解释下，首先是在mdl_request本身去查看有没有相等的或者stronger的ticket，如果存在，则直接使用。否则创建一个ticket，查找上锁对象对应的lock，没有则创建。检查lock是否可以被赋给本事务，如果可以直接返回，否则等待这个lock；</p>
<ul>
<li>
<h1>锁等待与唤醒</h1>
</li>
</ul>
<p>字典对象的锁等待是发生在两个事物对同一对象上不兼容的锁导致的。当然，由于lock的唯一性，先到先得，后到的只能等待。</p>
<p>如何判断一个lock是否可以grant给一个TX？这需要结合lock结构来看了，lock上有两个成员，grant和wait，grant代表此lock允许的事物都上了哪些锁，wait表示等待的事务需要上哪些锁。其判断一个事物是否可以grant的逻辑如下：</p>
<div>
<div id="highlighter_903796">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</td>
<td>
<div>
<div><code>If(compatible(lock.grant, tx.locktype))</code></div>
<div><code>    </code><code>If (compatible(lock.wait, tx.locktype))</code></div>
<div><code>    </code><code>return</code> <code>can_grant;</code></div>
<div><code>    </code><code>End </code><code>if</code></div>
<div><code>End </code><code>if</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>即首先判断grant中的锁类型和当前事务是否兼容，然后判断wait中的锁类型和当前事务是否兼容。细心的话，会想到，wait中的锁类型是不需要和当前事务进行兼容性比较的，这是不是说这个比较是多余的了？其实也不是，因为wait的兼容性矩阵和上面的矩阵是不一样的，wait的兼容性矩阵感觉是在DDL等待的情况下，防止DML继续进来（wait矩阵就不写出来了，大家可以去代码里看下）。</p>
<p>比如：</p>
<p>TX1                                                      TX2                                                       TX3</p>
<p>SELECT T1</p>
<p>DROP  T1</p>
<p>SELECT T1</p>
<p>这时候TX2会阻塞，TX3也会阻塞，被TX2阻塞，也就是说被wait的事件阻塞了，这样可能就是为了保证在DDL等待时，禁止再做DML了，因为在DDL面前，DML显得确实不是那么重要了。</p>
<p>如何唤醒被等待的事务呢？比如唤醒TX2，当TX1结束时，会调用release_all_locks_for_name，对被锁住的事务进行唤醒，具体操作封装在<strong>reschedule_waiters</strong>函数中，重置等待时间的标记位进行唤醒,重点代码如下:</p>
<div>
<div id="highlighter_819714">
<div></div>
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
</td>
<td>
<div>
<div><code>if</code> <code>(can_grant_lock(ticket-&gt;get_type(), ticket-&gt;get_ctx()))</code></div>
<div><code>    </code><code>{</code></div>
<div><code>      </code><code>if</code> <code>(! ticket-&gt;get_ctx()-&gt;m_wait.set_status(MDL_wait::GRANTED))</code></div>
<div><code>      </code><code>{</code></div>
<div><code>        </code><code>/*</code></div>
<div><code>          </code><code>Satisfy the found request by updating lock structures.</code></div>
<div><code>          </code><code>It is OK to do so even after waking up the waiter since any</code></div>
<div><code>          </code><code>session which tries to get any information about the state of</code></div>
<div><code>          </code><code>this lock has to acquire MDL_lock::m_rwlock first and thus,</code></div>
<div><code>          </code><code>when manages to do so, already sees an updated state of the</code></div>
<div><code>          </code><code>MDL_lock object.</code></div>
<div><code>        </code><code>*/</code></div>
<div><code>        </code><code>m_waiting.remove_ticket(ticket);</code></div>
<div><code>        </code><code>m_granted.add_ticket(ticket);</code></div>
<div><code>    </code><code>}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>今天把mdl系统总体上看了一下，对锁的请求、等待以及唤醒有了初步了解。并发性的问题是最难调试的，大家如果想做锁方面的实验，可以利用VS调试中的<span style="color: #ff0000;"><strong>冻结线程</strong></span>的功能，这样就可以确保并发情况控制完全按照你设计思路去呈现。</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>目前没有相关的文章</li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/mdl" title="MDL" rel="tag">MDL</a>, <a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e5%ad%97%e5%85%b8%e9%94%81" title="字典锁" rel="tag">字典锁</a>, <a href="http://ourmysql.com/archives/tag/%e6%ba%90%e7%a0%81%e5%ad%a6%e4%b9%a0" title="源码学习" rel="tag">源码学习</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/msLEnczin8F6qu6SO5n7nmeoXQg/0/da"><img src="http://feedads.g.doubleclick.net/~a/msLEnczin8F6qu6SO5n7nmeoXQg/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/msLEnczin8F6qu6SO5n7nmeoXQg/1/da"><img src="http://feedads.g.doubleclick.net/~a/msLEnczin8F6qu6SO5n7nmeoXQg/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1077/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mysql-锁表机制分析 解决数据库负载压力的侧面解析</title>
		<link>http://ourmysql.com/archives/1075</link>
		<comments>http://ourmysql.com/archives/1075#comments</comments>
		<pubDate>Sun, 25 Mar 2012 14:23:52 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL高级应用]]></category>
		<category><![CDATA[锁表]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1075</guid>
		<description><![CDATA[为了给高并发情况下的mysql进行更好的优化，有必要了解一下mysql查询更新时的锁表机制。]]></description>
			<content:encoded><![CDATA[<p>为了给高并发情况下的mysql进行更好的优化，有必要了解一下mysql查询更新时的锁表机制。</p>
<p><strong>一、概述<br />
</strong>MySQL有三种锁的级别：页级、表级、行级。<br />
MyISAM和MEMORY存储引擎采用的是表级锁（table-level locking）；BDB存储引擎采用的是页面锁（page-level<br />
locking），但也支持表级锁；InnoDB存储引擎既支持行级锁（row-level locking），也支持表级锁，但默认情况下是采用行级锁。<br />
MySQL这3种锁的特性可大致归纳如下：<br />
表级锁：开销小，加锁快；不会出现死锁；锁定粒度大，发生锁冲突的概率最高,并发度最低。<br />
行级锁：开销大，加锁慢；会出现死锁；锁定粒度最小，发生锁冲突的概率最低,并发度也最高。<br />
页面锁：开销和加锁时间界于表锁和行锁之间；会出现死锁；锁定粒度界于表锁和行锁之间，并发度一般。<br />
<strong>二、MyISAM表锁<br />
</strong>MyISAM存储引擎只支持表锁，是现在用得最多的存储引擎。<br />
<strong>1、查询表级锁争用情况<br />
</strong>可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺：<br />
mysql&gt; show status like ‘table%’;<br />
+———————–+———-+<br />
| Variable_name | Value |<br />
+———————–+———-+<br />
| Table_locks_immediate | 76939364 |<br />
| Table_locks_waited | 305089 |<br />
+———————–+———-+<br />
2 rows in set (0.00 sec)Table_locks_waited的值比较高，说明存在着较严重的表级锁争用情况。</p>
<p><strong>2、MySQL表级锁的锁模式<br />
</strong>MySQL的表级锁有两种模式：表共享读锁（Table Read Lock）和表独占写锁（Table Write<br />
Lock）。MyISAM在执行查询语句（SELECT）前，会自动给涉及的所有表加读锁，在执行更新操作（UPDATE、DELETE、INSERT等）前，会自动给涉及的表加写锁。<br />
所以对MyISAM表进行操作，会有以下情况：<br />
a、对MyISAM表的读操作（加读锁），不会阻塞其他进程对同一表的读请求，但会阻塞对同一表的写请求。只有当读锁释放后，才会执行其它进程的写操作。<br />
b、对MyISAM表的写操作（加写锁），会阻塞其他进程对同一表的读和写操作，只有当写锁释放后，才会执行其它进程的读写操作。<br />
下面通过例子来进行验证以上观点。数据表gz_phone里有二百多万数据，字段id,phone,ua,day。现在同时用多个客户端同时对该表进行操作分析。<br />
a、当我用客户端1进行一个比较长时间的读操作时，分别用客户端2进行读和写操作：<br />
client1:<br />
mysql&gt;select count(*) from gz_phone group by ua;<br />
75508 rows in set (3 min 15.87 sec) client2:<br />
select id,phone from gz_phone limit 1000,10;<br />
+——+——-+<br />
| id | phone |<br />
+——+——-+<br />
| 1001 | 2222 |<br />
| 1002 | 2222 |<br />
| 1003 | 2222 |<br />
| 1004 | 2222 |<br />
| 1005 | 2222 |<br />
| 1006 | 2222 |<br />
| 1007 | 2222 |<br />
| 1008 | 2222 |<br />
| 1009 | 2222 |<br />
| 1010 | 2222 |<br />
+——+——-+<br />
10 rows in set (0.01 sec)<br />
mysql&gt; update gz_phone set phone=’11111111111′ where id=1001;<br />
Query OK, 0 rows affected (2 min 57.88 sec)<br />
Rows matched: 1 Changed: 0 Warnings: 0<br />
说明当数据表有一个读锁时，其它进程的查询操作可以马上执行，但更新操作需等待读锁释放后才会执行。<br />
b、当用客户端1进行一个较长时间的更新操作时，用客户端2,3分别进行读写操作：<br />
client1:<br />
mysql&gt; update gz_phone set phone=’11111111111′;<br />
Query OK, 1671823 rows affected (3 min 4.03 sec)<br />
Rows matched: 2212070 Changed: 1671823 Warnings: 0 client2:<br />
mysql&gt; select id,phone,ua,day from gz_phone limit 10;<br />
+—-+——-+——————-+————+<br />
| id | phone | ua | day |<br />
+—-+——-+——————-+————+<br />
| 1 | 2222 | SonyEricssonK310c | 2007-12-19 |<br />
| 2 | 2222 | SonyEricssonK750c | 2007-12-19 |<br />
| 3 | 2222 | MAUI WAP Browser | 2007-12-19 |<br />
| 4 | 2222 | Nokia3108 | 2007-12-19 |<br />
| 5 | 2222 | LENOVO-I750 | 2007-12-19 |<br />
| 6 | 2222 | BIRD_D636 | 2007-12-19 |<br />
| 7 | 2222 | SonyEricssonS500c | 2007-12-19 |<br />
| 8 | 2222 | SAMSUNG-SGH-E258 | 2007-12-19 |<br />
| 9 | 2222 | NokiaN73-1 | 2007-12-19 |<br />
| 10 | 2222 | Nokia2610 | 2007-12-19 |<br />
+—-+——-+——————-+————+<br />
10 rows in set (2 min 58.56 sec) client3:<br />
mysql&gt; update gz_phone set phone=’55555′ where id=1;<br />
Query OK, 1 row affected (3 min 50.16 sec)<br />
Rows matched: 1 Changed: 1 Warnings: 0<br />
说明当数据表有一个写锁时，其它进程的读写操作都需等待读锁释放后才会执行。</p>
<p><strong>3、并发插入<br />
</strong>原则上数据表有一个读锁时，其它进程无法对此表进行更新操作，但在一定条件下，MyISAM表也支持查询和插入操作的并发进行。<br />
MyISAM存储引擎有一个系统变量concurrent_insert，专门用以控制其并发插入的行为，其值分别可以为0、1或2。<br />
a、当concurrent_insert设置为0时，不允许并发插入。<br />
b、当concurrent_insert设置为1时，如果MyISAM表中没有空洞（即表的中间没有被删除的行），MyISAM允许在一个进程读表的同时，另一个进程从表尾插入记录。这也是MySQL的默认设置。<br />
c、当concurrent_insert设置为2时，无论MyISAM表中有没有空洞，都允许在表尾并发插入记录。<br />
<strong><br />
4、MyISAM的锁调度<br />
</strong>由于MySQL认为写请求一般比读请求要重要，所以如果有读写请求同时进行的话，MYSQL将会优先执行写操作。这样MyISAM表在进行大量的更新操作时（特别是更新的字段中存在索引的情况下），会造成查询操作很难获得读锁，从而导致查询阻塞。<br />
我们可以通过一些设置来调节MyISAM的调度行为：<br />
a、通过指定启动参数low-priority-updates，使MyISAM引擎默认给予读请求以优先的权利。<br />
b、通过执行命令SET LOW_PRIORITY_UPDATES=1，使该连接发出的更新请求优先级降低。<br />
c、通过指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性，降低该语句的优先级。<br />
上面3种方法都是要么更新优先，要么查询优先的方法。这里要说明的就是，不要盲目的给mysql设置为读优先，因为一些需要长时间运行的查询操作，也会使写进程“饿死”。只有根据你的实际情况，来决定设置哪种操作优先。这些方法还是没有从根本上同时解决查询和更新的问题。<br />
在一个有大数据量高并发表的mysql里，我们还可采用另一种策略来进行优化，那就是通过mysql主从（读写）分离来实现负载均衡，这样可避免优先哪一种操作从而可能导致另一种操作的堵塞。下面将用一个篇幅来说明mysql的读写分离技术。</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-02-22 -- <a href="http://ourmysql.com/archives/1022" title="mysql-锁表机制分析">mysql-锁表机制分析</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/category/advanced" title="MySQL高级应用" rel="tag">MySQL高级应用</a>, <a href="http://ourmysql.com/archives/tag/%e9%94%81%e8%a1%a8" title="锁表" rel="tag">锁表</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/S_Tvemorg38IKRrBvhsPMmreQHI/0/da"><img src="http://feedads.g.doubleclick.net/~a/S_Tvemorg38IKRrBvhsPMmreQHI/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/S_Tvemorg38IKRrBvhsPMmreQHI/1/da"><img src="http://feedads.g.doubleclick.net/~a/S_Tvemorg38IKRrBvhsPMmreQHI/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1075/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mysql中index的使用</title>
		<link>http://ourmysql.com/archives/1073</link>
		<comments>http://ourmysql.com/archives/1073#comments</comments>
		<pubDate>Sun, 25 Mar 2012 14:21:36 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL基础知识]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[索引]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1073</guid>
		<description><![CDATA[索引用来快速地寻找那些具有特定值的记录，所有MySQL索引都以B-树的形式保存。如果没有索引，执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录，直至找到符合要求的记录。表里面的记录数量越多，这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引，MySQL无需扫描任何记录即可迅速得到目标记录所在的位置。如果表有1000个记录，通过索引查找记录至少要比顺序扫描记录快100倍。]]></description>
			<content:encoded><![CDATA[<div>分析实施总结报告：</div>
<div>我的测试报告如下：（注意是我的）数据十几万条<br />
采用zouql的索引优化后，每单条网站搜索的CPU负担至少上升一半，如果并发五六条搜索，那等着服务器挂好了<br />
经过彻夜的分析与研究（本人不是程序员，没有理论基础，花的时间也久些）得出：<br />
老柏默认的索引为多列索引，zouql的为单列索引.<br />
看了N多资料后，发现多列肯定是优于单列索引，想想老柏默认的索引是有道理的，否则他不会这样设计的，因为程序的SQL表字段的调用，他最清楚＾＾</div>
<div>还原老柏默认索引后，性能马上提高一半多.忘了默认索引的朋友我帮大家贴一下：<br />
首先删除所有的索引，除了ID那个，再运行下面：<br />
ALTER TABLE `dede_archives` ADD <a href="http://ourmysql.com/archives/tag/index" class="st_tag internal_tag" rel="tag" title="标签 index 下的日志">INDEX</a> `typeid` (sortrank,click,pubdate,senddate,lastpost,postnum);<br />
ALTER TABLE `dede_archives` ADD INDEX `typeid_2` (typeid,typeid2);<br />
ALTER TABLE `dede_archives` ADD INDEX `senddate` (memberID,mtype);<br />
ALTER TABLE `dede_archives` ADD INDEX `title` (title,keywords);</div>
<div>在使用这个默认索引前，我自已也测试了几个多列组合，性能也有所下降，我搞着玩的＾＾</div>
<div>＊另外补充，搜索页面或相关页面尽量不要加关键字搜索的那种链接，经过本人测试会增加搜索引擎的机器人的抓爬，会加重你服务器的负担，因为说了搜索是很占资源的，除非你服务器足够强，或你现在的数据量还不是很大，或你的网站还不是很被引擎关注.</p>
<p>以下是理论知识备忘：</p></div>
<div><strong>        一、什么是索引？<br />
</strong>　　索引用来快速地寻找那些具有特定值的记录，所有MySQL索引都以B-树的形式保存。如果没有索引，执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录，直至找到符合要求的记录。表里面的记录数量越多，这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引，MySQL无需扫描任何记录即可迅速得到目标记录所在的位置。如果表有1000个记录，通过索引查找记录至少要比顺序扫描记录快100倍。</p>
<p>假设我们创建了一个名为people的表：</p>
<p>CREATE TABLE people ( peopleid SMALLINT NOT NULL, name CHAR(50) NOT NULL );</p>
<p>然后，我们完全随机把1000个不同name值插入到people表。下图显示了people表所在数据文件的一小部分：</p>
<p>可以看到，在数据文件中name列没有任何明确的次序。如果我们创建了name列的索引，MySQL将在索引中排序name列：</p>
<p>对于索引中的每一项，MySQL在内部为它保存一个数据文件中实际记录所在位置的“指针”。因此，如果我们要查找name等于“Mike”记录的 peopleid（SQL命令为“SELECT peopleid FROM people WHERE name=&#8217;Mike&#8217;;”），MySQL能够在name的索引中查找“Mike”值，然后直接转到数据文件中相应的行，准确地返回该行的 peopleid（999）。在这个过程中，MySQL只需处理一个行就可以返回结果。如果没有“name”列的索引，MySQL要扫描数据文件中的所有记录，即1000个记录！显然，需要MySQL处理的记录数量越少，则它完成任务的速度就越快。</p>
<p><strong>二、索引的类型</strong><br />
MySQL提供多种索引类型供选择：</p>
<p>普通索引<br />
这是最基本的索引类型，而且它没有唯一性之类的限制。普通索引可以通过以下几种方式创建：<br />
创建索引，例如CREATE INDEX &lt;索引的名字&gt; ON tablename (列的列表);<br />
修改表，例如ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表);<br />
创建表的时候指定索引，例如CREATE TABLE tablename ( [...], INDEX [索引的名字] (列的列表) );</p>
<p>唯一性索引<br />
这种索引和前面的“普通索引”基本相同，但有一个区别：索引列的所有值都只能出现一次，即必须唯一。唯一性索引可以用以下几种方式创建：<br />
创建索引，例如CREATE UNIQUE INDEX &lt;索引的名字&gt; ON tablename (列的列表);<br />
修改表，例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);<br />
创建表的时候指定索引，例如CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) );</p>
<p>主键<br />
主键是一种唯一性索引，但它必须指定为“PRIMARY KEY”。如果你曾经用过AUTO_INCREMENT类型的列，你可能已经熟悉主键之类的概念了。主键一般在创建表的时候指定，例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。但是，我们也可以通过修改表的方式加入主键，例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。</p>
<p>全文索引<br />
MySQL从3.23.23版开始支持全文索引和全文检索。在MySQL中，全文索引的索引类型为FULLTEXT。全文索引可以在VARCHAR或者TEXT类型的列上创建。它可以通过CREATE TABLE命令创建，也可以通过ALTER TABLE或CREATE INDEX命令创建。对于大规模的数据集，通过ALTER TABLE（或者CREATE INDEX）命令创建全文索引要比把记录插入带有全文索引的空表更快。本文下面的讨论不再涉及全文索引，要了解更多信息，请参见MySQL documentation。</p>
<p><strong>三、单列索引与多列索引</strong><br />
索引可以是单列索引，也可以是多列索引。下面我们通过具体的例子来说明这两种索引的区别。假设有这样一个people表：</p>
<p>CREATE TABLE people ( peopleid SMALLINT NOT NULL AUTO_INCREMENT, firstname CHAR(50) NOT NULL, lastname CHAR(50) NOT NULL, age SMALLINT NOT NULL, townid SMALLINT NOT NULL, PRIMARY KEY (peopleid) );</p>
<p>下面是我们插入到这个people表的数据：</p>
<p>这个数据片段中有四个名字为“Mikes”的人（其中两个姓Sullivans，两个姓McConnells），有两个年龄为17岁的人，还有一个名字与众不同的Joe Smith。</p>
<p>这个表的主要用途是根据指定的用户姓、名以及年龄返回相应的peopleid。例如，我们可能需要查找姓名为Mike Sullivan、年龄17岁用户的peopleid（SQL命令为SELECT peopleid FROM people WHERE firstname=&#8217;Mike&#8217; AND lastname=&#8217;Sullivan&#8217; AND age=17;）。由于我们不想让MySQL每次执行查询就去扫描整个表，这里需要考虑运用索引。</p>
<p>首先，我们可以考虑在单个列上创建索引，比如firstname、lastname或者age列。如果我们创建firstname列的索引（ALTER TABLE people ADD INDEX firstname (firstname);），MySQL将通过这个索引迅速把搜索范围限制到那些firstname=&#8217;Mike&#8217;的记录，然后再在这个“中间结果集”上进行其他条件的搜索：它首先排除那些lastname不等于“Sullivan”的记录，然后排除那些age不等于17的记录。当记录满足所有搜索条件之后，MySQL就返回最终的搜索结果。</p>
<p>由于建立了firstname列的索引，与执行表的完全扫描相比，MySQL的效率提高了很多，但我们要求MySQL扫描的记录数量仍旧远远超过了实际所需要的。虽然我们可以删除firstname列上的索引，再创建lastname或者 age列的索引，但总地看来，不论在哪个列上创建索引搜索效率仍旧相似。</p>
<p>为了提高搜索效率，我们需要考虑运用多列索引。如果为firstname、lastname和age这三个列创建一个多列索引，MySQL只需一次检索就能够找出正确的结果！下面是创建这个多列索引的SQL命令：</p>
<p>ALTER TABLE people ADD INDEX fname_lname_age (firstname,lastname,age);</p>
<p>由于索引文件以B-树格式保存，MySQL能够立即转到合适的firstname，然后再转到合适的lastname，最后转到合适的age。在没有扫描数据文件任何一个记录的情况下，MySQL就正确地找出了搜索的目标记录！</p>
<p>那么，如果在firstname、lastname、age这三个列上分别创建单列索引，效果是否和创建一个firstname、lastname、 age的多列索引一样呢？答案是否定的，两者完全不同。当我们执行查询的时候，MySQL只能使用一个索引。如果你有三个单列的索引，MySQL会试图选择一个限制最严格的索引。但是，即使是限制最严格的单列索引，它的限制能力也肯定远远低于firstname、lastname、age这三个列上的多列索引。</p>
<p><strong>四、最左前缀</strong><br />
多列索引还有另外一个优点，它通过称为最左前缀（Leftmost Prefixing）的概念体现出来。继续考虑前面的例子，现在我们有一个firstname、lastname、age列上的多列索引，我们称这个索引为fname_lname_age。当搜索条件是以下各种列的组合时，MySQL将使用fname_lname_age索引：</p>
<p>firstname，lastname，age<br />
firstname，lastname<br />
firstname<br />
从另一方面理解，它相当于我们创建了(firstname，lastname，age)、(firstname，lastname)以及(firstname)这些列组合上的索引。下面这些查询都能够使用这个fname_lname_age索引：</p>
<p>SELECT peopleid FROM people WHERE firstname=&#8217;Mike&#8217; AND lastname=&#8217;Sullivan&#8217; AND age=&#8217;17&#8242;; SELECT peopleid FROM people WHERE firstname=&#8217;Mike&#8217; AND lastname=&#8217;Sullivan&#8217;; SELECT peopleid FROM people WHERE firstname=&#8217;Mike&#8217;; The following queries cannot use the index at all: SELECT peopleid FROM people WHERE lastname=&#8217;Sullivan&#8217;; SELECT peopleid FROM people WHERE age=&#8217;17&#8242;; SELECT peopleid FROM people WHERE lastname=&#8217;Sullivan&#8217; AND age=&#8217;17&#8242;;</p>
<p><strong>五、选择索引列</strong><br />
在性能优化过程中，选择在哪些列上创建索引是最重要的步骤之一。可以考虑使用索引的主要有两种类型的列：在WHERE子句中出现的列，在join子句中出现的列。请看下面这个查询：</p>
<p>SELECT age ## 不使用索引<br />
FROM people WHERE firstname=&#8217;Mike&#8217; ## 考虑使用索引<br />
AND lastname=&#8217;Sullivan&#8217; ## 考虑使用索引</p>
<p>这个查询与前面的查询略有不同，但仍属于简单查询。由于age是在SELECT部分被引用，MySQL不会用它来限制列选择操作。因此，对于这个查询来说，创建age列的索引没有什么必要。下面是一个更复杂的例子：</p>
<p>SELECT people.age, ##不使用索引<br />
town.name ##不使用索引<br />
FROM people LEFT JOIN town ON<br />
people.townid=town.townid ##考虑使用索引<br />
WHERE firstname=&#8217;Mike&#8217; ##考虑使用索引<br />
AND lastname=&#8217;Sullivan&#8217; ##考虑使用索引</p>
<p>与前面的例子一样，由于firstname和lastname出现在WHERE子句中，因此这两个列仍旧有创建索引的必要。除此之外，由于town表的townid列出现在join子句中，因此我们需要考虑创建该列的索引。</p>
<p>那么，我们是否可以简单地认为应该索引WHERE子句和join子句中出现的每一个列呢？差不多如此，但并不完全。我们还必须考虑到对列进行比较的操作符类型。MySQL只有对以下操作符才使用索引：&lt;，&lt;=，=，&gt;，&gt;=，BETWEEN，IN，以及某些时候的LIKE。可以在LIKE操作中使用索引的情形是指另一个操作数不是以通配符（%或者_）开头的情形。例如，“SELECT peopleid FROM people WHERE firstname LIKE &#8216;Mich%&#8217;;”这个查询将使用索引，但“SELECT peopleid FROM people WHERE firstname LIKE &#8216;%ike&#8217;;”这个查询不会使用索引。</p>
<p><strong>六、分析索引效率</strong><br />
现在我们已经知道了一些如何选择索引列的知识，但还无法判断哪一个最有效。MySQL提供了一个内建的SQL命令帮助</div>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-02-13 -- <a href="http://ourmysql.com/archives/970" title="类型转换对 MySQL 选择索引的影响">类型转换对 MySQL 选择索引的影响</a></li><li>2010-09-27 -- <a href="http://ourmysql.com/archives/850" title="冗余索引对查询效率的影响">冗余索引对查询效率的影响</a></li><li>2010-03-09 -- <a href="http://ourmysql.com/archives/817" title="Innodb 表和索引结构">Innodb 表和索引结构</a></li><li>2009-11-18 -- <a href="http://ourmysql.com/archives/783" title="Memory &#038; MyISAM 引擎小注意！">Memory &#038; MyISAM 引擎小注意！</a></li><li>2009-09-13 -- <a href="http://ourmysql.com/archives/737" title="Mysql combine index">Mysql combine index</a></li><li>2009-07-17 -- <a href="http://ourmysql.com/archives/631" title="查看索引使用情况">查看索引使用情况</a></li><li>2009-07-15 -- <a href="http://ourmysql.com/archives/623" title="memory型表支持Hash索引">memory型表支持Hash索引</a></li><li>2009-06-30 -- <a href="http://ourmysql.com/archives/601" title="mysql索引简单介绍">mysql索引简单介绍</a></li><li>2009-05-01 -- <a href="http://ourmysql.com/archives/551" title="mysql索引的一个技巧">mysql索引的一个技巧</a></li><li>2009-02-10 -- <a href="http://ourmysql.com/archives/445" title="MySQL里建立索引应该考虑数据库引擎的类型">MySQL里建立索引应该考虑数据库引擎的类型</a></li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/index" title="index" rel="tag">index</a>, <a href="http://ourmysql.com/archives/category/basic" title="MySQL基础知识" rel="tag">MySQL基础知识</a>, <a href="http://ourmysql.com/archives/tag/%e7%b4%a2%e5%bc%95" title="索引" rel="tag">索引</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/i3ZdALQZQEFa8QfjeGfbyoWjIDU/0/da"><img src="http://feedads.g.doubleclick.net/~a/i3ZdALQZQEFa8QfjeGfbyoWjIDU/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/i3ZdALQZQEFa8QfjeGfbyoWjIDU/1/da"><img src="http://feedads.g.doubleclick.net/~a/i3ZdALQZQEFa8QfjeGfbyoWjIDU/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1073/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[存储引擎基础知识]InnoDB与MyISAM的六大区别</title>
		<link>http://ourmysql.com/archives/1069</link>
		<comments>http://ourmysql.com/archives/1069#comments</comments>
		<pubDate>Sun, 25 Mar 2012 14:16:05 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL基础知识]]></category>
		<category><![CDATA[InnoDB]]></category>
		<category><![CDATA[MyISAM]]></category>
		<category><![CDATA[存储引擎]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1069</guid>
		<description><![CDATA[本文主要整理了Mysql 两大常用的存储引擎MyISAM，InnoDB的六大常见区别，来源于Mysql手册以及互联网的资料]]></description>
			<content:encoded><![CDATA[<p>本文主要整理了Mysql 两大常用的存储引擎MyISAM，InnoDB的六大常见区别，来源于Mysql手册以及互联网的资料</p>
<p>&nbsp;</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td colspan="3" valign="top" width="568">
<p align="center"><strong><a href="http://ourmysql.com/archives/tag/innodb" class="st_tag internal_tag" rel="tag" title="标签 InnoDB 下的日志">InnoDB</a></strong><strong>与</strong><strong><a href="http://ourmysql.com/archives/tag/myisam" class="st_tag internal_tag" rel="tag" title="标签 MyISAM 下的日志">Myisam</a></strong><strong>的六大区别</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="121">&nbsp;</td>
<td valign="top" width="273"><strong>MyISAM</strong></td>
<td valign="top" width="175"><strong>InnoDB</strong></td>
</tr>
<tr>
<td valign="top" width="121"><strong>构成上的区别：</strong></td>
<td valign="top" width="273">每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始，扩展名指出文件类型。</p>
<p>.frm文件存储表定义。</p>
<p>数据文件的扩展名为.MYD (MYData)。</p>
<p>索引文件的扩展名是.MYI (MYIndex)。</td>
<td valign="top" width="175">基于磁盘的资源是InnoDB表空间数据文件和它的日志文件，InnoDB 表的大小只受限于操作系统文件的大小，一般为 2GB</td>
</tr>
<tr>
<td valign="top" width="121"><strong>事务处理上方面</strong><strong>:</strong></td>
<td valign="top" width="273">MyISAM类型的表强调的是性能，其执行数度比InnoDB类型更快，但是不提供事务支持</td>
<td valign="top" width="175">InnoDB提供事务支持事务，外部键等高级数据库功能</td>
</tr>
<tr>
<td valign="top" width="121"><strong>SELECT UPDATE,INSERT</strong><strong>，</strong><strong>Delete</strong><strong>操作</strong><strong></strong></td>
<td valign="top" width="273">如果执行大量的SELECT，MyISAM是更好的选择</td>
<td valign="top" width="175"><strong>1.</strong>如果你的数据执行大量的<strong>INSERT</strong><strong>或</strong><strong>UPDATE</strong>，出于性能方面的考虑，应该使用InnoDB表</p>
<p><strong>2.DELETE FROM table</strong>时，InnoDB不会重新建立表，而是一行一行的删除。</p>
<p><strong>3.LOAD TABLE FROM MASTER</strong>操作对InnoDB是不起作用的，解决方法是首先把InnoDB表改成MyISAM表，导入数据后再改成InnoDB表，但是对于使用的额外的InnoDB特性（例如外键）的表不适用</td>
</tr>
<tr>
<td valign="top" width="121"><strong>对</strong><strong>AUTO_INCREMENT</strong><strong>的操作</strong></p>
<p><strong> </strong></td>
<td valign="top" width="273">每表一个AUTO_INCREMEN列的内部处理。</p>
<p><strong>MyISAM</strong><strong>为</strong><strong>INSERT</strong><strong>和</strong><strong>UPDATE</strong><strong>操作自动更新这一列</strong>。这使得AUTO_INCREMENT列更快（至少10%）。在序列顶的值被删除之后就不能再利用。(当AUTO_INCREMENT列被定义为多列索引的最后一列，可以出现重使用从序列顶部删除的值的情况）。</p>
<p>AUTO_INCREMENT值可用ALTER TABLE或myisamch来重置</p>
<p>对于AUTO_INCREMENT类型的字段，InnoDB中必须包含只有该字段的索引，但是在MyISAM表中，可以和其他字段一起建立联合索引</p>
<p>更好和更快的auto_increment处理</td>
<td valign="top" width="175">如果你为一个表指定AUTO_INCREMENT列，在数据词典里的InnoDB表句柄包含一个名为自动增长计数器的计数器，它被用在为该列赋新值。</p>
<p>自动增长计数器仅被存储在主内存中，而不是存在磁盘上</p>
<p>关于该计算器的算法实现，请参考</p>
<p><strong>AUTO_INCREMENT</strong><strong>列在</strong><strong>InnoDB</strong><strong>里如何工作</strong></td>
</tr>
<tr>
<td valign="top" width="121"><strong>表的具体行数</strong><strong></strong></td>
<td valign="top" width="273">select count(*) from table,MyISAM只要简单的读出保存好的行数，注意的是，当count(*)语句包含 where条件时，两种表的操作是一样的</td>
<td valign="top" width="175">InnoDB 中不保存表的具体行数，也就是说，执行select count(*) from table时，InnoDB要扫描一遍整个表来计算有多少行</td>
</tr>
<tr>
<td valign="top" width="121"><strong>锁</strong><strong></strong></td>
<td valign="top" width="273">表锁</td>
<td valign="top" width="175">提供行锁(locking on row level)，提供与 Oracle 类型一致的不加锁读取(non-locking read in<br />
SELECTs)，另外，InnoDB表的行锁也不是绝对的，如果在执行一个SQL语句时MySQL不能确定要扫描的范围，InnoDB表同样会锁全表，例如update table set num=1 where name like “%aaa%”</td>
</tr>
</tbody>
</table>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2012-02-22 -- <a href="http://ourmysql.com/archives/1020" title="MyISAM和InnoDB的不同优化">MyISAM和InnoDB的不同优化</a></li><li>2009-07-13 -- <a href="http://ourmysql.com/archives/616" title="MyISAM和InnoDB的插入性能测试">MyISAM和InnoDB的插入性能测试</a></li><li>2008-10-14 -- <a href="http://ourmysql.com/archives/156" title="MySQL中MyISAM引擎与InnoDB引擎性能简单测试">MySQL中MyISAM引擎与InnoDB引擎性能简单测试</a></li><li>2008-10-14 -- <a href="http://ourmysql.com/archives/153" title="MyISAM和InnoDB的异同">MyISAM和InnoDB的异同</a></li><li>2012-03-25 -- <a href="http://ourmysql.com/archives/1063" title="MySQL 5.0 5.1 下InnoDB的预热方法">MySQL 5.0 5.1 下InnoDB的预热方法</a></li><li>2012-03-25 -- <a href="http://ourmysql.com/archives/1061" title="测试快速关闭innodb的方法">测试快速关闭innodb的方法</a></li><li>2012-03-25 -- <a href="http://ourmysql.com/archives/1059" title="MySQL5.1 InnoDB Plugin 启动信息详解 以及与MySQL5.0的InnoDB启动信息对比">MySQL5.1 InnoDB Plugin 启动信息详解 以及与MySQL5.0的InnoDB启动信息对比</a></li><li>2012-03-25 -- <a href="http://ourmysql.com/archives/1056" title="安装mysql with innodb 支持，支持事务的mysql">安装mysql with innodb 支持，支持事务的mysql</a></li><li>2011-07-22 -- <a href="http://ourmysql.com/archives/956" title="“ERROR 1235 (42000): skip-innodb is defined”的误导">“ERROR 1235 (42000): skip-innodb is defined”的误导</a></li><li>2011-03-30 -- <a href="http://ourmysql.com/archives/920" title="InnoDB的多版本一致性读的实现 ">InnoDB的多版本一致性读的实现 </a></li></ul>
	标签：<a href="http://ourmysql.com/archives/tag/innodb" title="InnoDB" rel="tag">InnoDB</a>, <a href="http://ourmysql.com/archives/tag/myisam" title="MyISAM" rel="tag">MyISAM</a>, <a href="http://ourmysql.com/archives/category/basic" title="MySQL基础知识" rel="tag">MySQL基础知识</a>, <a href="http://ourmysql.com/archives/tag/%e5%ad%98%e5%82%a8%e5%bc%95%e6%93%8e" title="存储引擎" rel="tag">存储引擎</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/r552ynMHKGpsOz1zIwUDlMJFwts/0/da"><img src="http://feedads.g.doubleclick.net/~a/r552ynMHKGpsOz1zIwUDlMJFwts/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/r552ynMHKGpsOz1zIwUDlMJFwts/1/da"><img src="http://feedads.g.doubleclick.net/~a/r552ynMHKGpsOz1zIwUDlMJFwts/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1069/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL慢语句处理</title>
		<link>http://ourmysql.com/archives/1067</link>
		<comments>http://ourmysql.com/archives/1067#comments</comments>
		<pubDate>Sun, 25 Mar 2012 14:13:57 +0000</pubDate>
		<dc:creator>OurMySQL</dc:creator>
				<category><![CDATA[MySQL优化设计]]></category>
		<category><![CDATA[慢语句]]></category>

		<guid isPermaLink="false">http://ourmysql.com/?p=1067</guid>
		<description><![CDATA[依稀记得是上周开始部署了mysql的节点监控程序到线上环境，设定的查询周期是90秒一次，超过10秒的查询和超过x个连接的时候就会发报警邮件给DBA。这个在镜像库部署的时候，会经常发现开发人员查一下没用索引的语句或者没有limit n的语句，这些没语句会对数据库造成很大的影响，例如一个几千万条记录的大表要全部扫描，或者是不停的做filesort，对数据库和服务器造成io影响等。这是镜像库上面的情况。]]></description>
			<content:encoded><![CDATA[<p>依稀记得是上周开始部署了mysql的节点监控程序到线上环境，设定的查询周期是90秒一次，超过10秒的查询和超过x个连接的时候就会发报警邮件给DBA。</p>
<p>这个在镜像库部署的时候，会<strong>经常发现开发人员查一下没用索引的语句或者没有</strong><strong>limit n</strong><strong>的语句，这些没语句会对数据库造成很大的影响，例如一个几千万条记录的大表要全部扫描，或者是不停的做</strong><strong>filesort</strong><strong>，对数据库和服务器造成</strong><strong>io</strong><strong>影响</strong>等。这是镜像库上面的情况。</p>
<p>而到了线上库，除了出现没有索引的语句，没有用limit的语句，还多了一个情况,<strong>mysql</strong><strong>连接数过多的问题。</strong>说到这里，先来看看以前我们的监控做法</p>
<p>1. 部署zabbix等开源分布式监控系统，获取每天的数据库的io，cpu，连接数</p>
<p>2. 部署每周性能统计，包含数据增加量，iostat，vmstat，datasize的情况</p>
<p>3. Mysql slowlog收集，列出top 10</p>
<p>&nbsp;</p>
<p>以前以为做了这些监控已经是很完美了，现在部署了mysql节点进程监控之后，才发现很多弊端</p>
<p>第一种做法的弊端: zabbix太庞大，而且不是在mysql内部做的监控，很多数据不是非常准备，现在一般都是用来查阅历史的数据情况</p>
<p>第二种做法的弊端：因为是每周只跑一次，很多情况没法发现和报警</p>
<p>第三种做法的弊端: 当节点的slowlog非常多的时候，top10就变得没意义了，而且很多时候会给出那些是一定要跑的定期任务语句给你。。参考的价值不大</p>
<p>&nbsp;</p>
<p><strong>最后总结一下节点监控的好处</strong></p>
<p>1． 轻量级的监控，而且是实时的，还可以根据实际的情况来定制和修改</p>
<p>2． 设置了过滤程序，可以对那些一定要跑的语句进行过滤</p>
<p>3． 及时发现那些没有用索引，或者是不合法的查询，虽然这很耗时去处理那些慢语句，但这样可以避免数据库挂掉，还是值得的</p>
<p>4.  在数据库出现连接数过多的时候，程序会自动保存当前数据库的processlist，DBA进行原因查找的时候这可是利器</p>
<p>5.  使用mysqlbinlog 来分析的时候，可以得到明确的数据库状态异常的时间段</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>目前没有相关的文章</li></ul>
	标签：<a href="http://ourmysql.com/archives/category/optimize" title="MySQL优化设计" rel="tag">MySQL优化设计</a>, <a href="http://ourmysql.com/archives/tag/%e6%85%a2%e8%af%ad%e5%8f%a5" title="慢语句" rel="tag">慢语句</a><br />

<p><a href="http://feedads.g.doubleclick.net/~a/3pkDUWhfIbDbg7EGA09FGrl_ReM/0/da"><img src="http://feedads.g.doubleclick.net/~a/3pkDUWhfIbDbg7EGA09FGrl_ReM/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/3pkDUWhfIbDbg7EGA09FGrl_ReM/1/da"><img src="http://feedads.g.doubleclick.net/~a/3pkDUWhfIbDbg7EGA09FGrl_ReM/1/di" border="0" ismap="true"></img></a></p>]]></content:encoded>
			<wfw:commentRss>http://ourmysql.com/archives/1067/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

