<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Some reminiscences, some memories &#187; mysql</title>
	<atom:link href="http://www.mikespook.com/index.php/tag/mysql/feed" rel="self" type="application/rss+xml" />
	<link>http://www.mikespook.com</link>
	<description>Just another boring day</description>
	<lastBuildDate>Tue, 10 Jan 2012 03:14:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Mysql 性能改进——最快实践</title>
		<link>http://www.mikespook.com/2009/06/mysql-%e6%80%a7%e8%83%bd%e6%94%b9%e8%bf%9b%e2%80%94%e2%80%94%e6%9c%80%e5%bf%ab%e5%ae%9e%e8%b7%b5/</link>
		<comments>http://www.mikespook.com/2009/06/mysql-%e6%80%a7%e8%83%bd%e6%94%b9%e8%bf%9b%e2%80%94%e2%80%94%e6%9c%80%e5%bf%ab%e5%ae%9e%e8%b7%b5/#comments</comments>
		<pubDate>Mon, 29 Jun 2009 05:10:01 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[优化]]></category>
		<category><![CDATA[性能]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=361</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
没错，标题我没打错。这里不是最佳实践，而是最快实践。在服务器上线，巨大的压力导致相应缓慢的时候，最佳实践已经毫无意义。这个时候，目的只有一个：最快改善性能，给开发人员重新设计、调整应用留出一定的时间。

这里不是细腻的微调，而是最粗旷的拉升。用最简单（可快速实施），变更最少（尽量避免变更引入新的 bug 和问题）的方法迅速改善 mysql 的性能。所以我这里的最快实践，不一定是最好的，不一定是最有效的，但是一定是最快能看到性能改善的方法。

tmp_table_size

<span class="readmore"><a href="http://www.mikespook.com/2009/06/mysql-%e6%80%a7%e8%83%bd%e6%94%b9%e8%bf%9b%e2%80%94%e2%80%94%e6%9c%80%e5%bf%ab%e5%ae%9e%e8%b7%b5/" title="Mysql 性能改进——最快实践">阅读全文——共1489字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>没错，标题我没打错。这里不是最佳实践，而是最快实践。在服务器上线，巨大的压力导致相应缓慢的时候，最佳实践已经毫无意义。这个时候，目的只有一个：最快改善性能，给开发人员重新设计、调整应用留出一定的时间。</p>
<p>这里不是细腻的微调，而是最粗旷的拉升。用最简单（可快速实施），变更最少（尽量避免变更引入新的 bug 和问题）的方法迅速改善 mysql 的性能。所以我这里的最快实践，不一定是最好的，不一定是最有效的，但是一定是最快能看到性能改善的方法。<span id="more-361"></span></p>
<h1>tmp_table_size</h1>
<p>实施难度：容易</p>
<p>实施时间：短</p>
<p>实施效果：明显</p>
<p>tmp_table_size 默认 32M，根据手册上的说法，这个限制了内存临时表的大小。增大这个值可以立刻改善 mysql 的性能，虽然不是万灵丹，但却是个救命药。网上解释很多不多说了。</p>
<h1>thread_cache_size</h1>
<p>实施难度：容易</p>
<p>实施时间：短</p>
<p>实施效果：一般</p>
<p>在使用 SHOW STATUS; 查看 mysql 参数时，如果 thread_created 这个值很高的话，可以将 thread_cache_size 设置得大一些。内存允许的情况下，128 或者更大都是可以考虑的。mysql 通过内建的线程复用机制来实现了一个连接池。如果你的应用出现 max connection 的情况话（php 发生这种情况尤为严重），还是请开启 thread_cache_size 吧。</p>
<h1>索引</h1>
<p>实施难度：一般</p>
<p>实施时间：一般</p>
<p>实施效果：明显</p>
<p>不知什么时候，有一位高人说“对于 where 查询的条件字段，都加上索引会提高查询效率”。于是大家忙不停的将所有可能的字段都加上了索引。潘多拉的魔盒从此打开……</p>
<p>上图：</p>
<p><a href="http://www.mikespook.com/wp-content/uploads/2009/06/nouse_index.JPG"><img class="size-full wp-image-362 alignnone" title="nouse_index" src="http://www.mikespook.com/wp-content/uploads/2009/06/nouse_index.JPG" alt="nouse_index" width="554" height="114" /></a></p>
<p>这样的表中，假设 t2 的总记录数不超过10 条。如果 t2_c1 这个字段有这样的查询 select * from t1 where t2_c1 = 1; 不少童鞋都会在 t2_c1 上加多一个索引，为了让这个搜索更快一些。这会是真的么？</p>
<p>显然不是！</p>
<p>在 t2_c1 字段的数据差异很小的情况下，使用索引不会比全表扫描快。更有可能的情况是，索引不但导致 t1 表数据修改变慢，同时导致查询变慢。为什么？大家先学习一下“随机存取”和“连续预读”的差异就明白了。不必要的索引还是去掉吧！我甚至见过索引比数据都大的表，如果读索引的速度比直接读数据都慢，这会有什么后果？太可怕了……</p>
<h1>只查要用到的列</h1>
<p>实施难度：大</p>
<p>实施时间：长</p>
<p>实施效果：明显</p>
<p>这绝对是老生常谈，但是总有人不在意。他们心中有疑问：“为什么？为什么？为什么 SELECT * FROM table 会慢？”他们心中有梦想：“这不可能吧，扫描的都是那么多数据，那几个表。索引使用也一样有效。”</p>
<p>的确，数据扫描的记录数不会因为列的限制而减少，索引的影响也不因为列的限制而改变。不过每个内存页面中存放记录的条数会因为列的不同发不同。为了说明这个问题，我专门构造了一个 140万条记录，每条记录大约 50字节的表。如果应用只是用结果集的主键，使用 select * from table 查询比使用 select id from table 慢了近一倍。其实道理也很简单，结果集行记录变小了，内存页面中每个页面可以放的记录数就多了。内存页面的交换就减少，I/O减少。同时采用连续预读I/O效率提高。进而查询速度提升。</p>
<p>在业务逻辑都确定的情况下，每个方法所用到的字段也都确定了。这个时候可以快速将那些没有用到的字段从查询语句中剔除。查询效率自然提升。</p>
<p>按照上面的顺序进行快速优化，可以在不改变业务逻辑和代码逻辑的情况下迅速提升 mysql 性能，同时可避免应用长时间下下线，也为进一步优化争取时间。何乐而不为？</p>
<p>如果还有更快、更好的方法，我再补充吧！</p>
<p><strong>“性能是改进出来的，不是设计出来的！”</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2009/06/mysql-%e6%80%a7%e8%83%bd%e6%94%b9%e8%bf%9b%e2%80%94%e2%80%94%e6%9c%80%e5%bf%ab%e5%ae%9e%e8%b7%b5/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MySQL InnoDB 隔离级别探索</title>
		<link>http://www.mikespook.com/2009/05/mysql-innodb-%e9%9a%94%e7%a6%bb%e7%ba%a7%e5%88%ab%e6%8e%a2%e7%b4%a2/</link>
		<comments>http://www.mikespook.com/2009/05/mysql-innodb-%e9%9a%94%e7%a6%bb%e7%ba%a7%e5%88%ab%e6%8e%a2%e7%b4%a2/#comments</comments>
		<pubDate>Tue, 12 May 2009 09:28:53 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[innodb]]></category>
		<category><![CDATA[isolation]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[隔离级别]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=317</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
概述

本文会简单介绍 Mysql 使用的支持事务的存储引擎 InnoDB 的隔离级别，以及每个隔离级别下回产生的并发问题。同时为了更加深刻的理解 InnoDB 引擎的隔离级别，还会探讨如何通过加锁解决不同隔离级别下的并发问题。本文使用的实验环境是 mysql-5.1.33-win32，其他版本的 MySQL 可能会有不同。

隔离级别标准

<span class="readmore"><a href="http://www.mikespook.com/2009/05/mysql-innodb-%e9%9a%94%e7%a6%bb%e7%ba%a7%e5%88%ab%e6%8e%a2%e7%b4%a2/" title="MySQL InnoDB 隔离级别探索">阅读全文——共5060字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<h1>概述</h1>
<p>本文会简单介绍 Mysql 使用的支持事务的存储引擎 InnoDB 的隔离级别，以及每个隔离级别下回产生的并发问题。同时为了更加深刻的理解 InnoDB 引擎的隔离级别，还会探讨如何通过加锁解决不同隔离级别下的并发问题。本文使用的实验环境是 mysql-5.1.33-win32，其他版本的 MySQL 可能会有不同。<span id="more-317"></span></p>
<h1>隔离级别标准</h1>
<p>SQL 标准中定义了四个隔离级别，他们分别是：</p>
<div>
<table id="n15_" border="1" cellspacing="0" cellpadding="3" width="100%" bordercolor="#000000">
<tbody>
<tr>
<td width="50%">
<pre>READ-UNCOMMITTED</pre>
</td>
<td width="50%">读未提交</td>
</tr>
<tr>
<td width="50%">
<pre>READ-COMMITTED</pre>
</td>
<td width="50%">读提交</td>
</tr>
<tr>
<td width="50%">
<pre>REPEATABLE-READ</pre>
</td>
<td width="50%">可重复读</td>
</tr>
<tr>
<td width="50%">
<pre>SERIALIZABLE</pre>
</td>
<td width="50%">串行化</td>
</tr>
</tbody>
</table>
</div>
<p>在 InnoDB 中根据 SQL:1992 事务隔离级别，使用 REPEATABLE-READ 作为默认隔离级别。</p>
<h1>并发产生的问题</h1>
<p>为了说明下面的并发问题，首先建立一个表 foobar：</p>
<pre lang="SQL">DROP TABLE IF EXISTS `foobar`;
CREATE TABLE `foobar` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `value` int(10) unsigned NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;</pre>
<p>并插入一条数据：</p>
<pre lang="SQL">INSERT INTO `foobar`(`value`) VALUES(0);</pre>
<p>为了避免隔离级别本身对并发问题的影响，需要将 MySQL 的全局隔离级别设置为最低的 READ-UNCOMMITTED：</p>
<pre lang="SQL">SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;</pre>
<p>同时为了模拟并发，下面的存储过程中使用了 SLEEP 函数，以保证语句按照预定顺序执行。</p>
<h3>脏读</h3>
<p>当两个事物并发时，事务 A 可以读到另事务 B 的中间状态的数据。若事务 B 回滚，或在之后的语句中修改了这个数据，就会有不一致的现象发生。<br />
例如，下面这两个事务：</p>
<pre lang="SQL">CREATE PROCEDURE `PROC_FOOBAR_A` ()
    BEGIN
        START TRANSACTION;
        UPDATE `foobar` SET `value`= 100 WHERE `id` = 1;
        #SELECT SLEEP(10);
        ROLLBACK;
    END</pre>
<pre lang="SQL">CREATE PROCEDURE `PROC_FOOBAR_B` ()
    BEGIN
        START TRANSACTION;
        SELECT * FROM `foobar` f WHERE `id` = 1;
        COMMIT;
    END</pre>
<p>其中，PROC_FOOBAR_A 的 SLEEP 函数的调用是为了模拟并发，让 PROC_FOOBAR_B 的语句正好在 UPDATE 和 ROLLBACK 之间执行。</p>
<p>在两个 session 中按次序执行这两个存储过程。会发现，PROC_FOOBAR_B 的查询语句读取到 id = 1 的记录中 value = 100，而在存储过程执行过后，由于 PROC_FOOBAR_B 的 ROLLBACK 语句的关系，foobar 包中 id = 1 的记录中实际 value = 0。</p>
<h3>写覆盖</h3>
<p>当两个事物并发时，事务 A 读出、运算、修改了某一个数据，事务 B 在事务 A 修改之前读出了同一份数据，在事务 A 修改之后修改了同一数据。从而造成事务 A 的修改丢失。<br />
创建下面的存储过程，使得每执行一次这个存储过程 id = 1 的 value 就增加 100：</p>
<pre lang="SQL">CREATE PROCEDURE  `PROC_FOOBAR_C`()
    BEGIN
        START TRANSACTION;
        SELECT @v:=`value` FROM `foobar` WHERE `id` = 1;
        #SELECT SLEEP(10);
        UPDATE `foobar` SET `value`= @v + 100 WHERE `id` = 1;
        COMMIT;
    END</pre>
<p>在两个不同的 session 中顺序执行两次 PROC_FOOBAR_C，由于 SLEEP 函数的存在，就保证了第二次执行的 SELECT 一定在第一次执行的查询语句之后，更新语句之前。假设初始 value = 0，由于写覆盖的存在，两次 PROC_FOOBAR_C 的执行，value 只增长了 100，而不是预期的 200。</p>
<p>实际上，由于 InnoDB 的特性，PROC_FOOBAR_C 这个存储过程即使在 SERIALIZABLE 级别下也是会产生问题的。后面会详细解释这个问题产生的原因以及对应策略。</p>
<h3>不可重复读</h3>
<p>当两个事务并发时，事务 A 读出了数据，然后事务 B 修改了数据，这时事务 A 再次读出数据时，第一次读出的数据和第二次读出的数据不一致。</p>
<p>创建下面的存储过程：</p>
<pre lang="SQL">CREATE PROCEDURE `PROC_FOOBAR_D` ()
    BEGIN
        START TRANSACTION;
        SELECT `value` FROM `foobar` WHERE `id` = 1;
        #SELECT SLEEP(10);
        SELECT `value` FROM `foobar` WHERE `id` = 1;
        COMMIT;
    END</pre>
<pre lang="SQL">CREATE PROCEDURE `PROC_FOOBAR_E` ()
    BEGIN
        START TRANSACTION;
        UPDATE `foobar` SET `value`= 1 WHERE `id` = 1;
        COMMIT;
    END</pre>
<p>在两个 session 中按次序执行这两个存储过程。会发现 PROC_FOOBAR_D 的两次查询结果不一致，对于一些需要复审数据的业务中这会带来严重的影响。</p>
<h3>幻像</h3>
<p>当两个事务并发时，事务 A 读出了一组数据，然后事务 B 在这组数据上进行了增加或者删除，这样就产生了幻像。</p>
<p>创建下面的存储过程：</p>
<pre lang="SQL">CREATE PROCEDURE `PROC_FOOBAR_F` ()
    BEGIN
        START TRANSACTION;
        SELECT * FROM `foobar` WHERE `value` > 100;
        #SELECT SLEEP(10);
        SELECT * FROM `foobar` WHERE `value` > 100;
        DELETE FROM `foobar` WHERE `value` > 100;
        SELECT * FROM `foobar` WHERE `value` > 100;
        COMMIT;
    END</pre>
<pre lang="SQL">CREATE PROCEDURE `PROC_FOOBAR_G` ()
    BEGIN
        START TRANSACTION;
        SELECT * FROM `foobar` WHERE `value` > 100;
        INSERT INTO `foobar` (`value`) VALUES(101),(102),(103);
        SELECT * FROM `foobar` WHERE `value` > 100;
        #SELECT SLEEP(10);
        SELECT * FROM `foobar` WHERE `value` > 100;
        COMMIT;
    END</pre>
<p>并且向表中再插入两条记录：</p>
<pre lang="SQL">INSERT INTO `foobar` (`value`) VALUES(200),(300);</pre>
<p>在两个 session 中按次序执行这两个存储过程。PROC_FOOBAR_F 在查询时在第一次查询时有 value = {200, 300}；在第二次查询时，由于 PROC_FOOBAR_G 插入了三条记录 value = {101, 102, 103}，从而得到结果 value = {200, 300, 101, 102, 103}；执行 DELETE 语句执行后，第三次查询得到空数据集。PROC_FOOBAR_G 第一次查询时得到 value = {200, 300}，第二次查询时已经插入三条记录得到 value = {200, 300, 101, 102, 103}，在 PROC_FOOBAR_F 执行了删除操作后的查询反而得到 value = {101, 102, 103}。两个存储过程在执行了删除后得到的数据产生了一个不一致现象。</p>
<p>需要说明的是，这里存在 MySQL 的 InnoDB 在处理上面的特殊性，与其他数据库产生的幻像呈现方式并不一致。</p>
<h1>不同隔离级别下可能的并发问题</h1>
<p>下表描述了四个隔离级别和并发时产生的问题之间的关系，使用以上存储过程进行测试。这里由于 InnoDB 的全局隔离级别是在设置了隔离级别之后的所有新的 session 都使用的默认隔离级别，这里为了方便起见（不用反复开启新的 session），只设置 session 的隔离级别：</p>
<pre lang="SQL">SET SESSION TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED| REPEATABLE READ | SERIALIZABLE};</pre>
<div>
<table id="nj1h" border="1" cellspacing="0" cellpadding="3" width="100%" bordercolor="#000000">
<tbody>
<tr>
<td width="20%"></td>
<td width="20%">脏读</td>
<td width="20%">写覆盖</td>
<td width="20%">不可重复读</td>
<td width="20%">幻像</td>
</tr>
<tr>
<td width="20%">
<pre>READ-UNCOMMITTED</pre>
</td>
<td width="20%">是</td>
<td width="20%">是</td>
<td width="20%">是</td>
<td width="20%">是</td>
</tr>
<tr>
<td width="20%">
<pre>READ-COMMITTED</pre>
</td>
<td width="20%">否</td>
<td width="20%">是</td>
<td width="20%">是</td>
<td width="20%">否</td>
</tr>
<tr>
<td width="20%">
<pre>REPEATABLE-READ</pre>
</td>
<td width="20%">否</td>
<td width="20%">是</td>
<td width="20%">否</td>
<td width="20%">否</td>
</tr>
<tr>
<td width="20%">
<pre>SERIALIZABLE</pre>
</td>
<td width="20%">否</td>
<td width="20%">否（死锁）</td>
<td width="20%">否</td>
<td width="20%">否</td>
</tr>
</tbody>
</table>
</div>
<p>“是”表示会发生并发问题，“否”表示不会发生并发问题。</p>
<h3>写覆盖到底怎么了？</h3>
<p>可以发现，使用 PROC_FOOBAR_C 做写覆盖的测试时，在所有隔离级别都未能真正解决写覆盖的问题。虽然在 SERIALIZABLE 级别并未发生写覆盖，但是两个事务中必有一个因为死锁而异常中断。这是因为 InnoDB 对于 SELECT 语句处理的一些特殊性决定的。</p>
<p>InnoDB 在 READ-UNCOMMITTED、READ-COMMITTED 和 REPEATABLE-READ 级别中，未明确加锁的 SELECT 语句都使用“持续非锁定读”的查询方式，这种方式下，查询语句不对读取的表加任何锁。在事务内看到的是事务开始时刻前，所有已经提交的事务的结果的快照（利用多版本的方式）。而在 SERIALIZABLE 级别，未明确加锁的 SELECT 语句被隐式转换为 SELECT &#8230; LOCK IN SHARE MODE，由于增加了共享锁（读锁），两个并发的事务发生了资源争夺，导致了死锁的发生（默认情况下，MySQL 会中断代价小的那个事务的运行，通常是后执行的事务）。</p>
<p>为了验证这点，对 PROC_FOOBAR_C 做如下修改：</p>
<pre lang="SQL">CREATE PROCEDURE `PROC_FOOBAR_C_1`()
    BEGIN
        START TRANSACTION;
        SELECT @v:=`value` FROM foobar WHERE `id` = 1 LOCK IN SHARE MODE;
        #SELECT SLEEP(5);
        UPDATE `foobar` SET `value`= @v + 100 WHERE `id` = 1;
        COMMIT;
    END</pre>
<p>用同样的方式测试写覆盖，会发现在所有隔离级别下，都会发生死锁。</p>
<p>虽然通过加共享锁的方式解决了写覆盖的问题，但是每次都使用了死锁的方式来避免。这对于一个应用系统来说是很不好的，需要额外增加很多错误处理。</p>
<p>那么让事务真正可序列化的方法是加排他锁（写锁）：</p>
<pre lang="SQL">CREATE PROCEDURE `PROC_FOOBAR_C_2`()
    BEGIN
        START TRANSACTION;
        SELECT @v:=`value` FROM foobar WHERE `id` = 1 FOR UPDATE;
        #SELECT SLEEP(5);
        UPDATE `foobar` SET `value`= @v + 100 WHERE `id` = 1;
        COMMIT;
    END</pre>
<p>使用这种方式，可以让两个并发的事务执行，通过其中一个事务等待，而变成顺序执行（序列化执行）。也就真正解决了在 MySQL 的 InnoDB 引擎中的写覆盖问题。</p>
<h3>幻像是怎么样解决的？</h3>
<p>而对于幻像或者说幽灵问题，InnoDB 使用 Next-Key 锁定<del datetime="2009-05-12T15:10:34+00:00">，通俗的说也就是对索引加锁（谓词锁）</del>。Next-Key 锁定是联合了记录锁和间隙锁的一个锁形式，通常也被称作谓词锁。记录锁是指加在索引记录上的锁。间隙锁是指加在索引记录之间的缝隙、第一条记录前或最后一条记录后的锁。（基于<a href="http://www.mikale.org/" target="_blank">路神</a>的建议，这里采用更加严谨的方式来描述。）特别值得说明的是，在手册上有这样的描述：InnoDB对索引记录设置的锁定也映像索引记录之前的“间隙”。如果一个用户对一个索引上的记录R有共享或独占的锁定，另一个用户 不能紧接在R之前以索引的顺序插入一个新索引记录。说明 Next-Key 锁在记录上存在共享或独占锁时生效。由于 READ-UNCOMMITTED 级别实际上是不加任何锁的，所以 Next-Key 锁定并不在该级别生效。</p>
<h1>请帮助我</h1>
<p>本文是为了完成高级数据库这门课的学期作业“默认隔离级别下写覆盖的处理”而编写。但在实验过程中发现 InnoDB 并没有像 SQL Server 那样使用 READ-COMMITTED 作为默认隔离级别，同时对于查询和加锁方式也有不同。觉得有必要仔细探索一下 InnoDB 引擎的隔离级别，以及各个隔离级别下并发问题的处理方式。如果我在实验中有什么遗漏或者错误，务必请指出！谢谢！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2009/05/mysql-innodb-%e9%9a%94%e7%a6%bb%e7%ba%a7%e5%88%ab%e6%8e%a2%e7%b4%a2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>MYSQL 存储过程中，中文变量出现乱码的解决办法</title>
		<link>http://www.mikespook.com/2008/03/mysql-%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e4%b8%ad%ef%bc%8c%e4%b8%ad%e6%96%87%e5%8f%98%e9%87%8f%e5%87%ba%e7%8e%b0%e4%b9%b1%e7%a0%81%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95/</link>
		<comments>http://www.mikespook.com/2008/03/mysql-%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e4%b8%ad%ef%bc%8c%e4%b8%ad%e6%96%87%e5%8f%98%e9%87%8f%e5%87%ba%e7%8e%b0%e4%b9%b1%e7%a0%81%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95/#comments</comments>
		<pubDate>Thu, 20 Mar 2008 03:30:21 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[乱码]]></category>
		<category><![CDATA[存储过程]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/index.php/archives/105</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
昨天下班前发现有个存储过程有问题，无法正确的查询数据。

数据表和字段都是 utf8 存储的中文内容。在连接数据库后也使用 SET NAMES utf8; 设置了编码。但是在存储过程中无法查询中文内容的字段。由于一些逻辑处理的原因，存储过程使用了游标，同时定义了一些局部变量。



<span class="readmore"><a href="http://www.mikespook.com/2008/03/mysql-%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e4%b8%ad%ef%bc%8c%e4%b8%ad%e6%96%87%e5%8f%98%e9%87%8f%e5%87%ba%e7%8e%b0%e4%b9%b1%e7%a0%81%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95/" title="MYSQL 存储过程中，中文变量出现乱码的解决办法">阅读全文——共695字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>昨天下班前发现有个存储过程有问题，无法正确的查询数据。</p>
<p>数据表和字段都是 utf8 存储的中文内容。在连接数据库后也使用 SET NAMES utf8; 设置了编码。但是在存储过程中无法查询中文内容的字段。由于一些逻辑处理的原因，存储过程使用了游标，同时定义了一些局部变量。</p>
<p><span id="more-105"></span></p>
<p>CREATE PROCEDURE `PROC_FOOBAR`(id INTEGER)<br />
BEGIN</p>
<p>DECLARE user_id VARCHAR(32) ;</p>
<p>&#8211; 省略代码若干</p>
<p>END</p>
<p>user_id 在代码中参与了一个 查询  SELECT  *  FROM `table1` WHERE `UID` = user_id。</p>
<p>在存储过程中输出 user_id 发现英文内容正常，但是中文内容乱码。</p>
<p>开始以为存储过程的编码有问题，但是不使用变量的内容又正常。上网查了一下，有人遇到同样的问题：</p>
<p><a href="http://www.google.com/url?sa=t&amp;ct=res&amp;cd=1&amp;url=http%3A%2F%2Fbugs.mysql.com%2Fbug.php%3Fid%3D28567&amp;ei=aNXhR-GwGJGq6wOtoaC0CA&amp;usg=AFQjCNEy654sRAlz-r0xRYGcMuR34i0W3Q&amp;sig2=LD5T7o5d_96SV3YpQJRROA">http://www.google.com/url?sa=t&amp;ct=res&amp;cd=1&amp;url=http%3A%2F%2Fbugs.mysql.com%2Fbug.php%3Fid%3D28567&amp;ei=aNXhR-GwGJGq6wOtoaC0CA&amp;usg=AFQjCNEy654sRAlz-r0xRYGcMuR34i0W3Q&amp;sig2=LD5T7o5d_96SV3YpQJRROA</a></p>
<p>但是显然，他的问题只要正确设置编码即可。</p>
<p>突然灵光一现，NND，character set 这个东西我忽略了。正解如下：</p>
<p>CREATE PROCEDURE `PROC_FOOBAR`(id INTEGER)<br />
BEGIN</p>
<p>DECLARE code, user_id VARCHAR(32) CHARACTER SET utf8;</p>
<p>&#8211; 省略代码若干</p>
<p>END</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2008/03/mysql-%e5%ad%98%e5%82%a8%e8%bf%87%e7%a8%8b%e4%b8%ad%ef%bc%8c%e4%b8%ad%e6%96%87%e5%8f%98%e9%87%8f%e5%87%ba%e7%8e%b0%e4%b9%b1%e7%a0%81%e7%9a%84%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

