<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾mysql推荐</title>
    <link>https://itindex.net/tags/mysql</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/tags/mysql</link>
    </image>
    <item>
      <title>delete 清空表之后，磁盘空间未发生变化？</title>
      <link>https://itindex.net/detail/62777-delete-%E7%A3%81%E7%9B%98%E7%A9%BA%E9%97%B4-%E5%8F%98%E5%8C%96</link>
      <description>&lt;p&gt;  &lt;a href="https://mp.weixin.qq.com/s/6xo-p0z3HIE4tlsIWqqFBg" rel="nofollow noreferrer"&gt;上篇文章&lt;/a&gt;结尾和小伙伴们留了一个小问题，就是关于   &lt;code&gt;optimize table&lt;/code&gt; 命令，今天我想花点时间再来和小伙伴们聊一聊这个话题。&lt;/p&gt; &lt;h2&gt;1. 删除空洞&lt;/h2&gt; &lt;h3&gt;1.1 案例展示&lt;/h3&gt; &lt;p&gt;首先我们先来看这样一个例子。&lt;/p&gt; &lt;p&gt;我现在有一个名为 sakila 的数据库，该库中有一个 film 表，这个表中有 1000 条记录，我么先来看下这 1000 条记录占用了多少存储空间：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043874721" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;小伙伴们可以看到，这个文件大小是   &lt;code&gt;360448&lt;/code&gt; 个字节。&lt;/p&gt; &lt;p&gt;我们现在执行 delete 命令将这个表清空：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;delete from film;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;然后再来查看这个文件的大小：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043874722" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;小伙伴们看到，这个表中的数据没有减少，甚至还增加了！这是咋回事？&lt;/p&gt; &lt;h3&gt;1.2 分析&lt;/h3&gt; &lt;blockquote&gt;以下所说的删除皆指通过 delete 命令删除，不包括通过 truncate/drop 删除。&lt;/blockquote&gt; &lt;p&gt;MySQL 中的数据删除操作有点像我们平日里做业务开发时用的逻辑删除，当你想要删除掉一行数据的时候，这行数据其实并没有被真正的删除掉，只是暂时给标记为删除了而已。&lt;/p&gt; &lt;p&gt;经过前面文章的介绍，小伙伴们应该已经清楚，MySQL 表中的数据最终是以 B+Tree 的形式保存在磁盘中的，当你要删除一条记录的时候，那么对应的叶子上的数据就会被标记为已删除，类似下面这样：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043874723" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;当 ID 为 6 的记录被删除掉之后，这块空间并不会立马被释放出来，MySQL 只是在这个位置做一个删除标记，将来要是还有一个 ID 为 6 的数据被插入进来，就会插入到这里。&lt;/p&gt; &lt;p&gt;因此我们看到，一张表在经过 N 多次删除之后，就会出现大量这种情况，这种就称之为删除空洞。&lt;/p&gt; &lt;h2&gt;2. 插入空洞&lt;/h2&gt; &lt;p&gt;前面所说的删除会造成空洞，其实插入也会造成空洞。&lt;/p&gt; &lt;p&gt;松哥在之前的文章中和小伙伴们分享过，InnoDB 引擎的表中不建议使用随机字符串作为 ID，因为随机字符串插入会造成页分裂。页分裂之后，在分裂之前的叶子中，也有可能会空出来新的空间，造成空洞。&lt;/p&gt; &lt;p&gt;例如下面这个例子：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043874724" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;在上图这个 B+Tree 中，继续插入 5，就会造成页分裂，页分裂之后，2 所在的数据页（InnoDB 操作磁盘的最小单位是数据页）就会有空余，这也是空洞的一种。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043874725" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;当然更新索引上的值也会造成空洞，因为更新相当于插入+删除。&lt;/p&gt; &lt;h2&gt;3. optimize table&lt;/h2&gt; &lt;p&gt;想要解决这个问题，我们可以使用 optimize table 命令来实现。该命令可以用来重新整理表空间，并优化文件碎片。接下来我们针对前面 1.1 小节中的案例，来试试 optimize table 命令是否有效：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043874726" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;这上面有一句提示，说   &lt;code&gt;Table does not support optimize, doing recreate + analyze instead&lt;/code&gt;，看这个意思，似乎是说当前这个 InndoDB 引擎的表不支持 optimize 操作，不过我们不用管，我们现在去查看表文件大小：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043874727" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，表文件数据其实已经减少了。&lt;/p&gt; &lt;p&gt;那么这句提示是咋回事呢？&lt;/p&gt; &lt;p&gt;我们以 MySQL 官方文档介绍为准来看下：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043874728" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;从这段话中可以看到，在 InnoDB 中使用 optimize 命令，相关的操作最终会被映射为   &lt;code&gt;alter table ...&lt;/code&gt;，这个操作松哥在  &lt;a href="https://mp.weixin.qq.com/s/6xo-p0z3HIE4tlsIWqqFBg" rel="nofollow noreferrer"&gt;上篇文章&lt;/a&gt;中和小伙伴们介绍过了，这也可以实现索引的重整并且释放掉未使用的空间，所以，网上有人说 optimize table 命令不适用于 InnoDB 引擎的表这个说法是不正确的。&lt;/p&gt; &lt;p&gt;同时，官方文档中这段介绍还提到了 optimize 操作是 online DDL 的。online DDL 意味着在执行 optimize 重整表的时候，并不会阻塞正在进行的 CURD 操作。具体流程如下：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;首先建立一个临时文件，这个临时文件用来扫描表原始表主键的所有数据页。&lt;/li&gt;  &lt;li&gt;根据第一步获取到的表记录生成一个 B+Tree，将这个生成的 B+Tree 存储到临时文件中。&lt;/li&gt;  &lt;li&gt;由于第二步会比较耗时，在第二步执行过程中，如果有针对原始表的 CRUD 操作，则先将操作记录到一个日志文件中，等到第二步的临时文件生成后，在把日志文件应用到临时文件中，就可以获取到一个最新的数据表了。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;好啦，这就是关于 optimize table 的操作细节，小伙伴们 GET 到了吧~&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql 数据库</category>
      <guid isPermaLink="true">https://itindex.net/detail/62777-delete-%E7%A3%81%E7%9B%98%E7%A9%BA%E9%97%B4-%E5%8F%98%E5%8C%96</guid>
      <pubDate>Wed, 07 Jun 2023 15:58:03 CST</pubDate>
    </item>
    <item>
      <title>前缀索引，在性能和空间中寻找平衡</title>
      <link>https://itindex.net/detail/62745-%E5%89%8D%E7%BC%80-%E7%B4%A2%E5%BC%95-%E6%80%A7%E8%83%BD</link>
      <description>&lt;p&gt;@[toc]  &lt;br /&gt;我们在项目的具体实践中，有时候会遇到一些比较特殊的字段，例如身份证号码。&lt;/p&gt; &lt;p&gt;松哥之前有一个小伙伴做黑龙江省的政务服务网，里边有一些涉及到用户身份证存储的场景，由于存储的数据大部分都是当地的，此时如果想给身份证号码建立索引的话，小伙伴们知道，身份证前六位是地址码，在这样的场景下，给身份证字段建立索引的话，前六位的区分度是很低的，甚至前十位的区分度都很低（因为出生年份毕竟有限，一个省份上千万人口，出生年份重复率是很高的），不仅浪费存储空间，查询性能还低。&lt;/p&gt; &lt;p&gt;那么有没有办法解决这个问题呢？我们今天就来聊一聊前缀索引，聊完之后相信大家自己就有答案了。&lt;/p&gt; &lt;h2&gt;1.什么是前缀索引&lt;/h2&gt; &lt;p&gt;有时候为了提升索引的性能，我们只对字段的前几个字符建立索引，这样做既可以节约空间，还能减少字符串的比较时间，B+Tree 上需要存储的索引字符串更短，也能在一定程度上降低索引树的高度，提高查询效率。&lt;/p&gt; &lt;p&gt;MySQL 中的前缀索引有点类似于 Oracle 中对字段使用 Left 函数来建立函数索引，只不过 MySQL 的这个前缀索引在查询时是内部自动完成匹配的，并不需要使用 Left 函数。&lt;/p&gt; &lt;p&gt;不过前缀索引有一个缺陷，就是有可能会降低索引的  &lt;strong&gt;选择性&lt;/strong&gt;。&lt;/p&gt; &lt;h2&gt;2.什么是索引选择性&lt;/h2&gt; &lt;p&gt;关于索引的选择性（Index Selectivity），它是指不重复的索引值（也称为基数 cardinality)和数据表的记录总数的比值，取值范围在   &lt;code&gt;(0,1]&lt;/code&gt; 之间。索引的选择性越高则查询效率越高，因为选择性高的索引可以让 MySQL 在查找时过滤掉更多的行。&lt;/p&gt; &lt;p&gt;那有小伙伴要问了，是不是选择性越高的索引越好呢？当然不是！索引选择性最高为 1，如果索引选择性为 1，就是唯一索引了，搜索的时候就能直接通过搜索条件定位到具体一行记录！这个时候虽然性能最好，但是也是最费空间的，  &lt;strong&gt;这不符合我们创建前缀索引的初衷&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;我们一开始之所以要创建前缀索引而不是唯一索引，  &lt;strong&gt;就是希望能够在索引的性能和空间之间找到一个平衡&lt;/strong&gt;，我们希望能够选择足够长的前缀以保证较高的选择性（这样在查询的过程中就不需要扫描很多行），但是又希望索引不要太过于占用存储空间。&lt;/p&gt; &lt;p&gt;那么我们该如何选择一个合适的索引选择性呢？索引前缀应该足够长，以便前缀索引的选择性接近于索引的整个列，即前缀的基数应该接近于完整列的基数。&lt;/p&gt; &lt;p&gt;首先我们可以通过如下 SQL 得到全列选择性：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;SELECT COUNT(DISTINCT column_name) / COUNT(*) FROM table_name;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;然后再通过如下 SQL 得到某一长度前缀的选择性：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;SELECT COUNT(DISTINCT LEFT(column_name, prefix_length)) / COUNT(*) FROM table_name;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;在上面这条 SQL 执行的时候，我们要注意选择合适的 prefix_length，直至计算结果约等于全列选择性的时候，就是最佳结果了。&lt;/p&gt; &lt;h2&gt;3.创建前缀索引&lt;/h2&gt; &lt;h3&gt;3.1 一个小案例&lt;/h3&gt; &lt;p&gt;举个例子，我们来创建一个前缀索引看看。&lt;/p&gt; &lt;p&gt;松哥这里使用的数据样例是网上找的一个测试脚本，有 300W+ 条数据，做 SQL 测试优化是够用了，小伙伴们在公众号后台回复   &lt;code&gt;mysql-data-samples&lt;/code&gt; 获取脚本下载链接。&lt;/p&gt; &lt;p&gt;我们来大致上看下这个表结构：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043719955" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;这个表有一个 user_uuid 字段，我们就在这个字段上做文章。&lt;/p&gt; &lt;blockquote&gt;Git 小伙伴们应该都会用吧？不同于 Svn，Git 上的版本号不是数字而是一个 Hash 字符串，但是我们在具体应用的时候，比如你要做版本回退，此时并不需要输入完整的的版本号，只需要输入版本号前几个字符就行了，因为根据前面这一部分就能确定出版本号了。&lt;/blockquote&gt; &lt;p&gt;那么这张表里边的 user_uuid 字段也是这意思，如果我们想给 user_uuid 字段建立索引，就没有必要给完整的字符串建立索引，我们只需要给一部分字符串建立索引。&lt;/p&gt; &lt;p&gt;可能有小伙伴还是不太明白，我举一个例子，比如说我现在想按照 user_uuid 字段来查询，但是查询条件我没有必要写完整的 user_uuid，我只需要写前面一部分就可以区分出我想要的记录了，我们来看如下一条 SQL：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043719956" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;大家看到，user_uuid 我只需要给出一部分就能唯一锁定一条记录。&lt;/p&gt; &lt;blockquote&gt;当然，上面这个 SQL 是松哥测试过的，给定的   &lt;code&gt;&amp;apos;39352f%&amp;apos;&lt;/code&gt; 条件不能再短了，再短就会查出来两条甚至多条记录。&lt;/blockquote&gt; &lt;p&gt;通过上面这个例子我们就可以看出来，如果给 user_uuid 字段建立索引，可能并不需要给完整的字符串建立索引，只需要给一部分前缀字符串建立索引。&lt;/p&gt; &lt;p&gt;那么给前面几个字符串建立索引呢？这个可不是拍脑门，需要科学计算，我们继续往下看。&lt;/p&gt; &lt;h3&gt;3.2 前缀索引&lt;/h3&gt; &lt;p&gt;首先我们通过如下 SQL 来看一下 user_uuid 全列索引选择性是多少：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;SELECT COUNT(DISTINCT user_uuid) / COUNT(*) FROM system_user;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043719957" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，结果为 1。全列选择性为 1 说明这一列的值都是唯一不重复的。&lt;/p&gt; &lt;p&gt;接下来我们先来试几个不同的 prefix_length，看看选择性如何。&lt;/p&gt; &lt;p&gt;松哥这里一共测试了 5 个不同的 prefix_length，大家来看看各自的选择性：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043719958" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;8 和 9 的选择性是一样的，因为在 uuid 字符串中，第 9 个字符串是   &lt;code&gt;-&lt;/code&gt;，所有的 uuid 第九个字符串都一样，所以 8 个字符和 9 个字符串的区分度就一样。&lt;/p&gt; &lt;p&gt;当 prefix_length 为 10 的时候，选择性就已经是 1 了，意思是，在这 300W+ 条数据中，如果我用 user_uuid 这个字段去查询的话，只需要输入前十个字符，就能唯一定位到一条具体的记录了。&lt;/p&gt; &lt;p&gt;那还等啥，赶紧创建前缀索引呗：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;alter table system_user add index user_uuid_index(user_uuid(10));&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;查看刚刚创建的前缀索引：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;show index from system_user;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043719959" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，第二行就是我们刚刚创建的前缀索引。&lt;/p&gt; &lt;p&gt;接下来我们分析查询语句中是否用到该索引：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;select * from system_user where user_uuid=&amp;apos;39352f81-165e-4405-9715-75fcdf7f7068&amp;apos;;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043719960" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到，这个前缀索引已经用上了。&lt;/p&gt; &lt;p&gt;具体搜索流程是这样：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;从    &lt;code&gt;user_uuid_index&lt;/code&gt; 索引中找到第一个值为    &lt;code&gt;39352f81-1&lt;/code&gt; 的记录（user_uuid 的前十个字符）。&lt;/li&gt;  &lt;li&gt;由于 user_uuid 是二级索引，叶子结点保存的是主键值，所以此时拿到了主键 id 为 1。&lt;/li&gt;  &lt;li&gt;拿着主键 id 去回表，在主键索引上找到 id 为 1 的行的完整记录，返回给 server 层。&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;server 层判断其 user_uuid 是不是     &lt;code&gt;39352f81-165e-4405-9715-75fcdf7f7068&lt;/code&gt;（所以执行计划的 Extra 为 Using where）。&lt;/p&gt;   &lt;ol&gt;    &lt;li&gt;如果不是，这行记录丢弃。&lt;/li&gt;    &lt;li&gt;如果是，将该记录加入结果集。&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;  &lt;li&gt;索引叶子结点上数据之间是有单向链表维系的，所以接着第一步查找的结果，继续向后读取下一条记录，然后重复 2、3、4 步，直到在 user_uuid_index 上取到的值不为    &lt;code&gt;39352f81-1&lt;/code&gt; 时，循环结束。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;  &lt;strong&gt;如果我们建立了前缀索引并且前缀索引的选择性为 1，那么就不需要第 5 步了，如果前缀索引选择性小于 1，就需要第五步。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;从上面的案例中，小伙伴们看到，我们既节省了空间，又提高了搜索效率。&lt;/p&gt; &lt;h3&gt;3.3 一个问题&lt;/h3&gt; &lt;p&gt;使用了前缀索引后，我们来看一个问题，大家来看如下一条查询 SQL：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;select user_uuid from system_user where user_uuid=&amp;apos;39352f81-165e-4405-9715-75fcdf7f7068&amp;apos;;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这次不是   &lt;code&gt;select *&lt;/code&gt;，而是   &lt;code&gt;select user_uuid&lt;/code&gt;，小伙伴们知道，这里应该是要用到覆盖索引，我们来看看执行计划：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000043719961" title=""&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;咦，说好的索引覆盖呢？（注意看 Extra 是 Using where 不是 Using index）。&lt;/p&gt; &lt;p&gt;大家想想，前缀索引中，B+Tree 里保存的就不是完整的   &lt;code&gt;user_uuid&lt;/code&gt; 字段的值，必须要回表才能拿到需要的数据。  &lt;strong&gt;所以，用了前缀索引，就用不了覆盖索引了。&lt;/strong&gt;&lt;/p&gt; &lt;h2&gt;4. 回到开始的问题&lt;/h2&gt; &lt;p&gt;在本文一开始，松哥抛出了一个问题，如何给身份证建立索引更高效？&lt;/p&gt; &lt;p&gt;由于身份证前六位区分度太低，所以我们可以考虑将身份证倒序存储，倒序存储之后，为前六位或者前八位（可以自行计算选择性）建立前缀索引，这样的建立的索引选择性就会比较高，同时对空间的占用也会比较小。在查询的时候使用 reverse 反转身份证号码即可，像下面这样：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;explain select * from user where id_card=reverse(&amp;apos;正序的身份证号码&amp;apos;);&lt;/code&gt;&lt;/pre&gt; &lt;h2&gt;5.小结&lt;/h2&gt; &lt;p&gt;好啦，这就是前缀索引，感兴趣的小伙伴赶紧体验一把吧~&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql java java-ee java-web</category>
      <guid isPermaLink="true">https://itindex.net/detail/62745-%E5%89%8D%E7%BC%80-%E7%B4%A2%E5%BC%95-%E6%80%A7%E8%83%BD</guid>
      <pubDate>Mon, 24 Apr 2023 11:03:40 CST</pubDate>
    </item>
    <item>
      <title>索引失效底层原理分析，这么多年终于有人讲清楚了</title>
      <link>https://itindex.net/detail/60942-%E7%B4%A2%E5%BC%95-%E5%8E%9F%E7%90%86-%E5%88%86%E6%9E%90</link>
      <description>&lt;h2&gt;前言&lt;/h2&gt; &lt;p&gt;  &lt;code&gt;吊打面试官&lt;/code&gt;又来啦，今天我们讲讲MySQL  &lt;code&gt;索引为什么会失效&lt;/code&gt;，很多文章和培训机构的教程，都只会告诉你，在什么情况下索引会失效。&lt;/p&gt; &lt;p&gt;比如：没遵循最佳左前缀法则、范围查询的右边会失效、like查询用不到索引等等&lt;/p&gt; &lt;p&gt;但是没有一个人告诉你，  &lt;code&gt;索引失效的原理&lt;/code&gt;是什么，  &lt;code&gt;老哥&lt;/code&gt;今天就告诉大家，让你们  &lt;code&gt;知其然&lt;/code&gt;，还要  &lt;code&gt;知其所以然&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVcHumC" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;单值索引B+树图&lt;/h2&gt; &lt;p&gt;单值索引在B+树的结构里，一个节点只存一个键值对&lt;/p&gt; &lt;p&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVcHumF" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;联合索引&lt;/h2&gt; &lt;p&gt;开局一张图，由数据库的  &lt;code&gt;a&lt;/code&gt;字段和  &lt;code&gt;b&lt;/code&gt;字段组成一个  &lt;code&gt;联合索引&lt;/code&gt;。  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVcHumI" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;从本质上来说，联合索引也是一个B+树，和单值索引不同的是，联合索引的键值对不是1，而是大于1个。&lt;/p&gt; &lt;h3&gt;a, b 排序分析&lt;/h3&gt; &lt;p&gt;a顺序：1，1，2，2，3，3&lt;/p&gt; &lt;p&gt;b顺序：1，2，1，4，1，2&lt;/p&gt; &lt;p&gt;大家可以发现a字段是有序排列，b字段是无序排列（因为B+树只能选一个字段来构建有序的树）&lt;/p&gt; &lt;p&gt;一不小心又会发现，在a相等的情况下，b字段是有序的。&lt;/p&gt; &lt;p&gt;大家想想平时编程中我们要对两个字段排序，是不是先按照第一个字段排序，如果第一个字段出现相等的情况，就用第二个字段排序。这个排序方式同样被用到了B+树里。&lt;/p&gt; &lt;h3&gt;分析最佳左前缀原理&lt;/h3&gt; &lt;h4&gt;先举一个遵循最佳左前缀法则的例子&lt;/h4&gt; &lt;pre&gt;  &lt;code&gt;select * from testTable where a=1 and b=2&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;strong&gt;   &lt;code&gt;分析如下：&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;首先a字段在B+树上是有序的，所以我们可以通过二分查找法来定位到a=1的位置。&lt;/p&gt; &lt;p&gt;其次在a确定的情况下，b是相对有序的，因为有序，所以同样可以通过二分查找法找到b=2的位置。&lt;/p&gt; &lt;h4&gt;再来看看不遵循最佳左前缀的例子&lt;/h4&gt; &lt;pre&gt;  &lt;code&gt;select * from testTable where b=2&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;strong&gt;   &lt;code&gt;分析如下：&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;我们来回想一下b有顺序的前提：在a确定的情况下。&lt;/p&gt; &lt;p&gt;现在你的a都飞了，那b肯定是不能确定顺序的，在一个无序的B+树上是无法用二分查找来定位到b字段的。&lt;/p&gt; &lt;p&gt;所以这个时候，是用不上索引的。大家懂了吗？  &lt;br /&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVcHumQ" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h3&gt;范围查询右边失效原理&lt;/h3&gt; &lt;h4&gt;举例&lt;/h4&gt; &lt;pre&gt;  &lt;code&gt;select * from testTable where a&amp;gt;1 and b=2&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;strong&gt;   &lt;code&gt;分析如下：&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;首先a字段在B+树上是有序的，所以可以用二分查找法定位到1，然后将所有大于1的数据取出来，a可以用到索引。&lt;/p&gt; &lt;p&gt;b有序的前提是a是确定的值，那么现在a的值是取大于1的，可能有10个大于1的a，也可能有一百个a。&lt;/p&gt; &lt;p&gt;大于1的a那部分的B+树里，b字段是无序的（开局一张图），所以b不能在无序的B+树里用二分查找来查询，b用不到索引。&lt;/p&gt; &lt;h3&gt;like索引失效原理&lt;/h3&gt; &lt;pre&gt;  &lt;code&gt;where name like &amp;quot;a%&amp;quot;

where name like &amp;quot;%a%&amp;quot;

where name like &amp;quot;%a&amp;quot;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;strong&gt;我们先来了解一下%的用途&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;code&gt;%放在右边&lt;/code&gt;，代表查询以&amp;quot;a&amp;quot;开头的数据，如：abc&lt;/li&gt;  &lt;li&gt;   &lt;code&gt;两个%%&lt;/code&gt;，代表查询数据中包含&amp;quot;a&amp;quot;的数据，如：cab、cba、abc&lt;/li&gt;  &lt;li&gt;   &lt;code&gt;%放在左边&lt;/code&gt;，代表查询以&amp;quot;a&amp;quot;为结尾的数据，如cba&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;为什么%放在右边有时候能用到索引&lt;/h4&gt; &lt;ul&gt;  &lt;li&gt;%放右边叫做：   &lt;code&gt;前缀&lt;/code&gt;&lt;/li&gt;  &lt;li&gt;%%叫做：   &lt;code&gt;中缀&lt;/code&gt;&lt;/li&gt;  &lt;li&gt;%放在左边叫做：   &lt;code&gt;后缀&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;没错，这里依然是最佳左前缀法则这个概念&lt;/p&gt; &lt;p&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVcHumU" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;大家可以看到，上面的B+树是由字符串组成的。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;字符串的排序方式&lt;/code&gt;：先按照第一个字母排序，如果第一个字母相同，就按照第二个字母排序。。。以此类推&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;   &lt;code&gt;开始分析&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;一、%号放右边（前缀）&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;由于B+树的索引顺序，是按照首字母的大小进行排序，前缀匹配又是匹配首字母。所以可以在B+树上进行有序的查找，查找首字母符合要求的数据。所以有些时候可以用到索引。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;二、%号放左边&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;是匹配字符串尾部的数据，我们上面说了排序规则，尾部的字母是没有顺序的，所以不能按照索引顺序查询，就用不到索引。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;三、两个%%号&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;这个是查询任意位置的字母满足条件即可，只有首字母是进行索引排序的，其他位置的字母都是相对无序的，所以查找任意位置的字母是用不上索引的。&lt;/p&gt; &lt;h2&gt;总结&lt;/h2&gt; &lt;p&gt;这里把一些经典的索引失效案例给大家分析了，希望能引发大家的思考，能够通过这些案例，明白其他情况索引失效的原理。&lt;/p&gt; &lt;p&gt;之后我们在讲讲，如何通过索引查询到数据整个流程，  &lt;code&gt;InnoDB&lt;/code&gt;和  &lt;code&gt;MyISAM&lt;/code&gt;两个引擎底层索引的实现区别。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;授人以鱼不如授人以渔&lt;/code&gt;，这一瞬间，老哥感觉自己特别的  &lt;code&gt;shuai&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVcHum1" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;关注我，一个自学进入大厂的高级Java开发工程师&lt;/strong&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>java mysql mysql索引 mysql索引优化 索引</category>
      <guid isPermaLink="true">https://itindex.net/detail/60942-%E7%B4%A2%E5%BC%95-%E5%8E%9F%E7%90%86-%E5%88%86%E6%9E%90</guid>
      <pubDate>Fri, 16 Oct 2020 10:40:21 CST</pubDate>
    </item>
    <item>
      <title>ElasticSearch 索引 VS MySQL 索引</title>
      <link>https://itindex.net/detail/60831-elasticsearch-%E7%B4%A2%E5%BC%95-vs</link>
      <description>&lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi01tzwikkj31hc0u0toq.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#21069;&amp;#35328;" title="&amp;#21069;&amp;#35328;"&gt;&lt;/a&gt;前言&lt;/h1&gt; &lt;p&gt;这段时间在维护产品的搜索功能，每次在管理台看到   &lt;code&gt;elasticsearch&lt;/code&gt; 这么高效的查询效率我都很好奇他是如何做到的。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi18y8no3wj318y0dcac1.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这甚至比在我本地使用   &lt;code&gt;MySQL&lt;/code&gt; 通过主键的查询速度还快。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi18yukebvj30ps0363yo.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;p&gt;为此我搜索了相关资料：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi18z0c7plj30u016z4er.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这类问题网上很多答案，大概意思呢如下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;ES 是基于    &lt;code&gt;Lucene&lt;/code&gt; 的全文检索引擎，它会对数据进行分词后保存索引，擅长管理大量的索引数据，相对于    &lt;code&gt;MySQL&lt;/code&gt; 来说不擅长经常更新数据及关联查询。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;说的不是很透彻，没有解析相关的原理；不过既然反复提到了索引，那我们就从索引的角度来对比下两者的差异。&lt;/p&gt;
 &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#MySQL-&amp;#32034;&amp;#24341;" title="MySQL &amp;#32034;&amp;#24341;"&gt;&lt;/a&gt;MySQL 索引&lt;/h1&gt; &lt;p&gt;先从   &lt;code&gt;MySQL&lt;/code&gt; 说起，索引这个词想必大家也是烂熟于心，通常存在于一些查询的场景，是典型的空间换时间的案例。&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;以下内容以 Innodb 引擎为例。&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#24120;&amp;#35265;&amp;#30340;&amp;#25968;&amp;#25454;&amp;#32467;&amp;#26500;" title="&amp;#24120;&amp;#35265;&amp;#30340;&amp;#25968;&amp;#25454;&amp;#32467;&amp;#26500;"&gt;&lt;/a&gt;常见的数据结构&lt;/h2&gt; &lt;p&gt;假设由我们自己来设计   &lt;code&gt;MySQL&lt;/code&gt; 的索引，大概会有哪些选择呢？&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://crossoverjie.top/#&amp;#25955;&amp;#21015;&amp;#34920;" title="&amp;#25955;&amp;#21015;&amp;#34920;"&gt;&lt;/a&gt;散列表&lt;/h3&gt; &lt;p&gt;首先我们应当想到的是散列表，这是一个非常常见且高效的查询、写入的数据结构，对应到   &lt;code&gt;Java&lt;/code&gt; 中就是   &lt;code&gt;HashMap&lt;/code&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi18zcyxozj31ap0u0gph.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这个数据结构应该不需要过多介绍了，它的写入效率很高  &lt;code&gt;O(1)&lt;/code&gt;,比如我们要查询   &lt;code&gt;id=3&lt;/code&gt; 的数据时，需要将 3 进行哈希运算，然后再这个数组中找到对应的位置即可。&lt;/p&gt;
 &lt;p&gt;但如果我们想查询   &lt;code&gt;1≤id≤6&lt;/code&gt; 这样的区间数据时，散列表就不能很好的满足了，由于它是无序的，所以得将所有数据遍历一遍才能知道哪些数据属于这个区间。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://crossoverjie.top/#&amp;#26377;&amp;#24207;&amp;#25968;&amp;#32452;" title="&amp;#26377;&amp;#24207;&amp;#25968;&amp;#32452;"&gt;&lt;/a&gt;有序数组&lt;/h3&gt; &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi18zpdguqj31600aiaax.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;有序数组的查询效率也很高，当我们要查询   &lt;code&gt;id=4&lt;/code&gt; 的数据时，只需要通过二分查找也能高效定位到数据  &lt;code&gt;O(logn)&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;同时由于数据也是有序的，所以自然也能支持区间查询；这么看来有序数组适合用做索引咯?&lt;/p&gt;
 &lt;p&gt;自然是不行，它有另一个重大问题；假设我们插入了   &lt;code&gt;id=2.5&lt;/code&gt; 的数据，就得同时将后续的所有数据都移动一位，这个写入效率就会变得非常低。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://crossoverjie.top/#&amp;#24179;&amp;#34913;&amp;#20108;&amp;#21449;&amp;#26641;" title="&amp;#24179;&amp;#34913;&amp;#20108;&amp;#21449;&amp;#26641;"&gt;&lt;/a&gt;平衡二叉树&lt;/h3&gt; &lt;p&gt;既然有序数组的写入效率不高，那我们就来看看写入效率高的，很容易就能想到二叉树；这里我们以平衡二叉树为例：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi18zx774dj313g0jwn36.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;由于平衡二叉树的特性：&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;左节点小于父节点、右节点大于父节点。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;所以假设我们要查询   &lt;code&gt;id=11&lt;/code&gt; 的数据，只需要查询   &lt;code&gt;10—&amp;gt;12—&amp;gt;11&lt;/code&gt; 便能最终找到数据，时间复杂度为  &lt;code&gt;O(logn)&lt;/code&gt;，同理写入数据时也为  &lt;code&gt;O(logn)&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;但依然不能很好的支持区间范围查找，假设我们要查询  &lt;code&gt;5≤id≤20&lt;/code&gt; 的数据时，需要先查询10节点的左子树再查询10节点的右子树最终才能查询到所有数据。&lt;/p&gt;
 &lt;p&gt;导致这样的查询效率并不高。&lt;/p&gt;
 &lt;h3&gt;  &lt;a href="http://crossoverjie.top/#&amp;#36339;&amp;#34920;" title="&amp;#36339;&amp;#34920;"&gt;&lt;/a&gt;跳表&lt;/h3&gt; &lt;p&gt;跳表可能不像上边提到的散列表、有序数组、二叉树那样日常见的比较多，但其实   &lt;code&gt;Redis&lt;/code&gt; 中的   &lt;code&gt;sort set&lt;/code&gt; 就采用了跳表实现。&lt;/p&gt;
 &lt;p&gt;这里我们简单介绍下跳表实现的数据结构有何优势。&lt;/p&gt;
 &lt;p&gt;我们都知道即便是对一个  &lt;strong&gt;有序链表&lt;/strong&gt;进行查询效率也不高，由于它不能使用数组下标进行二分查找，所以时间复杂度是  &lt;code&gt;o(n)&lt;/code&gt;&lt;/p&gt;
 &lt;p&gt;但我们也可以巧妙的优化链表来变相的实现二分查找，如下图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi190dy4wqj31bw0hc448.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;我们可以为最底层的数据提取出一级索引、二级索引，根据数据量的不同，我们可以提取出 N 级索引。&lt;/p&gt;
 &lt;p&gt;当我们查询时便可以利用这里的索引变相的实现了二分查找。&lt;/p&gt;
 &lt;p&gt;假设现在要查询   &lt;code&gt;id=13&lt;/code&gt; 的数据，只需要遍历   &lt;code&gt;1—&amp;gt;7—&amp;gt;10—&amp;gt;13&lt;/code&gt; 四个节点便可以查询到数据，当数越多时，效率提升会更明显。&lt;/p&gt;
 &lt;p&gt;同时区间查询也是支持，和刚才的查询单个节点类似，只需要查询到起始节点，然后依次往后遍历（  &lt;strong&gt;链表有序&lt;/strong&gt;）到目标节点便能将整个范围的数据查询出来。&lt;/p&gt;
 &lt;p&gt;同时由于我们在索引上不会存储真正的数据，只是存放一个指针，相对于最底层存放数据的链表来说占用的空间便可以忽略不计了。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#24179;&amp;#34913;&amp;#20108;&amp;#21449;&amp;#26641;&amp;#30340;&amp;#20248;&amp;#21270;" title="&amp;#24179;&amp;#34913;&amp;#20108;&amp;#21449;&amp;#26641;&amp;#30340;&amp;#20248;&amp;#21270;"&gt;&lt;/a&gt;平衡二叉树的优化&lt;/h2&gt; &lt;p&gt;但其实   &lt;code&gt;MySQL&lt;/code&gt; 中的   &lt;code&gt;Innodb&lt;/code&gt; 并没有采用跳表，而是使用的一个叫做   &lt;code&gt;B+&lt;/code&gt; 树的数据结构。&lt;/p&gt;
 &lt;p&gt;这个数据结构不像是二叉树那样大学老师当做基础数据结构经常讲到，由于这类数据结构都是在实际工程中根据需求场景在基础数据结构中演化而来。&lt;/p&gt;
 &lt;p&gt;比如这里的   &lt;code&gt;B+&lt;/code&gt; 树就可以认为是由平衡二叉树演化而来。&lt;/p&gt;
 &lt;p&gt;刚才我们提到二叉树的区间查询效率不高，针对这一点便可进行优化：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi190og1h6j31o40u0k67.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在原有二叉树的基础上优化后：所有的非叶子都不存放数据，只是作为叶子节点的索引，数据全部都存放在叶子节点。&lt;/p&gt;
 &lt;p&gt;这样所有叶子节点的数据都是有序存放的，便能很好的支持区间查询。&lt;/p&gt;
 &lt;p&gt;只需要先通过查询到起始节点的位置，然后在叶子节点中依次往后遍历即可。&lt;/p&gt;
 &lt;p&gt;当数据量巨大时，很明显索引文件是不能存放于内存中，虽然速度很快但消耗的资源也不小；所以   &lt;code&gt;MySQL&lt;/code&gt; 会将索引文件直接存放于磁盘中。&lt;/p&gt;
 &lt;p&gt;这点和后文提到 elasticsearch 的索引略有不同。&lt;/p&gt;
 &lt;p&gt;由于索引存放于磁盘中，所以我们要尽可能的减少与磁盘的 IO（磁盘 IO 的效率与内存不在一个数量级）&lt;/p&gt;
 &lt;p&gt;通过上图可以看出，我们要查询一条数据至少得进行 4 次IO，很明显这个 IO 次数是与树的高度密切相关的，树的高度越低 IO 次数就会越少，同时性能也会越好。&lt;/p&gt;
 &lt;p&gt;那怎样才能降低树的高度呢？&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi190wn3k2j311u0jo43r.jpg"&gt;&lt;/img&gt;    &lt;/p&gt;
 &lt;p&gt;我们可以尝试把二叉树变为三叉树，这样树的高度就会下降很多，这样查询数据时的 IO 次数自然也会降低，同时查询效率也会提高许多。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;这其实就是 B+ 树的由来。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#20351;&amp;#29992;&amp;#32034;&amp;#24341;&amp;#30340;&amp;#19968;&amp;#20123;&amp;#24314;&amp;#35758;" title="&amp;#20351;&amp;#29992;&amp;#32034;&amp;#24341;&amp;#30340;&amp;#19968;&amp;#20123;&amp;#24314;&amp;#35758;"&gt;&lt;/a&gt;使用索引的一些建议&lt;/h2&gt; &lt;p&gt;其实通过上图对   &lt;code&gt;B+树&lt;/code&gt;的理解，也能优化日常工作的一些小细节；比如为什么需要最好是有序递增的？&lt;/p&gt;
 &lt;p&gt;假设我们写入的主键数据是无序的，那么有可能后写入数据的 id 小于之前写入的，这样在维护   &lt;code&gt;B+树&lt;/code&gt; 索引时便有可能需要移动已经写好数据。&lt;/p&gt;
 &lt;p&gt;如果是按照递增写入数据时则不会有这个考虑，每次只需要依次写入即可。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;所以我们才会要求数据库主键尽量是趋势递增的，不考虑分表的情况时最合理的就是自增主键。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;整体来看思路和跳表类似，只是针对使用场景做了相关的调整（比如数据全部存储于叶子节点）。&lt;/p&gt;
 &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#ES-&amp;#32034;&amp;#24341;" title="ES &amp;#32034;&amp;#24341;"&gt;&lt;/a&gt;ES 索引&lt;/h1&gt; &lt;p&gt;  &lt;code&gt;MySQL&lt;/code&gt; 聊完了，现在来看看   &lt;code&gt;Elasticsearch&lt;/code&gt; 是如何来使用索引的。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#27491;&amp;#25490;&amp;#32034;&amp;#24341;" title="&amp;#27491;&amp;#25490;&amp;#32034;&amp;#24341;"&gt;&lt;/a&gt;正排索引&lt;/h2&gt; &lt;p&gt;在 ES 中采用的是一种名叫  &lt;code&gt;倒排索引&lt;/code&gt;的数据结构；在正式讲倒排索引之前先来聊聊和他相反的  &lt;code&gt;正排索引&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi193iw11cj313u0bsgms.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;以上图为例，我们可以通过   &lt;code&gt;doc_id&lt;/code&gt; 查询到具体对象的方式称为使用  &lt;code&gt;正排索引&lt;/code&gt;，其实也能理解为一种散列表。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;本质是通过 key 来查找 value。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;比如通过   &lt;code&gt;doc_id=4&lt;/code&gt; 便能很快查询到   &lt;code&gt;name=jetty wang,age=20&lt;/code&gt; 这条数据。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#20498;&amp;#25490;&amp;#32034;&amp;#24341;" title="&amp;#20498;&amp;#25490;&amp;#32034;&amp;#24341;"&gt;&lt;/a&gt;倒排索引&lt;/h2&gt; &lt;p&gt;那如果反过来我想查询   &lt;code&gt;name&lt;/code&gt; 中包含了   &lt;code&gt;li&lt;/code&gt; 的数据有哪些？这样如何高效查询呢？&lt;/p&gt;
 &lt;p&gt;仅仅通过上文提到的正排索引显然起不到什么作用，只能依次将所有数据遍历后判断名称中是否包含   &lt;code&gt;li&lt;/code&gt; ；这样效率十分低下。&lt;/p&gt;
 &lt;p&gt;但如果我们重新构建一个索引结构：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi1940f4trj314e0f0q4e.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;当要查询   &lt;code&gt;name&lt;/code&gt; 中包含   &lt;code&gt;li&lt;/code&gt; 的数据时，只需要通过这个索引结构查询到   &lt;code&gt;Posting List&lt;/code&gt; 中所包含的数据，再通过映射的方式查询到最终的数据。&lt;/p&gt;
 &lt;p&gt;这个索引结构其实就是  &lt;code&gt;倒排索引&lt;/code&gt;。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#Term-Dictionary" title="Term Dictionary"&gt;&lt;/a&gt;Term Dictionary&lt;/h2&gt; &lt;p&gt;但如何高效的在这个索引结构中查询到   &lt;code&gt;li&lt;/code&gt; 呢，结合我们之前的经验，只要我们将   &lt;code&gt;Term&lt;/code&gt; 有序排列，便可以使用二叉树搜索树的数据结构在  &lt;code&gt;o(logn)&lt;/code&gt; 下查询到数据。&lt;/p&gt;
 &lt;p&gt;将一个文本拆分成一个一个独立  &lt;code&gt;Term&lt;/code&gt; 的过程其实就是我们常说的分词。&lt;/p&gt;
 &lt;p&gt;而将所有   &lt;code&gt;Term&lt;/code&gt; 合并在一起就是一个   &lt;code&gt;Term Dictionary&lt;/code&gt;，也可以叫做单词词典。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;英文的分词相对简单，只需要通过空格、标点符号将文本分隔便能拆词，中文则相对复杂，但也有许多开源工具做支持（由于不是本文重点，对分词感兴趣的可以自行搜索）。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;当我们的文本量巨大时，分词后的   &lt;code&gt;Term&lt;/code&gt; 也会很多，这样一个倒排索引的数据结构如果存放于内存那肯定是不够存的，但如果像   &lt;code&gt;MySQL&lt;/code&gt; 那样存放于磁盘，效率也没那么高。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#Term-Index" title="Term Index"&gt;&lt;/a&gt;Term Index&lt;/h2&gt; &lt;p&gt;所以我们可以选择一个折中的方法，既然无法将整个   &lt;code&gt;Term Dictionary&lt;/code&gt; 放入内存中，那我们可以为  &lt;code&gt;Term Dictionary&lt;/code&gt; 创建一个索引然后放入内存中。&lt;/p&gt;
 &lt;p&gt;这样便可以高效的查询  &lt;code&gt;Term Dictionary&lt;/code&gt; ，最后再通过  &lt;code&gt;Term Dictionary&lt;/code&gt; 查询到   &lt;code&gt;Posting List&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;相对于   &lt;code&gt;MySQL&lt;/code&gt; 中的   &lt;code&gt;B+树&lt;/code&gt;来说也会减少了几次  &lt;code&gt;磁盘IO&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi191apu10j30ua0h0q7c.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这个   &lt;code&gt;Term Index&lt;/code&gt; 我们可以使用这样的   &lt;code&gt;Trie树&lt;/code&gt; 也就是我们常说的  &lt;code&gt;字典树&lt;/code&gt; 来存放。&lt;/p&gt;
 &lt;p&gt;更多关于字典树的内容请查看  &lt;a href="https://crossoverjie.top/2019/01/14/netty/cim02-v1.0.1/"&gt;这里&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi191fxwgxj318o094782.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如果我们是以   &lt;code&gt;j&lt;/code&gt; 开头的   &lt;code&gt;Term&lt;/code&gt; 进行搜索，首先第一步就是通过在内存中的   &lt;code&gt;Term Index&lt;/code&gt; 查询出以   &lt;code&gt;j&lt;/code&gt; 打头的   &lt;code&gt;Term&lt;/code&gt; 在   &lt;code&gt;Term Dictionary&lt;/code&gt; 字典文件中的哪个位置（这个位置可以是一个文件指针，可能是一个区间范围）。&lt;/p&gt;
 &lt;p&gt;紧接着在将这个位置区间中的所有   &lt;code&gt;Term&lt;/code&gt; 取出，由于已经排好序，便可通过二分查找快速定位到具体位置；这样便可查询出   &lt;code&gt;Posting List&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;最终通过   &lt;code&gt;Posting List&lt;/code&gt; 中的位置信息便可在原始文件中将目标数据检索出来。&lt;/p&gt;
 &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#26356;&amp;#22810;&amp;#20248;&amp;#21270;" title="&amp;#26356;&amp;#22810;&amp;#20248;&amp;#21270;"&gt;&lt;/a&gt;更多优化&lt;/h2&gt; &lt;p&gt;当然   &lt;code&gt;ElasticSearch&lt;/code&gt; 还做了许多针对性的优化，当我们对两个字段进行检索时，就可以利用   &lt;code&gt;bitmap&lt;/code&gt; 进行优化。&lt;/p&gt;
 &lt;p&gt;比如现在需要查询   &lt;code&gt;name=li and age=18&lt;/code&gt; 的数据，这时我们需要通过这两个字段将各自的结果   &lt;code&gt;Posting List&lt;/code&gt; 取出。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi194k1jk5j3138080t9e.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;最简单的方法是分别遍历两个集合，取出重复的数据，但这个明显效率低下。&lt;/p&gt;
 &lt;p&gt;这时我们便可使用   &lt;code&gt;bitmap&lt;/code&gt; 的方式进行存储（还节省存储空间），同时利用先天的  &lt;code&gt;位与&lt;/code&gt;   &lt;strong&gt;**计算便可得出结果&lt;/strong&gt;。**&lt;/p&gt;
 &lt;p&gt;  &lt;code&gt;[1, 3, 5]&lt;/code&gt;       ⇒   &lt;code&gt;10101&lt;/code&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;code&gt;[1, 2, 4, 5]&lt;/code&gt; ⇒   &lt;code&gt;11011&lt;/code&gt;&lt;/p&gt;
 &lt;p&gt;这样两个二进制数组求与便可得出结果：&lt;/p&gt;
 &lt;p&gt;  &lt;code&gt;10001&lt;/code&gt; ⇒   &lt;code&gt;[1, 5]&lt;/code&gt;&lt;/p&gt;
 &lt;p&gt;最终反解出   &lt;code&gt;Posting List&lt;/code&gt; 为  &lt;code&gt;[1, 5]&lt;/code&gt;,这样的效率自然是要高上许多。&lt;/p&gt;
 &lt;p&gt;同样的查询需求在   &lt;code&gt;MySQL&lt;/code&gt; 中并没有特殊优化，只是先将数据量小的数据筛选出来之后再筛选第二个字段，效率自然也就没有   &lt;code&gt;ES&lt;/code&gt; 高。&lt;/p&gt;
 &lt;p&gt;当然在最新版的   &lt;code&gt;ES&lt;/code&gt; 中也会对   &lt;code&gt;Posting List&lt;/code&gt; 进行压缩，具体压缩规则可以查看  &lt;a href="https://www.elastic.co/cn/blog/frame-of-reference-and-roaring-bitmaps" rel="external" target="_blank"&gt;官方文档&lt;/a&gt;，这里就不具体介绍了。&lt;/p&gt;
 &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#24635;&amp;#32467;" title="&amp;#24635;&amp;#32467;"&gt;&lt;/a&gt;总结&lt;/h1&gt; &lt;p&gt;最后我们来总结一下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://tva1.sinaimg.cn/large/007S8ZIlly1gi194xf948j315y0ca40z.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;通过以上内容可以看出再复杂的产品最终都是基础数据结构组成，只是会对不同应用场景针对性的优化，所以打好数据结构与算法的基础后再看某个新的技术或中间件时才能快速上手，甚至自己就能知道优化方向。&lt;/p&gt;
 &lt;p&gt;最后画个饼，后续我会尝试按照   &lt;code&gt;ES&lt;/code&gt; 倒排索引的思路做一个单机版的搜索引擎，只有自己写一遍才能加深理解。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;更好的阅读体验请访问   &lt;a href="https://www.notion.so/ElasticSearch-VS-MySQL-54bddcc092c64c26b2127f1fb9772a23" rel="external" target="_blank"&gt;此处&lt;/a&gt;：   &lt;a href="https://www.notion.so/ElasticSearch-VS-MySQL-54bddcc092c64c26b2127f1fb9772a23" rel="external" target="_blank"&gt;https://www.notion.so/ElasticSearch-VS-MySQL-54bddcc092c64c26b2127f1fb9772a23&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;你的点赞与分享是对我最大的支持&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>数据结构 MySQL Elasticsearch 倒排索引 B+ 树</category>
      <guid isPermaLink="true">https://itindex.net/detail/60831-elasticsearch-%E7%B4%A2%E5%BC%95-vs</guid>
      <pubDate>Mon, 24 Aug 2020 08:10:36 CST</pubDate>
    </item>
    <item>
      <title>MySQL 同步复制及高可用方案总结</title>
      <link>https://itindex.net/detail/60490-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</link>
      <description>&lt;h4&gt;  &lt;strong&gt;1.前言&lt;/strong&gt;&lt;/h4&gt;
 &lt;p&gt;mysql作为应用程序的数据存储服务，要实现mysql数据库的高可用。必然要使用的技术就是数据库的复制，如果主节点出现故障可以手动的切换应用到从节点，这点相信运维同学都是知道，并且可以实现的。但是这种情况只是手动的切换，对可用性有要求的业务需要分别实现主库和从库的高可用，保障在数据库出现down机的情况下，可以自动实现数据库的故障转移，保障应用的可用性和用户体验。&lt;/p&gt;
 &lt;p&gt;本文将会对一些常用的数据库高可用方案进行介绍，根据你不同的场景，选择合适的高可用方案即可。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;2.MMM高可用方案&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h6&gt;  &lt;strong&gt;2.1.Mysql-MMM介绍&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;MMM(Master-Master replication managerfor Mysql，Mysql主主复制管理器)是一套灵活的脚本程序，基于perl实现，用来对mysql replication进行监控和故障迁移，并能管理mysql Master-Master复制的配置(同一时间只有一个节点是可写的)。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;2.2.组件&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;  &lt;strong&gt;mmm_mond：&lt;/strong&gt;监控进程，负责所有的监控工作，决定和处理所有节点角色活动。此脚本需要在监管机上运行。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;mmm_agentd：&lt;/strong&gt;运行在每个mysql服务器上的代理进程，完成监控的探针工作和执行简单的远端服务设置。此脚本需要在被监管机上运行。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;mmm_control：&lt;/strong&gt;一个简单的脚本，提供管理mmm_mond进程的命令。&lt;/p&gt;
 &lt;p&gt;mysql-mmm的监管端会提供多个虚拟IP（VIP），包括一个可写VIP，多个可读VIP，通过监管的管理，这些IP会绑定在可用mysql之上，当某一台mysql宕机时，监管会将VIP迁移至其他mysql。&lt;/p&gt;
 &lt;p&gt;在整个监管过程中，需要在mysql中添加相关授权用户，以便让mysql可以支持监理机的维护。授权的用户包括一个mmm_monitor用户和一个mmm_agent用户，如果想使用mmm的备份工具则还要添加一个mmm_tools用户。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;2.3.架构图&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;正常工作时：  &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313467" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主节点故障时：  &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313468" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;2.4.MMM优点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）高可用性，扩展性好，出现故障自动转移，对于主主同步，在同一时间只提供一台数据库写操作，保证数据的一致性。&lt;/p&gt;
 &lt;p&gt;（2）配置简单，容易操作。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;2.5.MMM缺点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）需要一台备份服务器，浪费资源&lt;/p&gt;
 &lt;p&gt;（2）需要多个虚拟IP&lt;/p&gt;
 &lt;p&gt;（3）agent可能意外终止，引起裂脑。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;3.MHA介绍&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;MHA（Master High Availability）目前在MySQL高可用方面是一个相对成熟的解决方案，它由日本DeNA公司youshimaton（现就职于Facebook公司）开发，是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中，MHA能做到在0~30秒之内自动完成数据库的故障切换操作，并且在进行故障切换的过程中，MHA能在最大程度上保证数据的一致性，以达到真正意义上的高可用。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.1.MHA架构介绍&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;该软件由两部分组成：MHA Manager（管理节点）和MHA Node（数据节点）。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群，也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上，MHA Manager会定时探测集群中的master节点，当master出现故障时，它可以自动将最新数据的slave提升为新的master，然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。&lt;/p&gt;
 &lt;p&gt;在MHA自动故障切换过程中，MHA试图从宕机的主服务器上保存二进制日志，最大程度的保证数据的不丢失(配合mysql半同步复制效果更佳)，但这并不总是可行的。例如，如果主服务器硬件故障或无法通过ssh访问，MHA没法保存二进制日志，只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制，可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志，MHA可以将最新的二进制日志应用于其他所有的slave服务器上，因此可以保证所有节点的数据一致性。&lt;/p&gt;
 &lt;p&gt;注意：目前MHA主要支持一主多从的架构，要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器，一主二从，即一台充当master，一台充当备用master，另外一台充当从库，因为至少需要三台服务器，出于机器成本的考虑，淘宝也在该基础上进行了改造，目前淘宝TMHA已经支持一主一从。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.2.MHA架构图&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;正常工作时架构图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313469" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主库down机时架构：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313466" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.3.故障转移过程&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）从宕机崩溃的master保存二进制日志事件（binlog events）;&lt;/p&gt;
 &lt;p&gt;（2）识别含有最新更新的slave；&lt;/p&gt;
 &lt;p&gt;（3）应用差异的中继日志（relay log）到其他的slave；&lt;/p&gt;
 &lt;p&gt;（4）应用从master保存的二进制日志事件（binlog events）；&lt;/p&gt;
 &lt;p&gt;（5）提升一个slave为新的master；&lt;/p&gt;
 &lt;p&gt;（6）使其他的slave连接新的master进行复制；&lt;/p&gt;
 &lt;p&gt;（7）在新的master启动vip地址，保证前端请求可以发送到新的master。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.4.MHA优点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）不需要备份服务器&lt;/p&gt;
 &lt;p&gt;（2）不改变现有环境&lt;/p&gt;
 &lt;p&gt;（3）操作非常简单&lt;/p&gt;
 &lt;p&gt;（4）可以进行日志的差异修复&lt;/p&gt;
 &lt;p&gt;（5）可以将任意slave提升为master&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.5.MHA缺点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）需要全部节点做ssh秘钥&lt;/p&gt;
 &lt;p&gt;（2）MHA出现故障后配置文件会被修改，如果再次故障转移需要重新修改配置文件。&lt;/p&gt;
 &lt;p&gt;（3）自带的脚本还需要进一步补充完善，且用perl开发，二次开发困难。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;4.DRBD+（heartbeat,corosync）&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h6&gt;4.1.  &lt;strong&gt;方案简介&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;本方案采用Heartbeat或者corosync双机热备软件来保证数据库的高稳定性和连续性，数据的一致性由DRBD这个工具来保证（如果可以尽量放到分布式存储上面）。默认情况下只有一台mysql在工作，当主mysql服务器出现问题后，系统将自动切换到备机上继续提供服务，当主数据库修复完毕，又将服务切回继续由主mysql提供服务。&lt;/p&gt;
 &lt;h6&gt;4.2.  &lt;strong&gt;组件&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;Heartbeat,corosync作为心跳检测机制，监控primary节点的状态。当主节点宕掉之后，迅速提升secondary节点为新的主节点，并切换IP；&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;drbd&lt;/strong&gt;负责数据同步&lt;/p&gt;
 &lt;h6&gt;4.3.  &lt;strong&gt;架构图&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313465" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h6&gt;4.4.  &lt;strong&gt;数据同步过程&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;mysql进行刷盘时，会通过不同的sync方式，最终将数据写入disk；&lt;/p&gt;
 &lt;p&gt;drbd收到刷盘成功的信息后，将对应的磁盘块位置，和变更动作，通过网络传递至secondary节点；&lt;/p&gt;
 &lt;p&gt;secondary的drbd接收到变更信息后，将这些信息落盘；&lt;/p&gt;
 &lt;h6&gt;4.5.  &lt;strong&gt;切换过程&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;前提：secondary节点的mysql服务不启动；&lt;/p&gt;
 &lt;p&gt;heartbeat检测到primary的mysql服务停止，则摘掉IP、umount掉数据盘、将primary切换为secondary；&lt;/p&gt;
 &lt;p&gt;在原来的secondary上，提升drbd同步为primary，挂载数据盘，启动mysql服务、绑定IP；&lt;/p&gt;
 &lt;p&gt;从库跟着IP和端口自动进行迁移；&lt;/p&gt;
 &lt;h6&gt;4.6.  &lt;strong&gt;方案优点&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;（1）历史悠久、安全性高、稳定性高、可用性高、出现故障自动切换。&lt;/p&gt;
 &lt;p&gt;（2）数据一致性强&lt;/p&gt;
 &lt;h6&gt;4.7.  &lt;strong&gt;方案缺点&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;（1）需要一台备份服务器，浪费资源&lt;/p&gt;
 &lt;p&gt;（2）不方便扩展&lt;/p&gt;
 &lt;p&gt;（3）无论drbd还是headbetart，corosync都可能发生裂脑&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;5.Mysql route介绍&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h6&gt;  &lt;strong&gt;5.1.什么是mysql route&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;MySQL Router是处于应用client和dbserver之间的轻量级代理程序，它能检测，分析和转发查询到后端数据库实例，并把结果返回给client。是mysql-proxy的一个替代品。其架构图和功能如下。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313470" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;（1）Router实现读写分离，程序不是直接连接数据库IP，而是固定连接到mysql router。MySQL Router对前端应用是透明的。应用程序把MySQL Router当作是普通的mysql实例，把查询发给MySQL Router,而MySQL Router会把查询结果返回给前端的应用程序。&lt;/p&gt;
 &lt;p&gt;（2）从数据库服务器故障，业务可以正常运行。由MySQL Router来进行自动下线不可用服务器。程序配置不需要任何修改。&lt;/p&gt;
 &lt;p&gt;（3）主数据库故障，由MySQL Router来决定主从自动切换，业务可以正常访问。程序配置不需要做任何修改。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;5.2.读写分离原理&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;MySQL Router接受前端应用程序请求后，根据不同的端口来区分读写，把连接读写端口的所有查询发往主库，把连接只读端口的select查询以轮询方式发往多个从库，从而实现读写分离的目的。读写返回的结果会交给MySQL Router,由MySQL Router返回给客户端的应用程序。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;5.3.Mysql router用途&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;MySQL Router的主要用途是读写分离，主主故障自动切换，负载均衡，连接池等。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;5.4.Mysql router主主故障自动切换的坑&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;Mysql router主主故障切换功能经过测试没有问题，但是有一个比较大的坑需要注意，主库发生切换之后，从库的连接的master服务器地址不会发生改变，需要自己写脚本进行判断。&lt;/p&gt;
 &lt;h6&gt;5.5.优点&lt;/h6&gt;
 &lt;p&gt;（1）基于DAL层实现mysql的高可用。&lt;/p&gt;
 &lt;p&gt;（2）可以同时实现主主故障切换和读写分离。&lt;/p&gt;
 &lt;p&gt;（3）插件式架构允许用户进行额外的功能扩展。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;5.6.缺点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）高可用功能需要进一步完善：存在主库切换之后，从库不会自动切换主库地址的坑。&lt;/p&gt;
 &lt;p&gt;（2）读写情况使用不同端口，需要修改应用程序。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;6.mysql Cluster&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;国内用的非常少，主要因为一下三点：&lt;/p&gt;
 &lt;p&gt;（1）需要更改存储引擎&lt;/p&gt;
 &lt;p&gt;（2）付费&lt;/p&gt;
 &lt;p&gt;（3）国内几乎没有使用案例&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;高可用，可用率达99.999%&lt;/p&gt;
 &lt;h4&gt;7.结束语&lt;/h4&gt;
 &lt;p&gt;上面的高可用方案，只是我自己比较熟悉的，而且也是应用比较多的。mysql毕竟发展了有20多年了，各种高可用方案还是很多的，其他的高可用方案各位钥匙有兴趣，可以自己研究。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql 数据库 运维 后端 程序员</category>
      <guid isPermaLink="true">https://itindex.net/detail/60490-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</guid>
      <pubDate>Thu, 09 Apr 2020 12:11:16 CST</pubDate>
    </item>
    <item>
      <title>等保测评2.0：MySQL安全审计</title>
      <link>https://itindex.net/detail/60443-mysql-%E5%AE%89%E5%85%A8-%E5%AE%A1%E8%AE%A1</link>
      <description>&lt;h2&gt;    一、说明&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;    本篇文章主要说一说MySQL数据库安全审计控制点的相关内容和理解。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;    MySQL除了自身带有的审计功能外，还存在着一些其它的审计插件。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;虽然遇到这些插件的概率不高，我还是把这些插件的基本参数都列出来，到时候如果真遇到了，也不至于一头雾水。&lt;/p&gt;
 &lt;h2&gt;    二、测评项&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;a)应启用安全审计功能，审计覆盖到每个用户，对重要的用户行为和重要安全事件进行审计；&lt;/p&gt;
  &lt;p&gt;b)审计记录应包括事件的日期和时间、用户、事件类型、事件是否成功及其他与审计相关的信息； &lt;/p&gt;
  &lt;p&gt;c)应对审计记录进行保护，定期备份，避免受到未预期的删除、修改或覆盖等； &lt;/p&gt;
  &lt;p&gt;d)应对审计进程进行保护，防止未经授权的中断。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;    三、测评项a&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;a)应启用安全审计功能，审计覆盖到每个用户，对重要的用户行为和重要安全事件进行审计；&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h3&gt;    3.1. 自带的审计功能&lt;/h3&gt;
 &lt;p&gt;    在MySQL中自带了审计功能——general log，它会记录所有关于mysql的sql语句（所以会给服务器和数据库带来很大的资源占用）。&lt;/p&gt;
 &lt;p&gt;不过仅仅从测评要求的角度来说，如果开启了general log，那么是符合测评项a的。&lt;/p&gt;
 &lt;p&gt;    查询的时候，可以使用log或者general关键词，这里用的是general（不过用log要好一些）：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;show global variables like &amp;apos;%general%&amp;apos;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;      &lt;img src="https://image.3001.net/images/20200229/1582976195_5e5a4cc3ce556.png!small"&gt;&lt;/img&gt;图中的general_log变量的值为OFF，则表示没有开启。&lt;/p&gt;
 &lt;p&gt;general_log_file则表示日志存储在哪，图中是存储在一个文件中。&lt;/p&gt;
 &lt;p&gt;    MySQL 5.1.6版开始，可以将日志存储在表当中，这个由log_output参数进行控制，值为file，则代表存储在文件中，为table，则代表存储在gengera_log表中。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;mysql&amp;gt; show variables like &amp;apos;log_output&amp;apos;;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_output    | TABLE |
+---------------+-------+
mysql&amp;gt; select * from general_log;
| 2018-07-17 23:00:12 | root[root] @ localhost [] |         2 | 1132333306 | Query        | select * from test.student3&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    也可以去看一看/etc/my.cnf文件，查看是否启用了general_log：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;[mysqld]
general_log = on                                          // on为开启；off为关闭
general_log_file = /var/log/generalLog.log         // 审计信息存储位置
log_timestamps = SYSTEM                                // 设置日志文件的输出时间为地方时&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    修改my.cnf文件和设置global变量的区别在于，设置global变量，则数据库重启后设置就失效了。&lt;/p&gt;
 &lt;p&gt;而修改my.cnf文件，数据库每次启动后，都会先去my.cnf读取变量的值，再传给相应的global变量。&lt;/p&gt;
 &lt;p&gt;下面其它变量也是如此。&lt;/p&gt;
 &lt;p&gt;    另外要说的一点是，变量general_log的类型是bool，可以设置的值为OFF（或者0），以及ON（或者1），所以设置为ON和1是一个意思。  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976204_5e5a4ccc6f149.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;    3.2. MariaDB的Audit Plugin插件&lt;/h3&gt;
 &lt;p&gt;    该插件可以用于MySQL的一些版本上，比如MySQL 5.7.18。&lt;/p&gt;
 &lt;p&gt;    该插件的相关变量为：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SHOW GLOBAL VARIABLES LIKE &amp;apos;server_audit%&amp;apos;;
+-------------------------------+-----------------------+
| Variable_name                 | Value                 |
+-------------------------------+-----------------------+
| server_audit_events           | CONNECT,QUERY,TABLE   |
| server_audit_excl_users       |                       |
| server_audit_file_path        | server_audit.log      |
| server_audit_file_rotate_now  | OFF                   |
| server_audit_file_rotate_size | 1000000               |
| server_audit_file_rotations   | 9                     |
| server_audit_incl_users       |                       |
| server_audit_logging          | ON                    |
| server_audit_mode             | 0                     |
| server_audit_output_type      | file                  |
| server_audit_query_log_limit  | 1024                  |
| server_audit_syslog_facility  | LOG_USER              |
| server_audit_syslog_ident     | mysql-server_auditing |
| server_audit_syslog_info      |                       |
| server_audit_syslog_priority  | LOG_INFO              |
+-------------------------------+-----------------------+&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    解释如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;server_audit_output_type：指定日志输出类型，可为SYSLOG或FILE
server_audit_logging：启动或关闭审计
server_audit_events：指定记录事件的类型，可以用逗号分隔的多个值(connect,query,table)，如果开启了查询缓存(query cache)，查询直接从查询缓存返回数据，将没有table记录
server_audit_file_path：如server_audit_output_type为FILE，使用该变量设置存储日志的文件，可以指定目录，默认存放在数据目录的server_audit.log文件中
server_audit_file_rotate_size：限制日志文件的大小
server_audit_file_rotations：指定日志文件的数量，如果为0日志将从不轮转
server_audit_file_rotate_now：强制日志文件轮转
server_audit_incl_users：指定哪些用户的活动将记录，connect将不受此变量影响，该变量比server_audit_excl_users优先级高
server_audit_syslog_facility：默认为LOG_USER，指定facility
server_audit_syslog_ident：设置ident，作为每个syslog记录的一部分
server_audit_syslog_info：指定的info字符串将添加到syslog记录
server_audit_syslog_priority：定义记录日志的syslogd priority
server_audit_excl_users：该列表的用户行为将不记录，connect将不受该设置影响
server_audit_mode：标识版本，用于开发测试&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    这里我们比较关注的是server_audit_logging、server_audit_events、server_audit_output_type、server_audit_file_path、server_audit_file_rotate_size、server_audit_file_rotations、server_audit_file_rotate_now。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;server_audit_logging：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;即为是否开启，bool类型，值为ON（1）以及OFF（0）。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;server_audit_events：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;记录的事件，如果为空字符串，则代表记录所有的事件。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;CONNECT：连接、断开连接和失败的连接，包括错误代码&lt;/p&gt;
  &lt;p&gt;QUERY：以纯文本形式执行的查询及其结果，包括由于语法或权限错误而失败的查询&lt;/p&gt;
  &lt;p&gt;TABLE：受查询执行影响的表&lt;/p&gt;
  &lt;p&gt;QUERY_DDL：与QUERY相同，但只筛选DDL类型的查询（create、alter、drop、rename和truncate语句，create/drop[procedure/function/user]和rename user除外（它们不是DDL）&lt;/p&gt;
  &lt;p&gt;QUERY_DML：与QUERY相同，但只筛选DML类型的查询（do、call、load data/xml、delete、insert、select、update、handler和replace语句）&lt;/p&gt;
  &lt;p&gt;QUERY_DCL：与QUERY相同，但只筛选DCL类型的查询（create user、drop user、rename user、grant、revoke和set password语句）&lt;/p&gt;
  &lt;p&gt;QUERY_DML_NO_SELECT：与QUERY_DML相同，但不记录SELECT查询。（从1.4.4版开始）（do、call、load data/xml、delete、insert、update、handler和replace语句）&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;      &lt;strong&gt;server_audit_file_path:&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;当server_audit_output_type为file时，将路径和文件名设置为日志文件。如果指定的路径作为目录存在，那么将在该目录内创建名为“ server_audit.log”的日志。否则，该值将被视为文件名。默认值“ server_audit.log”，这意味着将在数据库目录中创建此文件。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;server_audit_file_rotate_size、server_audit_file_rotations、server_audit_file_rotate_now：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;当server_audit_output_type为file时，是否强制轮转（server_audit_file_rotate_now），每个日志文件的最大大小（server_audit_file_rotate_size），以及日志文件的最大数量（server_audit_file_rotations）。&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;    更多变量的相关的解释可以查看官方文档：   &lt;a href="https://mariadb.com/kb/en/mariadb-audit-plugin-options-and-system-variables/"&gt;MariaDB Audit Plugin Options and System Variables&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;    那么，从这个插件的功能来看，基本上默认配置就可以满足测评项的要求。&lt;/p&gt;
 &lt;h3&gt;    3.3. MySQL Enterprise Audit Plugin&lt;/h3&gt;
 &lt;p&gt;    MySQL 企业版的 Enterprise Edition 中自带 Audit Plugin ，名为 audit_log.so。&lt;/p&gt;
 &lt;p&gt;    对于该插件，可以在my.cnf文件中加入以下参数启用它：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;    [mysqld]  
    plugin-load=audit_log.so&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    也可以查询插件，看到插件的状态：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;mysql&amp;gt; SELECT PLUGIN_NAME, PLUGIN_STATUS
       FROM INFORMATION_SCHEMA.PLUGINS
       WHERE PLUGIN_NAME LIKE &amp;apos;audit%&amp;apos;;
+-------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+-------------+---------------+
| audit_log   | ACTIVE        |
+-------------+---------------+&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    该插件的相关系统变量为：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;mysql&amp;gt; SHOW VARIABLES LIKE &amp;apos;audit_log%&amp;apos;;
+-----------------------------+--------------+
| Variable_name               | Value        |
+-----------------------------+--------------+
| audit_log_buffer_size       | 1048576      |
| audit_log_connection_policy | ALL          |
| audit_log_current_session   | OFF          |
| audit_log_exclude_accounts  |              |
| audit_log_file              | audit.log    |
| audit_log_filter_id         | 0            |
| audit_log_flush             | OFF          |
| audit_log_format            | NEW          |
| audit_log_include_accounts  |              |
| audit_log_policy            | ALL          |
| audit_log_rotate_on_size    | 0            |
| audit_log_statement_policy  | ALL          |
| audit_log_strategy          | ASYNCHRONOUS |
+-----------------------------+--------------+&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;      &lt;strong&gt;audit_log_connection_policy：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;控制审核日志插件如何将连接事件写入其日志文件的策略  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976219_5e5a4cdb6a873.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;audit_log_exclude_accounts：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;不应记录事件的帐户，除此之外的账户的事件都会被记录。该值应为NULL或包含一个或多个用逗号分隔的帐户名列表的字符串。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;audit_log_file：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;日志记录的文件名，可以是相对路径（相对于数据库目录）和完整路径。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;audit_log_format：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;日志格式，可以是 OLD（旧样式XML）， NEW（新样式XML，默认值）和（从MySQL 5.7.21开始）JSON。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;audit_log_include_accounts：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;要包括在审核日志记录中的帐户。如果设置了此变量，则仅审核这些帐户。&lt;/p&gt;
 &lt;p&gt;注意，audit_log_exclude_accounts与audit_log_include_accounts是互斥的，它们之间只有一个的值为非null，不能同时为非null。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;audit_log_policy：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;事件记录策略  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976225_5e5a4ce15bf06.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;audit_log_rotate_on_size：&lt;/strong&gt;  &lt;br /&gt;如果 audit_log_rotate_on_size 值为0，则审核日志插件不会执行自动日志文件轮换。而是手动使用audit_log_flush刷新日志文件。在这种情况下，请在刷新文件之前在服务器外部手动重命名该文件（要不然原来的记录就没了）。&lt;/p&gt;
 &lt;p&gt;如果该 audit_log_rotate_on_size 值大于0，则会自动进行基于大小的日志文件轮换。每当写入日志文件导致其大小超过该 audit_log_rotate_on_size 值时，审核日志插件都会关闭当前日志文件，将其重命名，然后打开一个新的日志文件。&lt;/p&gt;
 &lt;p&gt;如果将此变量设置为不是4096的倍数的值，它将被截断为最接近的倍数。（因此，将其设置为小于4096的效果是将其设置为0且不进行旋转，除非手动进行。）&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;audit_log_statement_policy：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;应该被记录的语句事件，在服务器启动的时候如果audit_log_statement_policy和audit_log_policy都显示赋予了值，那么audit_log_statement_policy可能会被audit_log_policy覆盖。  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976231_5e5a4ce7a65db.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;    更多详细的解释请看官方文档：   &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/audit-log.html"&gt;MySQL Enterprise Audit&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;    基本上默认配置也足够满足测评项要求了。&lt;/p&gt;
 &lt;h3&gt;    3.4. McAfee的libaudit_plugin&lt;/h3&gt;
 &lt;p&gt;    也是一个审核插件，其相关参数如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SHOW GLOBAL VARIABLES LIKE &amp;apos;%audi%&amp;apos;; 
audit_json_file　　　　　　　 #是否开启audit功能（ON\OFF）
audit_json_log_file　　　　　#log日志名称及存储位置，默认mysql的data目录
audit_record_cmds=&amp;apos;&amp;apos;　　　　#设置需要监控的SQL命令，默认全部（即该值为null）
audit_record_cmds=&amp;apos;insert,delete,update,create,drop,alter,grant,truncate&amp;apos;   #这是一些例子
audit_record_objs=&amp;apos;&amp;apos;          #设置需要监控的数据库名称和表名，默认全部（即该值为null）
audit_record_objs=‘mysql.*’　　  #一个例子
audit_whitelist_users　　　　#用户白名单&lt;/code&gt;&lt;/pre&gt;
 &lt;blockquote&gt;  &lt;p&gt;    更多详细解释请查看官方文档：   &lt;a href="https://github.com/mcafee/mysql-audit/wiki/Configuration"&gt;McAfee的audit&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;    基本上启用后就满足测评项要求了。&lt;/p&gt;
 &lt;h2&gt;四、测评项b&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;b)审计记录应包括事件的日期和时间、用户、事件类型、事件是否成功及其他与审计相关的信息；&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;    只要启用了审计功能，无论是自带的审计还是插件，在记录的信息上都能满足这个要求。&lt;/p&gt;
 &lt;h3&gt;    4.1. 自带的审计功能&lt;/h3&gt;
 &lt;p&gt;    其记录内容如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;mysql&amp;gt; select * from general_log;
| 2018-07-17 23:00:12 | root[root] @ localhost [] |         2 | 1132333306 | Query        | select * from test.student3&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;    4.2. MariaDB的Audit Plugin插件&lt;/h3&gt;
 &lt;p&gt;    其记录内容如下：  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976243_5e5a4cf3d5d8e.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;    日志的格式解释可看官方文档：   &lt;a href="https://mariadb.com/kb/en/mariadb-audit-plugin-log-format/"&gt;MariaDB Audit Plugin – Log Format&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h3&gt;    4.3. MySQL Enterprise Audit Plugin&lt;/h3&gt;
 &lt;p&gt;    该插件的日志文件可以是XML或者JSON格式，以XML为例：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt; &amp;lt;AUDIT_RECORD&amp;gt;
  &amp;lt;TIMESTAMP&amp;gt;2019-10-03T14:09:38 UTC&amp;lt;/TIMESTAMP&amp;gt;
  &amp;lt;RECORD_ID&amp;gt;6_2019-10-03T14:06:33&amp;lt;/RECORD_ID&amp;gt;
  &amp;lt;NAME&amp;gt;Query&amp;lt;/NAME&amp;gt;
  &amp;lt;CONNECTION_ID&amp;gt;5&amp;lt;/CONNECTION_ID&amp;gt;
  &amp;lt;STATUS&amp;gt;0&amp;lt;/STATUS&amp;gt;
  &amp;lt;STATUS_CODE&amp;gt;0&amp;lt;/STATUS_CODE&amp;gt;
  &amp;lt;USER&amp;gt;root[root] @ localhost [127.0.0.1]&amp;lt;/USER&amp;gt;
  &amp;lt;OS_LOGIN/&amp;gt;
  &amp;lt;HOST&amp;gt;localhost&amp;lt;/HOST&amp;gt;
  &amp;lt;IP&amp;gt;127.0.0.1&amp;lt;/IP&amp;gt;
  &amp;lt;COMMAND_CLASS&amp;gt;drop_table&amp;lt;/COMMAND_CLASS&amp;gt;
  &amp;lt;SQLTEXT&amp;gt;DROP TABLE IF EXISTS t&amp;lt;/SQLTEXT&amp;gt;
 &amp;lt;/AUDIT_RECORD&amp;gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;blockquote&gt;  &lt;p&gt;    相似的格式介绍请查看官方文档：   &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/audit-log-file-formats.html"&gt;审核日志文件格式&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h3&gt;    4.4. McAfee的libaudit_plugin&lt;/h3&gt;
 &lt;p&gt;    该插件日志文件的格式是json：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;{
    &amp;quot;msg-type&amp;quot;: &amp;quot;activity&amp;quot;,
    &amp;quot;date&amp;quot;: &amp;quot;1510038432019&amp;quot;,
    &amp;quot;thread-id&amp;quot;: &amp;quot;43&amp;quot;,
    &amp;quot;query-id&amp;quot;: &amp;quot;1891&amp;quot;,
    &amp;quot;user&amp;quot;: &amp;quot;root&amp;quot;,
    &amp;quot;priv_user&amp;quot;: &amp;quot;root&amp;quot;,
    &amp;quot;ip&amp;quot;: &amp;quot;&amp;quot;,
    &amp;quot;host&amp;quot;: &amp;quot;localhost&amp;quot;,
    &amp;quot;connect_attrs&amp;quot;: {
        &amp;quot;_os&amp;quot;: &amp;quot;linux-glibc2.5&amp;quot;,
        &amp;quot;_client_name&amp;quot;: &amp;quot;libmysql&amp;quot;,
        &amp;quot;_pid&amp;quot;: &amp;quot;4009&amp;quot;,
        &amp;quot;_client_version&amp;quot;: &amp;quot;5.7.9&amp;quot;,
        &amp;quot;_platform&amp;quot;: &amp;quot;x86_64&amp;quot;,
        &amp;quot;program_name&amp;quot;: &amp;quot;mysql&amp;quot;
    },
    &amp;quot;pid&amp;quot;: &amp;quot;4009&amp;quot;,
    &amp;quot;os_user&amp;quot;: &amp;quot;root&amp;quot;,
    &amp;quot;appname&amp;quot;: &amp;quot;mysql&amp;quot;,
    &amp;quot;rows&amp;quot;: &amp;quot;1&amp;quot;,
    &amp;quot;cmd&amp;quot;: &amp;quot;insert&amp;quot;,
    &amp;quot;objects&amp;quot;: [
        {
            &amp;quot;db&amp;quot;: &amp;quot;part&amp;quot;,
            &amp;quot;name&amp;quot;: &amp;quot;e&amp;quot;,
            &amp;quot;obj_type&amp;quot;: &amp;quot;TABLE&amp;quot;
        }
    ],
    &amp;quot;query&amp;quot;: &amp;quot;insert into e values (9898,&amp;apos;smart&amp;apos;,&amp;apos;james&amp;apos;)&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
 &lt;blockquote&gt;  &lt;p&gt;    详细的格式介绍请查看官方文档：   &lt;a href="https://github.com/mcafee/mysql-audit/wiki/Configuration"&gt;mysql-audit&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;    五、测评项c&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;c)应对审计记录进行保护，定期备份，避免受到未预期的删除、修改或覆盖等；&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h3&gt;    5.1. 要求1&lt;/h3&gt;
 &lt;p&gt;    对审计记录进行保护，那么这里就不细说了，说一下大概的原则。&lt;/p&gt;
 &lt;p&gt;无论是自带的审计还是审计插件，如果审核记录存储于文件中的，应该在操作系统上对这些日志文件的权限进行限定，仅允许数据库管理员可对这些文件进行访问、修改等。同时也要限制MySQL中的file_priv权限。&lt;/p&gt;
 &lt;p&gt;如果审核记录存储于数据库表中，那么也应该对数据库的表进行权限设置，仅数据库管理员可对审核记录表进行访问、修改等。&lt;/p&gt;
 &lt;h3&gt;    5.2. 要求2&lt;/h3&gt;
 &lt;p&gt;    定期备份就不用多做什么说明了，检查备份文件和备份策略即可。&lt;/p&gt;
 &lt;p&gt;    在这里有一个地方想探讨下，在等级保护2.0试行稿中，对日志的留存时间有要求：  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976255_5e5a4cffb5f3a.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;    这里的法律法规要求一般来说指的就是《网络安全法》，其中有关日志留存时间的条款如下：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;（三）采取监测、记录网络运行状态、网络安全事件的技术措施，并按照规定留存相关的网络日志不少于六个月；&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;    在等保正式2.0正式稿中，这个测评项被删除了，那么《网络安全法》对于日志留存时间（6个月）的要求是否落在了测评项c当中呢？  &lt;br /&gt;从基本要求来看，应该不是，其中没有这个要求：  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976261_5e5a4d0540763.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;    当然，既然网络安全法这么规定了，等级保护肯定还是有测评项来实现该要求的，就是在安全管理中心的集中管控的测评项中：  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976266_5e5a4d0aec61b.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;    按照我的个人理解，6个月的留存时间要求，应该是在集中管控的c测评项中去落实。&lt;/p&gt;
 &lt;p&gt;怎么测评呢？首先肯定要有相关的审计设备，也就是数据库审计以及综合日志审计设备，没有这些设备，集中管控d测评项的第一个要求就没法满足。&lt;/p&gt;
 &lt;p&gt;然后在这些设备中，查看汇总的审计记录留存时间是否满足了法律法规的要求。&lt;/p&gt;
 &lt;p&gt;    也就是，不需要跑去单个的设备上，查看每个设备的审计记录是否满足法律法规的要求。&lt;/p&gt;
 &lt;p&gt;否则，等级保护2.0正式稿中就不会将应确保审计记录的留存时间符合法律法规要求挪到集中管控里面去了。&lt;/p&gt;
 &lt;p&gt;    为什么说到这个呢？因为我在初级教程里看到了关于留存时间的要求：  &lt;br /&gt;  &lt;img src="https://image.3001.net/images/20200229/1582976274_5e5a4d1269126.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;    综上所述，我个人觉得关于日志留存时间6个月的要求，应该再集中管控的d测评项中进行统一描述，而不是在每个测评对象的安全审计的c测评项中进行描述。&lt;/p&gt;
 &lt;h2&gt;    六、测评项d&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;d)应对审计进程进行保护，防止未经授权的中断。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;    这个就比较简单了，有两个地方可以对审计进程进行配置。&lt;/p&gt;
 &lt;p&gt;一个是my.cnf，这里就需要操作系统上对配置文件的权限进行限制，只允许数据库管理有权限进行修改。（同时也要限制MySQL中的file_priv权限。）&lt;/p&gt;
 &lt;p&gt;另外一个就是那些变量了，似乎是需要super权限才可以设置全局变量，那么这里的话就需要查看super权限给了哪些账户。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;    *本文原创作者：起于凡而非于凡，本文属于FreeBuf原创奖励计划，未经许可禁止转载&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>数据安全 mysql 安全审计 等保2.0</category>
      <guid isPermaLink="true">https://itindex.net/detail/60443-mysql-%E5%AE%89%E5%85%A8-%E5%AE%A1%E8%AE%A1</guid>
      <pubDate>Sun, 22 Mar 2020 08:00:49 CST</pubDate>
    </item>
    <item>
      <title>ProxySQL+Mysql实现数据库读写分离实战</title>
      <link>https://itindex.net/detail/60438-proxysql-mysql-%E6%95%B0%E6%8D%AE%E5%BA%93</link>
      <description>&lt;p&gt;前面也写过几篇关于Mysql数据的文章：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://segmentfault.com/a/1190000021100914"&gt;MySQL集群高可用架构之MHA&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://segmentfault.com/a/1190000020693588"&gt;MySQL 同步复制及高可用方案总结&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://segmentfault.com/a/1190000020751216"&gt;官方工具｜MySQL Router 高可用原理与实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;今天给大家带来的是关于数据库读写分离相关的实战操作。&lt;/p&gt;
 &lt;h2&gt;ProxySQL介绍&lt;/h2&gt;
 &lt;p&gt;ProxySQL是一个高性能的MySQL中间件，拥有强大的规则引擎。具有以下特性：  &lt;a href="http://www.proxysql.com/" rel="nofollow noreferrer"&gt;http://www.proxysql.com/&lt;/a&gt;&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;1、连接池，而且是multiplexing     &lt;br /&gt;2、主机和用户的最大连接数限制     &lt;br /&gt;3、自动下线后端DB&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;延迟超过阀值&lt;/li&gt;
   &lt;li&gt;ping 延迟超过阀值&lt;/li&gt;
   &lt;li&gt;网络不通或宕机&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;4、强大的规则路由引擎&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;实现读写分离&lt;/li&gt;
   &lt;li&gt;查询重写&lt;/li&gt;
   &lt;li&gt;sql流量镜像&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;5、支持prepared statement     &lt;br /&gt;6、支持Query Cache     &lt;br /&gt;7、支持负载均衡，与gelera结合自动failover&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;整体环境介绍&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022074104" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h4&gt;1、系统环境&lt;/h4&gt;
 &lt;p&gt;三台服务器系统环境一致如下&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;[root@db1 ~]# cat /etc/redhat-release 
CentOS Linux release 7.4.1708 (Core) 
[root@db1 ~]# uname -r
3.10.0-693.el7.x86_64&lt;/code&gt;&lt;/pre&gt;
 &lt;h4&gt;2、IP地址与软件版本&lt;/h4&gt;
 &lt;blockquote&gt;  &lt;ul&gt;
   &lt;li&gt;proxy  192.168.22.171&lt;/li&gt;
   &lt;li&gt;db1     192.168.22.173&lt;/li&gt;
   &lt;li&gt;db2    192.168.22.174&lt;/li&gt;
   &lt;li&gt;mysql  5.7.17&lt;/li&gt;
   &lt;li&gt;proxy  sql 1.4.8&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;
 &lt;h4&gt;3、关闭防火墙、selinux&lt;/h4&gt;
 &lt;pre&gt;  &lt;code&gt;systemctl stop firewalld  #停止防火墙服务
systemctl disable firewalld  #禁止开机自启动
sed -i &amp;apos;s#SELINUX=enforcing#SELINUX=disabled#g&amp;apos;  /etc/selinux/conf  &amp;amp;&amp;amp; reboot
#用sed命令替换的试修改selinux的配置文件&lt;/code&gt;&lt;/pre&gt;
 &lt;h4&gt;4、mysql安装与主从同步&lt;/h4&gt;
 &lt;p&gt;安装请参考以下文章&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzI0MDQ4MTM5NQ==&amp;mid=2247484330&amp;idx=1&amp;sn=5e3e35bb5793258b86c096485fe02246&amp;chksm=e91b66b6de6cefa09740c86b70ef62d943298ef874fec0af1d66254701479196f6f785569cf2&amp;scene=21#wechat_redirect" rel="nofollow noreferrer"&gt;   &lt;strong&gt;LAMP架构应用实战——MySQL服务&lt;/strong&gt;&lt;/a&gt;  &lt;/p&gt;
 &lt;p&gt;主从同步请参以下文章&lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzI0MDQ4MTM5NQ==&amp;mid=2247484506&amp;idx=1&amp;sn=e2ae27f5b561485011846237bd74707b&amp;chksm=e91b6146de6ce850798c2fe564a5ff5a7096437ffde458b848350720bb39ff46e01a190c0e41&amp;scene=21#wechat_redirect" rel="nofollow noreferrer"&gt;   &lt;strong&gt;Linux系统MySQL数据库主从同步实战过程&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;安装布署过程&lt;/h2&gt;
 &lt;h3&gt;1、数据库主从同步&lt;/h3&gt;
 &lt;h5&gt;查看主从同步状态&lt;/h5&gt;
 &lt;pre&gt;  &lt;code&gt;mysql&amp;gt; show slave status\G
*************************** 1. row ***************************               
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.22.173                  
Master_User: rep                  
Master_Port: 3306                
Connect_Retry: 60              
Master_Log_File: master-log.000001
Read_Master_Log_Pos: 154               
Relay_Log_File: db2-relay-bin.000002
Relay_Log_Pos: 321        
Relay_Master_Log_File: master-log.000001  
Slave_IO_Running: Yes            
Slave_SQL_Running: Yes              
Replicate_Do_DB:           
Replicate_Ignore_DB:            
Replicate_Do_Table:        
Replicate_Ignore_Table:       
Replicate_Wild_Do_Table:   
Replicate_Wild_Ignore_Table:                    
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 154
Relay_Log_Space: 526
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No 
Master_SSL_CA_File: 
Master_SSL_CA_Path: 
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No 
Last_IO_Errno: 0 
Last_IO_Error: 
Last_SQL_Errno: 0 
Last_SQL_Error:
Replicate_Ignore_Server_Ids: 
Master_Server_Id: 1 
Master_UUID: 70a61633-63ae-11e8-ab86-000c29fe99ea 
Master_Info_File: /mysqldata/master.info
SQL_Delay: 0 
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates 
Master_Retry_Count: 86400 
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl: 
Master_SSL_Crlpath: 
Retrieved_Gtid_Set: 
Executed_Gtid_Set: 
Auto_Position: 0 
Replicate_Rewrite_DB:
Channel_Name: 
Master_TLS_Version:
1 row in set (0.00 sec)&lt;/code&gt;&lt;/pre&gt;
 &lt;h5&gt;检测主从同步&lt;/h5&gt;
 &lt;pre&gt;  &lt;code&gt;[root@db1 ~]# mysql -uroot -p -e &amp;quot;create database testdb;
&amp;quot;Enter password: 
[root@db1 ~]# mysql -uroot -p -e &amp;quot;show  databases;&amp;quot; |grep testdb
Enter password: 
testdb
#db2上查看是否同步
mysql&amp;gt; show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| testdb             |
+--------------------+
5 rows in set (0.01 sec)&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;2、准备proxySQL软件&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;[root@proxy ~]# wget https://github.com/sysown/proxysql/releases/download/v1.4.8/proxysql-1.4.8-1-centos7.x86_64.rpm
[root@proxy ~]# ll proxysql-1.4.8-1-centos7.x86_64.rpm 
-rw-r--r-- 1 root root 5977168 Apr 10 11:38 proxysql-1.4.8-1-centos7.x86_64.rpm&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;3、安装配置&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;[root@proxy ~]# yum install -y proxysql-1.4.8-1-centos7.x86_64.rpm
[root@proxy ~]# rpm -ql  proxysql
/etc/init.d/proxysql    #启动脚本
/etc/proxysql.cnf       #配置文件，仅在第一次(/var/lib/proxysql/proxysql.db文件不存在)启动时有效。启#动后可以在proxysql管理端中通过修改数据库的方式修改配置并生效(官方推荐方式。)
/usr/bin/proxysql       #主程序文件
/usr/share/proxysql/tools/proxysql_galera_checker.sh
/usr/share/proxysql/tools/proxysql_galera_writer.pl&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;4、配置文件详解&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;[root@proxy ~]# egrep -v &amp;quot;^#|^$&amp;quot; /etc/proxysql.cnf
datadir=&amp;quot;/var/lib/proxysql&amp;quot;     #数据目录
admin_variables=
{    
admin_credentials=&amp;quot;admin:admin&amp;quot;   #连接管理端的用户名与密码 
mysql_ifaces=&amp;quot;0.0.0.0:6032&amp;quot;       #管理端口，用来连接proxysql的管理数据库
}
mysql_variables=
{    
threads=4      #指定转发端口开启的线程数量    
max_connections=2048    
default_query_delay=0
default_query_timeout=36000000
have_compress=true
poll_timeout=2000
interfaces=&amp;quot;0.0.0.0:6033&amp;quot;        #指定转发端口，用于连接后端mysql数据库的，相当于代理作用
default_schema=&amp;quot;information_schema&amp;quot;
stacksize=1048576
server_version=&amp;quot;5.5.30&amp;quot;          #指定后端mysql的版本
connect_timeout_server=3000 
monitor_username=&amp;quot;monitor&amp;quot;
monitor_password=&amp;quot;monitor&amp;quot;
monitor_history=600000
monitor_connect_interval=60000
monitor_ping_interval=10000
monitor_read_only_interval=1500 
monitor_read_only_timeout=500  
ping_interval_server_msec=120000 
ping_timeout_server=500
commands_stats=true 
sessions_sort=true 
connect_retries_on_failure=10
}
mysql_servers =
(
)
mysql_users:
(
)
mysql_query_rules:
(
)
scheduler=
(
)
mysql_replication_hostgroups=
(
)

#因此我们使用官方推荐的方式来配置proxy sql&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;5、启动服务并查看&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;[root@proxy ~]# /etc/init.d/proxysql
startStarting ProxySQL: DONE!
[root@proxy ~]# ss -lntup|grep proxy
tcp    LISTEN     0   128   *:6032      *:*    users:((&amp;quot;proxysql&amp;quot;,pid=1199,fd=23))
tcp    LISTEN     0   128   *:6033      *:*    users:((&amp;quot;proxysql&amp;quot;,pid=1199,fd=22))
tcp    LISTEN     0   128   *:6033      *:*    users:((&amp;quot;proxysql&amp;quot;,pid=1199,fd=21))
tcp    LISTEN     0   128   *:6033      *:*    users:((&amp;quot;proxysql&amp;quot;,pid=1199,fd=20))
tcp    LISTEN     0   128   *:6033      *:*    users:((&amp;quot;proxysql&amp;quot;,pid=1199,fd=19))

#可以看出转发端口6033是启动了四个线程&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;6、在mysql上配置账号并授权&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;mysql&amp;gt; GRANT ALL ON *.* TO &amp;apos;proxysql&amp;apos;@&amp;apos;192.168.22.%&amp;apos; IDENTIFIED BY &amp;apos;123456&amp;apos;;
Query OK, 0 rows affected, 1 warning (0.03 sec)
mysql&amp;gt; flush privileges;
Query OK, 0 rows affected (0.02 sec)&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;7、proxysql默认数据库说明&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;[root@proxy ~]# yum install mysql -y
[root@proxy ~]# mysql -uadmin -padmin -h127.0.0.1 -P6032
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.5.30 (ProxySQL Admin Module)
Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.
Type &amp;apos;help;&amp;apos; or &amp;apos;\h&amp;apos; for help. Type &amp;apos;\c&amp;apos; to clear the current input statement.
MySQL [(none)]&amp;gt; show databases;
+-----+---------------+-------------------------------------+
| seq | name          | file                                |
+-----+---------------+-------------------------------------+
| 0   | main          |                                     |
| 2   | disk          | /var/lib/proxysql/proxysql.db       |
| 3   | stats         |                                     |
| 4   | monitor       |                                     |
| 5   | stats_history | /var/lib/proxysql/proxysql_stats.db |
+-----+---------------+-------------------------------------+
5 rows in set (0.00 sec)&lt;/code&gt;&lt;/pre&gt;
 &lt;blockquote&gt;
  &lt;strong&gt;main：&lt;/strong&gt;内存配置数据库，表里存放后端db实例、用户验证、路由规则等信息。表名以 runtime_开头的表示proxysql当前运行的配置内容，不能通过dml语句修改，只能修改对应的不以 runtime_ 开头的（在内存）里的表，然后 LOAD 使其生效， SAVE 使其存到硬盘以供下次重启加载。    &lt;br /&gt;  &lt;strong&gt;disk：&lt;/strong&gt;是持久化到硬盘的配置，sqlite数据文件。    &lt;br /&gt;  &lt;strong&gt;stats：&lt;/strong&gt;是proxysql运行抓取的统计信息，包括到后端各命令的执行次数、流量、processlist、查询种类汇总/执行时间等等。    &lt;br /&gt;  &lt;strong&gt;monitor：&lt;/strong&gt;库存储 monitor 模块收集的信息，主要是对后端db的健康/延迟检查。&lt;/blockquote&gt;
 &lt;h3&gt;8、proxysql的配置系统&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;ProxySQL具有一个复杂但易于使用的配置系统，可以满足以下需求：&lt;/strong&gt;&lt;/p&gt;
 &lt;blockquote&gt;1、允许轻松动态更新配置（这是为了让ProxySQL用户可以在需要零宕机时间配置的大型基础架构中使用它）。与MySQL兼容的管理界面可用于此目的。    &lt;br /&gt;2、允许尽可能多的配置项目动态修改，而不需要重新启动ProxySQL进程    &lt;br /&gt;3、可以毫不费力地回滚无效配置    &lt;br /&gt;4、这是通过多级配置系统实现的，其中设置从运行时移到内存，并根据需要持久保存到磁盘。&lt;/blockquote&gt;
 &lt;h6&gt;3级配置由以下几层组成：&lt;/h6&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://segmentfault.com/img/bVbEMxA" title="image.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;参考文章：  &lt;a href="https://github.com/sysown/proxysql/wiki/Configuring-ProxySQL" rel="nofollow noreferrer"&gt;https://github.com/sysown/pro...&lt;/a&gt;&lt;/p&gt;
 &lt;h3&gt;9、配置proxysql管理用户&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;proxysql默认的表信息如下&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;MySQL [main]&amp;gt; show tables;
+--------------------------------------------+
| tables                                     |
+--------------------------------------------+
| global_variables                           |
| mysql_collations                           |
| mysql_group_replication_hostgroups         |
| mysql_query_rules                          |
| mysql_query_rules_fast_routing             |
| mysql_replication_hostgroups               |
| mysql_servers                              |
| mysql_users                                |
| proxysql_servers                           |
| runtime_checksums_values                   |
| runtime_global_variables                   |
| runtime_mysql_group_replication_hostgroups |
| runtime_mysql_query_rules                  |
| runtime_mysql_query_rules_fast_routing     |
| runtime_mysql_replication_hostgroups       |
| runtime_mysql_servers                      |
| runtime_mysql_users                        |
| runtime_proxysql_servers                   |
| runtime_scheduler                          |
| scheduler                                  |
+--------------------------------------------+
20 rows in set (0.00 sec)

#这里是使用insert into语句来动态配置，而可以不需要重启
MySQL [(none)]&amp;gt; insert into mysql_servers(hostgroup_id,hostname,port,weight,comment) values(1,&amp;apos;db1&amp;apos;,&amp;apos;3306&amp;apos;,1,&amp;apos;Write Group&amp;apos;);
Query OK, 1 row affected (0.01 sec)

MySQL [(none)]&amp;gt; insert intomysql_servers(hostgroup_id,hostname,port,weight,comment) values(2,&amp;apos;db2&amp;apos;,&amp;apos;3307&amp;apos;,1,&amp;apos;Read Group&amp;apos;);
Query OK, 1 row affected (0.00 sec)

MySQL [(none)]&amp;gt; select * from mysql_servers;
+--------------+----------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
| hostgroup_id | hostname | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment     |
+--------------+----------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
| 1            | db1      | 3306 | ONLINE | 1      | 0           | 1000            | 0                   | 0       | 0              | Write Group |
| 2            | db2      | 3307 | ONLINE | 1      | 0           | 1000            | 0                   | 0       | 0              | Read Group  
|+--------------+----------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
2 rows in set (0.00 sec)

#接下来将刚刚在mysql客户端创建的用户写入到proxy sql主机的mysql_users表中，它也是用于proxysql客户端访问数据库，默认组是写组，当读写分离规则出现问题时，它会直接访问默认组的数据库。
MySQL [main]&amp;gt; INSERT INTO mysql_users(username,password,default_hostgroup) VALUES (&amp;apos;proxysql&amp;apos;,&amp;apos;123456&amp;apos;,1);
Query OK, 1 row affected (0.00 sec)

MySQL [main]&amp;gt; select * from mysql_users;
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+
| username | password | active | use_ssl | default_hostgroup | default_schema | schema_locked | transaction_persistent | fast_forward | backend | frontend | max_connections |
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+
| proxysql | 123456   | 1      | 0       | 1                 | NULL           | 0             | 1                      | 0            | 1       | 1        | 10000           |
+----------+----------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+1 
row in set (0.00 sec)&lt;/code&gt;&lt;/pre&gt;
 &lt;h6&gt;在mysql上添加监控的用户&lt;/h6&gt;
 &lt;pre&gt;  &lt;code&gt;mysql&amp;gt; GRANT SELECT ON *.* TO &amp;apos;monitor&amp;apos;@&amp;apos;192.168.22.%&amp;apos; IDENTIFIED BY &amp;apos;monitor&amp;apos;;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql&amp;gt; flush privileges;
Query OK, 0 rows affected (0.00 sec)

#在proxysql主机端配置监控用户
MySQL [main]&amp;gt; set mysql-monitor_username=&amp;apos;monitor&amp;apos;;
Query OK, 1 row affected (0.00 sec)

MySQL [main]&amp;gt; set mysql-monitor_password=&amp;apos;monitor&amp;apos;;
Query OK, 1 row affected (0.00 sec)

#参考文章：https://github.com/sysown/proxysql/wiki/ProxySQL-Configuration&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;10、配置proxysql的转发规则&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;MySQL [main]&amp;gt; insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(1,1,&amp;apos;^SELECT.*FOR UPDATE$&amp;apos;,1,1);
Query OK, 1 row affected (0.01 sec)

MySQL [main]&amp;gt; insert into mysql_query_rules(rule_id,active,match_digest,destination_hostgroup,apply)values(2,1,&amp;apos;^SELECT&amp;apos;,2,1);
Query OK, 1 row affected (0.00 sec)

MySQL [main]&amp;gt; select rule_id,active,match_digest,destination_hostgroup,apply from mysql_query_rules;
+---------+--------+----------------------+-----------------------+-------+
| rule_id | active | match_digest         | destination_hostgroup | apply |
+---------+--------+----------------------+-----------------------+-------+
| 1       | 1      | ^SELECT.*FOR UPDATE$ | 1                     | 1     |
| 2       | 1      | ^SELECT              | 2                     | 1     |
+---------+--------+----------------------+-----------------------+-------+
2 rows in set (0.00 sec)

#配置查询select的请求转发到hostgroup_id=2组上（读组）#征对select * from table_name  for update这样的修改语句，我们是需要将请求转到写组，也就是hostgroup_id=1#对于其它没有被规则匹配的请求全部转发到默认的组（mysql_users表中default_hostgroup）&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;11、更新配置到RUNTIME中&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;由上面的配置系统层级关系可以得知所有进来的请求首先是经过RUNTIME层&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;MySQL [main]&amp;gt; load mysql users to runtime;
Query OK, 0 rows affected (0.00 sec)

MySQL [main]&amp;gt; load mysql servers to runtime;
Query OK, 0 rows affected (0.02 sec)

MySQL [main]&amp;gt; load mysql query rules to runtime;
Query OK, 0 rows affected (0.00 sec)

MySQL [main]&amp;gt; load mysql variables to runtime;
Query OK, 0 rows affected (0.00 sec)

MySQL [main]&amp;gt; load admin variables to runtime;
Query OK, 0 rows affected (0.00 sec)&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;12、将所有配置保存至磁盘上&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;所有配置数据保存到磁盘上，也就是永久写入/var/lib/proxysql/proxysql.db这个文件中&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;MySQL [main]&amp;gt; save mysql users to disk;
Query OK, 0 rows affected (0.03 sec)

MySQL [main]&amp;gt; save mysql servers to disk;
Query OK, 0 rows affected (0.04 sec)

ySQL [main]&amp;gt; save mysql query rules to disk;
Query OK, 0 rows affected (0.03 sec)

MySQL [main]&amp;gt; save mysql variables to disk;
Query OK, 94 rows affected (0.02 sec)

MySQL [main]&amp;gt; save admin variables to disk;
Query OK, 31 rows affected (0.02 sec)

MySQL [main]&amp;gt; load mysql users to runtime;
Query OK, 0 rows affected (0.00 sec)&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;13、测试读写分离&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;[root@proxy ~]# mysql -uproxysql -p123456 -h 127.0.0.1 -P 6033
Welcome to the MariaDB monitor.Commands end with ; or \g.
Your MySQL connection id is 2Server version: 5.5.30 (ProxySQL)

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type &amp;apos;help;&amp;apos; or &amp;apos;\h&amp;apos; for help. Type &amp;apos;\c&amp;apos; to clear the current input statement.

MySQL [(none)]&amp;gt; show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| testdb             |
+--------------------+
5 rows in set (0.02 sec)#这才是我们真正的数据库啊&lt;/code&gt;&lt;/pre&gt;
 &lt;h5&gt;创建数据与表，测试读写分离情况&lt;/h5&gt;
 &lt;pre&gt;  &lt;code&gt;MySQL [(none)]&amp;gt; create database test_proxysql;
Query OK, 1 row affected (0.02 sec)

MySQL [(none)]&amp;gt; use test_proxysql;
Database changed

MySQL [test_proxysql]&amp;gt; create table test_tables(name varchar(20),age int(4));
Query OK, 0 rows affected (0.07 sec)

MySQL [test_proxysql]&amp;gt; insert into test_tables values(&amp;apos;zhao&amp;apos;,&amp;apos;30&amp;apos;);
Query OK, 1 row affected (0.09 sec)

MySQL [test_proxysql]&amp;gt; select * from test_tables;
+------+------+
| name | age  |
+------+------+
| zhao |   30 |
+------+------+
1 row in set (0.02 sec)&lt;/code&gt;&lt;/pre&gt;
 &lt;h5&gt;在proxysql管理端查看读写分离&lt;/h5&gt;
 &lt;pre&gt;  &lt;code&gt;MySQL [main]&amp;gt; select * from stats_mysql_query_digest;
+-----------+--------------------+----------+--------------------+------------------------------------------------------+------------+------------+------------+----------+----------+----------+
| hostgroup | schemaname         | username | digest             | digest_text                                          | count_star | first_seen | last_seen  | sum_time | min_time | max_time |
+-----------+--------------------+----------+--------------------+------------------------------------------------------+------------+------------+------------+----------+----------+----------+
| 2         | test_proxysql      | proxysql | 0x57CF7EC26C91DF9A | select * from test_tables                            |1          | 1527667635 | 1527667635 | 14253    | 14253    | 14253    |
| 1         | information_schema | proxysql | 0x226CD90D52A2BA0B | select @@version_comment limit ?                     | 1          | 1527667214 | 1527667214 | 0        | 0        | 0        |
| 1         | test_proxysql      | proxysql | 0xFF9877421CFBDA6F | insert into test_tables values(?,?)                  | 1          | 1527667623 | 1527667623 | 89033    | 89033    | 89033    |
| 1         | information_schema | proxysql | 0xE662AE2DEE853B44 | create database test-proxysql                        | 1          | 1527667316 | 1527667316 | 8470     | 8470     | 8470     |
| 1         | information_schema | proxysql | 0x02033E45904D3DF0 | show databases                                       | 1          | 1527667222 | 1527667222 | 19414    | 19414    | 19414    |
| 1         | information_schema | proxysql | 0xB9EF28C84E4207EC | create database test_proxysql                        | 1          | 1527667332 | 1527667332 | 15814    | 15814    | 15814    |
| 2         | information_schema | proxysql | 0x620B328FE9D6D71A | SELECT DATABASE()                                    | 1          | 1527667342 | 1527667342 | 23386    | 23386    | 23386    |
| 1         | test_proxysql      | proxysql | 0x02033E45904D3DF0 | show databases                                       | 1          | 1527667342 | 1527667342 | 2451     | 2451     | 2451     |
| 1         | test_proxysql      | proxysql | 0x59F02DA280268525 | create table test_tables                             | 1          | 1527667360 | 1527667360 | 9187     | 9187     | 9187     |
| 1         | test_proxysql      | proxysql | 0x99531AEFF718C501 | show tables                                          | 1          | 1527667342 | 1527667342 | 1001     | 1001     | 1001     |
| 1         | test_proxysql      | proxysql | 0xC745E37AAF6095AF | create table test_tables(name varchar(?),age int(?)) | 1          | 1527667558 | 1527667558 | 68935    | 68935    | 68935    |
+-----------+--------------------+----------+--------------------+------------------------------------------------------+------------+------------+------------+----------+----------+----------+
11 rows in set (0.01 sec)#从上述结果就可以看出读写分离配置是成功的，读请求是转发到2组，写请求转发到1组&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022074105" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h5&gt;整个读写分离的架构配置到此就完成了，但是此架构存在需要优化的地方，那就是  &lt;code&gt;此架构存在单点问题&lt;/code&gt;。实际生产环境中可采用  &lt;code&gt;MHA+ProxySQL+Mysql&lt;/code&gt;这类架构解决此问题，请持续关注!&lt;/h5&gt;
 &lt;p&gt;感谢此文对你有帮助的话，请点个赞然后转发支持一下，感谢大家！&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql linux 运维 程序员</category>
      <guid isPermaLink="true">https://itindex.net/detail/60438-proxysql-mysql-%E6%95%B0%E6%8D%AE%E5%BA%93</guid>
      <pubDate>Thu, 19 Mar 2020 17:13:36 CST</pubDate>
    </item>
    <item>
      <title>[译]配置 MySQL 主主复制</title>
      <link>https://itindex.net/detail/60193-mysql-%E5%A4%8D%E5%88%B6</link>
      <description>&lt;p&gt;原文:   &lt;a href="http://dbadiaries.com/how-to-setup-mysql-master-master-replication/" rel="external" target="_blank"&gt;How to Setup MySQL Master Master Replication&lt;/a&gt; by Andy Hayes。&lt;/p&gt;
 &lt;p&gt;MySQL 主-主 复制 (  &lt;code&gt;master master replication&lt;/code&gt;), 也叫做   &lt;code&gt;mysql chained replication&lt;/code&gt;、  &lt;code&gt;multi master replication&lt;/code&gt;和  &lt;code&gt;mysql daisy chaining replication&lt;/code&gt;, 它是MySQL复制功能的扩展(普通复制功能见文章下半部分)，允许创建多个主服务器，并且主服务器可以绑定多个从服务器。&lt;/p&gt;
 &lt;p&gt;本文介绍设置主-主复制的方法。&lt;/p&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;p&gt;  &lt;img alt="" src="https://colobu.com/mysql-master-master-replication.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在多主 mysql的配置中，除了第一个主服务器外。其它添加的主服务器既当作主也当作从。因此，可以将每个添加的从服务器变为主服务器。&lt;/p&gt;
 &lt;p&gt;看上面的图，可以比较直观的看出主主复制的配置。&lt;/p&gt;
 &lt;p&gt;在本教程中，我们使用三台服务器(实例)做例子；两个主一个从，但是首先会介绍为什么我们想要设置这种链式复制模式。&lt;/p&gt;
 &lt;p&gt;本文假定mysql其它的配置已经设置好了，我们只关注配置复制功能。&lt;/p&gt;
 &lt;h2&gt;主-主复制应用的场景&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;数据库迁移&lt;/li&gt;
  &lt;li&gt;提升复制的性能&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;使用主-主复制帮助数据库迁移&lt;/h3&gt;
 &lt;p&gt;我最近遇到了一个大数据库迁移项目，我们不得不迁移几百GB的数据到线上服务器。数据库位于两组数据库上，它们会被整合到一组数据库中。每次迁移都会在不同的日子进行。迁移之后，应用会被重新上线，宕机的时间要尽量的少。&lt;/p&gt;
 &lt;p&gt;备份和恢复数据库的常用方法包括 mysqldump或Percona Xtrabackup等工具。备份和恢复大的数据库是相当耗时的，尤其是使用mysqldump，所以我决定在每种情况下都让当前的数据库和新生产环境中的数据库保持同步。&lt;/p&gt;
 &lt;h2&gt;使用多主提升同步的性能&lt;/h2&gt;
 &lt;p&gt;系统架构师、应用架构师和数据库架构师使用MySQL数据库的一个主要原因就是可以水平的扩展数据库，使用一个或者多个从扩展读操作。&lt;/p&gt;
 &lt;p&gt;当每个从服务器连接到主时，它会在主服务器上产生额外的负载。每个从服务器必须接收二进制日志的完整副本。在有许多从服务器的环境中，这会增加主服务器上的网络负载，而主服务器开始成为瓶颈。此外，主服务器还不得不处理一部分请求，包括写入请求。&lt;/p&gt;
 &lt;p&gt;添加一个额外的主服务器作为从有助于分担主服务器的负载， 并且这种配置模型还可以自由扩展，以便可以创建多个次主服务器，所有这些次服务器都作为主服务器的从，具体配置取决于具体的需求。&lt;/p&gt;
 &lt;h2&gt;配置概览&lt;/h2&gt;
 &lt;p&gt;所有修改都会被持久化到  &lt;code&gt;my.cnf&lt;/code&gt;文件中并持久化，mysql实例会被重启。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主服务器 (primary)&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;开启 binary log&lt;/li&gt;
  &lt;li&gt;创建允许 secondary master server 连接的用户&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;主服务器 (secondary)&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;开启 binary log&lt;/li&gt;
  &lt;li&gt;开启 relay log&lt;/li&gt;
  &lt;li&gt;允许 log slave updates&lt;/li&gt;
  &lt;li&gt;创建允许slave连接的用户&lt;/li&gt;
  &lt;li&gt;指向 primary master log position&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;从服务器 (作为secondary master server的从)&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;允许 elay log&lt;/li&gt;
  &lt;li&gt;指向 secondary master log position&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;详细步骤&lt;/h2&gt;
 &lt;h3&gt;配置的改变&lt;/h3&gt;
 &lt;p&gt;下面的截图显示了我在虚拟机中对  &lt;code&gt;my.cnf&lt;/code&gt;所做的改变。&lt;/p&gt;
 &lt;p&gt;主服务器的配置 (primary)  &lt;br /&gt;  &lt;img alt="" src="https://colobu.com/mysql-master-master-replication-primary-master-config.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;次主服务器的配置(secondary)  &lt;br /&gt;  &lt;img alt="" src="https://colobu.com/mysql-master-master-replication-secondary-master-config.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;从服务器 (作为次主服务器的从)  &lt;br /&gt;  &lt;img alt="" src="https://colobu.com/mysql-master-master-replication-slave-config.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;与标准复制一样，  &lt;code&gt;binary log&lt;/code&gt;和  &lt;code&gt;relay logs&lt;/code&gt;通过  &lt;code&gt;log-bin&lt;/code&gt;和  &lt;code&gt;relay-log&lt;/code&gt;参数进行设置。配置中需要额外设置的是  &lt;code&gt;log-slave-updates&lt;/code&gt;,这个参数告诉次主服务器记录主服务器的改变到它的  &lt;code&gt;binary log&lt;/code&gt;中，这样从服务器才能读取到这些改变。&lt;/p&gt;
 &lt;h3&gt;在主服务器上创建一个复制账号， 允许它的从使用这个账号可以连接&lt;/h3&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;GRANT REPLICATION SLAVE ON *.* TO &amp;apos;user&amp;apos;@&amp;apos;host&amp;apos; IDENTIFIED BY &amp;apos;SecurePassword&amp;apos;;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h3&gt;找到主服务器的日志文件和位置，应用到次主服务器上&lt;/h3&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;SHOW MASTER STATUS\G;&lt;/div&gt;     &lt;div&gt;*************************** 1. row ***************************&lt;/div&gt;     &lt;div&gt;File: mysql-bin.000001&lt;/div&gt;     &lt;div&gt;Position: 897&lt;/div&gt;     &lt;div&gt;Binlog_Do_DB:&lt;/div&gt;     &lt;div&gt;Binlog_Ignore_DB:&lt;/div&gt;     &lt;div&gt;Executed_Gtid_Set:&lt;/div&gt;     &lt;div&gt;On the secondary master execute this changing to your host, user, log file etc accordingly:&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;



 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;CHANGE MASTER TO&lt;/div&gt;     &lt;div&gt;MASTER_HOST=&amp;apos;primarymaster.home&amp;apos;,&lt;/div&gt;     &lt;div&gt;MASTER_USER=&amp;apos;replication&amp;apos;,&lt;/div&gt;     &lt;div&gt;MASTER_PASSWORD=&amp;apos;SecurePassword&amp;apos;,&lt;/div&gt;     &lt;div&gt;MASTER_LOG_FILE=&amp;apos;mysql-bin.000001&amp;apos;,&lt;/div&gt;     &lt;div&gt;MASTER_LOG_POS=897;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;完成之后执行下面的命令启动从复制：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;START SLAVE;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;检查次主服务器的同步状态：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;SHOW SLAVE STATUS\G;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;如果复制正常，你会看到下面的几行：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;Slave_IO_Running: Yes&lt;/div&gt;     &lt;div&gt;Slave_SQL_Running: Yes&lt;/div&gt;     &lt;div&gt;Last_Error:&lt;/div&gt;     &lt;div&gt;Seconds_Behind_Master: 0&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h3&gt;在次主服务器和从服务器上执行步骤 2 和 3&lt;/h3&gt;
 &lt;p&gt;步骤相同，依次增加账号、主地址、日志文件和位置。&lt;/p&gt;
 &lt;h3&gt;测试&lt;/h3&gt;
 &lt;p&gt;在主服务器做一个更新操作，比如创建一个数据库，检查这个更新操作是否应用到次主服务器和从上。&lt;/p&gt;
 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;mysql 主-主复制可以很好的提升复制的性能，或者在数据库迁移的时候执行数据库的同步。&lt;/p&gt;
 &lt;p&gt;更多的信息你可以看官方文档：  &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/replication-solutions-performance.html" rel="external" target="_blank"&gt;16.3.6 Improving Replication Performance&lt;/a&gt;。&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;作者还了一系列关于mysql的文章，其中还介绍了如何配置主从模式，一并翻译，整合在这一篇文章中。&lt;/p&gt;
 &lt;h1&gt;MySQL 主从配置&lt;/h1&gt;

 &lt;p&gt;通常，可以配置MySQL复制实现规模扩展、方便提供报告或者提供MySQL数据库的备份。我以前写过一篇关于这个的  &lt;a href="http://dbadiaries.com/mysql-replication-use-cases/" rel="external" target="_blank"&gt;文章&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;整个过程依赖于二进制日志(  &lt;code&gt;binary log&lt;/code&gt;)，这些日志被输出到主服务器上的某个文件夹，从服务器读取这些日志并应用保持和主服务器的同步。&lt;/p&gt;
 &lt;p&gt;本文是一篇MySQL复制教程。我将设置MySQL复制功能，使用了两个MySQL服务器，一个主服务器和一个从服务器。本文假设服务器是新的，并且还没有在它们上创建数据库。&lt;/p&gt;
 &lt;p&gt;我不会列出所有可能的设置或配置。只是必要的复制设置。&lt;/p&gt;
 &lt;p&gt;需要对主服务器和从服务器的mysql配置文件进行更改。确保mysql配置文件中没有会导致任何网络问题的设置。关闭  &lt;code&gt;–skip-networking&lt;/code&gt;，并且确保  &lt;code&gt;–bind-address&lt;/code&gt;设置正确。&lt;/p&gt;
 &lt;h2&gt;主服务器复制功能的基本配置&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;code&gt;server-id&lt;/code&gt;：在配置中的每一个服务需要一个唯一的id, 起始   &lt;code&gt;1&lt;/code&gt;, 最大   &lt;code&gt;2^32&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;log-bin&lt;/code&gt;：二进制文件的前缀， 一般设置为“mysql-bin”, “bin-log” 等等。文件会写入到你指定的文件夹中，当然运行mysql的用户需要写入权限。&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;expire-logs-days&lt;/code&gt;：二进制文件保留的最大天数。谨慎设置这个值，确保从服务器如果同步慢的话不会因为文件永久删除导致丢失数据。&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;binlog-format&lt;/code&gt;：仔细阅读关于这个参数的官方文档，非常非常的重要。如果设置不正确则意味着你的从可能包含和主不一样的数据，可选的值为   &lt;code&gt;STATEMENT&lt;/code&gt;,    &lt;code&gt;ROW&lt;/code&gt; 和    &lt;code&gt;MIXED&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://colobu.com/setup-mysql-replication-master-config-file.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;配置完之后重启你的mysql服务，检查这些变量，看看文件夹下二进制文件是否存在。&lt;/p&gt;
 &lt;h2&gt;从服务器复制功能的基本配置&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;code&gt;server-id&lt;/code&gt;：在配置中的每一个服务需要一个唯一的id, 起始   &lt;code&gt;1&lt;/code&gt;, 最大   &lt;code&gt;2^32&lt;/code&gt;。&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;relay-log&lt;/code&gt;：– 从会读取它自己的relay log, 并且应用复制过来的命令到它自己的数据库中。舍不设置它并不重要因为服务器自己会创建relay log，但是我总是显式设置它，这样我就能确保这些日志文件输出到哪里。当然需要确保运行mysql的账号要有写入这个文件夹的权限。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://colobu.com/setup-mysql-master-slave-replication-slave-config-file.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;配置完之后重启你的mysql服务，检查这些变量是否已持久化。&lt;/p&gt;
 &lt;p&gt;对于其它一些有用的可选配置，检查官方的  &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/replication.html" rel="external" target="_blank"&gt;文档&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;在主服务器上创建复制数据的账号&lt;/h2&gt;
 &lt;p&gt;从需要认证才能连接到主上，你需要在主上创建一个账号，以便从使用这个账号连接主。&lt;/p&gt;
 &lt;p&gt;虽然你可以使用一个已存在的账号，但是实践上不建议你这么做，原因有二：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果必须停用这个账号：使用这个账号的某人离开了公司或者某些应用的登录不再需要，这会导致复制中断。&lt;/li&gt;
  &lt;li&gt;权限：复制需要特定的操作权限，你应该创建你的mysql账号，仅仅授权给它这些特定功能的权限。&lt;/li&gt;
&lt;/ul&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;GRANT REPLICATION SLAVE ON *.* TO &amp;apos;replication&amp;apos;@&amp;apos;yourhostname.com&amp;apos; IDENTIFIED BY &amp;apos;EnterSecurePasswordHere&amp;apos;;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;记录主机上二进制日志文件和位置&lt;/h2&gt;
 &lt;p&gt;二进制日志文件的目的就是用来记录主上的改变，这些改变需要同样应用到从上。您需要找到主二进制日志的文件和位置(偏移)并将其记录下来，然后将其应用到从服务器。当从复制启动时，它需要知道在哪个日志文件中的哪个点开始读取主服务器数据库的副本并对其应用更新。&lt;/p&gt;
 &lt;p&gt;您应该阻止对主服务的任何更新，然后记录日志位置。使用一行命令很容易做到这一点：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;FLUSH TABLES WITH READ LOCK;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;保持这个连接窗口，新建一个到主的连接窗口，运行下面的命令得到位置：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;SHOW MASTER STATUS;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;你会看到当前主正在写的文件以及当前log的位置，这些信息会用来配置从：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;mysql&amp;gt; show master status;&lt;/div&gt;     &lt;div&gt;+------------------+----------+--------------+------------------+&lt;/div&gt;     &lt;div&gt;| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |&lt;/div&gt;     &lt;div&gt;+------------------+----------+--------------+------------------+&lt;/div&gt;     &lt;div&gt;| mysql-bin.000002 |      154 |              |                  |&lt;/div&gt;     &lt;div&gt;+------------------+----------+--------------+------------------+&lt;/div&gt;     &lt;div&gt;1 row in set (0.00 sec)&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;使用主的信息配置从&lt;/h2&gt;
 &lt;p&gt;连接到从，使用  &lt;code&gt;CHANGE_MASTER_TO&lt;/code&gt;配置从：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;CHANGE MASTER TO&lt;/div&gt;     &lt;div&gt;MASTER_HOST=&amp;apos;master.yourdomain.com&amp;apos;,&lt;/div&gt;     &lt;div&gt;MASTER_USER=&amp;apos;replication&amp;apos;,&lt;/div&gt;     &lt;div&gt;MASTER_PASSWORD=&amp;apos;EnterSecurePasswordHere&amp;apos;,&lt;/div&gt;     &lt;div&gt;MASTER_PORT=3306,&lt;/div&gt;     &lt;div&gt;MASTER_LOG_FILE=&amp;apos;mysql-bin.000002&amp;apos;,&lt;/div&gt;     &lt;div&gt;MASTER_LOG_POS=154,&lt;/div&gt;     &lt;div&gt;MASTER_CONNECT_RETRY=10;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;  &lt;img alt="" src="https://colobu.com/mysql-replication-change-master-to.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在从上，运行下面的命令：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;START SLAVE;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;解锁主并检查主从状态&lt;/h2&gt;
 &lt;p&gt;在主上运行：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;UNLOCK TABLES;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;在从上运行：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;SHOW SLAVE STATUS;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;  &lt;img alt="" src="https://colobu.com/setup-mysql-replication-show-slave-status.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;检查看看是否有错误，确保你上面的设置设置正确。检查  &lt;code&gt;Master_Log_File&lt;/code&gt; 和   &lt;code&gt;Read_Master_Log_Pos&lt;/code&gt;&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;Master_Log_File: mysql-bin.000002&lt;/div&gt;     &lt;div&gt;Read_Master_Log_Pos: 154&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;测试&lt;/h2&gt;
 &lt;p&gt;在主上创建一个空的数据库，确保从上也会创建这个空的数据库。&lt;/p&gt;
 &lt;p&gt;如果同步正常你可以准备在主上加载数据。&lt;/p&gt;
 &lt;p&gt;这是一个基本的mysql主从配置的教程。推荐你阅读官方文档了解mysql的  &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/replication.html" rel="external" target="_blank"&gt;主从复制的配置&lt;/a&gt;。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql 数据库</category>
      <guid isPermaLink="true">https://itindex.net/detail/60193-mysql-%E5%A4%8D%E5%88%B6</guid>
      <pubDate>Mon, 02 Dec 2019 14:30:00 CST</pubDate>
    </item>
    <item>
      <title>MySQL 同步复制及高可用方案总结</title>
      <link>https://itindex.net/detail/60091-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</link>
      <description>&lt;h3&gt;1.前言&lt;/h3&gt;
 &lt;p&gt;mysql作为应用程序的数据存储服务，要实现mysql数据库的高可用。必然要使用的技术就是数据库的复制，如果主节点出现故障可以手动的切换应用到从节点，这点相信运维同学都是知道，并且可以实现的。但是这种情况只是手动的切换，对可用性有要求的业务需要分别实现主库和从库的高可用，保障在数据库出现down机的情况下，可以自动实现数据库的故障转移，保障应用的可用性和用户体验。&lt;/p&gt;
 &lt;p&gt;本文将会对一些常用的数据库高可用方案进行介绍，根据你不同的场景，选择合适的高可用方案即可。&lt;/p&gt;
 &lt;h3&gt;2.MMM高可用方案&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;2.1.Mysql-MMM介绍&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MMM(Master-Master replication managerfor Mysql，Mysql主主复制管理器)是一套灵活的脚本程序，基于perl实现，用来对mysql replication进行监控和故障迁移，并能管理mysql Master-Master复制的配置(同一时间只有一个节点是可写的)。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.2.组件&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;mmm_mond：监控进程，负责所有的监控工作，决定和处理所有节点角色活动。此脚本需要在监管机上运行。&lt;/p&gt;
 &lt;p&gt;mmm_agentd：运行在每个mysql服务器上的代理进程，完成监控的探针工作和执行简单的远端服务设置。此脚本需要在被监管机上运行。&lt;/p&gt;
 &lt;p&gt;mmm_control：一个简单的脚本，提供管理mmm_mond进程的命令。&lt;/p&gt;
 &lt;p&gt;mysql-mmm的监管端会提供多个虚拟IP（VIP），包括一个可写VIP，多个可读VIP，通过监管的管理，这些IP会绑定在可用mysql之上，当某一台mysql宕机时，监管会将VIP迁移至其他mysql。&lt;/p&gt;
 &lt;p&gt;在整个监管过程中，需要在mysql中添加相关授权用户，以便让mysql可以支持监理机的维护。授权的用户包括一个mmm_monitor用户和一个mmm_agent用户，如果想使用mmm的备份工具则还要添加一个mmm_tools用户。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.3.架构图&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;正常工作时：  &lt;br /&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000020693591?w=605&amp;h=461" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主节点故障时：  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154036.png" src="https://segmentfault.com/img/bVbyZvD" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154036.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.4.MMM优点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）高可用性，扩展性好，出现故障自动转移，对于主主同步，在同一时间只提供一台数据库写操作，保证数据的一致性。  &lt;br /&gt;（2）配置简单，容易操作。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.5.MMM缺点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）需要一台备份服务器，浪费资源  &lt;br /&gt;（2）需要多个虚拟IP  &lt;br /&gt;（3）agent可能意外终止，引起裂脑。&lt;/p&gt;
 &lt;h3&gt;3.MHA介绍&lt;/h3&gt;
 &lt;p&gt;MHA（Master High Availability）目前在MySQL高可用方面是一个相对成熟的解决方案，它由日本DeNA公司youshimaton（现就职于Facebook公司）开发，是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中，MHA能做到在0~30秒之内自动完成数据库的故障切换操作，并且在进行故障切换的过程中，MHA能在最大程度上保证数据的一致性，以达到真正意义上的高可用。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.1.MHA架构介绍&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;该软件由两部分组成：MHA Manager（管理节点）和MHA Node（数据节点）。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群，也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上，MHA Manager会定时探测集群中的master节点，当master出现故障时，它可以自动将最新数据的slave提升为新的master，然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。&lt;/p&gt;
 &lt;p&gt;在MHA自动故障切换过程中，MHA试图从宕机的主服务器上保存二进制日志，最大程度的保证数据的不丢失(配合mysql半同步复制效果更佳)，但这并不总是可行的。例如，如果主服务器硬件故障或无法通过ssh访问，MHA没法保存二进制日志，只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制，可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志，MHA可以将最新的二进制日志应用于其他所有的slave服务器上，因此可以保证所有节点的数据一致性。&lt;/p&gt;
 &lt;p&gt;注意：目前MHA主要支持一主多从的架构，要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器，一主二从，即一台充当master，一台充当备用master，另外一台充当从库，因为至少需要三台服务器，出于机器成本的考虑，淘宝也在该基础上进行了改造，目前淘宝TMHA已经支持一主一从。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.2.MHA架构图&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;正常工作时架构图：  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154220.png" src="https://segmentfault.com/img/bVbyZyh" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154220.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主库down机时架构：  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154249.png" src="https://segmentfault.com/img/bVbyZym" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154249.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.3.故障转移过程&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）从宕机崩溃的master保存二进制日志事件（binlog events）;  &lt;br /&gt;（2）识别含有最新更新的slave；  &lt;br /&gt;（3）应用差异的中继日志（relay log）到其他的slave；  &lt;br /&gt;（4）应用从master保存的二进制日志事件（binlog events）；  &lt;br /&gt;（5）提升一个slave为新的master；  &lt;br /&gt;（6）使其他的slave连接新的master进行复制；  &lt;br /&gt;（7）在新的master启动vip地址，保证前端请求可以发送到新的master。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.4.MHA优点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）不需要备份服务器  &lt;br /&gt;（2）不改变现有环境  &lt;br /&gt;（3）操作非常简单  &lt;br /&gt;（4）可以进行日志的差异修复  &lt;br /&gt;（5）可以将任意slave提升为master&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.5.MHA缺点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）需要全部节点做ssh秘钥  &lt;br /&gt;（2）MHA出现故障后配置文件会被修改，如果再次故障转移需要重新修改配置文件。  &lt;br /&gt;（3）自带的脚本还需要进一步补充完善，且用perl开发，二次开发困难。&lt;/p&gt;
 &lt;h3&gt;4.DRBD+（heartbeat,corosync）&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;4.1.方案简介&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;本方案采用Heartbeat或者corosync双机热备软件来保证数据库的高稳定性和连续性，数据的一致性由DRBD这个工具来保证（如果可以尽量放到分布式存储上面）。默认情况下只有一台mysql在工作，当主mysql服务器出现问题后，系统将自动切换到备机上继续提供服务，当主数据库修复完毕，又将服务切回继续由主mysql提供服务。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.2.组件&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Heartbeat,corosync作为心跳检测机制，监控primary节点的状态。当主节点宕掉之后，迅速提升secondary节点为新的主节点，并切换IP；  &lt;br /&gt;drbd负责数据同步&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.3.架构图&lt;/strong&gt;  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154253.jpg" src="https://segmentfault.com/img/bVbyZyr" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154253.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.4.数据同步过程&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;mysql进行刷盘时，会通过不同的sync方式，最终将数据写入disk；  &lt;br /&gt;drbd收到刷盘成功的信息后，将对应的磁盘块位置，和变更动作，通过网络传递至secondary节点；&lt;/p&gt;
 &lt;p&gt;secondary的drbd接收到变更信息后，将这些信息落盘；&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.5.切换过程&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;前提：secondary节点的mysql服务不启动；&lt;/p&gt;
 &lt;p&gt;heartbeat检测到primary的mysql服务停止，则摘掉IP、umount掉数据盘、将primary切换为secondary；&lt;/p&gt;
 &lt;p&gt;在原来的secondary上，提升drbd同步为primary，挂载数据盘，启动mysql服务、绑定IP；&lt;/p&gt;
 &lt;p&gt;从库跟着IP和端口自动进行迁移；&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.6.方案优点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）历史悠久、安全性高、稳定性高、可用性高、出现故障自动切换。  &lt;br /&gt;（2）数据一致性强&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.7.方案缺点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）需要一台备份服务器，浪费资源  &lt;br /&gt;（2）不方便扩展  &lt;br /&gt;（3）无论drbd还是headbetart，corosync都可能发生裂脑&lt;/p&gt;
 &lt;h3&gt;5.Mysql route介绍&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;5.1.什么是mysql route&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL Router是处于应用client和dbserver之间的轻量级代理程序，它能检测，分析和转发查询到后端数据库实例，并把结果返回给client。是mysql-proxy的一个替代品。其架构图和功能如下。  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154257.png" src="https://segmentfault.com/img/bVbyZyx" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154257.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;（1）Router实现读写分离，程序不是直接连接数据库IP，而是固定连接到mysql router。MySQL Router对前端应用是透明的。应用程序把MySQL Router当作是普通的mysql实例，把查询发给MySQL Router,而MySQL Router会把查询结果返回给前端的应用程序。&lt;/p&gt;
 &lt;p&gt;（2）从数据库服务器故障，业务可以正常运行。由MySQL Router来进行自动下线不可用服务器。程序配置不需要任何修改。&lt;/p&gt;
 &lt;p&gt;（3）主数据库故障，由MySQL Router来决定主从自动切换，业务可以正常访问。程序配置不需要做任何修改。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.2.读写分离原理&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL Router接受前端应用程序请求后，根据不同的端口来区分读写，把连接读写端口的所有查询发往主库，把连接只读端口的select查询以轮询方式发往多个从库，从而实现读写分离的目的。读写返回的结果会交给MySQL Router,由MySQL Router返回给客户端的应用程序。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.3.Mysql router用途&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL Router的主要用途是读写分离，主主故障自动切换，负载均衡，连接池等。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.4.Mysql router主主故障自动切换的坑&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Mysql router主主故障切换功能经过测试没有问题，但是有一个比较大的坑需要注意，主库发生切换之后，从库的连接的master服务器地址不会发生改变，需要自己写脚本进行判断。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.5.优点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）基于DAL层实现mysql的高可用。  &lt;br /&gt;（2）可以同时实现主主故障切换和读写分离。  &lt;br /&gt;（3）插件式架构允许用户进行额外的功能扩展。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.6.缺点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）高可用功能需要进一步完善：存在主库切换之后，从库不会自动切换主库地址的坑。  &lt;br /&gt;（2）读写情况使用不同端口，需要修改应用程序。&lt;/p&gt;
 &lt;h3&gt;6.mysql Cluster&lt;/h3&gt;
 &lt;p&gt;国内用的非常少，主要因为一下三点：  &lt;br /&gt;（1）需要更改存储引擎  &lt;br /&gt;（2）付费  &lt;br /&gt;（3）国内几乎没有使用案例&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;优点：&lt;/strong&gt;  &lt;br /&gt;高可用，可用率达99.999%&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;6.1.结束语&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;上面的高可用方案，只是我自己比较熟悉的，而且也是应用比较多的。mysql毕竟发展了有20多年了，各种高可用方案还是很多的，其他的高可用方案各位钥匙有兴趣，可以自己研究。&lt;/p&gt;
 &lt;blockquote&gt;版权申明：作者：西门飞冰，一名90后it男，一直在北京工作，热爱运动，热爱冒险，热爱旅行。由作者原创投稿，版权归原创者所有。除非无法确认，我们都会标明作者及出处，如有侵权烦请告知，我们会立即删除并表示歉意，谢谢。&lt;/blockquote&gt;
 &lt;p&gt;关注 民工哥技术之路 微信公众号对话框回复关键字：1024 可以获取一份最新整理的技术干货：包括系统运维、数据库、redis、MogoDB、电子书、Java基础课程、Java实战项目、架构师综合教程、架构师实战项目、大数据、Docker容器、ELK Stack、机器学习、BAT面试精讲视频等。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql linux centos</category>
      <guid isPermaLink="true">https://itindex.net/detail/60091-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</guid>
      <pubDate>Tue, 15 Oct 2019 15:41:19 CST</pubDate>
    </item>
    <item>
      <title>MySQL中的半同步复制</title>
      <link>https://itindex.net/detail/58842-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</link>
      <description>&lt;p&gt;MySQL当前存在的三种复制模式有：异步模式、半同步模式和组复制模式。注意：MySQL复制模式没有“同步复制”这一项的，文章中只是为了读者方便理解半同步复制的概念才介绍了同步复制概念  &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/replication-semisync.html" rel="noopener" target="_blank"&gt;https://dev.mysql.com/doc/refman/8.0/en/replication-semisync.html&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;从MySQL5.5开始，MySQL以插件的形式支持半同步复制。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1. 异步复制（Asynchronous replication）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL默认的复制即是异步的，主库在执行完客户端提交的事务后会立即将结果返给给客户端，并不关心从库是否已经接收并处理，这样就会有一个问题，主如果crash掉了，此时主上已经提交的事务可能并没有传到从上，如果此时，强行将从提升为主，可能导致新主上的数据不完整。&lt;/p&gt;
 &lt;p&gt;异步复制是MySQL最早的也是当前使用最多的复制模式，异步复制提供了一种简单的主-从复制方法，包含一个主库（master）和备库（一个，或者多个） 之间，主库执行并提交了事务，在这之后（因此才称之为异步），这些事务才在从库上重新执行一遍（基于statement）或者变更数据内容（基于 row），主库不检测其从库上的同步情况。在服务器负载高、服务压力大的情况下主从产生延迟一直是其诟病。工作流程简图如下：&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://blog.haohtml.com/wp-content/uploads/2018/10/mysql_slave.jpeg"&gt;   &lt;img alt="" height="218" src="https://blog.haohtml.com/wp-content/uploads/2018/10/mysql_slave.jpeg" width="607"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;而同步复制（Fully synchronous replication，MySQL中没有此复制概念）&lt;/strong&gt;指当主库执行完一个事务，  &lt;strong&gt;所有的从库&lt;/strong&gt;都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回，所以全同步复制的性能必然会收到严重的影响。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2. 半同步复制（Semisynchronous replication）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;介于异步复制和全同步复制之间，主库在执行完客户端提交的事务后不是立刻返回给客户端，而是等待  &lt;strong&gt;至少一个&lt;/strong&gt;从库接收到并写到relay log中才返回给客户端。相对于异步复制，半同步复制提高了数据的安全性，同时它也造成了一定程度的延迟，这个延迟最少是一个TCP/IP往返的时间。所以，半同步复制最好在低延时的网络中使用。&lt;/p&gt;
 &lt;p&gt;MySQL5.5 的版本在异步同步的基础之上，以  &lt;strong&gt;插件&lt;/strong&gt;的形式实现了一个变种的同步方案，称之为半同步复制（semi-sync replication）。这个插件在原生的异步复制上，添加了一个同步的过程：当从库接收到了主库的变更（即事务）时，会通知主库。主库上的操作有两种：  &lt;strong&gt;接收到这个通知以后才去commit事务&lt;/strong&gt;；  &lt;strong&gt;接受到之后释放session&lt;/strong&gt;。这两种方式是由主库上的具体配置决定的。当主库收不到从库的变更通知超时时，由半同步复制自动切换到异步同步，这样就极大了保证了数据的一致性（至少一个从库），但是在性能上有所下降，特别是在网络不稳定的情况下，半同步和同步之间来回切换，对正常的业务是有影响的。其工作流程简图如下：&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://blog.haohtml.com/wp-content/uploads/2018/10/mysql_salve_2.jpeg"&gt;   &lt;img alt="" height="216" src="https://blog.haohtml.com/wp-content/uploads/2018/10/mysql_salve_2.jpeg" width="599"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3、Group Replication（组复制）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;不论是异步复制还是半同步复制，都是一个主下面一个从或是多个从的模式，在高并发下高负载下，都存在延迟情况，此时如果主节点出现异常，那么就会出现数据不 一致的情况，数据可能会丢，在金融级数据库中是不能容忍的。在这种情况下，急需出现一种模式来解决这些问题。在MySQL5.7.17的版本中，带着这些期待，新的复制模式“  &lt;strong&gt;组复制&lt;/strong&gt;“由此产生并GA了。&lt;/p&gt;
 &lt;p&gt;组复制的工作流程图如下：  &lt;a href="https://blog.haohtml.com/wp-content/uploads/2018/10/mysql_mrg.jpeg"&gt;   &lt;img alt="" height="262" src="https://blog.haohtml.com/wp-content/uploads/2018/10/mysql_mrg.jpeg" width="609"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;组复制的工作原理&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL组复制是一个MySQL插件，它建立在现有的MySQL复制基础结构上，利用了二进制日志，基于行的日志记录和全局事务标识符等功能。它集成了当前的MySQL框架，如性能模式、插件和服务基础设施等。&lt;/p&gt;
 &lt;p&gt;组复制（Group Replication）基于分布式一致性算法(Paxos协议的变体)实现，一个组允许部分节点挂掉，只要保证绝大多数节点仍然存活并且之间的通讯是没有问题的，那么这个组对外仍然能够提供服务，它是一种被使用在容错系统中的技术。Group Replication（复制组）是由能够相互通信的多个服务器（节点）组成的。在通信层，Group replication实现了一系列的机制：比如原子消息（atomic message delivery）和全序化消息（totalordering of messages）。这些原子化、抽象化的机制，为实现更先进的数据库复制方案提供了强有力的支持。&lt;/p&gt;
 &lt;p&gt;MySQL Group Replication正是基于这些技术和概念，实现了一种多主全更新的复制协议。简而言之，一个Group Replication就是一组节点，每个节点都可以独立执行事务，而读写事务则会在于group内的其他节点进行协调之后再commit。因此，当一个事务准备提交时，会自动在group内进行原子性的广播，告知其他节点变更了什么内容/执行了什么事务。这种原子广播的方式，使得这个事务在每一个节点上都保持着同样顺序。这意味着每一个节点都以同样的顺序，接收到了同样的事务日志，所以每一个节点以同样的顺序重演了这些事务日志，最终整个group保持了完全一致的状态。然而，不同的节点上执行的事务之间有可能存在资源争用。这种现象容易出现在两个不同的并发事务上。假设在不同的节点上有两个并发事务，更新了同一行数据，那么就会发生资源争用。面对这种情况，Group Replication判定先提交的事务为有效事务，会在整个group里面重放，后提交的事务会直接中断，或者回滚，最后丢弃掉。因此，这也是一个无共享的复制方案，每一个节点都保存了完整的数据副本。&lt;/p&gt;
 &lt;p&gt;从其工作的原理可以看出，Group Replication基于Paxos协议的一致性算法校验事务执行是否有冲突，然后顺序执行事务，达到最终的数据一致性，也就意味着部分节点可以存在延迟。可以设置多主同时写入和单主写入，通过设置 group_replication_single_primary_mode 来进行控制是多主还是单主，官方推荐  &lt;strong&gt;单主写入&lt;/strong&gt;，允许延迟，但延迟过大，则会触发限流规则（可配置的），整个集群会变的很慢，性能大打折扣。&lt;/p&gt;
 &lt;p&gt;更多组复制:   &lt;a href="http://www.sohu.com/a/124913450_354963" rel="noopener" target="_blank"&gt;http://www.sohu.com/a/124913450_354963&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;半同步复制的潜在问题&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;客户端事务在存储引擎层提交后，在得到从库确认的过程中，主库宕机了，此时，可能的情况有两种：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1. 事务还没发送到从库上&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;此时，客户端会收到事务提交失败的信息，客户端会重新提交该事务到新的主上，当宕机的主库重新启动后，以从库的身份重新加入到该主从结构中，会发现，该事务在从库中被提交了两次，一次是之前作为主的时候，一次是被新主同步过来的。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2. 事务已经发送到从库上&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;此时，从库已经收到并应用了该事务，但是客户端仍然会收到事务提交失败的信息，重新提交该事务到新的主上。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;无数据丢失的半同步复制&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;针对上述潜在问题，MySQL 5.7引入了一种新的半同步方案：Loss-Less半同步复制。&lt;/p&gt;
 &lt;p&gt;针对上面这个图，“Waiting Slave dump”被调整到“Storage Commit”之前。&lt;/p&gt;
 &lt;p&gt;当然，之前的半同步方案同样支持，MySQL 5.7.2引入了一个新的参数进行控制-rpl_semi_sync_master_wait_point&lt;/p&gt;
 &lt;p&gt;rpl_semi_sync_master_wait_point有两种取值&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;AFTER_SYNC&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;这个即新的半同步方案，Waiting Slave dump在Storage Commit之前。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;AFTER_COMMIT&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;老的半同步方案，如图所示。&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;三种复制方案的区别&lt;/strong&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;p&gt;   &lt;strong&gt;异步复制&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;MySQL复制默认是异步复制，Master将事件写入binlog，提交事务，自身并不知道slave是否接收是否处理；&lt;/p&gt;
  &lt;p&gt;缺点：不能保证所有事务都被所有slave接收。&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt;同步复制（MySQL中不存在此模式）&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;Master提交事务，直到事务在所有slave都已提交，才会返回客户端事务执行完毕信息；&lt;/p&gt;
  &lt;p&gt;缺点：完成一个事务可能造成延迟。&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt;半同步复制&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;当Master上开启半同步复制功能时，至少有一个slave开启其功能。当Master向slave提交事务，且事务已写入relay-log中并刷新到磁盘上，slave才会告知Master已收到；若Master提交事务受到阻塞，出现等待超时，在一定时间内Master 没被告知已收到，此时Master自动转换为异步复制机制；&lt;/p&gt;
  &lt;p&gt;注：半同步复制功能要在Master和slave上开启才会起作用，只开启一边，依然是异步复制。&lt;/p&gt;
&lt;/div&gt;
 &lt;div&gt;&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql MGR</category>
      <guid isPermaLink="true">https://itindex.net/detail/58842-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</guid>
      <pubDate>Sat, 06 Oct 2018 19:44:37 CST</pubDate>
    </item>
    <item>
      <title>在数据库中存储一棵树，实现无限级分类</title>
      <link>https://itindex.net/detail/58231-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%97%A0%E9%99%90-%E5%88%86%E7%B1%BB</link>
      <description>&lt;p&gt;  &lt;em&gt;原文发表于我的博客：    &lt;a href="https://blog.kaciras.net/article/36"&gt;https://blog.kaciras.net/article/36&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
 &lt;p&gt;在一些系统中，对内容进行分类是必需的功能。比如电商就需要对商品做分类处理，以便于客户搜索；论坛也会分为很多板块；门户网站、也得对网站的内容做各种分类。&lt;/p&gt;
 &lt;p&gt;分类对于一个内容展示系统来说是不可缺少的，本博客也需要这么一个功能。众所周知，分类往往具有从属关系，比如铅笔盒钢笔属于笔，笔又是文具的一种，当然钢笔还可以按品牌来细分，每个品牌下面还有各种系列...&lt;/p&gt;
 &lt;p&gt;这个例子中从属关系具有5层，从上到下依次是：文具-笔-钢笔-XX牌-A系列，但实际中分类的层数却是无法估计的，比如生物中的界门纲目科属种有7级。显然对分类的级数做限制是不合理的，一个良好的分类系统，其应当能实现无限级分类。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#26412;&amp;#21338;&amp;#23458;&amp;#30340;&amp;#20998;&amp;#31867;&amp;#26631;&amp;#31614;" src="https://segmentfault.com/img/bV753X?w=514&amp;h=121" title="&amp;#26412;&amp;#21338;&amp;#23458;&amp;#30340;&amp;#20998;&amp;#31867;&amp;#26631;&amp;#31614;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在写自己的博客网站时，刚好需要这么一个功能，听起来很简单，但是在实现时却发现，用关系数据库存储无限级分类并非易事。&lt;/p&gt;
 &lt;h1&gt;1.需求分析&lt;/h1&gt;
 &lt;p&gt;首先分析一下分类之间的关系是怎样的，很明显，一个分类下面有好多个下级分类，比如笔下面有铅笔和钢笔；那么反过来，一个下级分类能够属于几个上级分类呢？这其实并不确定，取决于如何对类型的划分。比如有办公用品和家具，那么办公桌可以同时属于这两者，不过这会带来一些问题，比如：我要显示从顶级分类到它之间的所有分类，那么这种情况下就很难决定办公用品和家具显示哪一个，并且如果是多对一，实现上将更加复杂，所以这里还是限定每个分类仅有一个上级分类。&lt;/p&gt;
 &lt;p&gt;现在，  &lt;strong&gt;分类的关系可以表述为一父多子的继承关系，正好对应数据结构中的树，那么问题就转化成了如何在数据库中存储一棵树，并且对分类所需要的操作有较好的支持。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;对于本博客来说，分类至少需要以下操作：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;对单个分类的增删改查等基本操作&lt;/li&gt;
  &lt;li&gt;查询一个分类的直属下级和所有下级，在现实某一分类下所有文章时需要使用&lt;/li&gt;
  &lt;li&gt;查询出由顶级分类到文章所在分类之间的所有分类，并且是有序的，用于显示在   &lt;a href="https://blog.kaciras.net/index"&gt;博客首页&lt;/a&gt;文章的简介的左下角&lt;/li&gt;
  &lt;li&gt;查询分类是哪一级的，比如顶级分类是1，顶级分类的直属下级是2，再往下依次递增&lt;/li&gt;
  &lt;li&gt;移动一个分类，换句话说就是把一个子树（或者仅单个节点）移动到另外的节点下面，这个在分类结构不合理，需要修改时使用&lt;/li&gt;
  &lt;li&gt;查询某一级的所有分类&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;在性能的衡量上，这些操作并不是平等的。查询操作使用的更加频繁，毕竟分类不会没事就改着玩，  &lt;strong&gt;性能考虑要以查询操作优先，特别是2和3分别用于搜索文章和在文章简介中显示其分类，所以是重中之重。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;另外，每个分类除了继承关系外，还有名字，简介等属性，也需要存储在数据库中。每个分类都有一个id，由数据库自动生成（自增主键）。&lt;/p&gt;
 &lt;p&gt;无限级多分类多存在于企业应用中，比如电商、博客平台等，这些应用里一般都有缓存机制，对于分类这种不频繁修改的数据，即使在底层数据库中存在缓慢的操作，只要上层缓存能够命中，一样有很快的响应速度。但是对于抽象的需求：  &lt;strong&gt;在关系数据库中存储一棵树，并不仅仅存在于有缓存的应用中，所以设计一个高效的存储方案，仍然有其意义。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;下面就以这个卖文具的电商的场景为例，针对这6条需求，设计一个数据库存储方案（对过程没兴趣可以直接转到第4节）。&lt;/p&gt;
 &lt;h1&gt;2.一些常见设计方案的不足&lt;/h1&gt;
 &lt;h2&gt;2.1 直接记录父分类的引用&lt;/h2&gt;
 &lt;p&gt;在许多编程语言中，继承关系都是一个类仅继承于一个父类，添加这种继承关系仅需要声明一下父类即可，比如JAVA中  &lt;strong&gt;extends&lt;/strong&gt; xxx。根据这种思想，我们仅需要在每个分类上添加上直属上级的id，即可存储它们之间的继承关系。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#29238;id&amp;#23383;&amp;#27573;&amp;#23384;&amp;#20648;&amp;#32487;&amp;#25215;&amp;#20851;&amp;#31995;" src="https://segmentfault.com/img/bV754g?w=600&amp;h=320" title="&amp;#29238;id&amp;#23383;&amp;#27573;&amp;#23384;&amp;#20648;&amp;#32487;&amp;#25215;&amp;#20851;&amp;#31995;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;表中  &lt;code&gt;parent&lt;/code&gt;即为直属上级分类的id，顶级分类设为0。这种方案简单易懂，仅存在一张表，并且没有冗余字段，存储空间占用极小，在数据库层面是一种很好的设计。&lt;/p&gt;
 &lt;p&gt;那么再看看对操作的支持情况怎么样，第一条单个增改查都是一句话完事就不多说了，删除的话记得把下级分类的id全部改成被删除分类的上级分类即可，也就多一个UPDATE。&lt;/p&gt;
 &lt;p&gt;第二条可就麻烦了，比如我要查文具的下级分类，预期结果是笔、铅笔、钢笔三个，但是并没有办法通过文具一次性就查到铅笔盒钢笔，因为这两者的关系间接存储在笔这个分类里，需要先查出直属下级（笔），才能够往下查询，这意味着需要递归，性能上一下子就差了很多。&lt;/p&gt;
 &lt;p&gt;第三条同样需要递归，因为通过一个分类，数据库中只存储了其直属父类，需要通过递归到顶级分类才能获取到它们之间的所有分类信息。&lt;/p&gt;
 &lt;p&gt;综上所述，最关键的两个需求都需要使用性能最差的递归方式,这种设计肯定是不行的。但还是继续看看剩下的几条吧。&lt;/p&gt;
 &lt;p&gt;第4个需求：查询分类是哪一级的？这个还是得需要递归或循环，查出所有上级分类的数量即为分类的层级。&lt;/p&gt;
 &lt;p&gt;移动分类倒是非常简单，直接更新父id即可，这也是这种方案的唯一优势了吧...如果你的分类修改比查询还多不妨就这么做吧。&lt;/p&gt;
 &lt;p&gt;最后一个查询某一级的所有分类，对于这个设计简直是灾难，它需要先找出所有一级分类，然后循环一遍，找出所有一级分类的子类就是二级分类...如此循环直到所需的级数为之。所以这种设计里，这个功能基本是废了。&lt;/p&gt;
 &lt;p&gt;这个方式也是一开始就能想到的，在数据量不大（层级不深）的情况下，因为其简单直观的特点，不失为一个好的选择，不过对于本项目来说还不够（本项目立志成为一流博客平台！！！）。&lt;/p&gt;
 &lt;h2&gt;2.2 路径列表&lt;/h2&gt;
 &lt;p&gt;从2.1节中可以看出，__之所以速度慢，就是因为在分类中仅仅存储了直属上级的关系，而需求却要查询出非直属上级。__针对这一点，我们的表中不仅仅记录父节点id，而是将它到顶级分类之间所有分类的id都保存下来。这个字段所保存的信息就像文件树里的路径一样，所以就叫做path吧。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#36335;&amp;#24452;&amp;#21015;&amp;#34920;&amp;#35774;&amp;#35745;" src="https://segmentfault.com/img/bV754I?w=700&amp;h=500" title="&amp;#36335;&amp;#24452;&amp;#21015;&amp;#34920;&amp;#35774;&amp;#35745;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如图所示，每个分类保存了它所有上级分类的列表，用逗号隔开，从左往右依次是从顶级分类到父分类的id。&lt;/p&gt;
 &lt;p&gt;查询下级时使用  &lt;code&gt;Like&lt;/code&gt;运算符来查找，比如查出所有笔的下级:&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT id,name FROM pathlist WHERE path LIKE &amp;apos;1,%&amp;apos;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;一句话搞定，  &lt;code&gt;LIKE&lt;/code&gt;的右边是笔的  &lt;code&gt;path&lt;/code&gt;字段的值加上模糊匹配，并且左联接能够使用索引，的效率也过得去。&lt;/p&gt;
 &lt;p&gt;查询笔的直属下级也同样可以用  &lt;code&gt;LIKE&lt;/code&gt;搞定：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT id,name FROM pathlist WHERE path LIKE &amp;apos;%2&amp;apos;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;而找出所有上级分类这个需求，直接查出  &lt;code&gt;path&lt;/code&gt;字段，然后在应用层里分割一下即可获得获得所有上级，并且顺序也能保证。&lt;/p&gt;
 &lt;p&gt;查询某一级的分类也简单，因为层级越深，  &lt;code&gt;path&lt;/code&gt;就越长，使用  &lt;code&gt;LENGTH()&lt;/code&gt;函数作为条件即可筛选出合适的结果。反过来，根据其长度也能够计算出分类的级别。&lt;/p&gt;
 &lt;p&gt;移动操作需要递归，因为每一个分类的  &lt;code&gt;path&lt;/code&gt;都是它父类的  &lt;code&gt;path&lt;/code&gt;加上父类的id,将分类及其所有子分类的  &lt;code&gt;path&lt;/code&gt;设为其父类的  &lt;code&gt;path&lt;/code&gt;并在最后追加父类的id即可。&lt;/p&gt;
 &lt;p&gt;在许多系统中都使用了这种方案，其各方面都具有可以接受的性能，理解起来也比较容易。但是其有两点不足：1.就是不遵守数据库范式，将列表数据直接作为字符串来存储，这将导致一些操作需要在上层解析  &lt;code&gt;path&lt;/code&gt;字段的值；2.就是字段长度是有限的，不能真正达到无限级深度，并且大字段对索引不利。如果你不在乎什么范式，分类层级也远达不到字段长度的限制，那么这种方案是可行的。&lt;/p&gt;
 &lt;h2&gt;2.3 前序遍历树&lt;/h2&gt;
 &lt;p&gt;这是一种在数据库里存储一棵树的解决方案。它的思想不是直接存储父节点的id，而是以前序遍历中的顺序来判断分类直接的关系。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#21069;&amp;#24207;&amp;#36941;&amp;#21382;&amp;#26641;" src="https://segmentfault.com/img/bV755v?w=411&amp;h=166" title="&amp;#21069;&amp;#24207;&amp;#36941;&amp;#21382;&amp;#26641;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;假设从根节点开始以前序遍历的方式依次访问这棵树中的节点，最开始的节点（“Food”）第一个被访问，将它左边设为1，然后按照顺序到了第二个阶段“Fruit”，给它的左边写上2，每访问一个节点数字就递增，访问到叶节点后返回，在返回的过程中将访问过的节点右边写也写上数字。这样，在遍历中给每个节点的左边和右边都写上了数字。最后，我们回到了根节点“Food”在右边写上18。下面是标上了数字的树，同时把遍历的顺序用箭头标出来了。 &lt;/p&gt;
 &lt;p&gt;我们称这些数字为左值和右值（如，“Meat”的左值是12，右值是17），这些数字包含了节点之间的关系。因为“Red”有3和6两个值，所以，它是有拥有1-18值的“Food”节点的后续。同样的，可以推断所有左值大于2并且右值小于11的节点，都是有2-11的“Fruit” 节点的后续。这样，树的结构就通过左值和右值储存下来了。 &lt;/p&gt;
 &lt;p&gt;这里就不贴表结构了，这种方式不如前面两种直观。效率上，查询全部下级的需求被很好的解决，而直属下级也可以通过添加父节点id的  &lt;code&gt;parent&lt;/code&gt;字段来解决。&lt;/p&gt;
 &lt;p&gt;因为左值更大右值更小的节点是下级节点，反之左值更小、右值更大的就是上级，故需求3：查询两个分类直接的所有分类可以通过左右值作为条件来解决，同样是一次查询即可。&lt;/p&gt;
 &lt;p&gt;添加新分类和删除分类需要修改在前序遍历中所有在指定节点之后的节点，甚至包括非父子节点。而移动分类也是如此，这个特性就非常不友好，在数据量大的情况下，改动一下可是很要命的。&lt;/p&gt;
 &lt;p&gt;查询某一级的所有分类，和查询分类是哪一级的，这两个需求无法解决，只能通过  &lt;code&gt;parent&lt;/code&gt;字段想第一种方式一样慢慢遍历。&lt;/p&gt;
 &lt;p&gt;综上所述，对于本项目而言，它还不如第二种，所以这个很复杂的方案也得否决掉。&lt;/p&gt;
 &lt;h1&gt;3.新方案的思考&lt;/h1&gt;
 &lt;p&gt;上面几种方案最接近理想的就是第二种，如果能解决字段长度问题和不符合范式，以及需要上层参与处理的问题就好了。不过不要急，先看看第二种方案的的优缺点的本质是什么。&lt;/p&gt;
 &lt;p&gt;在分析第二种方案的开头就提到，要确保效率，必须要在分类的信息中包含所有上级分类的信息，而不能仅仅只含有直属上级，所以才有了用一个  &lt;code&gt;varchar&lt;/code&gt;保存列表的字段。但反过来想想，数据库的表本身不就是用来保存列表这样结构化数据集合的工具吗，为何不能做一张关联表来代替  &lt;code&gt;path&lt;/code&gt;字段呢？&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;在路径列表的设计中，关键字段   &lt;code&gt;path&lt;/code&gt;的本质是存储了两种信息：一是所有上级分类的id，二是从顶级分类到每个父分类的距离。&lt;/strong&gt; 所以另增一张表，含有三个字段：一个是本分类的所有上级的id，一个是本分类的id，再加上该分类到每个上级分类的距离。这样这张表就能够起到与  &lt;code&gt;path&lt;/code&gt;字段相同的作用，而且还不违反数据库范式，最关键的是它不存在字段长度的限制！&lt;/p&gt;
 &lt;p&gt;经过一番折腾，终于找到了这个比较完美的方案。事实上在之后的查阅资料中，发现这个方案早就在一些系统中使用了，名叫ClosureTable。&lt;/p&gt;
 &lt;h1&gt;4.基于ClosureTable的无限级分类存储&lt;/h1&gt;
 &lt;p&gt;ClosureTable直译过来叫闭包表？不过不重要，ClosureTable以一张表存储节点之间的关系、其中包含了任何两个有关系（上下级）节点的关联信息&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="ClosureTable&amp;#28436;&amp;#31034;" src="https://segmentfault.com/img/bV7541?w=700&amp;h=320" title="ClosureTable&amp;#28436;&amp;#31034;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;定义关系表  &lt;code&gt;CategoryTree&lt;/code&gt;，其包含3个字段：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;code&gt;ancestor&lt;/code&gt; 祖先：上级节点的id&lt;/li&gt;
  &lt;li&gt;
   &lt;code&gt;descendant&lt;/code&gt; 子代：下级节点的id&lt;/li&gt;
  &lt;li&gt;
   &lt;code&gt;distance&lt;/code&gt; 距离：子代到祖先中间隔了几级&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;这三个字段的组合是唯一的，因为在树中，一条路径可以标识一个节点，所以可以直接把它们的组合作为主键。以图为例，节点6到它上一级的节点(节点4)距离为1在数据库中存储为  &lt;code&gt;ancestor=4&lt;/code&gt;,  &lt;code&gt;descendant=6&lt;/code&gt;,  &lt;code&gt;distance=1&lt;/code&gt;，到上两级的节点(节点1)距离为2,于是有  &lt;code&gt;ancestor=1&lt;/code&gt;,  &lt;code&gt;descendant=6&lt;/code&gt;,  &lt;code&gt;distance=2&lt;/code&gt;，到根节点的距离为3也是如此，最后还要包含一个到自己的连接，当然距离就是0了。&lt;/p&gt;
 &lt;p&gt;这样一来，不尽表中包含了所有的路径信息，还在带上了路径中每个节点的位置（距离），对于树结构常用的查询都能够很方便的处理。下面看看如何用用它来实现我们的需求。&lt;/p&gt;
 &lt;h2&gt;4.1 子节点查询&lt;/h2&gt;
 &lt;p&gt;查询id为5的节点的直属子节点：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT descendant FROM CategoryTree WHERE ancestor=5 AND distance=1&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;查询所有子节点：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT descendant FROM CategoryTree WHERE ancestor=5 AND distance&amp;gt;0&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;查询某个上级节点的子节点，换句话说就是查询具有指定上级节点的节点，也就是  &lt;code&gt;ancestor&lt;/code&gt;字段等于上级节点id即可，第二个距离  &lt;code&gt;distance&lt;/code&gt;决定了查询的对象是由上级往下那一层的，等于1就是往下一层（直属子节点），大于0就是所有子节点。这两个查询都是一句完成。&lt;/p&gt;
 &lt;h2&gt;4.2 路径查询&lt;/h2&gt;
 &lt;p&gt;查询由根节点到id为10的节点之间的所有节点，按照层级由小到大排序&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT ancestor FROM CategoryTree WHERE descendant=10 ORDER BY distance DESC&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;查询id为10的节点（含）到id为3的节点（不含）之间的所有节点，按照层级由小到大排序&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT ancestor FROM CategoryTree WHERE descendant=10 AND 
distance&amp;lt;(SELECT distance FROM CategoryTree WHERE descendant=10 AND ancestor=3) 
ORDER BY distance DESC&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;查询路径，只需要知道  &lt;code&gt;descendant&lt;/code&gt;即可，因为  &lt;code&gt;descendant&lt;/code&gt;字段所在的行就是记录这个节点与其上级节点的关系。如果要过滤掉一些则可以限制  &lt;code&gt;distance&lt;/code&gt;的值。&lt;/p&gt;
 &lt;h2&gt;4.3 查询节点所在的层级（深度）&lt;/h2&gt;
 &lt;p&gt;查询id为5的节点是第几级的&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT distance FROM CategoryTree WHERE descendant=5 AND ancestor=0&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;查询id为5的节点是id为10的节点往下第几级&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT distance FROM CategoryTree WHERE descendant=5 AND ancestor=10&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;查询层级（深度）非常简单，因为  &lt;code&gt;distance&lt;/code&gt;字段就是。直接以上下级的节点id为条件，查询距离即可。&lt;/p&gt;
 &lt;h2&gt;4.4 查询某一层的所有节点&lt;/h2&gt;
 &lt;p&gt;查询所有第三层的节点&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT descendant FROM CategoryTree WHERE ancestor=0 AND distance=3&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;这个就不详细说了，非常简单。&lt;/p&gt;
 &lt;h2&gt;4.5 插入&lt;/h2&gt;
 &lt;p&gt;插入和移动就不是那么方便了，当一个节点插入到某个父节点下方时，它将具有与父节点相似的路径，然后再加上一个自身连接即可。&lt;/p&gt;
 &lt;p&gt;所以插入操作需要两条语句，第一条复制父节点的所有记录，并把这些记录的  &lt;code&gt;distance&lt;/code&gt;加一，因为子节点到每个上级节点的距离都比它的父节点多一。当然  &lt;code&gt;descendant&lt;/code&gt;也要改成自己的。&lt;/p&gt;
 &lt;p&gt;例如把id为10的节点插入到id为5的节点下方（这里用了Mysql的方言）&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;INSERT INTO CategoryTree(ancestor,descendant,distance) (SELECT ancestor,10,distance+1 FROM CategoryTree WHERE descendant=5)&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;然后就是加入自身连接的记录。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;INSERT INTO CategoryTree(ancestor,descendant,distance) VALUES(10,10,0)&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;4.6 移动&lt;/h2&gt;
 &lt;p&gt;节点的移动没有很好的解决方法，因为新位置所在的深度、路径都可能不一样，这就导致移动操作不是仅靠UPDATE语句能完成的，这里选择删除+插入实现移动。&lt;/p&gt;
 &lt;p&gt;另外，在有子树的情况下，上级节点的移动还将导致下级节点的路径改变，所以移动上级节点之后还需要修复下级节点的记录，这就需要递归所有下级节点。&lt;/p&gt;
 &lt;p&gt;删除id=5节点的所有记录&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;DELETE FROM CategoryTree WHERE descendant=5&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;然后配合上面一节的插入操作实现移动。具体的实现直接上代码吧。&lt;/p&gt;
 &lt;p&gt;ClosureTableCategoryStore.java是主要的逻辑，这里只展示部分代码&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;    /**
     * 将一个分类移动到目标分类下面（成为其子分类）。被移动分类的子类将自动上浮（成为指定分类
     * 父类的子分类），即使目标是指定分类原本的父类。
     * &amp;lt;p&amp;gt;
     * 例如下图(省略顶级分类)：
     *       1                                     1
     *       |                                   / | \
     *       2                                  3  4  5
     *     / | \             move(2,7)               / \
     *    3  4  5         ---------------&amp;gt;          6   7
     *         / \                                 /  / | \
     *       6    7                               8  9  10 2
     *      /    /  \
     *     8    9    10
     *
     * @param id 被移动分类的id
     * @param target 目标分类的id
     * @throws IllegalArgumentException 如果id或target所表示的分类不存在、或id==target
     */
    public void move(int id, int target) {
        if(id == target) {
            throw new IllegalArgumentException(&amp;quot;不能移动到自己下面&amp;quot;);
        }
        moveSubTree(id, categoryMapper.selectAncestor(id, 1));
        moveNode(id, target);
    }

    /**
     * 将一个分类移动到目标分类下面（成为其子分类），被移动分类的子分类也会随着移动。
     * 如果目标分类是被移动分类的子类，则先将目标分类（连带子类）移动到被移动分类原来的
     * 的位置，再移动需要被移动的分类。
     * &amp;lt;p&amp;gt;
     * 例如下图(省略顶级分类)：
     *       1                                     1
     *       |                                     |
     *       2                                     7
     *     / | \           moveTree(2,7)         / | \
     *    3  4  5         ---------------&amp;gt;      9  10  2
     *         / \                                   / | \
     *       6    7                                 3  4  5
     *      /    /  \                                     |
     *     8    9    10                                   6
     *                                                    |
     *                                                    8
     *
     * @param id 被移动分类的id
     * @param target 目标分类的id
     * @throws IllegalArgumentException 如果id或target所表示的分类不存在、或id==target
     */
    public void moveTree(int id, int target) {
        /* 移动分移到自己子树下和无关节点下两种情况 */
        Integer distance = categoryMapper.selectDistance(id, target);
        if (distance == null) {
            // 移动到父节点或其他无关系节点，不需要做额外动作
        } else if (distance == 0) {
            throw new IllegalArgumentException(&amp;quot;不能移动到自己下面&amp;quot;);
        } else {
            // 如果移动的目标是其子类，需要先把子类移动到本类的位置
            int parent = categoryMapper.selectAncestor(id, 1);
            moveNode(target, parent);
            moveSubTree(target, target);
        }

        moveNode(id, target);
        moveSubTree(id, id);
    }

    /**
     * 将指定节点移动到另某节点下面，该方法不修改子节点的相关记录，
     * 为了保证数据的完整性，需要与moveSubTree()方法配合使用。
     *
     * @param id 指定节点id
     * @param parent 某节点id
     */
    private void moveNode(int id, int parent) {
        categoryMapper.deletePath(id);
        categoryMapper.insertPath(id, parent);
        categoryMapper.insertNode(id);
    }

    /**
     * 将指定节点的所有子树移动到某节点下
     * 如果两个参数相同，则相当于重建子树，用于父节点移动后更新路径
     *
     * @param id     指定节点id
     * @param parent 某节点id
     */
    private void moveSubTree(int id, int parent) {
        int[] subs = categoryMapper.selectSubId(id);
        for (int sub : subs) {
            moveNode(sub, parent);
            moveSubTree(sub, sub);
        }
    }&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;其中的categoryMapper 是Mybatis的Mapper，这里只展示部分代码&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;    /**
     * 查询某个节点的第N级父节点。如果id指定的节点不存在、操作错误或是数据库被外部修改，
     * 则可能查询不到父节点，此时返回null。
     *
     * @param id 节点id
     * @param n 祖先距离（0表示自己，1表示直属父节点）
     * @return 父节点id，如果不存在则返回null
     */
    @Select(&amp;quot;SELECT ancestor FROM CategoryTree WHERE descendant=#{id} AND distance=#{n}&amp;quot;)
    Integer selectAncestor(@Param(&amp;quot;id&amp;quot;) int id, @Param(&amp;quot;n&amp;quot;) int n);

    /**
     * 复制父节点的路径结构,并修改descendant和distance
     *
     * @param id 节点id
     * @param parent 父节点id
     */
    @Insert(&amp;quot;INSERT INTO CategoryTree(ancestor,descendant,distance) &amp;quot; +
            &amp;quot;(SELECT ancestor,#{id},distance+1 FROM CategoryTree WHERE descendant=#{parent})&amp;quot;)
    void insertPath(@Param(&amp;quot;id&amp;quot;) int id, @Param(&amp;quot;parent&amp;quot;) int parent);

    /**
     * 在关系表中插入对自身的连接
     *
     * @param id 节点id
     */
    @Insert(&amp;quot;INSERT INTO CategoryTree(ancestor,descendant,distance) VALUES(#{id},#{id},0)&amp;quot;)
    void insertNode(int id);

    /**
     * 从树中删除某节点的路径。注意指定的节点可能存在子树，而子树的节点在该节点之上的路径并没有改变，
     * 所以使用该方法后还必须手动修改子节点的路径以确保树的正确性
     *
     * @param id 节点id
     */
    @Delete(&amp;quot;DELETE FROM CategoryTree WHERE descendant=#{id}&amp;quot;)
    void deletePath(int id);&lt;/code&gt;&lt;/pre&gt;
 &lt;h1&gt;5.结语&lt;/h1&gt;
 &lt;p&gt;在分析推论后，终于找到了一种既有查询简单、效率高等优点，也符合数据库设计范式，而且是真正的无限级分类的设计。本方案的写入操作虽然需要递归，但相比于前序遍历树效率仍高出许多，并且在本博客系统中分类不会频繁修改。可见对于在关系数据库中存储一棵树的需求，ClosureTable是一种比较完美的解决方案。&lt;/p&gt;
 &lt;p&gt;完整的JAVA实现代码见   &lt;a href="https://github.com/Kaciras/Examples-java/tree/master/ClosureTable"&gt;https://github.com/Kaciras/Examples-java/tree/master/ClosureTable&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mybatis mysql java</category>
      <guid isPermaLink="true">https://itindex.net/detail/58231-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%97%A0%E9%99%90-%E5%88%86%E7%B1%BB</guid>
      <pubDate>Tue, 10 Apr 2018 14:57:23 CST</pubDate>
    </item>
    <item>
      <title>优化 MySQL： 3 个简单的小调整</title>
      <link>https://itindex.net/detail/58038-%E4%BC%98%E5%8C%96-mysql-%E5%B0%8F%E8%B0%83</link>
      <description>&lt;p&gt;我并不期望成为一个专家级的 DBA，但是，在我优化 MySQL 时，我推崇 80/20 原则，明确说就是通过简单的调整一些配置，你可以压榨出高达 80% 的性能提升。尤其是在服务器资源越来越便宜的当下。&lt;/p&gt;
 &lt;h3&gt;警告&lt;/h3&gt;
 &lt;ol&gt;
  &lt;li&gt;没有两个数据库或者应用程序是完全相同的。这里假设我们要调整的数据库是为一个“典型”的 Web 网站服务的，优先考虑的是快速查询、良好的用户体验以及处理大量的流量。&lt;/li&gt;
  &lt;li&gt;在你对服务器进行优化之前，请做好数据库备份！&lt;/li&gt;
&lt;/ol&gt;
 &lt;h3&gt;1、 使用 InnoDB 存储引擎&lt;/h3&gt;
 &lt;p&gt;如果你还在使用 MyISAM 存储引擎，那么是时候转换到 InnoDB 了。有很多的理由都表明 InnoDB 比 MyISAM 更有优势，如果你关注性能，那么，我们来看一下它们是如何利用物理内存的：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;MyISAM：仅在内存中保存索引。&lt;/li&gt;
  &lt;li&gt;InnoDB：在内存中保存索引   &lt;strong&gt;和&lt;/strong&gt;数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;结论：保存在内存的内容访问速度要比磁盘上的更快。&lt;/p&gt;
 &lt;p&gt;下面是如何在你的表上去转换存储引擎的命令：&lt;/p&gt; &lt;pre&gt;ALTER TABLE table_name ENGINE=InnoDB;&lt;/pre&gt; &lt;p&gt;  &lt;em&gt;注意：你已经创建了所有合适的索引，对吗？为了更好的性能，创建索引永远是第一优先考虑的事情。&lt;/em&gt;&lt;/p&gt;
 &lt;h3&gt;2、 让 InnoDB 使用所有的内存&lt;/h3&gt;
 &lt;p&gt;你可以在   &lt;code&gt;my.cnf&lt;/code&gt; 文件中编辑你的 MySQL 配置。使用   &lt;code&gt;innodb_buffer_pool_size&lt;/code&gt; 参数去配置在你的服务器上允许 InnoDB 使用物理内存数量。&lt;/p&gt;
 &lt;p&gt;对此（假设你的服务器  &lt;em&gt;仅仅&lt;/em&gt;运行 MySQL），公认的“经验法则”是设置为你的服务器物理内存的 80%。在保证操作系统不使用交换分区而正常运行所需要的足够内存之后 ，尽可能多地为 MySQL 分配物理内存。&lt;/p&gt;
 &lt;p&gt;因此，如果你的服务器物理内存是 32 GB，可以将那个参数设置为多达 25 GB。&lt;/p&gt; &lt;pre&gt;innodb_buffer_pool_size = 25600M&lt;/pre&gt; &lt;p&gt;*注意：（1）如果你的服务器内存较小并且小于 1 GB。为了适用本文的方法，你应该去升级你的服务器。 （2） 如果你的服务器内存特别大，比如，它有 200 GB，那么，根据一般常识，你也没有必要为操作系统保留多达 40 GB 的内存。 *&lt;/p&gt;
 &lt;h3&gt;3、 让 InnoDB 多任务运行&lt;/h3&gt;
 &lt;p&gt;如果服务器上的参数   &lt;code&gt;innodb_buffer_pool_size&lt;/code&gt; 的配置是大于 1 GB，将根据参数   &lt;code&gt;innodb_buffer_pool_instances&lt;/code&gt; 的设置， 将 InnoDB 的缓冲池划分为多个。&lt;/p&gt;
 &lt;p&gt;拥有多于一个的缓冲池的好处有：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;在多线程同时访问缓冲池时可能会遇到瓶颈。你可以通过启用多缓冲池来最小化这种争用情况：&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;对于缓冲池数量的官方建议是：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;为了实现最佳的效果，要综合考虑    &lt;code&gt;innodb_buffer_pool_instances&lt;/code&gt; 和    &lt;code&gt;innodb_buffer_pool_size&lt;/code&gt; 的设置，以确保每个实例至少有不小于 1 GB 的缓冲池。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;因此，在我们的示例中，将参数   &lt;code&gt;innodb_buffer_pool_size&lt;/code&gt; 设置为 25 GB 的拥有 32 GB 物理内存的服务器上。一个合适的设置为 25600M / 24 = 1.06 GB&lt;/p&gt; &lt;pre&gt;innodb_buffer_pool_instances = 24&lt;/pre&gt; &lt;p&gt;&lt;/p&gt;
 &lt;h3&gt;注意！&lt;/h3&gt;
 &lt;p&gt;在修改了   &lt;code&gt;my.cnf&lt;/code&gt; 文件后需要重启 MySQL 才能生效：&lt;/p&gt; &lt;pre&gt;sudo service mysql restart&lt;/pre&gt; &lt;p&gt;还有更多更科学的方法来优化这些参数，但是这几点可以作为一个通用准则来应用，将使你的 MySQL 服务器性能更好。&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://blog.jobbole.com/113659/"&gt;优化 MySQL： 3 个简单的小调整&lt;/a&gt;，首发于  &lt;a href="http://blog.jobbole.com"&gt;文章 - 伯乐在线&lt;/a&gt;。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>IT技术 MySQL 数据库</category>
      <guid isPermaLink="true">https://itindex.net/detail/58038-%E4%BC%98%E5%8C%96-mysql-%E5%B0%8F%E8%B0%83</guid>
      <pubDate>Fri, 09 Feb 2018 15:36:58 CST</pubDate>
    </item>
    <item>
      <title>记一次 MySQL 主从复制延迟的踩坑</title>
      <link>https://itindex.net/detail/57223-mysql-%E5%A4%8D%E5%88%B6-%E5%BB%B6%E8%BF%9F</link>
      <description>&lt;p&gt;最近开发中遇到的一个 MySQL 主从延迟的坑，记录并总结，避免再次犯同样的错误。&lt;/p&gt;
 &lt;h3&gt;情景&lt;/h3&gt;
 &lt;p&gt;一个活动信息需要审批，审批之后才能生效。因为之后活动要编辑，编辑后也可能触发审批，审批中展示的是编辑前的活动内容，考虑到字段比较多，也要保存审批活动的内容，因此设计采用了一张临时表，审批中的活动写进审批表(activity_tmp)，审批通过之后才把真正的活动内容写进活动表(activity)。表的简要设计如下，这里将活动内容字段合并为content展示：&lt;/p&gt; &lt;pre&gt;activity_tmp（）
id
status // 审批状态    
content //  审批阶段提交的活动内容

activity
id
content // 审批通过后真正展示的活动内容&lt;/pre&gt; &lt;p&gt;&lt;/p&gt;
 &lt;h3&gt;遇到的问题&lt;/h3&gt;
 &lt;p&gt;当时是有编辑触发审批的情况，发现审批通过之后活动内容是空的，于是开始追查问题的原因。这里说一句，当程序出问题的时候，95%都是代码的问题，先不要去怀疑环境出问题。好好的查日志，然后看看你的代码吧。&lt;/p&gt;
 &lt;h3&gt;追查问题回溯&lt;/h3&gt;
 &lt;p&gt;1、查activity_tmp表，发现当时提交审批的活动内容是正常的，而且状态也更新为审批通过了，怀疑是写入activity表失败 2、查activity表，发现审批后的内容确实没有写入，怀疑是代码问题 3、查看代码，代码逻辑没看出问题，怀疑数据库操作失败，查看日志 4、日志显示，有一句insert语句的活动内容为空，活动内容来自上一个mysql执行的是select语句，把该select语句拿出来放到线上的备库查询，发现活动内容是存在的。运行时查询为空，执行完毕后查询时内容存在，初步怀疑是主从延迟问题。 5、报错只是部分失败，确定是主从延迟的问题。&lt;/p&gt;
 &lt;h3&gt;当时的问题代码&lt;/h3&gt;
 &lt;p&gt;&lt;/p&gt; &lt;pre&gt;$intStatus = $arrInput[‘status’];
$this-&amp;gt;objActTmp-&amp;gt;updateInfoByAId($intActId, $intStatus);
// 更新后，马上查
$arrActContent = $this-&amp;gt;objActTmp-&amp;gt;getActByStatus($intStatus);&lt;/pre&gt; &lt;p&gt;这就是主从延迟出现的地方，update后，马上get，这是主从复制架构上开发的一个大忌。&lt;/p&gt;
 &lt;h3&gt;解决方案&lt;/h3&gt;
 &lt;p&gt;这类问题的解决方案有两种：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;修改代码逻辑&lt;/li&gt;
  &lt;li&gt;修改系统架构&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;对于修改代码逻辑，鄙人有两点见解：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果第二步获取的数据不需要第一步更新的status字段，那就先读，然后再更新&lt;/li&gt;
  &lt;li&gt;如果第二步获取的数据需要依赖第一步的status字段，那就在读出来的时候先判断是否为空，如果是空的，报错，下一次重试。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;总结&lt;/h3&gt;
 &lt;p&gt;其实之前也听到过这样的例子，但是由于没有亲身经历，所以只保留了一种理论上的记忆，实际上印象不深，经历了这么一次踩坑后，印象特别深刻，现在看到别人写这样的代码也能马上发现并指出。还是自己亲身去踩坑印象最深。&lt;/p&gt;
 &lt;p&gt;日志很重要，详细的日志更重要。日志要记录有用的信息，方便追查问题的时候去追溯问题的本质原因。我觉得日志就应该尽量做成飞机中的黑匣子，帮助我们保存“事故“发生时的所有相关信息。&lt;/p&gt;

 &lt;p&gt;  &lt;a href="http://blog.jobbole.com/111853/"&gt;记一次 MySQL 主从复制延迟的踩坑&lt;/a&gt;，首发于  &lt;a href="http://blog.jobbole.com"&gt;文章 - 伯乐在线&lt;/a&gt;。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>IT技术 MySQL 数据库</category>
      <guid isPermaLink="true">https://itindex.net/detail/57223-mysql-%E5%A4%8D%E5%88%B6-%E5%BB%B6%E8%BF%9F</guid>
      <pubDate>Sun, 16 Jul 2017 16:32:47 CST</pubDate>
    </item>
    <item>
      <title>如何理解并正确使用 MySQL 索引</title>
      <link>https://itindex.net/detail/57212-%E7%90%86%E8%A7%A3-%E6%AD%A3%E7%A1%AE-mysql</link>
      <description>&lt;h1&gt;1、概述&lt;/h1&gt;
 &lt;div&gt;
  &lt;p&gt;索引是存储引擎用于快速查找记录的一种数据结构，通过合理的使用数据库索引可以大大提高系统的访问性能，接下来主要介绍在MySql数据库中索引类型，以及如何创建出更加合理且高效的索引技巧。&lt;/p&gt;
  &lt;blockquote&gt;   &lt;p&gt;注：这里主要针对的是InnoDB存储引擎的B+Tree索引数据结构&lt;/p&gt;&lt;/blockquote&gt;
  &lt;h1&gt;2、索引的优点&lt;/h1&gt;
  &lt;p&gt;1、大大减轻了服务器需要扫描的数据量，从而提高了数据的检索速度&lt;/p&gt;
  &lt;p&gt;2、帮助服务器避免排序和临时表&lt;/p&gt;
  &lt;p&gt;3、可以将随机I/O变为顺序I/O&lt;/p&gt;
  &lt;h1&gt;3、索引的创建&lt;/h1&gt;
  &lt;p&gt;   &lt;strong&gt;3.1、主键索引&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;ALTER TABLE &amp;apos;table_name&amp;apos; ADD PRIMARY KEY &amp;apos;index_name&amp;apos; (&amp;apos;column&amp;apos;);&lt;/pre&gt;  &lt;p&gt;   &lt;strong&gt;3.2、唯一索引&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;ALTER TABLE &amp;apos;table_name&amp;apos; ADD UNIQUE &amp;apos;index_name&amp;apos; (&amp;apos;column&amp;apos;);&lt;/pre&gt;  &lt;p&gt;   &lt;strong&gt;3.3、普通索引&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;ALTER TABLE &amp;apos;table_name&amp;apos; ADD INDEX &amp;apos;index_name&amp;apos; (&amp;apos;column&amp;apos;);&lt;/pre&gt;  &lt;p&gt;   &lt;strong&gt;3.4、全文索引&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;ALTER TABLE &amp;apos;table_name&amp;apos; ADD FULLTEXT &amp;apos;index_name&amp;apos; (&amp;apos;column&amp;apos;);&lt;/pre&gt;  &lt;p&gt;   &lt;strong&gt;3.5、组合索引&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;ALTER TABLE &amp;apos;table_name&amp;apos; ADD INDEX &amp;apos;index_name&amp;apos; (&amp;apos;column1&amp;apos;, &amp;apos;column2&amp;apos;, ...);&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;h1&gt;4、B+Tree的索引规则&lt;/h1&gt;
  &lt;p&gt;创建一个测试的用户表&lt;/p&gt;  &lt;pre&gt;DROP TABLE IF EXISTS user_test;
CREATE TABLE user_test(
	id int AUTO_INCREMENT PRIMARY KEY,
	user_name varchar(30) NOT NULL,
	sex bit(1) NOT NULL DEFAULT b&amp;apos;1&amp;apos;,
	city varchar(50) NOT NULL,
	age int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;/pre&gt;  &lt;p&gt;创建一个组合索引： ALTER TABLE user_test ADD INDEX idx_user(user_name , city , age);&lt;/p&gt;
  &lt;h2&gt;4.1、索引有效的查询&lt;/h2&gt;
  &lt;h3&gt;   &lt;strong&gt;4.1.1、全值匹配&lt;/strong&gt;&lt;/h3&gt;
  &lt;p&gt;全值匹配指的是和索引中的所有列进行匹配，如：以上面创建的索引为例，在where条件后可同时查询（user_name，city，age）为条件的数据。&lt;/p&gt;
  &lt;blockquote&gt;   &lt;p&gt;注：与where后查询条件的顺序无关，这里是很多同学容易误解的一个地方&lt;/p&gt;&lt;/blockquote&gt;
  &lt;p&gt;&lt;/p&gt;  &lt;pre&gt;SELECT * FROM user_test WHERE user_name = &amp;apos;feinik&amp;apos; AND age = 26 AND city = &amp;apos;广州&amp;apos;;&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;h3&gt;   &lt;strong&gt;4.1.2、匹配最左前缀&lt;/strong&gt;&lt;/h3&gt;
  &lt;p&gt;匹配最左前缀是指优先匹配最左索引列，如：上面创建的索引可用于查询条件为：（user_name ）、（user_name, city）、（user_name , city , age）&lt;/p&gt;
  &lt;blockquote&gt;   &lt;p&gt;注：满足最左前缀查询条件的顺序与索引列的顺序无关，如：（city, user_name）、（age, city, user_name）&lt;/p&gt;&lt;/blockquote&gt;
  &lt;h3&gt;   &lt;strong&gt;4.1.3、匹配列前缀&lt;/strong&gt;&lt;/h3&gt;
  &lt;p&gt;指匹配列值的开头部分，如：查询用户名以feinik开头的所有用户&lt;/p&gt;  &lt;pre&gt;SELECT * FROM user_test WHERE user_name LIKE &amp;apos;feinik%&amp;apos;;&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;h3&gt;   &lt;strong&gt;4.1.4、匹配范围值&lt;/strong&gt;&lt;/h3&gt;
  &lt;p&gt;如：查询用户名以feinik开头的所有用户，这里使用了索引的第一列&lt;/p&gt;  &lt;pre&gt;SELECT * FROM user_test WHERE user_name LIKE &amp;apos;feinik%&amp;apos;;&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;h2&gt;4.2、索引的限制&lt;/h2&gt;
  &lt;p&gt;1、where查询条件中不包含索引列中的最左索引列，则无法使用到索引查询，如：&lt;/p&gt;  &lt;pre&gt;SELECT * FROM user_test WHERE city = &amp;apos;广州&amp;apos;;&lt;/pre&gt;  &lt;p&gt;或&lt;/p&gt;  &lt;pre&gt;SELECT * FROM user_test WHERE age= 26;&lt;/pre&gt;  &lt;p&gt;或&lt;/p&gt;  &lt;pre&gt;SELECT * FROM user_test WHERE city = &amp;apos;广州&amp;apos; AND age = &amp;apos;26&amp;apos;;&lt;/pre&gt;  &lt;p&gt;2、即使where的查询条件是最左索引列，也无法使用索引查询用户名以feinik结尾的用户&lt;/p&gt;  &lt;pre&gt;SELECT * FROM user_test WHERE user_name like &amp;apos;%feinik&amp;apos;;&lt;/pre&gt;  &lt;p&gt;3、如果where查询条件中有某个列的范围查询，则其右边的所有列都无法使用索引优化查询，如：&lt;/p&gt;  &lt;pre&gt;SELECT * FROM user_test WHERE user_name = &amp;apos;feinik&amp;apos; AND city LIKE &amp;apos;广州%&amp;apos; AND age = 26;&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;h1&gt;5、高效的索引策略&lt;/h1&gt;
  &lt;h2&gt;5.1、索引列不能是表达式的一部分，也不能作为函数的参数，否则无法使用索引查询。&lt;/h2&gt;
  &lt;p&gt;SELECT * FROM user_test WHERE user_name = concat(user_name, ‘fei’);&lt;/p&gt;
  &lt;h2&gt;5.2、前缀索引&lt;/h2&gt;
  &lt;p&gt;有时候需要索引很长的字符列，这会增加索引的存储空间以及降低索引的效率，一种策略是可以使用哈希索引，还有一种就是可以使用前缀索引，前缀索引是选择字符列的前n个字符作为索引，这样可以大大节约索引空间，从而提高索引效率。&lt;/p&gt;
  &lt;h3&gt;   &lt;strong&gt;5.2.1、前缀索引的选择性&lt;/strong&gt;&lt;/h3&gt;
  &lt;p&gt;前缀索引要选择足够长的前缀以保证高的选择性，同时又不能太长，我们可以通过以下方式来计算出合适的前缀索引的选择长度值：&lt;/p&gt;
  &lt;p&gt;（1）&lt;/p&gt;  &lt;pre&gt;SELECT COUNT(DISTINCT index_column)/COUNT(*) FROM table_name; -- index_column代表要添加前缀索引的列&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;blockquote&gt;   &lt;p&gt;注：通过以上方式来计算出前缀索引的选择性比值，比值越高说明索引的效率也就越高效。&lt;/p&gt;&lt;/blockquote&gt;
  &lt;p&gt;（2）&lt;/p&gt;  &lt;pre&gt;SELECT

COUNT(DISTINCT LEFT(index_column,1))/COUNT(*),

COUNT(DISTINCT LEFT(index_column,2))/COUNT(*),

COUNT(DISTINCT LEFT(index_column,3))/COUNT(*)

...

FROM table_name;&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;blockquote&gt;   &lt;p&gt;注：通过以上语句逐步找到最接近于（1）中的前缀索引的选择性比值，那么就可以使用对应的字符截取长度来做前缀索引了&lt;/p&gt;&lt;/blockquote&gt;
  &lt;h3&gt;5.2.2、前缀索引的创建&lt;/h3&gt;
  &lt;p&gt;&lt;/p&gt;  &lt;pre&gt;ALTER TABLE table_name ADD INDEX index_name (index_column(length));&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;h3&gt;5.2.3、使用前缀索引的注意点&lt;/h3&gt;
  &lt;p&gt;前缀索引是一种能使索引更小，更快的有效办法，但是MySql无法使用前缀索引做ORDER BY 和 GROUP BY以及使用前缀索引做覆盖扫描。&lt;/p&gt;
  &lt;h2&gt;5.3、选择合适的索引列顺序&lt;/h2&gt;
  &lt;p&gt;在组合索引的创建中索引列的顺序非常重要，正确的索引顺序依赖于使用该索引的查询方式，对于组合索引的索引顺序可以通过经验法则来帮助我们完成：将选择性最高的列放到索引最前列，该法则与前缀索引的选择性方法一致，但并不是说所有的组合索引的顺序都使用该法则就能确定，还需要根据具体的查询场景来确定具体的索引顺序。&lt;/p&gt;
  &lt;h2&gt;5.4 聚集索引与非聚集索引&lt;/h2&gt;
  &lt;p&gt;   &lt;strong&gt;1、聚集索引&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;聚集索引决定数据在物理磁盘上的物理排序，一个表只能有一个聚集索引，如果定义了主键，那么InnoDB会通过主键来聚集数据，如果没有定义主键，InnoDB会选择一个唯一的非空索引代替，如果没有唯一的非空索引，InnoDB会隐式定义一个主键来作为聚集索引。&lt;/p&gt;
  &lt;p&gt;聚集索引可以很大程度的提高访问速度，因为聚集索引将索引和行数据保存在了同一个B-Tree中，所以找到了索引也就相应的找到了对应的行数据，但在使用聚集索引的时候需注意避免随机的聚集索引（一般指主键值不连续，且分布范围不均匀），如使用UUID来作为聚集索引性能会很差，因为UUID值的不连续会导致增加很多的索引碎片和随机I/O，最终导致查询的性能急剧下降。&lt;/p&gt;
  &lt;p&gt;   &lt;strong&gt;2、非聚集索引&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;与聚集索引不同的是非聚集索引并不决定数据在磁盘上的物理排序，且在B-Tree中包含索引但不包含行数据，行数据只是通过保存在B-Tree中的索引对应的指针来指向行数据，如：上面在（user_name，city, age）上建立的索引就是非聚集索引。&lt;/p&gt;
  &lt;h2&gt;5.5、覆盖索引&lt;/h2&gt;
  &lt;p&gt;如果一个索引（如：组合索引）中包含所有要查询的字段的值，那么就称之为覆盖索引，如：&lt;/p&gt;  &lt;pre&gt;SELECT user_name, city, age FROM user_test WHERE user_name = &amp;apos;feinik&amp;apos; AND age &amp;gt; 25;&lt;/pre&gt;  &lt;p&gt;因为要查询的字段（user_name, city, age）都包含在组合索引的索引列中，所以就使用了覆盖索引查询，查看是否使用了覆盖索引可以通过执行计划中的Extra中的值为Using index则证明使用了覆盖索引，覆盖索引可以极大的提高访问性能。&lt;/p&gt;
  &lt;h2&gt;5.6、如何使用索引来排序&lt;/h2&gt;
  &lt;p&gt;在排序操作中如果能使用到索引来排序，那么可以极大的提高排序的速度，要使用索引来排序需要满足以下两点即可。&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;1、ORDER BY子句后的列顺序要与组合索引的列顺序一致，且所有排序列的排序方向（正序/倒序）需一致&lt;/li&gt;
   &lt;li&gt;2、所查询的字段值需要包含在索引列中，及满足覆盖索引&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;   &lt;strong&gt;通过例子来具体分析&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;在user_test表上创建一个组合索引&lt;/p&gt;  &lt;pre&gt;ALTER TABLE user_test ADD INDEX index_user(user_name , city , age);&lt;/pre&gt;  &lt;p&gt;可以使用到索引排序的案例&lt;/p&gt;  &lt;pre&gt;1、SELECT user_name, city, age FROM user_test ORDER BY user_name;

2、SELECT user_name, city, age FROM user_test ORDER BY user_name, city;

3、SELECT user_name, city, age FROM user_test ORDER BY user_name DESC, city DESC;

4、SELECT user_name, city, age FROM user_test WHERE user_name = &amp;apos;feinik&amp;apos; ORDER BY city;&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;blockquote&gt;   &lt;p&gt;注：第4点比较特殊一点，如果where查询条件为索引列的第一列，且为常量条件，那么也可以使用到索引&lt;/p&gt;&lt;/blockquote&gt;
  &lt;p&gt;无法使用索引排序的案例&lt;/p&gt;
  &lt;p&gt;1、sex不在索引列中&lt;/p&gt;  &lt;pre&gt;SELECT user_name, city, age FROM user_test ORDER BY user_name, sex;&lt;/pre&gt;  &lt;p&gt;2、排序列的方向不一致&lt;/p&gt;  &lt;pre&gt;SELECT user_name, city, age FROM user_test ORDER BY user_name ASC, city DESC;&lt;/pre&gt;  &lt;p&gt;3、所要查询的字段列sex没有包含在索引列中&lt;/p&gt;  &lt;pre&gt;SELECT user_name, city, age, sex FROM user_test ORDER BY user_name;&lt;/pre&gt;  &lt;p&gt;4、where查询条件后的user_name为范围查询，所以无法使用到索引的其他列&lt;/p&gt;  &lt;pre&gt;SELECT user_name, city, age FROM user_test WHERE user_name LIKE &amp;apos;feinik%&amp;apos; ORDER BY city;&lt;/pre&gt;  &lt;p&gt;5、多表连接查询时，只有当ORDER BY后的排序字段都是第一个表中的索引列（需要满足以上索引排序的两个规则）时，方可使用索引排序。如：再创建一个用户的扩展表user_test_ext，并建立uid的索引。&lt;/p&gt;  &lt;pre&gt;DROP TABLE IF EXISTS user_test_ext;

CREATE TABLE user_test_ext(

    id int AUTO_INCREMENT PRIMARY KEY,

    uid int NOT NULL,

    u_password VARCHAR(64) NOT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE user_test_ext ADD INDEX index_user_ext(uid);&lt;/pre&gt;  &lt;p&gt;走索引排序&lt;/p&gt;  &lt;pre&gt;SELECT user_name, city, age FROM user_test u LEFT JOIN user_test_ext ue ON u.id = ue.uid ORDER BY u.user_name;&lt;/pre&gt;  &lt;p&gt;不走索引排序&lt;/p&gt;  &lt;pre&gt;SELECT user_name, city, age FROM user_test u LEFT JOIN user_test_ext ue ON u.id = ue.uid ORDER BY ue.uid;&lt;/pre&gt;  &lt;p&gt;&lt;/p&gt;
  &lt;h1&gt;6、总结&lt;/h1&gt;
  &lt;p&gt;本文主要讲了B+Tree树结构的索引规则，不同索引的创建，以及如何正确的创建出高效的索引技巧来尽可能的提高查询速度，当然了关于索引的使用技巧不单单只有这些，关于索引的更多技巧还需平时不断的积累相关经验。&lt;/p&gt;
&lt;/div&gt;
 &lt;p&gt;  &lt;a href="http://blog.jobbole.com/111845/"&gt;如何理解并正确使用 MySQL 索引&lt;/a&gt;，首发于  &lt;a href="http://blog.jobbole.com"&gt;文章 - 伯乐在线&lt;/a&gt;。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>IT技术 MySQL 数据库 索引</category>
      <guid isPermaLink="true">https://itindex.net/detail/57212-%E7%90%86%E8%A7%A3-%E6%AD%A3%E7%A1%AE-mysql</guid>
      <pubDate>Fri, 14 Jul 2017 12:47:18 CST</pubDate>
    </item>
    <item>
      <title>来自Facebook的一些MySQL运维经验</title>
      <link>https://itindex.net/detail/56944-facebook-mysql-%E8%BF%90%E7%BB%B4</link>
      <description>&lt;p&gt;  &lt;img alt="facebook&amp;#36816;&amp;#32500;" height="520" src="http://tektea-img.b0.upaiyun.com/blog/2017/05/facebook-780x520.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1. 概要&lt;/strong&gt;&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;每台机器都使用多实例的模型。 每个机器放多个实例，每个实例放多个DB。&lt;/p&gt;
 &lt;p&gt;一些信息可以参考： https://www.youtube.com/watch?v=UBHcmP2TSvk&lt;/p&gt;
 &lt;p&gt;多实例之间没有进行资源隔离，这么做是让每个实例都能发挥最大性能。&lt;/p&gt;
 &lt;p&gt;目前大部分核心业务已切换成MyRocks引擎，在机器硬件配置不变的情况，约可节省一半机器。&lt;/p&gt;
 &lt;p&gt;放在MyRocks上的核心业务主要有：Feed、Post、社交图谱等读写混合业务。&lt;/p&gt;
 &lt;p&gt;MyRocks项目地址：https://github.com/facebook/mysql-5.6&lt;/p&gt;
 &lt;p&gt;另外，MariaDB 10.2版本也即将整合MyRocks引擎。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2. 高可用机制&lt;/strong&gt;&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;采用基于GTID的一主多从结构，外加一个基于lossless semi-sync机制的mysqlbinlog实现的binlog server（可以理解为MySQL 5.7的loss zero replication）。&lt;/p&gt;
 &lt;p&gt;基于多数派实现自动选主。&lt;/p&gt;
 &lt;p&gt;基于配置中心实现切换，未使用VIP。&lt;/p&gt;
 &lt;p&gt;在认为semi-sync复制可保证主从数据一致性的假设前提下，发生故障切换时，利用上述的binlog server中的日志进行补全后再选新主、切换。&lt;/p&gt;
 &lt;p&gt;若个别情况下由于特殊原因，出现从库全部挂掉的情况，会将全部请求切到主库，由它扛起所有的业务服务压力。&lt;/p&gt;
 &lt;p&gt;某个从库挂掉时，可以动态摘除。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3. 备份机制&lt;/strong&gt;&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;所有的备份都是基于mysqldump实现，之所以采用mysqldump逻辑备份好处有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;无需备份索引，只备份数据；&lt;/li&gt;
  &lt;li&gt;备份文件压缩比高，更节省磁盘空间；&lt;/li&gt;
  &lt;li&gt;改进了mysqldump，备份过程中还进行额外压缩；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;上面提到，因为采用多实例、多DB结构，备份时可以多DB并行备份。当然了，也会控制并行备份的数量，避免影响在线业务性能。&lt;/p&gt;
 &lt;p&gt;备份放在集中存储（HDFS）上， 据说已达EB级别容量。&lt;/p&gt;
 &lt;p&gt;关于备份的作用定位：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;供数据分析环境拉数据&lt;/li&gt;
  &lt;li&gt;供灾难恢复&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;4. 如何快速部署从库&lt;/strong&gt;&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;可使用xtrabackup在现有存活的SLAVE实例上备份，也可在主库上发起备份，再利用WDT（或者是BT）协议传输到异地，用于拉起从库。&lt;/p&gt;
 &lt;p&gt;关于WDT项目：https://github.com/facebook/wdt&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5. 高度自动化&lt;/strong&gt;&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;面对大规模的数据库实例，手工处理完全不现实。目前在facebook主要是利用Python开发内部DB运维平台，所以Python技能方面要求比较高。&lt;/p&gt;
 &lt;p&gt;采用他们自已的osc工具执行Online DDL（也是本次DTCC大会上lulu的分享主题），它最早用PHP开发，虽早已开源，但实在不好用，所以几乎只在内部使用。这个工具不同于pt-osc，相对来说更有优势，比如可以避免使用pt-osc最常遇到的主从数据延迟问题。&lt;/p&gt;
 &lt;p&gt;项目地址：  &lt;a&gt;https://github.com/facebookincubator/OnlineSchemaChange&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;6. 团队结构及技能树&lt;/strong&gt;&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;DBA团队更多的是负责私有DB云平台的建设。&lt;/p&gt;
 &lt;p&gt;Schema设计及DB拆分等由性能优化团队负责。&lt;/p&gt;
 &lt;p&gt;在线表结构变更：数据库资源申请由质量服务团队负责，做到资源的合理分布、分配，如果某个业务只需要个位数级别的DB实例，可以自行在私有DB云平台中申请部署，当数量比较大时，需要先经过质量服务团队评估通过。&lt;/p&gt;
 &lt;p&gt;数据库资源申请由质量服务团队负责，做到资源的合理分布、分配。如果某个业务需要小量DB实例，可以自行在私有DB云平台中申请部署；当数量比较大时，需要先经过质量服务团队评估通过才可以。&lt;/p&gt;
 &lt;p&gt;文章来自微信公众号：云DB&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>运维干货 DBA Facebook mysql 数据库运维</category>
      <guid isPermaLink="true">https://itindex.net/detail/56944-facebook-mysql-%E8%BF%90%E7%BB%B4</guid>
      <pubDate>Sun, 21 May 2017 00:12:18 CST</pubDate>
    </item>
    <item>
      <title>FAQ系列 | 是什么导致MySQL数据库服务器磁盘I/O高？</title>
      <link>https://itindex.net/detail/56214-faq-%E7%B3%BB%E5%88%97-mysql</link>
      <description>&lt;h2&gt;0、导读&lt;/h2&gt;
 &lt;p&gt;有个MySQL服务器的磁盘I/O总有过高报警，怎么回事？&lt;/p&gt;
 &lt;h2&gt;1、问题&lt;/h2&gt;
 &lt;p&gt;我的朋友小明，TA有个MySQL服务器最近总是报告磁盘I/O非常高，想着我这有免费的不用白不用的企业技术服务（TA自己这么想的），就找我帮忙给把把脉。&lt;/p&gt;
 &lt;p&gt;作为一个经验丰富(踩坑不断)的DBA，出现这种问题，一般来说，磁盘I/O很高无非是下面几个原因引起：&lt;/p&gt;
 &lt;p&gt;磁盘子系统设备性能差，或采用ext2/ext3之类文件系统，或采用cfq之类的ioscheduler，所以IOPS提上不去；&lt;/p&gt;
 &lt;p&gt;SQL效率不高，比如没有索引，或者一次性读取大量数据，所以需要更多的I/O；&lt;/p&gt;
 &lt;p&gt;可用内存太小，内存中能缓存/缓冲的数据不多，所以需要更多的I/O。&lt;/p&gt;
 &lt;p&gt;方法论已有，接下来就是动手开始排查了。&lt;/p&gt;
 &lt;h2&gt;2、排查&lt;/h2&gt;
 &lt;p&gt;先看磁盘I/O设备，是由十几块SSD组成的RAID10阵列，按理说I/O性能应该不至于太差，看iops和%util的数据也确实如此。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#30913;&amp;#30424;I/O&amp;#35774;&amp;#22791;" height="252" src="http://tektea-img.b0.upaiyun.com/blog/2016/11/115.png" width="975"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;再来看下文件系统、io scheduler的因素，发现采用xfs文件系统，而且io scheduler用的是noop，看来也不是这个原因。而且看了下iostat的数据，发现iops也不算低，说明I/O能力还是可以的。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#25991;&amp;#20214;&amp;#31995;&amp;#32479;" height="359" src="http://tektea-img.b0.upaiyun.com/blog/2016/11/29.jpg" width="848"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;再来看看当前的processlist，以及slow query log，也没发现当前有特别明显的slow query，所以也不是这个原因了。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="processlist" height="201" src="http://tektea-img.b0.upaiyun.com/blog/2016/11/37.png" width="803"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;现在只剩下内存不足这个因素了，看了下服务器物理内存是64G，用系统命令   &lt;strong&gt;free&lt;/strong&gt; 看了下，发现大部分都在cached，而free的也不多。观察InnoDB相关的配置以及status，看能不能找到端倪。&lt;/p&gt;
 &lt;p&gt;首先，看下 innodb-buffer-pool-size 分配了多少：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="innodb-buffer-pool-size" height="98" src="http://tektea-img.b0.upaiyun.com/blog/2016/11/44.png" width="631"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;嗯，分配了18G，好像不是太多啊~&lt;/p&gt;
 &lt;p&gt;再看一下 innodb status：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#20998;&amp;#37197;" height="252" src="http://tektea-img.b0.upaiyun.com/blog/2016/11/53.png" width="540"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;重点关注下几个wait值，再看下show engine innodb结果：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="wait&amp;#20540;" height="154" src="http://tektea-img.b0.upaiyun.com/blog/2016/11/63.png" width="680"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;关注下unpurge列表大小，看起来还是比较大的（有111万）。&lt;/p&gt;
 &lt;p&gt;更为诡异的是，在已经停掉SLAVE IO &amp;amp; SQL线程后，发现redo log还在一直增长...&lt;/p&gt;
 &lt;p&gt;第一次看&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="SLAVE IO &amp; SQL&amp;#32447;&amp;#31243;" height="136" src="http://tektea-img.b0.upaiyun.com/blog/2016/11/74.png" width="425"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;停掉SLAVE线程后过阵子再看&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="SLAVE&amp;#32447;&amp;#31243;" height="139" src="http://tektea-img.b0.upaiyun.com/blog/2016/11/82.png" width="425"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;看到这里，有经验的DBA应该基本上能想明白了，主要是因为 innodb buffer pool 太小，导致了下面几个后果：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;dirty page 和 data page 之间相互“排挤抢占”，所以会出现 Innodb_buffer_pool_wait_free 事件；&lt;/li&gt;
  &lt;li&gt;redo log 也没办法及时刷新到磁盘中，所以在SLAVE线程停掉后，能看到LSN还在持续增长；&lt;/li&gt;
  &lt;li&gt;同时我们也看到unpurge的列表也积攒到很大（111万），这导致了ibdata1文件涨到了146G之大，不过这个可能也是因为有某些事务长时间未提交。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;还有，不知道大家注意到没，Innodb_row_lock_current_waits 的值竟然是 18446744073709551615（想想bigint多大），显然不可能啊。事实上，这种情况已经碰到过几次了，明明当前没有行锁，这个 status 值却不小，查了一下官方bug库，竟然只报告了一例，bug id是#71520。&lt;/p&gt;
 &lt;h1&gt;3、解决&lt;/h1&gt;
 &lt;p&gt;既然知道原因，问题解决起来也就快了，我们主要做了下面几个调整：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;调大innodb-buffer-pool-size，原则上不超过物理内存的70%，所以设置为40G；&lt;/li&gt;
  &lt;li&gt;调大innodb-purge-thread，原来是1，调整成4；&lt;/li&gt;
  &lt;li&gt;调大innodb_io_capacity和innodb_io_capacity_max，值分别为2万和2.5万；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;调整完后，重启实例（5.7版本前调整innodb-buffer-pool-size 和 innodb-purge-thread 需要重启才生效）。再经观察，发现IOPS下降的很快，不再告警，同时 Innodb_buffer_pool_wait_free 也一直为 0，unpurge列表降到了数千级别，搞定，收工，继续搬砖卖茶~&lt;/p&gt;
 &lt;p&gt;作者：叶金荣&lt;/p&gt;
 &lt;p&gt;文章出处：老叶茶馆（订阅号ID：iMySQL_WX）&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>运维经验 MySQL数据库</category>
      <guid isPermaLink="true">https://itindex.net/detail/56214-faq-%E7%B3%BB%E5%88%97-mysql</guid>
      <pubDate>Thu, 17 Nov 2016 10:37:59 CST</pubDate>
    </item>
    <item>
      <title>MySQL与PostgreSQL相比哪个更好？</title>
      <link>https://itindex.net/detail/56471-mysql-postgresql</link>
      <description>&lt;p&gt;网上已经有很多拿PostgreSQL与MySQL比较的文章了，这篇文章只是对一些重要的信息进行下梳理。在开始分析前，先来看下这两张图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="205" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/mysql-postgresql.png" width="589"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;MySQL&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL声称自己是最流行的开源数据库。LAMP中的M指的就是MySQL。构建在LAMP上的应用都会使用MySQL，如WordPress、Drupal等大多数php开源程序。MySQL最初是由MySQL AB开发的，然后在2008年以10亿美金的价格卖给了Sun公司，Sun公司又在2010年被Oracle收购。Oracle支持MySQL的多个版本：Standard、Enterprise、Classic、Cluster、Embedded与Community。其中有一些是免费下载的，另外一些则是收费的。其核心代码基于GPL许可，由于MySQL被控制在Oracle，社区担心会对MySQL的开源会有影响，所以开发了一些分支，比如：   &lt;a href="https://www.biaodianfu.com/mysql-percona-or-mariadb.html"&gt;MariaDB和Percona&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;PostgreSQL标榜自己是世界上最先进的开源数据库。PostgreSQL的一些粉丝说它能与Oracle相媲美，而且没有那么昂贵的价格和傲慢的客服。最初是1985年在加利福尼亚大学伯克利分校开发的，作为Ingres数据库的后继。PostgreSQL是完全由社区驱动的开源项目。它提供了单个完整功能的版本，而不像MySQL那样提供了多个不同的社区版、商业版与企业版。PostgreSQL基于自由的BSD/MIT许可，组织可以使用、复制、修改和重新分发代码，只需要提供一个版权声明即可。&lt;/p&gt;
 &lt;h2&gt;MySQL与PostgreSQL的对比&lt;/h2&gt;
 &lt;p&gt;MySQL的背后是一个成熟的商业公司，而PostgreSQL的背后是一个庞大的志愿开发组。这使得MySQL的开发过程更为慎重，而PostgreSQL的反应更为迅速。这样的两种背景直接导致了各自固有的优点和缺点。&lt;/p&gt;
 &lt;h3&gt;PostgreSQL相对于MySQL的优势&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;1）不仅仅是关系型数据库&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;除了存储正常的数据类型外，还支持存储：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;array，不管是一位数组还是多为数组均支持&lt;/li&gt;
  &lt;li&gt;json（hStore）和jsonb，相比使用text存储接送要高效很多&lt;/li&gt;
&lt;/ul&gt;
 &lt;blockquote&gt;  &lt;p&gt;   &lt;strong&gt;json和jsonb之间的区别&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;jsonb和json在更高的层面上看起来几乎是一样的，但在存储实现上是不同的。&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;json存储完的文本，json列会每次都解析存储的值，它不支持索引，但你可以为查询创建表达式索引。&lt;/li&gt;
   &lt;li&gt;jsonb存储的二进制格式，避免了重新解析数据结构。它支持索引，这意味着你可以不使用指定的索引就能查询任何路径。&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;当我们比较写入数据速度时，由于数据存储的方式的原因，jsonb会比json稍微的慢一点。json列会每次都解析存储的值，这意味着键的顺序要和输入的时候一样。但jsonb不同，以二进制格式存储且不保证键的顺序。因此，如果你有软件需要依赖键的顺序，jsonb可能不是你的应用的最佳选择。使用jsonb的优势还在于你可以轻易的整合关系型数据和非关系型数据， PostgreSQL对于mongodb这类的基于文档的数据库是个不小的威胁，毕竟如果一个表中只有一列数据的类型是半结构化的，没有必要为了迁就它而整个表的设计采用schemaless的结构。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;2&lt;/strong&gt;  &lt;strong&gt;）支持地理信息处理扩展&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;PostGIS 为PostgreSQL提供了存储空间地理数据的支持，使PostgreSQL成为了一个空间数据库，能够进行空间数据管理、数量测量与几何拓扑分析。在功能上，和MYSQL对比，PostGIS具有下列优势：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="514" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/postgis.png" width="657"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;O2O业务场景中的LBS业务使用PostgreSQL + PostGIS有无法比拟的优势。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3&lt;/strong&gt;  &lt;strong&gt;）可以快速构建REST API&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;PostgREST 可以方便的为任何 PostgreSQL 数据库提供完全的 RESTful API 服务。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4&lt;/strong&gt;  &lt;strong&gt;）支持树状结构&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;支持R-trees这样可扩展的索引类型，可以更方便地处理一些特殊数据。MySQL 处理树状的设计会很复杂, 而且需要写很多代码, 而 PostgreSQL 可以高效处理树结构。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5&lt;/strong&gt;  &lt;strong&gt;）有极其强悍的 SQL 编程能力&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;支持递归，有非常丰富的统计函数和统计语法支持。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;MySQL：支持 CREATE PROCEDURE 和 CREATE FUNCTION 语句。存储过程可以用 SQL 和 C++ 编写。用户定义函数可以用 SQL、C 和 C++ 编写。&lt;/li&gt;
  &lt;li&gt;PostgreSQL：没有单独的存储过程，都是通过函数实现的。用户定义函数可以用 PL/pgSQL（专用的过程语言）、PL/Tcl、PL/Perl、PL/Python 、SQL 和 C 编写。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;6&lt;/strong&gt;  &lt;strong&gt;）外部数据源支持&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;可以把 70 种外部数据源 (包括 Mysql, Oracle, CSV, hadoop …) 当成自己数据库中的表来查询。Postgres有一个针对这一难题的解决方案：一个名为“外部数据封装器（Foreign Data Wrapper，FDW）”的特性。该特性最初由PostgreSQL社区领袖Dave Page四年前根据SQL标准SQL/MED（SQL Management of External Data）开发。FDW提供了一个SQL接口，用于访问远程数据存储中的远程大数据对象，使DBA可以整合来自不相关数据源的数据，将它们存入Postgres数据库中的一个公共模型。这样，DBA就可以访问和操作其它系统管理的数据，就像在本地Postgres表中一样。例如，使用FDW for MongoDB，数据库管理员可以查询来自文档数据库的数据，并使用SQL将它与来自本地Postgres表的数据相关联。借助这种方法，用户可以将数据作为行、列或JSON文档进行查看、排序和分组。他们甚至可以直接从Postgres向源文档数据库写入（插入、更细或删除）数据，就像一个一体的无缝部署。也可以对Hadoop集群或MySQL部署做同样的事。FDW使Postgres可以充当企业的中央联合数据库或“Hub”。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;7&lt;/strong&gt;  &lt;strong&gt;）没有字符串长度限制&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;一般关系型数据库的字符串有限定长度8k左右，无限长 TEXT 类型的功能受限，只能作为外部大数据访问。而PostgreSQL的 TEXT 类型可以直接访问，SQL语法内置正则表达式，可以索引，还可以全文检索，或使用xml xpath。MySQL 的各种text字段有不同的限制，要手动区分 small text, middle text, large text… PostgreSQL 没有这个限制，text 能支持各种大小。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;8&lt;/strong&gt;  &lt;strong&gt;）支持图结构数据存储&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;没有具体使用过，具体可以自己搜索下。参考链接：  &lt;a href="https://mp.weixin.qq.com/s/cjor82wgDu5gzDvTYpLDWw"&gt;https://mp.weixin.qq.com/s/cjor82wgDu5gzDvTYpLDWw&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;9&lt;/strong&gt;  &lt;strong&gt;）支持窗口函数&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;窗口函数提供跨行相关的当前查询行集执行计算的能力。仅当调用跟着OVER子句的聚集函数，作为窗口函数；否则它们作为常规的聚合函数。窗口也是一种分组，但和 group by 的分组不同。窗口，可以提供分组之外，还可以执行对每个窗口进行计算。可以相像成是group by 后，然后对每个分组进行计算，而不像Group by ，只是单纯地分组。MySQL 不支持 OVER 子句, 而PostgreSQL支持。OVER 子句能简单的解决 “每组取 top 5” 的这类问题。MySQL支持的SQL语法(ANSI SQL标准)的很小一部分。不支持递归查询、通用表表达式（Oracle的with 语句）或者窗口函数（分析函数）。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;10&lt;/strong&gt;  &lt;strong&gt;）对索引的支持更强&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;PostgreSQL 的可以使用函数和条件索引，这使得PostgreSQL数据库的调优非常灵活，mysql就没有这个功能，条件索引在web应用中很重要。对于索引类型：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;MySQL：取决于存储引擎。MyISAM：BTREE，InnoDB：BTREE。&lt;/li&gt;
  &lt;li&gt;PostgreSQL：支持 B-树、哈希、R-树和 Gist 索引。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;InnoDB的表和索引都是按相同的方式存储。也就是说表都是索引组织表。这一般要求主键不能太长而且插入时的主键最好是按顺序递增，否则对性能有很大影响。PostgreSQL不存在这个问题。&lt;/p&gt;
 &lt;p&gt;索引类型方面，MySQL取决于存储引擎。MyISAM：BTREE，InnoDB：BTREE。PostgreSQL支持 B-树、哈希、R-树和 Gist 索引。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;11&lt;/strong&gt;  &lt;strong&gt;）集群支持更好&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Mysql Cluster可能与你的想象有较大差异。开源的cluster软件较少。复制(Replication)功能是异步的并且有很大的局限性。例如，它是单线程的(single-threaded)，因此一个处理能力更强的Slave的恢复速度也很难跟上处理能力相对较慢的Master。&lt;/p&gt;
 &lt;p&gt;PostgreSQL有丰富的开源cluster软件支持。plproxy 可以支持语句级的镜像或分片，slony 可以进行字段级的同步设置，standby 可以构建WAL文件级或流式的读写分离集群，同步频率和集群策略调整方便，操作非常简单。&lt;/p&gt;
 &lt;p&gt;另外，PostgreSQL的主备复制属于物理复制，相对于MySQL基于binlog的逻辑复制，数据的一致性更加可靠，复制性能更高，对主机性能的影响也更小。对于WEB应用来说，复制的特性很重要，mysql到现在也是异步复制，pgsql可以做到同步，异步，半同步复制。还有mysql的同步是基于binlog复制，类似oracle golden gate,是基于stream的复制，做到同步很困难，这种方式更加适合异地复制，pgsql的复制基于wal，可以做到同步复制。同时，pgsql还提供stream复制。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;12&lt;/strong&gt;  &lt;strong&gt;）事务隔离做的更好&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL 的事务隔离级别 repeatable read 并不能阻止常见的并发更新, 得加锁才可以, 但悲观锁会影响性能, 手动实现乐观锁又复杂. 而 PostgreSQL 的列里有隐藏的乐观锁 version 字段, 默认的 repeatable read 级别就能保证并发更新的正确性, 并且又有乐观锁的性能。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;13&lt;/strong&gt;  &lt;strong&gt;）对于字符支持更好一些&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL 里需要 utf8mb4 才能显示 emoji 的坑, PostgreSQL 没这个坑。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;14&lt;/strong&gt;  &lt;strong&gt;）对表连接支持较完整&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;对表连接支持较完整，MySQL只有一种表连接类型:嵌套循环连接(nested-loop),不支持排序-合并连接(sort-merge join)与散列连接(hash join)。PostgreSQL都支持。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;15&lt;/strong&gt;  &lt;strong&gt;）存储方式支持更大的数据量&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;PostgreSQL主表采用堆表存放，MySQL采用索引组织表，能够支持比MySQL更大的数据量。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;16&lt;/strong&gt;  &lt;strong&gt;）时间精度更高&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL对于时间、日期、间隔等时间类型没有秒以下级别的存储类型，而PostgreSQL可以精确到秒以下。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;17&lt;/strong&gt;  &lt;strong&gt;）优化器的功能较完整&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL对复杂查询的处理较弱，查询优化器不够成熟，explain看执行计划的结果简单。性能优化工具与度量信息不足。&lt;/p&gt;
 &lt;p&gt;PostgreSQL很强大的查询优化器，支持很复杂的查询处理。explain返回丰富的信息。提供了一些性能视图，可以方便的看到发生在一个表和索引上的select、delete、update、insert统计信息，也可以看到cache命中率。网上有一个开源的pgstatspack工具。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;18)&lt;/strong&gt;  &lt;strong&gt;序列支持更好&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL 不支持多个表从同一个序列中取 id, 而 PostgreSQL 可以。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;19）对子查询支持更好&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;对子查询的支持。虽然在很多情况下在SQL语句中使用子查询效率低下，而且绝大多数情况下可以使用带条件的多表连接来替代子查询，但是子查询的存在在很多时候仍然不可避免。而且使用子查询的SQL语句与使用带条件的多表连接相比具有更高的程序可读性。几乎任何数据库的子查询 (subquery) 性能都比 MySQL 好。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;20&lt;/strong&gt;  &lt;strong&gt;）增加列更加简单&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL表增加列，基本上是重建表和索引，会花很长时间。PostgreSQL表增加列，只是在数据字典中增加表定义，不会重建表.&lt;/p&gt;
 &lt;h3&gt;MySQL相对于PostgreSQL的优势&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;1）MySQL&lt;/strong&gt;  &lt;strong&gt;比PostgreSQL更流行&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;流行对于一个商业软件来说，也是一个很重要的指标，流行意味着更多的用户，意味着经受了更多的考验，意味着更好的商业支持、意味着更多、更完善的文档资料。易用，很容易安装。第三方工具，包括可视化工具，让用户能够很容易入门。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2&lt;/strong&gt;  &lt;strong&gt;）回滚实现更优&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;innodb的基于回滚段实现的MVCC机制，相对PG新老数据一起存放的基于XID的MVCC机制，是占优的。新老数据一起存放，需要定时触发VACUUM，会带来多余的IO和数据库对象加锁开销，引起数据库整体的并发能力下降。而且VACUUM清理不及时，还可能会引发数据膨胀。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3&lt;/strong&gt;  &lt;strong&gt;）在Windows上运行更可靠&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;与PostgreSQL相比，MySQL更适宜在Windows环境下运行。MySQL作为一个本地的Windows应用程序运行(在 NT/Win2000/WinXP下，是一个服务)，而PostgreSQL是运行在Cygwin模拟环境下。PostgreSQL在Windows下运行没有MySQL稳定，应该是可以想象的。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4&lt;/strong&gt;  &lt;strong&gt;）线程模式相比进程模式的优势&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL使用了线程，而PostgreSQL使用的是进程。在不同线程之间的环境转换和访问公用的存储区域显然要比在不同的进程之间要快得多。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;进程模式对多CPU利用率比较高。进程模式共享数据需要用到共享内存，而线程模式数据本身就是在进程空间内都是共享的，不同线程访问只需要控制好线程之间的同步。&lt;/li&gt;
  &lt;li&gt;线程模式对资源消耗比较少。所以MySQL能支持远比PostgreSQL多的更多的连接。但PostgreSQL中有优秀的连接池软件软件，如pgbouncer和pgpool，所以通过连接池也可以支持很多的连接。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;5&lt;/strong&gt;  &lt;strong&gt;）权限设置上更加完善&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL在权限系统上比PostgreSQL某些方面更为完善。PostgreSQL只支持对于每一个用户在一个数据库上或一个数据表上的 INSERT、SELECT和UPDATE/DELETE的授权，而MySQL允许你定义一整套的不同的数据级、表级和列级的权限。对于列级的权限， PostgreSQL可以通过建立视图，并确定视图的权限来弥补。MySQL还允许你指定基于主机的权限，这对于目前的PostgreSQL是无法实现的，但是在很多时候，这是有用的。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;6&lt;/strong&gt;  &lt;strong&gt;）存储引擎插件化机制&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL的存储引擎插件化机制，使得它的应用场景更加广泛，比如除了innodb适合事务处理场景外，myisam适合静态数据的查询场景。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;7&lt;/strong&gt;  &lt;strong&gt;）适应24/7运行&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL可以适应24/7运行。在绝大多数情况下，你不需要为MySQL运行任何清除程序。PostgreSQL目前仍不完全适应24/7运行，这是因为你必须每隔一段时间运行一次VACUUM。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;8&lt;/strong&gt;  &lt;strong&gt;）更加试用于简单的场景&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;PostgreSQL只支持堆表，不支持索引组织表，Innodb只支持索引组织表。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;索引组织表的优势：表内的数据就是按索引的方式组织，数据是有序的，如果数据都是按主键来访问，那么访问数据比较快。而堆表，按主键访问数据时，是需要先按主键索引找到数据的物理位置。&lt;/li&gt;
  &lt;li&gt;索引组织表的劣势：索引组织表中上再加其它的索引时，其它的索引记录的数据位置不再是物理位置，而是主键值，所以对于索引组织表来说，主键的值不能太大，否则占用的空间比较大。&lt;/li&gt;
  &lt;li&gt;对于索引组织表来说，如果每次在中间插入数据，可能会导致索引分裂，索引分裂会大大降低插入的性能。所以对于使用innodb来说，我们一般最好让主键是一个无意义的序列，这样插入每次都发生在最后，以避免这个问题。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;由于索引组织表是按一个索引树，一般它访问数据块必须按数据块之间的关系进行访问，而不是按物理块的访问数据的，所以当做全表扫描时要比堆表慢很多，这可能在OLTP中不明显，但在数据仓库的应用中可能是一个问题。&lt;/p&gt;
 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;MySQL从一开始就没有打算做所有事情，因而它在功能方面有一定的局限性，并不能满足一些先进应用程序的要求。MySQL对某些功能（例如引用、事务、审计等）的实现方式使得它与其他的关系型数据库相比缺少了一些可靠性。对于简单繁重的读取操作，使用PostgreSQL可能有点小题大做，同时性能也比MySQL这样的同类产品要差。除非你需要绝对的数据完整性，ACID遵从性或者设计复杂，否则PostgreSQL对于简单的场景而言有点多余。&lt;/p&gt;
 &lt;p&gt;如何你确定只在MySQL和PostgreSQL中进行选择，以下规则总是有效的：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果你的操作系统是Windows，你应该使用MySQL。&lt;/li&gt;
  &lt;li&gt;当绝对需要可靠性和数据完整性的时候，PostgreSQL是更好的选择。&lt;/li&gt;
  &lt;li&gt;如果需要数据库执行定制程序，那么可扩展的PostgreSQL是更好的选择。&lt;/li&gt;
  &lt;li&gt;你的应用处理的是地理数据，由于R-TREES的存在，你应该使用PostgreSQL。&lt;/li&gt;
  &lt;li&gt;如果你对数据库并不了十分了解，甚至不知道事务、存储过程等究竟是什么，你应该使用MySQL。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;以上内容只是个人观点，如果你有不同的看法可以回复评论。&lt;/p&gt;
 &lt;p&gt;The post   &lt;a href="https://www.biaodianfu.com/mysql-vs-postgresql.html" rel="nofollow"&gt;MySQL与PostgreSQL相比哪个更好？&lt;/a&gt; appeared first on   &lt;a href="https://www.biaodianfu.com" rel="nofollow"&gt;标点符&lt;/a&gt;.&lt;/p&gt;
 &lt;div&gt;
  &lt;p&gt;Related posts:   &lt;ol&gt;
    &lt;li&gt;     &lt;a href="https://www.biaodianfu.com/mysql-myisam-innodb.html" rel="bookmark" title="MySQL&amp;#23384;&amp;#20648;&amp;#24341;&amp;#25806;&amp;#20013;&amp;#30340;MyISAM&amp;#21644;InnoDB"&gt;MySQL存储引擎中的MyISAM和InnoDB &lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="https://www.biaodianfu.com/postgresql.html" rel="bookmark" title="PostgreSQL&amp;#30340;&amp;#31616;&amp;#21333;&amp;#20171;&amp;#32461;"&gt;PostgreSQL的简单介绍 &lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="https://www.biaodianfu.com/mysql-percona-or-mariadb.html" rel="bookmark" title="MySQL&amp;#20998;&amp;#25903;&amp;#30340;&amp;#36873;&amp;#25321;&amp;#65306;Percona&amp;#36824;&amp;#26159;MariaDB"&gt;MySQL分支的选择：Percona还是MariaDB &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>程序开发 MySQL PostgreSQL</category>
      <guid isPermaLink="true">https://itindex.net/detail/56471-mysql-postgresql</guid>
      <pubDate>Thu, 29 Dec 2016 12:08:38 CST</pubDate>
    </item>
    <item>
      <title>MySQL 大表优化方案</title>
      <link>https://itindex.net/detail/56424-mysql-%E5%A4%A7%E8%A1%A8-%E4%BC%98%E5%8C%96</link>
      <description>&lt;p&gt;当MySQL单表记录数过大时，增删改查性能都会急剧下降，可以参考以下步骤来优化：&lt;/p&gt;
 &lt;h3&gt;单表优化&lt;/h3&gt;
 &lt;p&gt;除非单表数据未来会一直不断上涨，否则不要一开始就考虑拆分，拆分会带来逻辑、部署、运维的各种复杂度，一般以整型值为主的表在  &lt;code&gt;千万级&lt;/code&gt;以下，字符串为主的表在  &lt;code&gt;五百万&lt;/code&gt;以下是没有太大问题的。而事实上很多时候MySQL单表的性能依然有不少优化空间，甚至能正常支撑千万级以上的数据量：&lt;/p&gt;
 &lt;h4&gt;字段&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;尽量使用   &lt;code&gt;TINYINT&lt;/code&gt;、   &lt;code&gt;SMALLINT&lt;/code&gt;、   &lt;code&gt;MEDIUM_INT&lt;/code&gt;作为整数类型而非   &lt;code&gt;INT&lt;/code&gt;，如果非负则加上   &lt;code&gt;UNSIGNED&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;VARCHAR&lt;/code&gt;的长度只分配真正需要的空间&lt;/li&gt;
  &lt;li&gt;使用枚举或整数代替字符串类型&lt;/li&gt;
  &lt;li&gt;尽量使用   &lt;code&gt;TIMESTAMP&lt;/code&gt;而非   &lt;code&gt;DATETIME&lt;/code&gt;，&lt;/li&gt;
  &lt;li&gt;单表不要有太多字段，建议在20以内&lt;/li&gt;
  &lt;li&gt;避免使用NULL字段，很难查询优化且占用额外索引空间&lt;/li&gt;
  &lt;li&gt;用整型来存IP&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;索引&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;索引并不是越多越好，要根据查询有针对性的创建，考虑在   &lt;code&gt;WHERE&lt;/code&gt;和   &lt;code&gt;ORDER BY&lt;/code&gt;命令上涉及的列建立索引，可根据   &lt;code&gt;EXPLAIN&lt;/code&gt;来查看是否用了索引还是全表扫描&lt;/li&gt;
  &lt;li&gt;应尽量避免在   &lt;code&gt;WHERE&lt;/code&gt;子句中对字段进行   &lt;code&gt;NULL&lt;/code&gt;值判断，否则将导致引擎放弃使用索引而进行全表扫描&lt;/li&gt;
  &lt;li&gt;值分布很稀少的字段不适合建索引，例如”性别”这种只有两三个值的字段&lt;/li&gt;
  &lt;li&gt;字符字段只建前缀索引&lt;/li&gt;
  &lt;li&gt;字符字段最好不要做主键&lt;/li&gt;
  &lt;li&gt;不用外键，由程序保证约束&lt;/li&gt;
  &lt;li&gt;尽量不用   &lt;code&gt;UNIQUE&lt;/code&gt;，由程序保证约束&lt;/li&gt;
  &lt;li&gt;使用多列索引时主意顺序和查询条件保持一致，同时删除不必要的单列索引&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;查询SQL&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;可通过开启慢查询日志来找出较慢的SQL&lt;/li&gt;
  &lt;li&gt;不做列运算：   &lt;code&gt;SELECT id WHERE age + 1 = 10&lt;/code&gt;，任何对列的操作都将导致表扫描，它包括数据库教程函数、计算表达式等等，查询时要尽可能将操作移至等号右边&lt;/li&gt;
  &lt;li&gt;sql语句尽可能简单：一条sql只能在一个cpu运算；大语句拆小语句，减少锁时间；一条大sql可以堵死整个库&lt;/li&gt;
  &lt;li&gt;不用   &lt;code&gt;SELECT *&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;OR&lt;/code&gt;改写成   &lt;code&gt;IN&lt;/code&gt;：   &lt;code&gt;OR&lt;/code&gt;的效率是n级别，   &lt;code&gt;IN&lt;/code&gt;的效率是log(n)级别，in的个数建议控制在200以内&lt;/li&gt;
  &lt;li&gt;不用函数和触发器，在应用程序实现&lt;/li&gt;
  &lt;li&gt;避免   &lt;code&gt;%xxx&lt;/code&gt;式查询&lt;/li&gt;
  &lt;li&gt;少用   &lt;code&gt;JOIN&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;使用同类型进行比较，比如用   &lt;code&gt;&amp;apos;123&amp;apos;&lt;/code&gt;和   &lt;code&gt;&amp;apos;123&amp;apos;&lt;/code&gt;比，   &lt;code&gt;123&lt;/code&gt;和   &lt;code&gt;123&lt;/code&gt;比&lt;/li&gt;
  &lt;li&gt;尽量避免在   &lt;code&gt;WHERE&lt;/code&gt;子句中使用 != 或 &amp;lt;&amp;gt; 操作符，否则将引擎放弃使用索引而进行全表扫描&lt;/li&gt;
  &lt;li&gt;对于连续数值，使用   &lt;code&gt;BETWEEN&lt;/code&gt;不用   &lt;code&gt;IN&lt;/code&gt;：   &lt;code&gt;SELECT id FROM t WHERE num BETWEEN 1 AND 5&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;列表数据不要拿全表，要使用   &lt;code&gt;LIMIT&lt;/code&gt;来分页，每页数量也不要太大&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;引擎&lt;/h4&gt;
 &lt;p&gt;目前广泛使用的是MyISAM和InnoDB两种引擎：&lt;/p&gt;
 &lt;h5&gt;MyISAM&lt;/h5&gt;
 &lt;p&gt;MyISAM引擎是MySQL 5.1及之前版本的默认引擎，它的特点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;不支持行锁，读取时对需要读到的所有表加锁，写入时则对表加排它锁&lt;/li&gt;
  &lt;li&gt;不支持事务&lt;/li&gt;
  &lt;li&gt;不支持外键&lt;/li&gt;
  &lt;li&gt;不支持崩溃后的安全恢复&lt;/li&gt;
  &lt;li&gt;在表有读取查询的同时，支持往表中插入新纪录&lt;/li&gt;
  &lt;li&gt;支持   &lt;code&gt;BLOB&lt;/code&gt;和   &lt;code&gt;TEXT&lt;/code&gt;的前500个字符索引，支持全文索引&lt;/li&gt;
  &lt;li&gt;支持延迟更新索引，极大提升写入性能&lt;/li&gt;
  &lt;li&gt;对于不会进行修改的表，支持压缩表，极大减少磁盘空间占用&lt;/li&gt;
&lt;/ul&gt;
 &lt;h5&gt;InnoDB&lt;/h5&gt;
 &lt;p&gt;InnoDB在MySQL 5.5后成为默认索引，它的特点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持行锁，采用MVCC来支持高并发&lt;/li&gt;
  &lt;li&gt;支持事务&lt;/li&gt;
  &lt;li&gt;支持外键&lt;/li&gt;
  &lt;li&gt;支持崩溃后的安全恢复&lt;/li&gt;
  &lt;li&gt;不支持全文索引&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;总体来讲，MyISAM适合  &lt;code&gt;SELECT&lt;/code&gt;密集型的表，而InnoDB适合  &lt;code&gt;INSERT&lt;/code&gt;和  &lt;code&gt;UPDATE&lt;/code&gt;密集型的表&lt;/p&gt;
 &lt;h4&gt;系统调优参数&lt;/h4&gt;
 &lt;p&gt;可以使用下面几个工具来做基准测试：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://github.com/akopytov/sysbench"&gt;sysbench&lt;/a&gt;：一个模块化，跨平台以及多线程的性能测试工具&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/tmcallaghan/iibench-mysql"&gt;iibench-mysql&lt;/a&gt;：基于 Java 的 MySQL/Percona/MariaDB 索引进行插入性能测试工具&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/Percona-Lab/tpcc-mysql"&gt;tpcc-mysql&lt;/a&gt;：Percona开发的TPC-C测试工具&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;具体的调优参数内容较多，具体可参考官方文档，这里介绍一些比较重要的参数：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;back_log：back_log值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。也就是说，如果MySql的连接数据达到max_connections时，新来的请求将会被存在堆栈中，以等待某一连接释放资源，该堆栈的数量即back_log，如果等待连接的数量超过back_log，将不被授予连接资源。可以从默认的50升至500&lt;/li&gt;
  &lt;li&gt;wait_timeout：数据库连接闲置时间，闲置连接会占用内存资源。可以从默认的8小时减到半小时&lt;/li&gt;
  &lt;li&gt;max_user_connection: 最大连接数，默认为0无上限，最好设一个合理上限&lt;/li&gt;
  &lt;li&gt;thread_concurrency：并发线程数，设为CPU核数的两倍&lt;/li&gt;
  &lt;li&gt;skip_name_resolve：禁止对外部连接进行DNS解析，消除DNS解析时间，但需要所有远程主机用IP访问&lt;/li&gt;
  &lt;li&gt;key_buffer_size：索引块的缓存大小，增加会提升索引处理速度，对MyISAM表性能影响最大。对于内存4G左右，可设为256M或384M，通过查询   &lt;code&gt;show status like &amp;apos;key_read%&amp;apos;&lt;/code&gt;，保证   &lt;code&gt;key_reads / key_read_requests&lt;/code&gt;在0.1%以下最好&lt;/li&gt;
  &lt;li&gt;innodb_buffer_pool_size：缓存数据块和索引块，对InnoDB表性能影响最大。通过查询   &lt;code&gt;show status like &amp;apos;Innodb_buffer_pool_read%&amp;apos;&lt;/code&gt;，保证   &lt;code&gt; (Innodb_buffer_pool_read_requests – Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests&lt;/code&gt;越高越好&lt;/li&gt;
  &lt;li&gt;innodb_additional_mem_pool_size：InnoDB存储引擎用来存放数据字典信息以及一些内部数据结构的内存空间大小，当数据库对象非常多的时候，适当调整该参数的大小以确保所有数据都能存放在内存中提高访问效率，当过小的时候，MySQL会记录Warning信息到数据库的错误日志中，这时就需要该调整这个参数大小&lt;/li&gt;
  &lt;li&gt;innodb_log_buffer_size：InnoDB存储引擎的事务日志所使用的缓冲区，一般来说不建议超过32MB&lt;/li&gt;
  &lt;li&gt;query_cache_size：缓存MySQL中的ResultSet，也就是一条SQL语句执行的结果集，所以仅仅只能针对select语句。当某个表的数据有任何任何变化，都会导致所有引用了该表的select语句在Query Cache中的缓存数据失效。所以，当我们的数据变化非常频繁的情况下，使用Query Cache可能会得不偿失。根据命中率   &lt;code&gt;(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))&lt;/code&gt;进行调整，一般不建议太大，256MB可能已经差不多了，大型的配置型静态数据可适当调大.   &lt;br /&gt;
可以通过命令   &lt;code&gt;show status like &amp;apos;Qcache_%&amp;apos;&lt;/code&gt;查看目前系统Query catch使用大小&lt;/li&gt;
  &lt;li&gt;read_buffer_size：MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区，MySql会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁，可以通过增加该变量值以及内存缓冲区大小提高其性能&lt;/li&gt;
  &lt;li&gt;sort_buffer_size：MySql执行排序使用的缓冲大小。如果想要增加   &lt;code&gt;ORDER BY&lt;/code&gt;的速度，首先看是否可以让MySQL使用索引而不是额外的排序阶段。如果不能，可以尝试增加sort_buffer_size变量的大小&lt;/li&gt;
  &lt;li&gt;read_rnd_buffer_size：MySql的随机读缓冲区大小。当按任意顺序读取行时(例如，按照排序顺序)，将分配一个随机读缓存区。进行排序查询时，MySql会首先扫描一遍该缓冲，以避免磁盘搜索，提高查询速度，如果需要排序大量数据，可适当调高该值。但MySql会为每个客户连接发放该缓冲空间，所以应尽量适当设置该值，以避免内存开销过大。&lt;/li&gt;
  &lt;li&gt;record_buffer：每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描，可能想要增加该值&lt;/li&gt;
  &lt;li&gt;thread_cache_size：保存当前没有与连接关联但是准备为后面新的连接服务的线程，可以快速响应连接的线程请求而无需创建新的&lt;/li&gt;
  &lt;li&gt;table_cache：类似于thread_cache_size，但用来缓存表文件，对InnoDB效果不大，主要用于MyISAM&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;升级硬件&lt;/h4&gt;
 &lt;p&gt;Scale up，这个不多说了，根据MySQL是CPU密集型还是I/O密集型，通过提升CPU和内存、使用SSD，都能显著提升MySQL性能&lt;/p&gt;
 &lt;h3&gt;读写分离&lt;/h3&gt;
 &lt;p&gt;也是目前常用的优化，从库读主库写，一般不要采用双主或多主引入很多复杂性，尽量采用文中的其他方案来提高性能。同时目前很多拆分的解决方案同时也兼顾考虑了读写分离&lt;/p&gt;
 &lt;h3&gt;缓存&lt;/h3&gt;
 &lt;p&gt;缓存可以发生在这些层次：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;MySQL内部：在   &lt;a&gt;系统调优参数&lt;/a&gt;介绍了相关设置&lt;/li&gt;
  &lt;li&gt;数据访问层：比如MyBatis针对SQL语句做缓存，而Hibernate可以精确到单个记录，这里缓存的对象主要是持久化对象   &lt;code&gt;Persistence Object&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;应用服务层：这里可以通过编程手段对缓存做到更精准的控制和更多的实现策略，这里缓存的对象是数据传输对象   &lt;code&gt;Data Transfer Object&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Web层：针对web页面做缓存&lt;/li&gt;
  &lt;li&gt;浏览器客户端：用户端的缓存&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;可以根据实际情况在一个层次或多个层次结合加入缓存。这里重点介绍下服务层的缓存实现，目前主要有两种方式：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;直写式（Write Through）：在数据写入数据库后，同时更新缓存，维持数据库与缓存的一致性。这也是当前大多数应用缓存框架如Spring Cache的工作方式。这种实现非常简单，同步好，但效率一般。&lt;/li&gt;
  &lt;li&gt;回写式（Write Back）：当有数据要写入数据库时，只会更新缓存，然后异步批量的将缓存数据同步到数据库上。这种实现比较复杂，需要较多的应用逻辑，同时可能会产生数据库与缓存的不同步，但效率非常高。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;表分区&lt;/h3&gt;
 &lt;p&gt;MySQL在5.1版引入的分区是一种简单的水平拆分，用户需要在建表的时候加上分区参数，对应用是透明的无需修改代码&lt;/p&gt;
 &lt;p&gt;对用户来说，分区表是一个独立的逻辑表，但是底层由多个物理子表组成，实现分区的代码实际上是通过对一组底层表的对象封装，但对SQL层来说是一个完全封装底层的黑盒子。MySQL实现分区的方式也意味着索引也是按照分区的子表定义，没有全局索引&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://segmentfault.com/img/remote/1460000006767126"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;用户的SQL语句是需要针对分区表做优化，SQL条件中要带上分区条件的列，从而使查询定位到少量的分区上，否则就会扫描全部分区，可以通过  &lt;code&gt;EXPLAIN PARTITIONS&lt;/code&gt;来查看某条SQL语句会落在那些分区上，从而进行SQL优化，如下图5条记录落在两个分区上：&lt;/p&gt; &lt;pre&gt;mysql&amp;gt; explain partitions select count(1) from user_partition where id in (1,2,3,4,5);
+----+-------------+----------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table          | partitions | type  | possible_keys | key     | key_len | ref  | rows | Extra                    |
+----+-------------+----------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
|  1 | SIMPLE      | user_partition | p1,p4      | range | PRIMARY       | PRIMARY | 8       | NULL |    5 | Using where; Using index |
+----+-------------+----------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
1 row in set (0.00 sec)&lt;/pre&gt; &lt;p&gt;分区的好处是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;可以让单表存储更多的数据&lt;/li&gt;
  &lt;li&gt;分区表的数据更容易维护，可以通过清楚整个分区批量删除大量数据，也可以增加新的分区来支持新插入的数据。另外，还可以对一个独立分区进行优化、检查、修复等操作&lt;/li&gt;
  &lt;li&gt;部分查询能够从查询条件确定只落在少数分区上，速度会很快&lt;/li&gt;
  &lt;li&gt;分区表的数据还可以分布在不同的物理设备上，从而搞笑利用多个硬件设备&lt;/li&gt;
  &lt;li&gt;可以使用分区表赖避免某些特殊瓶颈，例如InnoDB单个索引的互斥访问、ext3文件系统的inode锁竞争&lt;/li&gt;
  &lt;li&gt;可以备份和恢复单个分区&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;分区的限制和缺点：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;一个表最多只能有1024个分区&lt;/li&gt;
  &lt;li&gt;如果分区字段中有主键或者唯一索引的列，那么所有主键列和唯一索引列都必须包含进来&lt;/li&gt;
  &lt;li&gt;分区表无法使用外键约束&lt;/li&gt;
  &lt;li&gt;NULL值会使分区过滤无效&lt;/li&gt;
  &lt;li&gt;所有分区必须使用相同的存储引擎&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;分区的类型：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;RANGE分区：基于属于一个给定连续区间的列值，把多行分配给分区&lt;/li&gt;
  &lt;li&gt;LIST分区：类似于按RANGE分区，区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择&lt;/li&gt;
  &lt;li&gt;HASH分区：基于用户定义的表达式的返回值来进行选择的分区，该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式&lt;/li&gt;
  &lt;li&gt;KEY分区：类似于按HASH分区，区别在于KEY分区只支持计算一列或多列，且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;分区适合的场景有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;最适合的场景数据的时间序列性比较强，则可以按时间来分区，如下所示：&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;&lt;/p&gt; &lt;pre&gt;CREATE TABLE members (
    firstname VARCHAR(25) NOT NULL,
    lastname VARCHAR(25) NOT NULL,
    username VARCHAR(16) NOT NULL,
    email VARCHAR(35),
    joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (
    PARTITION p0 VALUES LESS THAN (1960),
    PARTITION p1 VALUES LESS THAN (1970),
    PARTITION p2 VALUES LESS THAN (1980),
    PARTITION p3 VALUES LESS THAN (1990),
    PARTITION p4 VALUES LESS THAN MAXVALUE
);&lt;/pre&gt; &lt;p&gt;查询时加上时间范围条件效率会非常高，同时对于不需要的历史数据能很容的批量删除。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果数据有明显的热点，而且除了这部分数据，其他数据很少被访问到，那么可以将热点数据单独放在一个分区，让这个分区的数据能够有机会都缓存在内存中，查询时只访问一个很小的分区表，能够有效使用索引和缓存&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;另外MySQL有一种早期的简单的分区实现 – 合并表（merge table），限制较多且缺乏优化，不建议使用，应该用新的分区机制来替代&lt;/p&gt;
 &lt;h3&gt;垂直拆分&lt;/h3&gt;
 &lt;p&gt;垂直分库是根据数据库里面的数据表的相关性进行拆分，比如：一个数据库里面既存在用户数据，又存在订单数据，那么垂直拆分可以把用户数据放到用户库、把订单数据放到订单库。垂直分表是对数据表进行垂直拆分的一种方式，常见的是把一个多字段的大表按常用字段和非常用字段进行拆分，每个表里面的数据记录数一般情况下是相同的，只是字段不一样，使用主键关联&lt;/p&gt;
 &lt;p&gt;比如原始的用户表是：&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://segmentfault.com/img/remote/1460000006158196"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;垂直拆分后是：&lt;/p&gt;
 &lt;p&gt;  &lt;img src="http://jbcdn2.b0.upaiyun.com/2016/12/f066fbd7e88ba7c70bbbc660023cbde9.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;垂直拆分的优点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;可以使得行数据变小，一个数据块(Block)就能存放更多的数据，在查询时就会减少I/O次数(每次查询时读取的Block 就少)&lt;/li&gt;
  &lt;li&gt;可以达到最大化利用Cache的目的，具体在垂直拆分的时候可以将不常变的字段放一起，将经常改变的放一起&lt;/li&gt;
  &lt;li&gt;数据维护简单&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;缺点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;主键出现冗余，需要管理冗余列&lt;/li&gt;
  &lt;li&gt;会引起表连接JOIN操作（增加CPU开销）可以通过在业务服务器上进行join来减少数据库压力&lt;/li&gt;
  &lt;li&gt;依然存在单表数据量过大的问题（需要水平拆分）&lt;/li&gt;
  &lt;li&gt;事务处理复杂&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;水平拆分&lt;/h3&gt;
 &lt;h4&gt;概述&lt;/h4&gt;
 &lt;p&gt;水平拆分是通过某种策略将数据分片来存储，分库内分表和分库两部分，每片数据会分散到不同的MySQL表或库，达到分布式的效果，能够支持非常大的数据量。前面的表分区本质上也是一种特殊的库内分表&lt;/p&gt;
 &lt;p&gt;库内分表，仅仅是单纯的解决了单一表数据过大的问题，由于没有把表的数据分布到不同的机器上，因此对于减轻MySQL服务器的压力来说，并没有太大的作用，大家还是竞争同一个物理机上的IO、CPU、网络，这个就要通过分库来解决&lt;/p&gt;
 &lt;p&gt;前面垂直拆分的用户表如果进行水平拆分，结果是：&lt;/p&gt;
 &lt;p&gt;  &lt;img src="http://jbcdn2.b0.upaiyun.com/2016/12/310d92e0b41e4d36ef4aa160c76261a6.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;实际情况中往往会是垂直拆分和水平拆分的结合，即将  &lt;code&gt;Users_A_M&lt;/code&gt;和  &lt;code&gt;Users_N_Z&lt;/code&gt;再拆成  &lt;code&gt;Users&lt;/code&gt;和  &lt;code&gt;UserExtras&lt;/code&gt;，这样一共四张表&lt;/p&gt;
 &lt;p&gt;水平拆分的优点是:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;不存在单库大数据和高并发的性能瓶颈&lt;/li&gt;
  &lt;li&gt;应用端改造较少&lt;/li&gt;
  &lt;li&gt;提高了系统的稳定性和负载能力&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;缺点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;分片事务一致性难以解决&lt;/li&gt;
  &lt;li&gt;跨节点Join性能差，逻辑复杂&lt;/li&gt;
  &lt;li&gt;数据多次扩展难度跟维护量极大&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;分片原则&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;能不分就不分，参考   &lt;a&gt;单表优化&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;分片数量尽量少，分片尽量均匀分布在多个数据结点上，因为一个查询SQL跨分片越多，则总体性能越差，虽然要好于所有数据在一个分片的结果，只在必要的时候进行扩容，增加分片数量&lt;/li&gt;
  &lt;li&gt;分片规则需要慎重选择做好提前规划，分片规则的选择，需要考虑数据的增长模式，数据的访问模式，分片关联性问题，以及分片扩容问题，最近的分片策略为范围分片，枚举分片，一致性Hash分片，这几种分片都有利于扩容&lt;/li&gt;
  &lt;li&gt;尽量不要在一个事务中的SQL跨越多个分片，分布式事务一直是个不好处理的问题&lt;/li&gt;
  &lt;li&gt;查询条件尽量优化，尽量避免Select * 的方式，大量数据结果集下，会消耗大量带宽和CPU资源，查询尽量避免返回大量结果集，并且尽量为频繁使用的查询语句建立索引。&lt;/li&gt;
  &lt;li&gt;通过数据冗余和表分区赖降低跨库Join的可能&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;这里特别强调一下分片规则的选择问题，如果某个表的数据有明显的时间特征，比如订单、交易记录等，则他们通常比较合适用时间范围分片，因为具有时效性的数据，我们往往关注其近期的数据，查询条件中往往带有时间字段进行过滤，比较好的方案是，当前活跃的数据，采用跨度比较短的时间段进行分片，而历史性的数据，则采用比较长的跨度存储。&lt;/p&gt;
 &lt;p&gt;总体上来说，分片的选择是取决于最频繁的查询SQL的条件，因为不带任何Where语句的查询SQL，会遍历所有的分片，性能相对最差，因此这种SQL越多，对系统的影响越大，所以我们要尽量避免这种SQL的产生。&lt;/p&gt;
 &lt;h4&gt;解决方案&lt;/h4&gt;
 &lt;p&gt;由于水平拆分牵涉的逻辑比较复杂，当前也有了不少比较成熟的解决方案。这些方案分为两大类：客户端架构和代理架构。&lt;/p&gt;
 &lt;h5&gt;客户端架构&lt;/h5&gt;
 &lt;p&gt;通过修改数据访问层，如JDBC、Data Source、MyBatis，通过配置来管理多个数据源，直连数据库，并在模块内完成数据的分片整合，一般以Jar包的方式呈现&lt;/p&gt;
 &lt;p&gt;这是一个客户端架构的例子：&lt;/p&gt;
 &lt;p&gt;  &lt;img src="http://jbcdn2.b0.upaiyun.com/2016/12/94e00559495cedbc8e8cd2b9f4bfe65f.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;可以看到分片的实现是和应用服务器在一起的，通过修改Spring JDBC层来实现&lt;/p&gt;
 &lt;p&gt;客户端架构的优点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;应用直连数据库，降低外围系统依赖所带来的宕机风险&lt;/li&gt;
  &lt;li&gt;集成成本低，无需额外运维的组件&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;缺点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;限于只能在数据库访问层上做文章，扩展性一般，对于比较复杂的系统可能会力不从心&lt;/li&gt;
  &lt;li&gt;将分片逻辑的压力放在应用服务器上，造成额外风险&lt;/li&gt;
&lt;/ul&gt;
 &lt;h5&gt;代理架构&lt;/h5&gt;
 &lt;p&gt;通过独立的中间件来统一管理所有数据源和数据分片整合，后端数据库集群对前端应用程序透明，需要独立部署和运维代理组件&lt;/p&gt;
 &lt;p&gt;这是一个代理架构的例子：&lt;/p&gt;
 &lt;p&gt;  &lt;img src="http://jbcdn2.b0.upaiyun.com/2016/12/947bcb039f358368024ca4ccd70a880f.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;代理组件为了分流和防止单点，一般以集群形式存在，同时可能需要Zookeeper之类的服务组件来管理&lt;/p&gt;
 &lt;p&gt;代理架构的优点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;能够处理非常复杂的需求，不受数据库访问层原来实现的限制，扩展性强&lt;/li&gt;
  &lt;li&gt;对于应用服务器透明且没有增加任何额外负载&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;缺点是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;需部署和运维独立的代理中间件，成本高&lt;/li&gt;
  &lt;li&gt;应用需经过代理来连接数据库，网络上多了一跳，性能有损失且有额外风险&lt;/li&gt;
&lt;/ul&gt;
 &lt;h5&gt;各方案比较&lt;/h5&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;&lt;/th&gt;
   &lt;th&gt;出品方&lt;/th&gt;
   &lt;th&gt;架构模型&lt;/th&gt;
   &lt;th&gt;支持数据库&lt;/th&gt;
   &lt;th&gt;分库&lt;/th&gt;
   &lt;th&gt;分表&lt;/th&gt;
   &lt;th&gt;读写分离&lt;/th&gt;
   &lt;th&gt;外部依赖&lt;/th&gt;
   &lt;th&gt;是否开源&lt;/th&gt;
   &lt;th&gt;实现语言&lt;/th&gt;
   &lt;th&gt;支持语言&lt;/th&gt;
   &lt;th&gt;最后更新&lt;/th&gt;
   &lt;th&gt;Github星数&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://www.mysql.com/products/enterprise/fabric.html"&gt;MySQL Fabric&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;MySQL官方&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;python&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;4个月前&lt;/td&gt;
   &lt;td&gt;35&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/alibaba/cobar"&gt;Cobar&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;阿里巴巴&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;两年前&lt;/td&gt;
   &lt;td&gt;1287&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/alibaba/cobarclient"&gt;Cobar Client&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;阿里巴巴&lt;/td&gt;
   &lt;td&gt;客户端架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;三年前&lt;/td&gt;
   &lt;td&gt;344&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/alibaba/tb_tddl"&gt;TDDL&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;淘宝&lt;/td&gt;
   &lt;td&gt;客户端架构&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;Diamond&lt;/td&gt;
   &lt;td&gt;只开源部分&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;未知&lt;/td&gt;
   &lt;td&gt;519&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/Qihoo360/Atlas"&gt;Atlas&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;奇虎360&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;C&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;10个月前&lt;/td&gt;
   &lt;td&gt;1941&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/brucexx/heisenberg"&gt;Heisenberg&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;百度熊照&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;2个月前&lt;/td&gt;
   &lt;td&gt;197&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/jojoin/TribeDB"&gt;TribeDB&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;个人&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;NodeJS&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;3个月前&lt;/td&gt;
   &lt;td&gt;126&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/dangdangdotcom/sharding-jdbc"&gt;ShardingJDBC&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;当当&lt;/td&gt;
   &lt;td&gt;客户端架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;当天&lt;/td&gt;
   &lt;td&gt;1144&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/gaoxianglong/shark"&gt;Shark&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;个人&lt;/td&gt;
   &lt;td&gt;客户端架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;两天前&lt;/td&gt;
   &lt;td&gt;84&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/flike/kingshard"&gt;KingShard&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;个人&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Golang&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;两天前&lt;/td&gt;
   &lt;td&gt;1836&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="http://www.onexsoft.com/?page_id=3383"&gt;OneProxy&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;平民软件&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;否&lt;/td&gt;
   &lt;td&gt;未知&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;未知&lt;/td&gt;
   &lt;td&gt;未知&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="http://mycat.io/"&gt;MyCat&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;社区&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;两天前&lt;/td&gt;
   &lt;td&gt;1270&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/youtube/vitess"&gt;Vitess&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;Youtube&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Golang&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;当天&lt;/td&gt;
   &lt;td&gt;3636&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/siddontang/mixer"&gt;Mixer&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;个人&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Golang&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;9个月前&lt;/td&gt;
   &lt;td&gt;472&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/tumblr/jetpants"&gt;JetPants&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;Tumblr&lt;/td&gt;
   &lt;td&gt;客户端架构&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Ruby&lt;/td&gt;
   &lt;td&gt;Ruby&lt;/td&gt;
   &lt;td&gt;10个月前&lt;/td&gt;
   &lt;td&gt;957&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/hibernate/hibernate-shards"&gt;HibernateShard&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;Hibernate&lt;/td&gt;
   &lt;td&gt;客户端架构&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;4年前&lt;/td&gt;
   &lt;td&gt;57&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/makersoft/mybatis-shards"&gt;MybatisShard&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;MakerSoft&lt;/td&gt;
   &lt;td&gt;客户端架构&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;11个月前&lt;/td&gt;
   &lt;td&gt;119&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;a href="https://github.com/twitter/gizzard"&gt;Gizzard&lt;/a&gt;&lt;/td&gt;
   &lt;td&gt;Twitter&lt;/td&gt;
   &lt;td&gt;代理架构&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;有&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;是&lt;/td&gt;
   &lt;td&gt;Java&lt;/td&gt;
   &lt;td&gt;无限制&lt;/td&gt;
   &lt;td&gt;3年前&lt;/td&gt;
   &lt;td&gt;2087&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;如此多的方案，如何进行选择？可以按以下思路来考虑：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;确定是使用代理架构还是客户端架构。中小型规模或是比较简单的场景倾向于选择客户端架构，复杂场景或大规模系统倾向选择代理架构&lt;/li&gt;
  &lt;li&gt;具体功能是否满足，比如需要跨节点   &lt;code&gt;ORDER BY&lt;/code&gt;，那么支持该功能的优先考虑&lt;/li&gt;
  &lt;li&gt;不考虑一年内没有更新的产品，说明开发停滞，甚至无人维护和技术支持&lt;/li&gt;
  &lt;li&gt;最好按大公司-&amp;gt;社区-&amp;gt;小公司-&amp;gt;个人这样的出品方顺序来选择&lt;/li&gt;
  &lt;li&gt;选择口碑较好的，比如github星数、使用者数量质量和使用者反馈&lt;/li&gt;
  &lt;li&gt;开源的优先，往往项目有特殊需求可能需要改动源代码&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;按照上述思路，推荐以下选择：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;客户端架构：ShardingJDBC&lt;/li&gt;
  &lt;li&gt;代理架构：MyCat或者Atlas&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;兼容MySQL且可水平扩展的数据库&lt;/h3&gt;
 &lt;p&gt;目前也有一些开源数据库兼容MySQL协议，如：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://github.com/pingcap/tidb"&gt;TiDB&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://www.cubrid.org"&gt;Cubrid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;但其工业品质和MySQL尚有差距，且需要较大的运维投入，如果想将原始的MySQL迁移到可水平扩展的新数据库中，可以考虑一些云数据库：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;阿里云PetaData&lt;/li&gt;
  &lt;li&gt;阿里云OceanBase&lt;/li&gt;
  &lt;li&gt;腾讯云DCDB&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;NoSQL&lt;/h3&gt;
 &lt;p&gt;在MySQL上做Sharding是一种戴着镣铐的跳舞，事实上很多大表本身对MySQL这种RDBMS的需求并不大，并不要求ACID，可以考虑将这些表迁移到NoSQL，彻底解决水平扩展问题，例如：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;日志类、监控类、统计类数据&lt;/li&gt;
  &lt;li&gt;非结构化或弱结构化数据&lt;/li&gt;
  &lt;li&gt;对事务要求不强，且无太多关联操作的数据&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;a href="http://blog.jobbole.com/109242/"&gt;MySQL 大表优化方案&lt;/a&gt;，首发于  &lt;a href="http://blog.jobbole.com"&gt;文章 - 伯乐在线&lt;/a&gt;。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>IT技术 MySQL 数据库</category>
      <guid isPermaLink="true">https://itindex.net/detail/56424-mysql-%E5%A4%A7%E8%A1%A8-%E4%BC%98%E5%8C%96</guid>
      <pubDate>Fri, 23 Dec 2016 22:38:21 CST</pubDate>
    </item>
    <item>
      <title>数据仓库中的SQL性能优化（MySQL篇）</title>
      <link>https://itindex.net/detail/56815-%E6%95%B0%E6%8D%AE%E4%BB%93%E5%BA%93-sql-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96</link>
      <description>&lt;p&gt;做数据仓库的头两年，使用高配置单机 + MySQL的方式来实现所有的计算（包括数据的ETL，以及报表计算。没有OLAP）。用过MySQL自带的MYISAM和列存储引擎Infobright。这篇文章总结了自己和团队在那段时间碰到的一些常见性能问题和解决方案。&lt;/p&gt;
 &lt;p&gt;P.S.如果没有特别指出，下面说的mysql都是指用MYISAM做存储引擎。&lt;/p&gt;
 &lt;h1&gt;利用已有数据，避免重复计算&lt;/h1&gt; &lt;p&gt;业务需求中往往有计算一周/一个月的某某数据，比如计算最近一周某个特定页面的PV/UV。这里出现的问题就是实现的时候直接取整周的日志数据，然后进行计算。这样其实就出现了重复计算，某一天的数据在不同的日子里被重复计算了7次。&lt;/p&gt;
 &lt;p&gt;解决办法非常之简单，就是把计算进行切分，如果是算PV，做法就是每天算好当天的PV，那么一周的PV就把算好的7天的PV相加。如果是算UV，那么每天从日志数据取出相应的访客数据，把最近七天的访客数据单独保存在一个表里面，计算周UV的时候直接用这个表做计算，而不需要从原始日志数据中抓上一大把数据来算了。&lt;/p&gt;
 &lt;p&gt;这是一个非常简单的问题，甚至不需要多少SQL的知识，但是在开发过程中往往被视而不见。这就是只实现业务而忽略性能的表现。从小规模数据仓库做起的工程师，如果缺乏这方面的意识和做事规范，就容易出现这种问题，等到数据仓库的数据量变得比较大的时候，才会发现。&lt;/p&gt;
 &lt;h1&gt;case when关键字的使用方法&lt;/h1&gt; &lt;p&gt;  &lt;code&gt;case when&lt;/code&gt;这个关键字，在做聚合的时候，可以很方便的将一份数据在一个SQL语句中进行分类的统计。举个例子，比如下面有一张成绩表(表名定为  &lt;code&gt;scores&lt;/code&gt;)： &lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;name&lt;/th&gt;
   &lt;th&gt;course&lt;/th&gt;
   &lt;th&gt;score&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;小明&lt;/td&gt;
   &lt;td&gt;语文&lt;/td&gt;
   &lt;td&gt;90&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小张&lt;/td&gt;
   &lt;td&gt;语文&lt;/td&gt;
   &lt;td&gt;94&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小红&lt;/td&gt;
   &lt;td&gt;语文&lt;/td&gt;
   &lt;td&gt;95&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小明&lt;/td&gt;
   &lt;td&gt;数学&lt;/td&gt;
   &lt;td&gt;96&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小张&lt;/td&gt;
   &lt;td&gt;数学&lt;/td&gt;
   &lt;td&gt;98&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小红&lt;/td&gt;
   &lt;td&gt;数学&lt;/td&gt;
   &lt;td&gt;94&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小明&lt;/td&gt;
   &lt;td&gt;英语&lt;/td&gt;
   &lt;td&gt;99&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小张&lt;/td&gt;
   &lt;td&gt;英语&lt;/td&gt;
   &lt;td&gt;96&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小红&lt;/td&gt;
   &lt;td&gt;英语&lt;/td&gt;
   &lt;td&gt;93&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;现在需要统计小张的平均成绩，小明的平均成绩和小明的语文成绩。SQL实现如下：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select      &lt;br /&gt;        avg (case when name =&amp;apos;小张&amp;apos; then score end) as xz_avg_score,     &lt;br /&gt;        avg (case when name =&amp;apos;小明&amp;apos; then score end) as xm_avg_score,     &lt;br /&gt;        avg (case when name =&amp;apos;小明&amp;apos; and course = &amp;apos;语文&amp;apos; then score end) as xm_yuwen_score      &lt;br /&gt;from scores;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;如果现在这个成绩表有1200万条的数据，包含了400万的名字 * 3个科目，上面的计算需要多长时间？我做了一个简单的测试，答案是5.5秒。  &lt;br /&gt;而如果我们把sql改成下面的写法：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select      &lt;br /&gt;       avg (case when name =&amp;apos;小张&amp;apos; then score end) as xz_avg_score,     &lt;br /&gt;       avg (case when name =&amp;apos;小明&amp;apos; then score end) as xm_avg_score,     &lt;br /&gt;       avg (case when name =&amp;apos;小明&amp;apos; and course = &amp;apos;语文&amp;apos; then score end)      &lt;br /&gt;as xm_yuwen_score      &lt;br /&gt;from scores where name in (&amp;apos;小张&amp;apos;, &amp;apos;小明&amp;apos;);     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;这样的话，只需要3.3秒就能完成。&lt;/p&gt;
 &lt;p&gt;之所以后面一种写法总是比前面一种写法快，不同之处就在于是否先在  &lt;code&gt;where&lt;/code&gt;里面把数据过滤掉。前一种写法扫描了三遍全表的数据（做一个  &lt;code&gt;case when&lt;/code&gt;扫一遍），后面的写法扫描一遍全表，把数据过滤了之后，  &lt;code&gt;case when&lt;/code&gt;就不用过这么多数据量了。&lt;/p&gt;
 &lt;p&gt;跟进一步说，如果在name字段上有索引，那么后一种写法将会更快，测试结果只用0.05秒，而前面一种情况，sql优化器是判断不出来能用索引的，时间依然是5.5秒。&lt;/p&gt;
 &lt;p&gt;在实际工作中，开发经常只是为了实现功能逻辑，而习惯了在  &lt;code&gt;case when&lt;/code&gt;中限制条件取数据。这样在出现类似例子中的需求时，没有把应该限制的条件写到  &lt;code&gt;where&lt;/code&gt;里面。这是在实际代码中发现最多的一类问题。&lt;/p&gt;
 &lt;h1&gt;分页取数方式&lt;/h1&gt; &lt;p&gt;在数据仓库中有一个重要的基础步骤，就是对数据进行清洗。比如数据源的数据如果以JSON方式存储，在mysql的数据仓库就必须将json中需要的字段提取出来，做成单独的表字段。这个步骤用sql直接处理很麻烦，所以可以用主流编程语言（比如java）的json库进行解析。解析的时候需要读取数据，一次性读取进来是不可能的，所以要分批读取（相当于分页了）。&lt;/p&gt;
 &lt;p&gt;最初的实现方式就是标记住每次取数据的偏移量，然后一批批读取：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select json_obj from t limit 10000,10000;     &lt;br /&gt;select json_obj from t limit 20000,10000;     &lt;br /&gt;select json_obj from t limit 30000,10000;     &lt;br /&gt;/*略去很多行……*/     &lt;br /&gt;select json_obj from t limit 990000,10000;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;这样的代码，在开始几句sql的时候执行速度还行，但是到后面会越来越慢，因为每次要读取大量数据再丢弃，其实是一种浪费。&lt;/p&gt;
 &lt;p&gt;高效的实现方式，可以是用表中的主键进行分页。如果数据是按照主键排序的，那么可以是这样（这么做是要求主键的取值序列是连续的。假设主键的取值序列我们比较清楚，是从10001-1000000的连续值）：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select json_obj from t where t.id &amp;gt; 10000 limit 10000;     &lt;br /&gt;select json_obj from t where t.id &amp;gt; 20000 limit 10000;     &lt;br /&gt;select json_obj from t where t.id &amp;gt; 30000 limit 10000;     &lt;br /&gt;/*略去很多行……*/     &lt;br /&gt;select json_obj from t where t.id &amp;gt; 990000 limit 10000;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;就算数据不是按主键排序的，也可以通过限制主键的范围来分页。这样处理的话，主键的取值序列不连续也没有太大问题，就是每次拿到的数据会比理想中的少一些，反正是用在数据处理，不影响正确性：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select json_obj from t where t.id &amp;gt; 10000 and t.id &amp;lt;= 20000;     &lt;br /&gt;select json_obj from t where t.id &amp;gt; 20000 and t.id &amp;lt;= 30000;     &lt;br /&gt;select json_obj from t where t.id &amp;gt; 30000 and t.id &amp;lt;= 40000;     &lt;br /&gt;/*略去很多行……*/     &lt;br /&gt;select json_obj from t where t.id &amp;gt; 990000 and t.id &amp;lt;= 1000000;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;这样的话，由于主键上面有索引，取数据速度就不会受到数据的具体位置的影响了。&lt;/p&gt;
 &lt;h1&gt;索引使用&lt;/h1&gt; &lt;p&gt;索引的使用是关系数据库的SQL优化中一个非常重要的主题，也是一个常识性的东西。但是工程师在实际开发中往往是加完索引就觉得万事大吉了，也不去检查索引是否被正确的使用了，所以会经常出一些瞎猫碰到死耗子或者是似是而非的情况。&lt;/p&gt;
 &lt;h2&gt;索引调整&lt;/h2&gt; &lt;p&gt;前面说到开发人员在对索引的了解似是而非的时候只知道要加索引，而不知道为什么加。&lt;/p&gt;
 &lt;p&gt;比如现在有一个数据集，对应非常常见的网站统计场景。这个数据集有两个表组成，其中一个是流量表  &lt;code&gt;item_visits&lt;/code&gt;，每条记录表示某一个商品（item）被访问了一次，包括访问者的一些信息，比如用户id，用户名等等，有将近800万条数据。示例如下：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;item_id&lt;/th&gt;
   &lt;th&gt;visitor_id&lt;/th&gt;
   &lt;th&gt;visitor_name&lt;/th&gt;
   &lt;th&gt;visitor_city&lt;/th&gt;
   &lt;th&gt;url&lt;/th&gt;
   &lt;th&gt;……&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;55&lt;/td&gt;
   &lt;td&gt;用户001&lt;/td&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;10&lt;/td&gt;
   &lt;td&gt;245&lt;/td&gt;
   &lt;td&gt;用户002&lt;/td&gt;
   &lt;td&gt;2&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;3&lt;/td&gt;
   &lt;td&gt;2&lt;/td&gt;
   &lt;td&gt;用户003&lt;/td&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;10&lt;/td&gt;
   &lt;td&gt;148&lt;/td&gt;
   &lt;td&gt;用户004&lt;/td&gt;
   &lt;td&gt;3&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;3&lt;/td&gt;
   &lt;td&gt;75&lt;/td&gt;
   &lt;td&gt;用户005&lt;/td&gt;
   &lt;td&gt;4&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;7&lt;/td&gt;
   &lt;td&gt;422&lt;/td&gt;
   &lt;td&gt;用户006&lt;/td&gt;
   &lt;td&gt;4&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;3&lt;/td&gt;
   &lt;td&gt;10&lt;/td&gt;
   &lt;td&gt;用户007&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;另一个表是商品表  &lt;code&gt;items&lt;/code&gt;，包含1200多种商品，字段有商品名字和所属种类：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;item_id&lt;/th&gt;
   &lt;th&gt;item_name&lt;/th&gt;
   &lt;th&gt;item_type&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;iphone 5&lt;/td&gt;
   &lt;td&gt;手机&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;2&lt;/td&gt;
   &lt;td&gt;iphone 5s&lt;/td&gt;
   &lt;td&gt;手机&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;现在有一个需求，计算每个商品种类（item_type）被访问的次数。sql的实现不难：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select item_type, count(*) as visit_num     &lt;br /&gt;from items a     &lt;br /&gt;join item_visit b using (item_id)     &lt;br /&gt;group by item_type;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;开发人员知道，在join的时候，其中的一个表的join key要加索引，然后他发现  &lt;code&gt;visit&lt;/code&gt;表在  &lt;code&gt;item_id&lt;/code&gt;字段上已经有索引了，所以就打完收工了。到这里为止一切都没有问题。但是后来这个需求有改动，需要限制用户的城市是某个固定城市，比如  &lt;code&gt;visitor_city = 1&lt;/code&gt;，那么显然sql变成了：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select item_type, count(*) as visit_num     &lt;br /&gt;from items a     &lt;br /&gt;join item_visit b using (item_id)     &lt;br /&gt;where visitor_city = 1     &lt;br /&gt;group by item_type;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;开发人员按照需求修改sql之后对于索引的调整无动于衷，因为他觉得，我已经用上索引了呀。而实际上很明显的，只需要在  &lt;code&gt;visitor_city&lt;/code&gt;和  &lt;code&gt;items&lt;/code&gt;表的  &lt;code&gt;item_id&lt;/code&gt;上都加上索引，就能极大的减少时间。原因就在于开发人员“感觉”能用上索引，而且开发阶段试运行时间没问题就OK，并不关心是不是有更好的索引使用方式，甚至不确认是否用上了索引。在这样的一个真实案例中，原有的sql在后期出现了运行缓慢的现象，才逐渐被发掘出问题。&lt;/p&gt;
 &lt;h2&gt;覆盖索引&lt;/h2&gt; &lt;p&gt;针对上面那个需求（不限制city），假设现在两个表的的item_id字段都有索引，而且把  &lt;code&gt;count(*)&lt;/code&gt;换成  &lt;code&gt;count(visitor_city)&lt;/code&gt;，还是会得到完全一样的结果，但是会对执行时间有什么影响？&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select item_type, count(visitor_city) as visit_num     &lt;br /&gt;from items a     &lt;br /&gt;join item_visit b using (item_id)     &lt;br /&gt;group by item_type;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;测试结果表明，  &lt;code&gt;count(*)&lt;/code&gt;版本用时57秒，  &lt;code&gt;count(visitor_city)&lt;/code&gt;版本用时70秒。原因在哪里？主要就在于  &lt;code&gt;count(*)&lt;/code&gt;版本中  &lt;code&gt;item_visits&lt;/code&gt;表只需要用到  &lt;code&gt;item_id&lt;/code&gt;，所以可以直接用索引来代替数据访问，这就是覆盖索引。  &lt;br /&gt;在实际开发中，一方面要创造使用覆盖索引的机会，不要无谓的增加不需要的字段到查询语句中，上面的案例就是反面例子，实际开发中就有这样的情况发生。另一方面，根据具体的查询也要在成本允许的情况下构造覆盖索引，这样比普通的索引有更少的IO，自然有更快的访问速度。&lt;/p&gt;
 &lt;h2&gt;强制索引&lt;/h2&gt; &lt;p&gt;有时候mysql的执行计划会不恰当的使用索引，这个时候就要求开发人员有一定的排查能力，并且根据实际情况调整索引。当然，这种情况还是比较少见的。Mysql用错索引的主要原因在于mysql是根据IO和CPU的代价来估算是否用索引，或者用哪种索引，而这个估算基于的统计信息有可能不准确。&lt;/p&gt;
 &lt;p&gt;强制使用索引主要在两种场景下碰到。第一种是针对where过滤条件的索引，这个时候的语法是  &lt;code&gt;force index (index_name)&lt;/code&gt;。比如在ETL中常见的数据抽取，利用时间戳增量抽取当天新增或者更新的数据：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select * from user where update_timestamp &amp;gt;= curdate() – 1;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;一般在更新时间戳上会有索引，但是有时候mysql会判断出某一天的更新量特别大，比如超过了20%，那么根据数据的选择性，mysql决定不用索引。但实际上这个判断有可能是不准确的，如果表比较大而且在线上服务时间较长，还是有可能发生的，这个时候可以通过强制使用索引保证抽取的稳定性（当然，这要基于你对业务的了解，保证抽取量能维持在一个稳定的水平，不会发生超大更新量的情况）：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select * from user force index (update_timestamp)      &lt;br /&gt;where update_timestamp &amp;gt;= curdate() – 1;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;强制使用索引的第二种情况，是join时对索引的选择。数据仓库中有时候会出现一种计算场景，对一个按日统计的报表中某一天的小部分数据进行更新。比如有一个按日统计的用户pv表（  &lt;code&gt;user_pv_byday&lt;/code&gt;，一天约50万用户，表中有1个月数据，共1500万）：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;user_id&lt;/th&gt;
   &lt;th&gt;pv&lt;/th&gt;
   &lt;th&gt;stat_date&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;10&lt;/td&gt;
   &lt;td&gt;2013-01-01&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;2&lt;/td&gt;
   &lt;td&gt;15&lt;/td&gt;
   &lt;td&gt;2013-01-01&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;14&lt;/td&gt;
   &lt;td&gt;2013-01-02&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;2&lt;/td&gt;
   &lt;td&gt;19&lt;/td&gt;
   &lt;td&gt;2013-01-02&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;另一个表是当天小部分用户的pv表（  &lt;code&gt;user_pv_to_update&lt;/code&gt;，1000条数据）：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;user_id&lt;/th&gt;
   &lt;th&gt;pv&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;2&lt;/td&gt;
   &lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;两个表的索引情况是，  &lt;code&gt;user_pv_byday&lt;/code&gt;表的  &lt;code&gt;user_id&lt;/code&gt;和  &lt;code&gt;stat_date&lt;/code&gt;字段有索引，  &lt;code&gt;user_pv_to_update&lt;/code&gt;表的  &lt;code&gt;user_id&lt;/code&gt;字段有索引。  &lt;br /&gt;现在要把  &lt;code&gt;user_pv_to_update&lt;/code&gt;的pv数据更新到  &lt;code&gt;user_pv_byday&lt;/code&gt;当天的数据中：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;update user_pv_byday a      &lt;br /&gt;join user_pv_to_update b on a.user_id = b.user_id      &lt;br /&gt;set a.pv = b.pv     &lt;br /&gt;where a.stat_date = curdate() – 1;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;经过一段时间的线上运行之后，发现这个步骤越来越慢了。查了一下执行计划，发现mysql选择了  &lt;code&gt;user_pv_byday&lt;/code&gt;表的  &lt;code&gt;user_id&lt;/code&gt;做索引。于是，决定强制用上  &lt;code&gt;stat_date&lt;/code&gt;的索引。这样一来join的时候需要用到的就是  &lt;code&gt;user_pv_to_update&lt;/code&gt;上的  &lt;code&gt;user_id&lt;/code&gt;字段。这个时候就需要指定顺序，强制  &lt;code&gt;user_pv_byday&lt;/code&gt;表作为外层驱动表（  &lt;code&gt;user_pv_to_update&lt;/code&gt;则是nest loop的内层嵌套）：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;update user_pv_byday a      &lt;br /&gt;straight_join user_pv_to_update b on a.user_id = b.user_id      &lt;br /&gt;set a.pv = b.pv     &lt;br /&gt;where a.stat_date = curdate() – 1;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;然后来看一下两种写法的执行计划。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;原有写法：&lt;/strong&gt;&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;……&lt;/th&gt;
   &lt;th&gt;table&lt;/th&gt;
   &lt;th&gt;type&lt;/th&gt;
   &lt;th&gt;……&lt;/th&gt;
   &lt;th&gt;key&lt;/th&gt;
   &lt;th&gt;……&lt;/th&gt;
   &lt;th&gt;ref&lt;/th&gt;
   &lt;th&gt;rows&lt;/th&gt;
   &lt;th&gt;extra&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;b&lt;/td&gt;
   &lt;td&gt;ALL&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
   &lt;td&gt;1000&lt;/td&gt;
   &lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;a&lt;/td&gt;
   &lt;td&gt;ref&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;user_id&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;b.user_id&lt;/td&gt;
   &lt;td&gt;368&lt;/td&gt;
   &lt;td&gt;Using where&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;强制join顺序的写法：&lt;/strong&gt;&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;……&lt;/th&gt;
   &lt;th&gt;table&lt;/th&gt;
   &lt;th&gt;type&lt;/th&gt;
   &lt;th&gt;……&lt;/th&gt;
   &lt;th&gt;key&lt;/th&gt;
   &lt;th&gt;……&lt;/th&gt;
   &lt;th&gt;ref&lt;/th&gt;
   &lt;th&gt;rows&lt;/th&gt;
   &lt;th&gt;extra&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;a&lt;/td&gt;
   &lt;td&gt;ref&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;stat_date&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;const&lt;/td&gt;
   &lt;td&gt;485228&lt;/td&gt;
   &lt;td&gt;Using where&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;b&lt;/td&gt;
   &lt;td&gt;ref&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;user_id&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;a.user_id&lt;/td&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;Using where&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;根据执行计划中的数据，可以说mysql的选择没有错。原有写法需要读取的数据大致是1000 * 368约合37万，修改写法则是48万，修改写法读取的代价更大。但实际运行情况则是修改写法快过原来写法数倍。&lt;/p&gt;
 &lt;p&gt;不过，这个情况却不能随时重现。真要把这两个表写入空表并且重建索引再来查询，会发现  &lt;code&gt;straight_join&lt;/code&gt;的结果确实会更慢，也就是说在初始状态下，mysql的判断是对的。所以这样的问题只在日常运营中才会发生，无法重现，却是真实存在的，而且碰到类似这一类的应用场景，则必然发生。&lt;/p&gt;
 &lt;p&gt;究其原因，在这种情况下，由于数据不是一次性建成，而是按天陆续写入，所以  &lt;code&gt;user_pv_byday&lt;/code&gt;的  &lt;code&gt;user_id&lt;/code&gt;索引会进行反复的修改，造成索引碎片，极端严重的情况下还会导致索引完全失效。而  &lt;code&gt;stat_date&lt;/code&gt;上的索引由于是每天递增，所以完全没有碎片问题，而且读取数据是还是顺序读取，效率自然要高不少。另外，根据实际情况的观察，随着数据的积累，上面执行计划中368这个值还会变得更小，也就是统计信息会越来越不准确。&lt;/p&gt;
 &lt;p&gt;总之，mysql的执行计划在大部分情况下是没问题的，但是随着数据的不断积累修改，会逐渐出现mysql所不了解的细节，影响优化器的正常判断。这个时候如果能对表做一下重建也能让事情回到正轨，但是很多时候没有这个权限或者条件去做（比如你不是DBA，没有这种操作权限；或者表太大，没有完整的时间段可以操作）。那么强制索引使用就成了开发人员一个低成本的解决方案。&lt;/p&gt;
 &lt;h1&gt;过多的join&lt;/h1&gt; &lt;p&gt;在mysql中，需要join的表如果太多，会对性能造成很显著的下降。同样，举例说明。&lt;/p&gt;
 &lt;p&gt;首先生成一个表（表名  &lt;code&gt;test&lt;/code&gt;），这个表只有60条记录，6个字段，其中第一个字段为主键：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;pk&lt;/th&gt;
   &lt;th&gt;c1&lt;/th&gt;
   &lt;th&gt;c2&lt;/th&gt;
   &lt;th&gt;c3&lt;/th&gt;
   &lt;th&gt;c4&lt;/th&gt;
   &lt;th&gt;c5&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;1&lt;/td&gt;
   &lt;td&gt;11&lt;/td&gt;
   &lt;td&gt;21&lt;/td&gt;
   &lt;td&gt;31&lt;/td&gt;
   &lt;td&gt;41&lt;/td&gt;
   &lt;td&gt;51&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;2&lt;/td&gt;
   &lt;td&gt;12&lt;/td&gt;
   &lt;td&gt;22&lt;/td&gt;
   &lt;td&gt;32&lt;/td&gt;
   &lt;td&gt;42&lt;/td&gt;
   &lt;td&gt;52&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;3&lt;/td&gt;
   &lt;td&gt;13&lt;/td&gt;
   &lt;td&gt;23&lt;/td&gt;
   &lt;td&gt;33&lt;/td&gt;
   &lt;td&gt;43&lt;/td&gt;
   &lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;4&lt;/td&gt;
   &lt;td&gt;14&lt;/td&gt;
   &lt;td&gt;24&lt;/td&gt;
   &lt;td&gt;34&lt;/td&gt;
   &lt;td&gt;44&lt;/td&gt;
   &lt;td&gt;54&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;然后做一个查询：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select count(*)      &lt;br /&gt;from test a1     &lt;br /&gt;join test a2 using (pk)     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;也就是说让test表跟自己关联。计算的结果显然是60，而且几乎不费时间。&lt;/p&gt;
 &lt;p&gt;但是如果是这样的查询（十个  &lt;code&gt;test&lt;/code&gt;表关联），会花费多少时间？&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select count(*)     &lt;br /&gt;from test a1     &lt;br /&gt;join test a2 using (pk)     &lt;br /&gt;join test a3 using (pk)     &lt;br /&gt;join test a4 using (pk)     &lt;br /&gt;join test a5 using (pk)     &lt;br /&gt;join test a6 using (pk)     &lt;br /&gt;join test a7 using (pk)     &lt;br /&gt;join test a8 using (pk)     &lt;br /&gt;join test a9 using (pk)     &lt;br /&gt;join test a10 using (pk)     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;答案是：肯定超过5分钟。因为做了实际测试，5分钟还没有出结果。&lt;/p&gt;
 &lt;p&gt;那么mysql到底在干什么呢？用show processlist去看一下运行时情况：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;ID&lt;/th&gt;
   &lt;th&gt;……&lt;/th&gt;
   &lt;th&gt;COMMAND&lt;/th&gt;
   &lt;th&gt;TIME&lt;/th&gt;
   &lt;th&gt;STATE&lt;/th&gt;
   &lt;th&gt;INFO&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;121&lt;/td&gt;
   &lt;td&gt;……&lt;/td&gt;
   &lt;td&gt;QUERY&lt;/td&gt;
   &lt;td&gt;302&lt;/td&gt;
   &lt;td&gt;statistics&lt;/td&gt;
   &lt;td&gt;select count(*) from test a1 ……&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;原来是处在  &lt;code&gt;statistics&lt;/code&gt;的状态。这个状态，根据mysql的解释是在根据统计信息去生成执行计划。当然这个解释肯定是没有追根溯源。实际上mysql在生成执行计划的时候，其中有一个步骤，是确定表的join顺序。默认情况下，mysql会把所有join顺序全部排列出来，依次计算各个join顺序的执行代价并且取最优的那个。这样一来，n个表join会有n!种情况。十个表join就是10!，大概300万，所以难怪mysql要分析半天了。&lt;/p&gt;
 &lt;p&gt;而在实际开发过程中，曾经出现过30多个表关联的情况（有10^32种join顺序）。一旦出现，花费在statistics状态的时间往往是在1个小时以上，这还只是在表数据量都非常小，需要做顺序分析的点比较少的情况下。至于出现这种情况的原因，无外乎我们需要计算的汇总报表的字段太多，需要从各种各样的地方计算出来数据，然后再把数据拼接起来，报表在维护过程中不断添加字段，又由于种种原因没有去掉已经废弃的字段，这样字段必定会越来愈多，实现这些字段计算就需要用更多的临时计算结果表去关联到一起，结果需要关联的表也越来越多，成了mysql无法承受之重。&lt;/p&gt;
 &lt;p&gt;这个问题的解决方法有两个。从开发角度来说，可以控制join的表个数。如果需要join的表太多，可以根据业务上的分类，先做一轮join，把表的数量控制在一定范围内，然后拿到第一轮的join结果，再做第二轮全局join，这样就不会有问题了。从运维角度来说，可以设置  &lt;code&gt;optimizer_search_depth&lt;/code&gt;这个参数。它能够控制join顺序遍历的深度，进行贪婪搜索得到局部最优的顺序。一般有好多个表join的情况，都是上面说的相同维度的数据需要拼接成一张大表，对于join顺序基本上没什么要求。所以适当的把这个值调低，对于性能应该说没有影响。&lt;/p&gt;
 &lt;h1&gt;列存储引擎Infobright&lt;/h1&gt; &lt;p&gt;Infobright是基于mysql的存储引擎，具有列存储/列压缩和知识网格等特性，比较适合数据仓库的计算。使用起来也不需要考虑索引之类的问题，非常方便。不过经过一段时间的运用，也发现了个别需要注意的问题。&lt;/p&gt;
 &lt;p&gt;一个问题和myisam类似，不要取不需要的数据。这里说的不需要的数据，包括不需要的列（Infobright的使用常识。当然行存储也要注意，只不过影响相对比较小，所以没有专门提到），和不需要的行（行数是可以扩展的，行存储一行基本上都能存在一个存储单元中，但是列存储一列明显不可能存在一个存储单元中）。&lt;/p&gt;
 &lt;p&gt;第二个问题，就是Infobright在长字符检索的时候并不给力。一般来说，网站的访问日志中会有URL字段用来标识访问的具体地址。这样就有查找特定URL的需求。比如我要在一个日志表中查找某种类型的url的访问次数：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;select count(*) from log where url like &amp;apos;%mysql%&amp;apos;;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
 &lt;p&gt;类似这样在一个长字符串里面检索子串的需求，Infobright的执行时间测试下来是myisam的1.5-3倍。  &lt;br /&gt;至于速度慢的原因，这里给出一个简要的解释：Infobright作为列式数据库使用了列存储的常用特性，就是压缩（列式数据库的压缩率一般要能做到10%以内，Infobright也不例外）。另外为了加快查找速度，它还使用了一种叫知识网格检索方式，一般情况下能够极大的减少需要读取的数据量。关于知识网格的原理已经超出了本篇文章的讨论篇幅，可以看  &lt;a href="http://www.cnblogs.com/inmanhust/archive/2010/05/08/1730343.html" rel="external" target="_blank"&gt;这里&lt;/a&gt;了解。但是在查询url的时候，知识网格的优点无法体现出来，但是使用知识网格本身带来的检索代价和解压长字符串的代价却仍然存在，而且比查询一般的数字类字段要来的大的多。&lt;/p&gt;
 &lt;p&gt;解决办法有几种，比如  &lt;a href="http://www.infobright.org/images/uploads/blogs/how-to/How_To_Efficiently_Search_Strings_in_Infobright.pdf" rel="external" target="_blank"&gt;官方的方案&lt;/a&gt;是把长字符串MD5成一个数字，查询的时候加上数字作为补充查询条件。而  &lt;a href="http://weibo.com/1699016425/zzPJbzQFz" rel="external" target="_blank"&gt;这条微博&lt;/a&gt;给出的方法是进行分词然后再整数化。这些方案相对来说比较复杂，而我尝试过一种简单的解决方案（不过也有相当的局限性），就是根据这个长字段排序后再导入。这样一来按照该字段查询时，通过知识网格就能够屏蔽掉比较多的“数据包”（Infobright的数据压缩单元），而未排序的情况下符合条件的数据散布在各个“数据包”中，其解压工作量就大得多了。使用这个方法进行查询，测试下来其执行时间就只有mysql的0.5倍左右了。 &lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>data system mysql</category>
      <guid isPermaLink="true">https://itindex.net/detail/56815-%E6%95%B0%E6%8D%AE%E4%BB%93%E5%BA%93-sql-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96</guid>
      <pubDate>Tue, 30 Apr 2013 17:46:17 CST</pubDate>
    </item>
    <item>
      <title>MySQL 高性能存储引擎：TokuDB初探</title>
      <link>https://itindex.net/detail/56354-mysql-%E6%80%A7%E8%83%BD-%E5%BC%95%E6%93%8E</link>
      <description>&lt;p&gt;在安装MariaDB的时候了解到代替InnoDB的TokuDB，看简介非常的棒，这里对ToduDB做一个初步的整理，使用后再做更多的分享。&lt;/p&gt;
 &lt;h2&gt;什么是TokuDB？&lt;/h2&gt;
 &lt;p&gt;在MySQL最流行的支持全事务的引擎为INNODB。其特点是数据本身是用B-TREE来组织，数据本身即是庞大的根据主键聚簇的B-TREE索引。 所以在这点上，写入速度就会有些降低，因为要每次写入要用一次IO来做索引树的重排。特别是当数据量本身比内存大很多的情况下，CPU本身被磁盘IO纠缠的做不了其他事情了。这时我们要考虑如何减少对磁盘的IO来排解CPU的处境，常见的方法有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;把INNODB 个PAGE增大（默认16KB），但增大也就带来了一些缺陷。 比如，对磁盘进行CHECKPOINT的时间将延后。&lt;/li&gt;
  &lt;li&gt;把日志文件放到更快速的磁盘上，比如SSD。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;TokuDB 是一个支持事务的“新”引擎，有着出色的数据压缩功能，由美国 TokuTek 公司（现在已经被 Percona 公司收购）研发。拥有出色的数据压缩功能，如果您的数据写多读少，而且数据量比较大，强烈建议您使用TokuDB，以节省空间成本，并大幅度降低存储使用量和IOPS开销，不过相应的会增加 CPU 的压力。&lt;/p&gt;
 &lt;h2&gt;TokuDB 的特性&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;1&lt;/strong&gt;  &lt;strong&gt;.&lt;/strong&gt;  &lt;strong&gt;丰富的索引类型以及索引的快速创建&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;TokuDB 除了支持现有的索引类型外， 还增加了(第二)集合索引, 以满足多样性的覆盖索引的查询, 在快速创建索引方面提高了查询的效率&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.(&lt;/strong&gt;  &lt;strong&gt;第二)集合索引&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;也可以称作非主键的集合索引, 这类索引也包含了表中的所有列, 可以用于覆盖索引的查询需要, 比如以下示例, 在where 条件中直接命中 index_b 索引, 避免了从主键中再查找一次.&lt;/p&gt; &lt;pre&gt;CREATE TABLE table (
column_a INT,
column_b INT,
column_c INT,
PRIMARY KEY index_a (column_a),
CLUSTERING KEY index_b (column_b)) ENGINE = TokuDB;

SELECT column_c
FROM table
WHERE column_b BETWEEN 10 AND 100;&lt;/pre&gt; &lt;p&gt;见:   &lt;a href="http://tokutek.com/2009/05/introducing_multiple_clustering_indexes/"&gt;http://tokutek.com/2009/05/introducing_multiple_clustering_indexes/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.索引在线创建(Hot Index Creation)&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;TokuDB 允许直接给表增加索引而不影响更新语句(insert, update 等)的执行。可以通过变量 tokudb_create_index_online 来控制是否开启该特性, 不过遗憾的是目前还只能通过 CREATE INDEX 语法实现在线创建, 不能通过 ALTER TABLE 实现. 这种方式比通常的创建方式慢了许多, 创建的过程可以通过 show processlist 查看。不过 tokudb 不支持在线删除索引, 删除索引的时候会对标加全局锁。&lt;/p&gt; &lt;pre&gt;&amp;gt; SET tokudb_create_index_online=ON;
Query OK, 0 rows affected (0.00 sec)

&amp;gt; CREATE INDEX index ON table (field_name);&lt;/pre&gt; &lt;p&gt;  &lt;strong&gt;4.在线更改列(Add, Delete, Expand, Rename)&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;TokuDB 可以在轻微阻塞更新或查询语句的情况下， 允许实现以下操作：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;增加或删除表中的列&lt;/li&gt;
  &lt;li&gt;扩充字段: char, varchar, varbinary 和 int 类型的列&lt;/li&gt;
  &lt;li&gt;重命名列, 不支持字段类型: TIME, ENUM, BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;这些操作通常是以表锁级别阻塞(几秒钟时间)其他查询的执行, 当表记录下次从磁盘加载到内存的时候, 系统就会随之对记录进行修改操作(add, delete 或 expand)， 如果是 rename 操作, 则会在几秒钟的停机时间内完成所有操作。&lt;/p&gt;
 &lt;p&gt;TokuDB的这些操作不同于 InnoDB, 对表进行更新后可以看到 rows affected 为 0, 即更改操作会放到后台执行, 比较快速的原因可能是由于 Fractal-tree 索引的特性, 将随机的 IO 操作替换为顺序 IO 操作， Fractal-tree的特性中， 会将这些操作广播到所有行, 不像 InnoDB, 需要 open table 并创建临时表来完成.&lt;/p&gt;
 &lt;p&gt;看看官方对该特性的一些指导说明:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;所有的这些操作不是立即执行， 而是放到后台中由 Fractal Tree 完成, 操作包括主键和非主键索引。也可以手工强制执行这些操作, 使用 OPTIMIZE TABLE X 命令即可, TokuDB 从1.0 开始OPTIMIZE TABLE命令也支持在线完成, 但是不会重建索引&lt;/li&gt;
  &lt;li&gt;不要一次更新多列, 分开对每列进行操作&lt;/li&gt;
  &lt;li&gt;避免同时对一列进行 add, delete, expand 或 drop 操作&lt;/li&gt;
  &lt;li&gt;表锁的时间主要由缓存中的脏页(dirty page)决定, 脏页越多 flush 的时间就越长. 每做一次更新, MySQL 都会关闭一次表的连接以释放之前的资源&lt;/li&gt;
  &lt;li&gt;避免删除的列是索引的一部分, 这类操作会特别慢, 非要删除的话可以去掉索引和该列的关联再进行删除操作&lt;/li&gt;
  &lt;li&gt;扩充类的操作只支持 char, varchar, varbinary 和 int 类型的字段&lt;/li&gt;
  &lt;li&gt;一次只 rename 一列, 操作多列会降级为标准的 MySQL 行为, 语法中列的属性必须要指定上, 如下:
   &lt;ul&gt;
    &lt;li&gt;
     &lt;pre&gt;ALTER TABLE table
CHANGE column_old column_new
DATA_TYPE REQUIRED_NESS DEFAULT&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;rename 操作还不支持字段: TIME, ENUM, BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB.&lt;/li&gt;
  &lt;li&gt;不支持更新临时表;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;5.数据压缩&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;TokuDB中所有的压缩操作都在后台执行, 高级别的压缩会降低系统的性能, 有些场景下会需要高级别的压缩. 按照官方的建议: 6核数以下的机器建议标准压缩, 反之可以使用高级别的压缩。&lt;/p&gt;
 &lt;p&gt;每个表在 create table 或 alter table 的时候通过 ROW_FORMAT 来指定压缩的算法：&lt;/p&gt; &lt;pre&gt;CREATE TABLE table (
column_a INT NOT NULL PRIMARY KEY,
column_b INT NOT NULL) ENGINE=TokuDB
ROW_FORMAT=row_format;&lt;/pre&gt; &lt;p&gt;ROW_FORMAT默认由变量 tokudb_row_format 控制, 默认为 tokudb_zlib, 可以的值包括:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;tokudb_zlib: 使用 zlib 库的压缩模式，提供了中等级别的压缩比和中等级别的CPU消耗。&lt;/li&gt;
  &lt;li&gt;tokudb_quicklz: 使用 quicklz 库的压缩模式， 提供了轻量级的压缩比和较低基本的CPU消耗。&lt;/li&gt;
  &lt;li&gt;tokudb_lzma: 使用lzma库压缩模式，提供了高压缩比和高CPU消耗。&lt;/li&gt;
  &lt;li&gt;tokudb_uncompressed: 不使用压缩模式。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;6.Read free &lt;/strong&gt;  &lt;strong&gt;复制特性&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;得益于 Fracal Tree 索引的特性, TokuDB 的 slave 端能够以低于读IO的消耗来应用 master 端的变化, 其主要依赖 Fractal Tree 索引的特性，可以在配置里启用特性&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;insert/delete/update操作部分可以直接插入到合适的 Fractal Tree 索引中, 避免 read-modify-write 行为的开销;&lt;/li&gt;
  &lt;li&gt;delete/update 操作可以忽略唯一性检查带来的 IO 方面的开销&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;不好的是, 如果启用了 Read Free Replication 功能, Server 端需要做如下设置:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;master：复制格式必须为 ROW， 因为 tokudb 还没有实现对 auto-increment函数进行加锁处理, 所以多个并发的插入语句可能会引起不确定的 auto-increment值, 由此造成主从两边的数据不一致.&lt;/li&gt;
  &lt;li&gt;slave：开启 read-only; 关闭唯一性检查(set tokudb_rpl_unique_checks=0);关闭查找(read-modify-write)功能(set tokudb_rpl_lookup_rows=0);&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;slave 端的设置可以在一台或多台 slave 中设置：MySQL5.5 和 MariaDB5.5中只有定义了主键的表才能使用该功能, MySQL 5.6, Percona 5.6 和 MariaDB 10.X 没有此限制&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;7.事务, ACID 和恢复&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;默认情况下, TokuDB 定期检查所有打开的表, 并记录 checkpoint 期间所有的更新, 所以在系统崩溃的时候, 可以恢复表到之前的状态(ACID-compliant), 所有的已提交的事务会更新到表里,未提交的事务则进行回滚. 默认的检查周期每60s一次, 是从当前检查点的开始时间到下次检查点的开始时间, 如果 checkpoint 需要更多的信息, 下次的checkpoint 检查会立即开始, 不过这和 log 文件的频繁刷新有关. 用户也可以在任何时候手工执行 flush logs 命令来引起一次 checkpoint 检查; 在数据库正常关闭的时候, 所有开启的事务都会被忽略.&lt;/li&gt;
  &lt;li&gt;管理日志的大小: TokuDB 一直保存最近的checkpoing到日志文件中, 当日志达到100M的时候, 会起一个新的日志文件; 每次checkpoint的时候, 日志中旧于当前检查点的都会被忽略, 如果检查的周期设置非常大, 日志的清理频率也会减少。 TokuDB也会为每个打开的事务维护回滚日志, 日志的大小和事务量有关， 被压缩保存到磁盘中, 当事务结束后，回滚日志会被相应清理.&lt;/li&gt;
  &lt;li&gt;恢复: TokuDB自动进行恢复操作, 在崩溃后使用日志和回滚日志进行恢复, 恢复时间由日志大小(包括未压缩的回滚日志)决定.&lt;/li&gt;
  &lt;li&gt;禁用写缓存: 如果要保证事务安全, 就得考虑到硬件方面的写缓存. TokuDB 在 MySQL 里也支持事务安全特性(transaction safe), 对系统而言, 数据库更新的数据不一样真的写到磁盘里, 而是缓存起来, 在系统崩溃的时候还是会出现丢数据的现象, 比如TokuDB不能保证挂载的NFS卷可以正常恢复, 所以如果要保证安全,最好关闭写缓存, 但是可能会造成性能的降低.通常情况下需要关闭磁盘的写缓存, 不过考虑到性能原因, XFS文件系统的缓存可以开启, 不过穿线错误”Disabling barriers”后，就需要关闭缓存. 一些场景下需要关闭文件系统(ext3)缓存, LVM, 软RAID 和带有 BBU(battery-backed-up) 特性的RAID卡&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;8.过程追踪&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;TokuDB 提供了追踪长时间运行语句的机制. 对 LOAD DATA 命令来说，SHOW PROCESSLIST 可以显示过程信息, 第一个是类似 “Inserted about 1000000 rows” 的状态信息, 下一个是完成百分比的信息, 比如 “Loading of data about 45% done”; 增加索引的时候, SHOW PROCESSLIST 可以显示 CREATE INDEX 和 ALTER TABLE 的过程信息, 其会显示行数的估算值, 也会显示完成的百分比; SHOW PROCESSLIST 也会显示事务的执行情况, 比如 committing 或 aborting 状态.&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;9.迁移到 TokuDB&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;可以使用传统的方式更改表的存储引擎, 比如 “ALTER TABLE … ENGINE = TokuDB” 或 mysqldump 导出再倒入, INTO OUTFILE 和 LOAD DATA INFILE 的方式也可以。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;10.热备&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Percona Xtrabackup 还未支持 TokuDB 的热备功能, percona 也为表示有支持的打算   &lt;a href="http://www.percona.com/blog/2014/07/15/tokudb-tips-mysql-backups/"&gt;http://www.percona.com/blog/2014/07/15/tokudb-tips-mysql-backups/&lt;/a&gt; ;对于大表可以使用 LVM 特性进行备份,   &lt;a href="https://launchpad.net/mylvmbackup"&gt;https://launchpad.net/mylvmbackup&lt;/a&gt; , 或 mysdumper 进行备份。TokuDB 官方提供了一个热备插件 tokudb_backup.so, 可以进行在线备份, 详见   &lt;a href="https://github.com/Tokutek/tokudb-backup-plugin"&gt;https://github.com/Tokutek/tokudb-backup-plugin&lt;/a&gt;， 不过其依赖 backup-enterprise, 无法编译出 so 动态库, 是个商业的收费版本, 见   &lt;a href="https://www.percona.com/doc/percona-server/5.6/tokudb/tokudb_installation.html"&gt;https://www.percona.com/doc/percona-server/5.6/tokudb/tokudb_installation.html&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;TokuDB的优点:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;高压缩比，默认使用zlib进行压缩，尤其是对字符串(varchar,text等)类型有非常高的压缩比，比较适合存储日志、原始数据等。官方宣称可以达到1：12。&lt;/li&gt;
  &lt;li&gt;在线添加索引，不影响读写操作&lt;/li&gt;
  &lt;li&gt;HCADER 特性，支持在线字段增加、删除、扩展、重命名操作，（瞬间或秒级完成）&lt;/li&gt;
  &lt;li&gt;支持完整的ACID特性和事务机制&lt;/li&gt;
  &lt;li&gt;非常快的写入性能， Fractal-tree在事务实现上有优势,无undo log，官方称至少比innodb高9倍。&lt;/li&gt;
  &lt;li&gt;支持show processlist 进度查看&lt;/li&gt;
  &lt;li&gt;数据量可以扩展到几个TB；&lt;/li&gt;
  &lt;li&gt;不会产生索引碎片；&lt;/li&gt;
  &lt;li&gt;支持hot column addition,hot indexing,mvcc&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;TokuDB缺点：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;不支持外键(foreign key)功能，如果您的表有外键，切换到 TokuDB引擎后，此约束将被忽略。&lt;/li&gt;
  &lt;li&gt;TokuDB 不适大量读取的场景，因为压缩解压缩的原因。CPU占用会高2-3倍，但由于压缩后空间小，IO开销低，平均响应时间大概是2倍左右。&lt;/li&gt;
  &lt;li&gt;online ddl 对text,blob等类型的字段不适用&lt;/li&gt;
  &lt;li&gt;没有完善的热备工具，只能通过mysqldump进行逻辑备份&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;适用场景：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;访问频率不高的数据或历史数据归档&lt;/li&gt;
  &lt;li&gt;数据表非常大并且时不时还需要进行DDL操作&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;TokuDB的索引结构–分形树的实现&lt;/h2&gt;
 &lt;p&gt;TokuDB和InnoDB最大的不同在于TokuDB采用了一种叫做Fractal Tree的索引结构，使其在随机写数据的处理上有很大提升。目前无论是SQL Server，还是MySQL的innodb，都是用的B+Tree（SQL Server用的是标准的B-Tree）的索引结构。InnoDB是以主键组织的B+Tree结构，数据按照主键顺序排列。对于顺序的自增主键有很好的性能，但是不适合随机写入，大量的随机I/O会使数据页分裂产生碎片，索引维护开销很多大。TokuDB解决随机写入的问题得益于其索引结构，Fractal Tree 和 B-Tree的差别主要在于索引树的内部节点上，B-Tree索引的内部结构只有指向父节点和子节点的指针，而Fractal Tree的内部节点不仅有指向父节点和子节点的指针，还有一块Buffer区。当数据写入时会先落到这个Buffer区上，该区是一个FIFO结构，写是一个顺序的过程，和其他缓冲区一样，满了就一次性刷写数据。所以TokuDB上插入数据基本上变成了一个顺序添加的过程。&lt;/p&gt;
 &lt;p&gt;BTree和Fractal tree的比较：&lt;/p&gt;
 &lt;table width="444"&gt;

  &lt;tr&gt;
   &lt;td width="97"&gt;    &lt;strong&gt;Structure&lt;/strong&gt;&lt;/td&gt;
   &lt;td width="83"&gt;    &lt;strong&gt;Inserts&lt;/strong&gt;&lt;/td&gt;
   &lt;td width="122"&gt;    &lt;strong&gt;Point Queries&lt;/strong&gt;&lt;/td&gt;
   &lt;td width="142"&gt;    &lt;strong&gt;Range Queries&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="97"&gt;B-Tree&lt;/td&gt;
   &lt;td width="83"&gt;Horrible&lt;/td&gt;
   &lt;td width="122"&gt;Good&lt;/td&gt;
   &lt;td width="142"&gt;Good (young)&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="97"&gt;Append&lt;/td&gt;
   &lt;td width="83"&gt;Wonderful&lt;/td&gt;
   &lt;td width="122"&gt;Horrible&lt;/td&gt;
   &lt;td width="142"&gt;Horrible&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="97"&gt;Fractal Tree&lt;/td&gt;
   &lt;td width="83"&gt;Good&lt;/td&gt;
   &lt;td width="122"&gt;Good&lt;/td&gt;
   &lt;td width="142"&gt;Good&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h3&gt;Fractal tree(分形树)简介&lt;/h3&gt;
 &lt;p&gt;分形树是一种写优化的磁盘索引数据结构。 在一般情况下， 分形树的写操作（Insert/Update/Delete）性能比较好，同时它还能保证读操作近似于B+树的读性能。据Percona公司测试结果显示, TokuDB分形树的写性能优于InnoDB的B+树)， 读性能略低于B+树。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;ft-index&lt;/strong&gt;  &lt;strong&gt;的磁盘存储结构&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;ft-index采用更大的索引页和数据页（ft-index默认为4M, InnoDB默认为16K）， 这使得ft-index的数据页和索引页的压缩比更高。也就是说，在打开索引页和数据页压缩的情况下，插入等量的数据， ft-index占用的存储空间更少。ft-index支持在线修改DDL (Hot Schema Change)。 简单来讲，就是在做DDL操作的同时(例如添加索引)，用户依然可以执行写入操作， 这个特点是ft-index树形结构天然支持的。 此外， ft-index还支持事务(ACID)以及事务的MVCC(Multiple Version Cocurrency Control 多版本并发控制)， 支持崩溃恢复。正因为上述特点， Percona公司宣称TokuDB一方面带给客户极大的性能提升， 另一方面还降低了客户的存储使用成本。&lt;/p&gt;
 &lt;p&gt;ft-index的索引结构图如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="527" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/ft-index.png" width="858"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;灰色区域表示ft-index分形树的一个页，绿色区域表示一个键值，两格绿色区域之间表示一个儿子指针。 BlockNum表示儿子指针指向的页的偏移量。Fanout表示分形树的扇出，也就是儿子指针的个数。 NodeSize表示一个页占用的字节数。NonLeafNode表示当前页是一个非叶子节点，LeafNode表示当前页是一个叶子节点，叶子节点是最底层的存放Key-value键值对的节点， 非叶子节点不存放value。 Heigth表示树的高度， 根节点的高度为3， 根节点下一层节点的高度为2， 最底层叶子节点的高度为1。Depth表示树的深度，根节点的深度为0， 根节点的下一层节点深度为1。&lt;/p&gt;
 &lt;p&gt;分形树的树形结构非常类似于B+树, 它的树形结构由若干个节点组成（我们称之为Node或者Block，在InnoDB中，我们称之为Page或者页）。 每个节点由一组有序的键值组成。假设一个节点的键值序列为[3, 8], 那么这个键值将(-00, +00)整个区间划分为(-00, 3), [3, 8), [8, +00) 这样3个区间， 每一个区间就对应着一个儿子指针（Child指针）。 在B+树中， Child指针一般指向一个页， 而在分形树中，每一个Child指针除了需要指向一个Node的地址(BlockNum)之外，还会带有一个Message Buffer (msg_buffer)， 这个Message Buffer 是一个先进先出(FIFO)的队列，用来存放Insert/Delete/Update/HotSchemaChange这样的更新操作。&lt;/p&gt;
 &lt;p&gt;按照ft-index源代码的实现， 对ft-index中分形树更为严谨的说法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;节点(block或者node, 在InnoDB中我们称之为Page或者页)是由一组有序的键值组成， 第一个键值设置为null键值， 表示负无穷大。&lt;/li&gt;
  &lt;li&gt;节点分为两种类型，一种是叶子节点， 一种是非叶子节点。 叶子节点的儿子指针指向的是BasementNode, 非叶子节点指向的是正常的Node 。 这里的BasementNode节点存放的是多个K-V键值对， 也就是说最后所有的查找操作都需要定位到BasementNode才能成功获取到数据(Value)。这一点也和B+树的LeafPage类似， 数据(Value)都是存放在叶子节点， 非叶子节点用来存放键值(Key)做索引。 当叶子节点加载到内存后，为了快速查找到BasementNode中的数据(Value)， ft-index会把整个BasementNode中的key-value都转换为一棵弱平衡二叉树， 这棵平衡二叉树有一个很逗逼的名字，叫做   &lt;a href="https://en.wikipedia.org/wiki/Scapegoat_tree"&gt;替罪羊树&lt;/a&gt;。&lt;/li&gt;
  &lt;li&gt;每个节点的键值区间对应着一个儿子指针(Child Pointer)。 非叶子节点的儿子指针携带着一个   &lt;a href="https://github.com/Tokutek/ft-index/blob/master/ft/msg_buffer.cc"&gt;MessageBuffer&lt;/a&gt;， MessageBuffer是一个FIFO队列。用来存放Insert/Delete/Update/HotSchemaChange这样的更新操作。儿子指针以及MessageBuffer都会序列化存放在Node的磁盘文件中。&lt;/li&gt;
  &lt;li&gt;每个非叶子节点(Non Leaf Node)儿子指针的个数必须在[fantout/4, fantout]这个区间之内。 这里fantout是分形树（B+树也有这个概念）的一个参数，这个参数主要用来维持树的高度。当一个非叶子节点的儿子指针个数小于fantout/4 ， 那么我们认为这个节点的太空虚了，需要和其他节点合并为一个节点(Node Merge)， 这样能减少整个树的高度。当一个非叶子节点的儿子指针个数超过fantout， 那么我们认为这个节点太饱满了， 需要将一个节点一拆为二(Node Split)。 通过这种约束控制，理论上就能将磁盘数据维持在一个正常的相对平衡的树形结构，这样可以控制插入和查询复杂度上限。&lt;/li&gt;
  &lt;li&gt;注意： 在ft-index实现中，控制树平衡的条件更加复杂， 例如除了考虑fantout之外，还要保证节点总字节数在[NodeSize/4, NodeSize]这个区间， NodeSize一般为4M ，当不在这个区间时， 需要做对应的合并(Merge)或者分裂(Split)操作。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;分形树的Insert/Delete/Update实现&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;我们说到分形树是一种写优化的数据结构， 它的写操作性能要优于B+树的写操作性能。 那么它究竟如何做到更优的写操作性能呢？首先， 这里说的写操作性能，指的是随机写操作。 举个简单例子，假设我们在MySQL的InnoDB表中不断执行这个SQL语句： insert into sbtest set x = uuid()， 其中sbtest表中有一个唯一索引字段为x。 由于uuid()的随机性，将导致插入到sbtest表中的数据散落在各个不同的叶子节点(Leaf Node)中。 在B+树中， 大量的这种随机写操作将导致LRU-Cache中大量的热点数据页落在B+树的上层(如下图所示）。这样底层的叶子节点命中Cache的概率降低，从而造成大量的磁盘IO操作， 也就导致B+树的随机写性能瓶颈。但B+树的顺序写操作很快，因为顺序写操作充分利用了局部热点数据， 磁盘IO次数大大降低。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="176" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/b-tree.png" width="672"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;下面来说说分形树插入操作的流程。 为了方便后面描述，约定如下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;以Insert操作为例， 假定插入的数据为(Key, Value)&lt;/li&gt;
  &lt;li&gt;加载节点(Load Page)，都是先判断该节点是否命中LRU-Cache。仅当缓存不命中时， ft-index才会通过seed定位到偏移量读取数据页到内存&lt;/li&gt;
  &lt;li&gt;暂时不考虑崩溃日志和事务处理。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;详细流程如下：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;加载Root节点；&lt;/li&gt;
  &lt;li&gt;判断Root节点是否需要分裂(或合并)，如果满足分裂(或者合并)条件，则分裂(或者合并)Root节点。 具体分裂Root节点的流程，感兴趣的同学可以开开脑洞。&lt;/li&gt;
  &lt;li&gt;当Root节点height&amp;gt;0, 也就是Root是非叶子节点时， 通过二分搜索找到Key所在的键值区间Range，将(Key, Value)包装成一条消息(Insert, Key, Value) ， 放入到键值区间Range对应的Child指针的Message Buffer中。&lt;/li&gt;
  &lt;li&gt;当Root节点height=0时，即Root是叶子节点时， 将消息(Insert, Key, Value) 应用(Apply)到BasementNode上， 也就是插入(Key, Value)到BasementNode中。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;这里有一个非常诡异的地方，在大量的插入（包括随机和顺序插入）情况下， Root节点会经常性的被撑饱满，这将会导致Root节点做大量的分裂操作。然后，Root节点做了大量的分裂操作之后，产生大量的height=1的节点， 然后height=1的节点被撑爆满之后，又会产生大量height=2的节点， 最终树的高度越来越高。 这个诡异的之处就隐藏了分形树写操作性能比B+树高的秘诀： 每一次插入操作都落在Root节点就马上返回了， 每次写操作并不需要搜索树形结构最底层的BasementNode， 这样会导致大量的热点数据集中落在在Root节点的上层(此时的热点数据分布图类似于上图)， 从而充分利用热点数据的局部性，大大减少了磁盘IO操作。&lt;/p&gt;
 &lt;p&gt;Update/Delete操作的情况和Insert操作的情况类似， 但是需要特别注意的地方在于，由于分形树随机读性能并不如InnoDB的B+树。因此，Update/Delete操作需要细分为两种情况考虑，这两种情况测试性能可能差距巨大：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;覆盖式的Update/Delete (overwrite)。 也就是当key存在时， 执行Update/Delete； 当key不存在时，不做任何操作，也不需要报错。&lt;/li&gt;
  &lt;li&gt;严格匹配的Update/Delete。 当key存在时， 执行update/delete ; 当key不存在时， 需要报错给上层应用方。 在这种情况下，我们需要先查询key是否存在于ft-index的basementnode中，于是Point-Query默默的拖了Update/Delete操作的性能后退。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;此外，ft-index为了提升顺序写的性能，对顺序插入操作做了一些优化，例如  &lt;a href="http://www.kancloud.cn/taobaomysql/monthly/67144"&gt;顺序写加速&lt;/a&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;分形树的Point-Query实现&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在ft-index中， 类似select from table where id = ? （其中id是索引）的查询操作称之为Point-Query； 类似select from table where id &amp;gt;= ? and id &amp;lt;= ? （其中id是索引）的查询操作称之为Range-Query。 上文已经提到， Point-Query读操作性能并不如InnoDB的B+树， 这里详细描述Point-Query的相关流程。 （这里假设要查询的键值为Key）&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;加载Root节点，通过二分搜索确定Key落在Root节点的键值区间Range, 找到对应的Range的Child指针。&lt;/li&gt;
  &lt;li&gt;加载Child指针对应的的节点。 若该节点为非叶子节点，则继续沿着分形树一直往下查找，一直到叶子节点停止。 若当前节点为叶子节点，则停止查找。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;查找到叶子节点后，我们并不能直接返回叶子节点中的BasementNode的Value给用户。 因为分形树的插入操作是通过消息(Message)的方式插入的， 此时需要把从Root节点到叶子节点这条路径上的所有消息依次apply到叶子节点的BasementNode。 待apply所有的消息完成之后，查找BasementNode中的key对应的value，就是用户需要查找的值。&lt;/p&gt;
 &lt;p&gt;分形树的查找流程基本和 InnoDB的B+树的查找流程类似， 区别在于分形树需要将从Root节点到叶子节点这条路径上的messge buffer都往下推，并将消息apply到BasementNode节点上。注意查找流程需要下推消息， 这可能会造成路径上的部分节点被撑饱满，但是ft-index在查询过程中并不会对叶子节点做分裂和合并操作， 因为ft-index的设计原则是： Insert/Update/Delete操作负责节点的Split和Merge, Select操作负责消息的延迟下推(Lazy Push)。 这样，分形树就将Insert/Delete/Update这类更新操作通过未来的Select操作应用到具体的数据节点，从而完成更新。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;分形树的Range-Query实现&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;下面来介绍Range-Query的查询实现。简单来讲， 分形树的Range-Query基本等价于进行N次Point-Query操作，操作的代价也基本等价于N次Point-Query操作的代价。 由于分形树在非叶子节点的msg_buffer中存放着BasementNode的更新操作，因此我们在查找每一个Key的Value时，都需要从根节点查找到叶子节点， 然后将这条路径上的消息apply到basenmentNode的Value上。 这个流程可以用下图来表示。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="219" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/flow-1.png" width="569"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;但是在B+树中， 由于底层的各个叶子节点都通过指针组织成一个双向链表， 结构如下图所示。 因此，我们只需要从跟节点到叶子节点定位到第一个满足条件的Key, 然后不断在叶子节点迭代next指针，即可获取到Range-Query的所有Key-Value键值。因此，对于B+树的Range-Query操作来说，除了第一次需要从root节点遍历到叶子节点做随机写操作，后继数据读取基本可以看做是顺序IO。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="219" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/flow-2.png" width="569"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;通过比较分形树和B+树的Range-Query实现可以发现， 分形树的Range-Query查询代价明显比B+树代价高，因为分型树需要遍历Root节点的覆盖Range的整颗子树，而B+树只需要一次Seed到Range的起始Key，后续迭代基本等价于顺序IO。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;总体来说，分形树是一种写优化的数据结构，它的核心思想是利用节点的MessageBuffer缓存更新操作，充分利用数据局部性原理， 将随机写转换为顺序写，这样极大的提高了随机写的效率。Tokutek研发团队的iiBench测试结果显示： TokuDB的insert操作(随机写)的性能比InnoDB快很多，而Select操作(随机读)的性能低于InnoDB的性能，但是差距较小，同时由于TokuDB采用有4M的大页存储，使得压缩比较高。这也是Percona公司宣称TokuDB更高性能，更低成本的原因。&lt;/p&gt;
 &lt;p&gt;另外，在线更新表结构(Hot Schema Change)实现也是基于MessageBuffer来实现的， 但和Insert/Delete/Update操作不同的是， 前者的消息下推方式是广播式下推（父节点的一条消息，应用到所有的儿子节点）， 后者的消息下推方式单播式下推（父节点的一条消息，应用到对应键值区间的儿子节点)， 由于实现类似于Insert操作，所以不再展开描述。&lt;/p&gt;
 &lt;h2&gt;TokuDB的多版本并发控制(MVCC)&lt;/h2&gt;
 &lt;p&gt;在传统的关系型数据库（例如Oracle, MySQL, SQLServer）中，事务可以说是研发和讨论最核心内容。而事务最核心的性质就是ACID。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;A表示原子性，也就是组成事务的所有子任务只有两种结果：要么随着事务的提交，所有子任务都成功执行；要么随着事务的回滚，所有子任务都撤销。&lt;/li&gt;
  &lt;li&gt;C表示一致性，也就是无论事务提交或者回滚，都不能破坏数据的一致性约束，这些一致性约束包括键值唯一约束、键值关联关系约束等。&lt;/li&gt;
  &lt;li&gt;I表示隔离性，隔离性一般是针对多个并发事务而言的，也就是在同一个时间点，t1事务和t2事务读取的数据应该是隔离的，这两个事务就好像进了同一酒店的两间房间一样，各自在各自的房间里面活动，他们相互之间并不能看到各自在干嘛。&lt;/li&gt;
  &lt;li&gt;D表示持久性，这个性质保证了一个事务一旦承诺用户成功提交，那么即便是后继数据库进程crash或者操作系统crash，只要磁盘数据没坏，那么下次启动数据库后，这个事务的执行结果仍然可以读取到。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;TokuDB目前完全支持事务的ACID。 从实现上看， 由于TokuDB采用的  &lt;a href="http://openinx.github.io/2015/11/25/ft-index-implement/"&gt;分形树&lt;/a&gt;作为索引，而InnoDB采用B+树作为索引结构，因而TokuDB在事务的实现上和InnoDB有很大不同。&lt;/p&gt;
 &lt;p&gt;在InnoDB中， 设计了redo和undo两种日志，redo存放页的物理修改日志，用来保证事务的持久性； undo存放事务的逻辑修改日志，它实际存放了一条记录在多个并发事务下的多个版本，用来实现事务的隔离性(MVCC)和回滚操作。由于TokuDB的分形树采用消息传递的方式来做增删改更新操作，一条消息就是事务对该记录修改的一个版本，因此，在TokuDB源码实现中，并没有额外的undo-log的概念和实现，取而代之的是一条记录多条消息的管理机制。虽然一条记录多条消息的方式可以实现事务的MVCC，却无法解决事务回滚的问题，因此TokuDB额外设计了tokudb.rollback这个日志文件来做帮助实现事务回滚。&lt;/p&gt;
 &lt;p&gt;这里主要分析TokuDB的事务隔离性的实现，也就是常提到的多版本并发控制(MVCC)。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;TokuDB&lt;/strong&gt;  &lt;strong&gt;的事务表示&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在tokudb中， 在用户执行的一个事务，具体到存储引擎层面会被拆开成许多个小事务(这种小事务记为txn)。 例如用户执行这样一个事务：&lt;/p&gt; &lt;pre&gt;begin;
insert into hello set id = 1, value = &amp;apos;1&amp;apos;; 
commit;&lt;/pre&gt; &lt;p&gt;对应到TokuDB存储引擎的redo-log中的记录为：&lt;/p&gt; &lt;pre&gt;xbegin          &amp;apos;b&amp;apos;: lsn=236599 xid=15,0 parentxid=0,0 crc=29e4d0a1 len=53
xbegin          &amp;apos;b&amp;apos;: lsn=236600 xid=15,1 parentxid=15,0 crc=282cb1a1 len=53
enq_insert      &amp;apos;I&amp;apos;: lsn=236601 filenum=13 xid=15,1 key={...} value={...} crc=a42128e5 len=58
xcommit         &amp;apos;C&amp;apos;: lsn=236602 xid=15,1 crc=ec9bba3d len=37
xprepare        &amp;apos;P&amp;apos;: lsn=236603 xid=15,0 xa_xid={...} crc=db091de4 len=67
xcommit         &amp;apos;C&amp;apos;: lsn=236604 xid=15,0 crc=ec997b3d len=37&lt;/pre&gt; &lt;p&gt;对应的事务树如下图所示：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="157" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/p1.png" width="120"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;对一个较为复杂一点，带有savepoint的事务例子：&lt;/p&gt; &lt;pre&gt;begin;
insert into hello set id = 2, value = &amp;apos;2&amp;apos; ;
savepoint mark1;
insert into hello set id = 3, value = &amp;apos;3&amp;apos; ;
savepoint mark2;
commit;&lt;/pre&gt; &lt;p&gt;对应的redo-log的记录为：&lt;/p&gt; &lt;pre&gt;xbegin           &amp;apos;b&amp;apos;: lsn=236669 xid=17,0 parentxid=0,0 crc=c01888a6 len=53
xbegin           &amp;apos;b&amp;apos;: lsn=236670 xid=17,1 parentxid=17,0 crc=cf400ba6 len=53
enq_insert       &amp;apos;I&amp;apos;: lsn=236671 filenum=13 xid=17,1 key={...} value={...} crc=8ce371e3 len=58
xcommit          &amp;apos;C&amp;apos;: lsn=236672 xid=17,1 crc=ec4a923d len=37
xbegin           &amp;apos;b&amp;apos;: lsn=236673 xid=17,2 parentxid=17,0 crc=cb7c6fa6 len=53
xbegin           &amp;apos;b&amp;apos;: lsn=236674 xid=17,3 parentxid=17,2 crc=c9a4c3a6 len=53
enq_insert       &amp;apos;I&amp;apos;: lsn=236675 filenum=13 xid=17,3 key={...} value={...} crc=641148e2 len=58
xcommit          &amp;apos;C&amp;apos;: lsn=236676 xid=17,3 crc=ec4e143d len=37
xcommit          &amp;apos;C&amp;apos;: lsn=236677 xid=17,2 crc=ec4cf43d len=37
xprepare         &amp;apos;P&amp;apos;: lsn=236678 xid=17,0 xa_xid={...} crc=76e302b4 len=67
xcommit          &amp;apos;C&amp;apos;: lsn=236679 xid=17,0 crc=ec42b43d len=37&lt;/pre&gt; &lt;p&gt;这个事务组成的一棵事务树如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="255" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/p2.png" width="254"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在tokudb中，使用{parent_id, child_id}这样一个二元组来记录一个txn和其他txn的依赖关系。这样从根事务到叶子几点的一组标号就可以唯一标示一个txn， 这一组标号列表称之为xids， xids我认为也可以称为事务号。 例如txn3的xids = {17, 2, 3 } , txn2的xids = {17, 2}, txn1的xids= {17, 1}, txn0的xids = {17, 0}。&lt;/p&gt;
 &lt;p&gt;于是对于事务中的每一个操作(xbegin/xcommit/enq_insert/xprepare)，都有一个xids来标识这个操作所在的事务号。 TokuDB中的每一条消息（insert/delete/update消息）都会携带这样一个xids事务号。这个xids事务号，在TokuDB的实现中扮演这非常重要的角色，与之相关的功能也特别复杂。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;事务管理器&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;事务管理器用来管理TokuDB存储引擎所有事务集合， 它主要维护着这几个信息：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;活跃事务列表。活跃事务列表只会记录root事务，因为根据root事务其实可以找到整棵事务树的所有child事务。 这个事务列表保存这当前时间点已经开始，但是尚未结束的所有root事务。&lt;/li&gt;
  &lt;li&gt;镜像读事务列表（snapshot read transaction）。&lt;/li&gt;
  &lt;li&gt;活跃事务的引用列表(referenced_xids)。这个概念有点不好理解，假设一个活跃事务开始(xbegin)时间点为begin_id, 提交(xcommit)的时间点为end_id。那么referenced_xids就是维护(begin_id, end_id)这样一个二元组，这个二元组的用处就是可以找到一个事务的整个生命周期的所有活跃事务，用处主要是用来做后文说到的full gc操作。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;分形树LeafEntry&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;上文分形树的树形结构中说到，在做insert/delete/update这样的操作时，会把从root到leaf的所有消息都apply到LeafNode节点中。 为了后面详细描述apply的过程，先介绍下LeafNode的存储结构。&lt;/p&gt;
 &lt;p&gt;leafNode简单来说，就是由多个leafEntry组成，每个leafEntry就是一个{k, v1, v2, … }这样的键值对， 其中v1, v2 .. 表示一个key对应的值的多个版本。具体到一个key对应得leafEntry的结构详细如下图所示。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="255" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/leaf-entry.png" width="457"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;由上图看出，一个leafEntry其实就是一个栈， 这个栈底部[0~5]这一段表示已经提交(commited transaction)的事务的Value值。栈的顶部[6~9]这一段表示当前尚未提交的活跃事务(uncommited transaction)。 栈中存放的单个元素为(txid, type, len, data)这样一个四元组，表明了这个事务对应的value取值。更通用一点讲，[0, cxrs-1]这一段栈表示已经提交的事务，本来已经提交的事务不应存在于栈中，但之所以存在，就是因为有其他事务通过snapshot read的方式引用了这些事务，因此，除非所有引用[0, cxrs-1]这段事务的所有事务都提交，否则[0, cxrs-1]这段栈的事务就不会被回收。[cxrs, cxrs+pxrs-1]这一段栈表示当前活跃的尚未提交的事务列表，当这部分事务提交时，cxrs会往后移动，最终到栈顶。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;MVCC&lt;/strong&gt;  &lt;strong&gt;实现&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;1）写入操作&lt;/p&gt;
 &lt;p&gt;这里我们认为写入操作包括三种，分别为insert / delete / commit 三种类型。对于insert和delete这两种类型的写入操作，只需要在LeafEntry的栈顶放置一个元素即可。 如下图所示：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="255" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/mvvc.png" width="533"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;对于commit操作，只需把LeafEntry的栈顶元素放到cxrs这个指针处，然后收缩栈顶指针即可。如下图所示：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="255" src="https://www.biaodianfu.com/wp-content/uploads/2016/12/cxrs.png" width="549"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;2）读取操作&lt;/p&gt;
 &lt;p&gt;对读取操作而言， 数据库一般支持多个隔离级别。MySQL的InnoDB支持Read UnCommitted(RU)、Read REPEATABLE(RR)、Read Commited(RC)、SERIALIZABLE(S)。其中RU存在脏读的情况(脏读指读取到未提交的事务)， RC/RR/RU存在幻读的情况（幻读一般指一个事务在更新时可能会更新到其他事务已经提交的记录）。&lt;/p&gt;
 &lt;p&gt;TokuDB同样支持上述4中隔离级别， 在源码实现时, ft-index将事务的读取操作按照事务隔离级别分成3类:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;TXN_SNAPSHOT_NONE : 这类不需要snapshot read， SERIALIZABLE和Read Uncommited两个隔离级别属于这一类。&lt;/li&gt;
  &lt;li&gt;TXN_SNAPSHOT_ROOT : Read REPEATABLE隔离级别属于这类。在这种其情况下， 说明事务只需要读取到root事务对应的xid之前已经提交的记录即可。&lt;/li&gt;
  &lt;li&gt;TXN_SNAPSHOT_CHILD: READ COMMITTED属于这类。在这种情况下，儿子事务A需要根据自己事务的xid来找到snapshot读的版本，因为在这个事务A开启时，可能有其他事务B做了更新，并提交，那么事务A必须读取B更新之后的结果。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;多版本记录回收&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;随着时间的推移，越来越多的老事务被提交，新事务开始执行。 在分形树中的LeafNode中commited的事务数量会越来越多，假设不想方设法把这些过期的事务记录清理掉的话，会造成BasementNode节点占用大量空间，也会造成TokuDB的数据文件存放大量无用的数据。 在TokuDB中， 清理这些过期事务的操作称之为垃圾回收（Garbage Collection）。 其实InnoDB也存在过期事务回收这么一个过程，InnoDB的同一个Key的多个版本的Value存放在undo log 页上， 当事务过期时， 后台有一个purge线程专门来复杂清理这些过期的事务，从而腾出undo log页给后面的事务使用， 这样可以控制undo log无限增长。&lt;/p&gt;
 &lt;p&gt;TokuDB存储引擎中没有类似于InnoDB的purge线程来负责清理过期事务，因为过期事务的清理都是在执行更新操作是顺便GC的。 也就是在Insert/Delete/Update这些操作执行时，都会判断以下当前的LeafEntry是否满足GC的条件， 若满足GC条件时，就删除LeafEntry中过期的事务， 重新整理LeafEntry 的内存空间。按照TokuDB源码的实现，GC分为两种类型：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Simple GC：在每次apply 消息到leafentry 时， 都会携带一个gc_info， 这个gc_info 中包含了oldest_referenced_xid这个字段。 那么simple_gc的意思是什么呢？ simple_gc就是做一次简单的GC， 直接把commited的事务列表清理掉（记住要剩下一个commit事务的记录， 否则下次查找这条commited的记录怎么找的到？ ）。这就是simple_gc， 简单暴力高效。&lt;/li&gt;
  &lt;li&gt;Full GC：full gc的触发条件和gc流程都比较复杂， 根本意图都是要清理掉过期的已经提交的事务。这里不再展开。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;本文大致介绍了TokuDB事务的隔离性实现原理， 包括TokuDB的事务表示、分形树的LeafEntry的结构、MVCC的实现流程、多版本记录回收方式这些方面的内容。 TokuDB之所有没有undo log，就是因为分形树中的更新消息本身就记录了事务的记录版本。另外， TokuDB的过期事务回收也不需要像InnoDB那样专门开启一个后台线程异步回收，而是才用在更新操作执行的过程中分摊回收。总之，由于TokuDB基于分形树之上实现事务，因而各方面的思路都有大的差异，这也是TokuDB团队的创新吧。&lt;/p&gt;
 &lt;p&gt;参考资料：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="http://docs.tokutek.com/tokudb/tokudb-index-using-tokudb.html"&gt;http://docs.tokutek.com/tokudb/tokudb-index-using-tokudb.html&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://openinx.github.io/2015/12/13/ft-mvcc/"&gt;http://openinx.github.io/2015/12/13/ft-mvcc/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="http://openinx.github.io/2015/11/25/ft-index-implement/"&gt;http://openinx.github.io/2015/11/25/ft-index-implement/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://highdb.com/tokudb-%E7%89%B9%E6%80%A7%E6%A6%82%E8%A7%88/"&gt;https://highdb.com/tokudb-%E7%89%B9%E6%80%A7%E6%A6%82%E8%A7%88/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;The post   &lt;a href="https://www.biaodianfu.com/tokudb.html" rel="nofollow"&gt;MySQL 高性能存储引擎：TokuDB初探&lt;/a&gt; appeared first on   &lt;a href="https://www.biaodianfu.com" rel="nofollow"&gt;标点符&lt;/a&gt;.&lt;/p&gt;
 &lt;div&gt;
  &lt;p&gt;Related posts:   &lt;ol&gt;
    &lt;li&gt;     &lt;a href="https://www.biaodianfu.com/the-nested-set-model.html" rel="bookmark" title="&amp;#26641;&amp;#24418;&amp;#32467;&amp;#26500;&amp;#25968;&amp;#25454;&amp;#23384;&amp;#20648;&amp;#26041;&amp;#26696;&amp;#65288;&amp;#22235;&amp;#65289;&amp;#65306;&amp;#24038;&amp;#21491;&amp;#20540;&amp;#32534;&amp;#30721;"&gt;树形结构数据存储方案（四）：左右值编码 &lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="https://www.biaodianfu.com/centos-7-2-install-percona.html" rel="bookmark" title="Centos 7.2&amp;#23433;&amp;#35013;Percona&amp;#35760;&amp;#24405;"&gt;Centos 7.2安装Percona记录 &lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;     &lt;a href="https://www.biaodianfu.com/mysql-for-beginners.html" rel="bookmark" title="MySQL &amp;#21021;&amp;#32423;&amp;#25945;&amp;#31243;&amp;#65288;&amp;#19968;&amp;#65289;"&gt;MySQL 初级教程（一） &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>程序开发 MySQL</category>
      <guid isPermaLink="true">https://itindex.net/detail/56354-mysql-%E6%80%A7%E8%83%BD-%E5%BC%95%E6%93%8E</guid>
      <pubDate>Tue, 13 Dec 2016 12:52:20 CST</pubDate>
    </item>
    <item>
      <title>MySQL数据库事务隔离级别(Transaction Isolation Level)</title>
      <link>https://itindex.net/detail/54362-mysql-%E6%95%B0%E6%8D%AE%E5%BA%93-%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB</link>
      <description>&lt;p&gt;  &lt;img alt="2012061623452758.jpg" src="http://itbokeyun.com/res/img/db/2012061623452758.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
修改事务隔离级别的方法：   &lt;br /&gt;
1.全局修改，修改  &lt;code&gt;mysql.ini&lt;/code&gt;配置文件，在最后加上   &lt;br /&gt;
1 #可选参数有：  &lt;code&gt;READ-UNCOMMITTED&lt;/code&gt;,   &lt;code&gt;READ-COMMITTED&lt;/code&gt;,   &lt;code&gt;REPEATABLE-READ&lt;/code&gt;,   &lt;code&gt;SERIALIZABLE&lt;/code&gt;.   &lt;br /&gt;
2   &lt;code&gt;[mysqld]&lt;/code&gt;   &lt;br /&gt;
3   &lt;code&gt;transaction-isolation = REPEATABLE-READ&lt;/code&gt;   &lt;br /&gt;
这里全局默认是  &lt;code&gt;REPEATABLE-READ&lt;/code&gt;,其实MySQL本来默认也是这个级别   &lt;br /&gt;
2.对当前session修改，在登录mysql客户端后，执行命令：&lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;set session transaction isolation level read uncommitted;
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;  &lt;img alt="2012061623491621" src="http://itbokeyun.com/res/img/db/2012061623491621.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
要记住mysql有一个  &lt;code&gt;autocommit&lt;/code&gt;参数，默认是  &lt;code&gt;on&lt;/code&gt;，他的作用是每一条单独的查询都是一个事务，并且自动开始，自动提交（执行完以后就自动结束了，如果你要适用  &lt;code&gt;select for update&lt;/code&gt;，而不手动调用  &lt;code&gt;start transaction&lt;/code&gt;，这个  &lt;code&gt;for update&lt;/code&gt;的行锁机制等于没用，因为行锁在自动提交后就释放了），所以事务隔离级别和锁机制即使你不显式调用  &lt;code&gt;start transaction&lt;/code&gt;，这种机制在单独的一条查询语句中也是适用的，分析锁的运作的时候一定要注意这一点   &lt;/p&gt;

 &lt;h2&gt;锁机制：&lt;/h2&gt;

 &lt;p&gt;共享锁：由读表操作加上的锁，加锁后其他用户只能获取该表或行的共享锁，不能获取排它锁，也就是说只能读不能写            &lt;br /&gt;
排它锁：由写表操作加上的锁，加锁后其他用户不能获取该表或行的任何锁，典型是mysql事务中    &lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;start transaction;
select * from user where userId = 1 for update;
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;执行完这句以后   &lt;br /&gt;
1）当其他事务想要获取共享锁，比如事务隔离级别为  &lt;code&gt;SERIALIZABLE&lt;/code&gt;的事务，执行   &lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;　　select * from user;
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;将会被挂起，因为  &lt;code&gt;SERIALIZABLE&lt;/code&gt;的  &lt;code&gt;select&lt;/code&gt;语句需要获取共享锁   &lt;br /&gt;
2）当其他事务执行   &lt;/p&gt;

 &lt;pre&gt;  &lt;code&gt;　　select * from user where userId = 1 for update;
　　update user set userAge = 100 where userId = 1; 
&lt;/code&gt;&lt;/pre&gt;

 &lt;p&gt;也会被挂起，因为  &lt;code&gt;for update&lt;/code&gt;会获取这一行数据的排它锁，需要等到前一个事务释放该排它锁才可以继续进行      &lt;/p&gt;

 &lt;h2&gt;锁的范围:&lt;/h2&gt;

 &lt;p&gt;行锁: 对某行记录加上锁   &lt;br /&gt;
表锁: 对整个表加上锁   &lt;br /&gt;
这样组合起来就有,行级共享锁,表级共享锁,行级排他锁,表级排他锁   &lt;br /&gt;
下面来说说不同的事务隔离级别的实例效果，例子使用InnoDB，开启两个客户端A，B，在A中修改事务隔离级别，在B中开启事务并修改数据，然后在A中的事务查看B的事务修改效果(两个客户端相当于是两个连接，在一个客户端中的修改参数变量的值是不会影响到另外的一个客户端的)：&lt;/p&gt;

 &lt;h2&gt;1.READ-UNCOMMITTED(读取未提交内容)级别&lt;/h2&gt;

 &lt;p&gt;1)A修改事务级别并开始事务，对user表做一次查询   &lt;br /&gt;
  &lt;img alt="2012061623555483" src="http://itbokeyun.com/res/img/db/2012061623555483.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
2)B更新一条记录   &lt;br /&gt;
  &lt;img alt="2012061623571635" src="http://itbokeyun.com/res/img/db/2012061623571635.jpg"&gt;&lt;/img&gt;  &lt;br /&gt;
3）此时B事务还未提交，A在事务内做一次查询，发现查询结果已经改变   &lt;br /&gt;
  &lt;img alt="2012061623582736" src="http://itbokeyun.com/res/img/db/2012061623582736.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
4）B进行事务回滚   &lt;br /&gt;
  &lt;img alt="2012061623595947" src="http://itbokeyun.com/res/img/db/2012061623595947.jpg"&gt;&lt;/img&gt;  &lt;br /&gt;
5）A再做一次查询，查询结果又变回去了   &lt;br /&gt;
  &lt;img alt="2012061700003346" src="http://itbokeyun.com/res/img/db/2012061700003346.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
6）A表对user表数据进行修改   &lt;br /&gt;
  &lt;img alt="2012061701354025" src="http://itbokeyun.com/res/img/db/2012061701354025.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
7）B表重新开始事务后，对user表记录进行修改，修改被挂起，直至超时，但是对另一条数据的修改成功，说明A的修改对user表的数据行加行共享锁(因为可以使用select)   &lt;br /&gt;
  &lt;img alt="2012061701342352.jpg" src="http://itbokeyun.com/res/img/db/2012061701342352.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
可以看出  &lt;code&gt;READ-UNCOMMITTED&lt;/code&gt;隔离级别，当两个事务同时进行时，即使事务没有提交，所做的修改也会对事务内的查询做出影响，这种级别显然很不安全。但是在表对某行进行修改时，会对该行加上行共享锁   &lt;/p&gt;

 &lt;h2&gt;2. READ-COMMITTED（读取提交内容）&lt;/h2&gt;

 &lt;p&gt;1）设置A的事务隔离级别，并进入事务做一次查询   &lt;br /&gt;
  &lt;img alt="2012061700054697.jpg" src="http://itbokeyun.com/res/img/db/2012061700054697.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
2）B开始事务，并对记录进行修改   &lt;br /&gt;
  &lt;img alt="2012061700061597" src="http://itbokeyun.com/res/img/db/2012061700061597.jpg"&gt;&lt;/img&gt;      &lt;br /&gt;
3）A再对  &lt;code&gt;user&lt;/code&gt;表进行查询，发现记录没有受到影响   &lt;br /&gt;
  &lt;img alt="2012061700071978" src="http://itbokeyun.com/res/img/db/2012061700071978.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
4）B提交事务   &lt;br /&gt;
  &lt;img alt="2012061700082662" src="http://itbokeyun.com/res/img/db/2012061700082662.jpg"&gt;&lt;/img&gt;  &lt;br /&gt;
5）A再对user表查询，发现记录被修改   &lt;br /&gt;
  &lt;img alt="2012061700091610" src="http://itbokeyun.com/res/img/db/2012061700091610.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
6）A对user表进行修改   &lt;br /&gt;
  &lt;img alt="2012061700560848" src="http://itbokeyun.com/res/img/db/2012061700560848.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
7）B重新开始事务，并对user表同一条进行修改，发现修改被挂起，直到超时，但对另一条记录修改，却是成功，说明A的修改对user表加上了行共享锁(因为可以select)   &lt;br /&gt;
  &lt;img alt="2012061700565923" src="http://itbokeyun.com/res/img/db/2012061700565923.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
  &lt;img alt="2012061701045114" src="http://itbokeyun.com/res/img/db/2012061701045114.jpg"&gt;&lt;/img&gt;   　
  &lt;code&gt;READ-COMMITTED&lt;/code&gt;事务隔离级别，只有在事务提交后，才会对另一个事务产生影响，并且在对表进行修改时，会对表数据行加上行共享锁    &lt;/p&gt;

 &lt;h2&gt;3. REPEATABLE-READ(可重读)&lt;/h2&gt;

 &lt;p&gt;1）A设置事务隔离级别，进入事务后查询一次   &lt;br /&gt;
  &lt;img alt="2012061700133821" src="http://itbokeyun.com/res/img/db/2012061700133821.jpg"&gt;&lt;/img&gt;  &lt;br /&gt;
2）B开始事务，并对user表进行修改   &lt;br /&gt;
  &lt;img alt="2012061700220611" src="http://itbokeyun.com/res/img/db/2012061700220611.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
3）A查看user表数据，数据未发生改变   &lt;br /&gt;
  &lt;img alt="2012061700224979.jpg" src="http://itbokeyun.com/res/img/db/2012061700224979.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
4）B提交事务   &lt;br /&gt;
  &lt;img alt="2012061700240125" src="http://itbokeyun.com/res/img/db/2012061700240125.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
5）A再进行一次查询，结果还是没有变化   &lt;br /&gt;
  &lt;img alt="2012061700244168" src="http://itbokeyun.com/res/img/db/2012061700244168.jpg"&gt;&lt;/img&gt;    &lt;br /&gt;
6）A提交事务后，再查看结果，结果已经更新   &lt;br /&gt;
  &lt;img alt="2012061700270416" src="http://itbokeyun.com/res/img/db/2012061700270416.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
7）A重新开始事务，并对user表进行修改   &lt;br /&gt;
  &lt;img alt="2012061700593855" src="http://itbokeyun.com/res/img/db/2012061700593855.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
8）B表重新开始事务，并对user表进行修改，修改被挂起，直到超时，对另一条记录修改却成功，说明A对表进行修改时加了行共享锁(可以select)   &lt;br /&gt;
  &lt;img alt="2012061701011617" src="http://itbokeyun.com/res/img/db/2012061701011617.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
  &lt;code&gt;REPEATABLE-READ&lt;/code&gt;事务隔离级别，当两个事务同时进行时，其中一个事务修改数据对另一个事务不会造成影响，即使修改的事务已经提交也不会对另一个事务造成影响。   &lt;br /&gt;
在事务中对某条记录修改，会对记录加上行共享锁，直到事务结束才会释放。   &lt;/p&gt;

 &lt;h2&gt;4.SERIERLIZED(可串行化)&lt;/h2&gt;

 &lt;p&gt;1）修改A的事务隔离级别，并作一次查询   &lt;br /&gt;
  &lt;img alt="2012061700310467" src="http://itbokeyun.com/res/img/db/2012061700310467.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
2）B对表进行查询，正常得出结果，可知对user表的查询是可以进行的   &lt;br /&gt;
  &lt;img alt="2012061700355495" src="http://itbokeyun.com/res/img/db/2012061700355495.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
3）B开始事务，并对记录做修改，因为A事务未提交，所以B的修改处于等待状态，等待A事务结束，最后超时，说明A在对user表做查询操作后，对表加上了共享锁   &lt;br /&gt;
  &lt;img alt="2012061700334486" src="http://itbokeyun.com/res/img/db/2012061700334486.jpg"&gt;&lt;/img&gt;   &lt;br /&gt;
SERIALIZABLE事务隔离级别最严厉，在进行查询时就会对表或行加上共享锁，其他事务对该表将只能进行读操作，而不能进行写操作。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql</category>
      <guid isPermaLink="true">https://itindex.net/detail/54362-mysql-%E6%95%B0%E6%8D%AE%E5%BA%93-%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB</guid>
      <pubDate>Sat, 19 Sep 2015 08:00:00 CST</pubDate>
    </item>
    <item>
      <title>MySQL DBA面试全揭秘</title>
      <link>https://itindex.net/detail/55185-mysql-dba-%E9%9D%A2%E8%AF%95</link>
      <description>&lt;div&gt;
  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;
  &lt;p&gt;本文起源于有同学留言回复说想了解下MySQL DBA面试时可能涉及到的知识要点，那我们今天就来大概谈谈吧。&lt;/p&gt;
  &lt;p&gt;MySQL DBA职位最近几年特别热门，不少朋友让我帮忙推荐什么的，也有很多公司找不到合适的DBA。原因很简单，优秀的人才要么被大公司圈起来了，要么被创业公司高薪挖走，如果你既不是大公司，又不能出得起高价钱的土豪公司，想要找到优秀人才的几率堪比买彩票中奖的概率，哈哈。&lt;/p&gt;
  &lt;p&gt;本文可以作为MySQL DBA面试官，以及候选人的   &lt;em&gt;双向参考&lt;/em&gt; ：）&lt;/p&gt;
  &lt;h2&gt;面试流程&lt;/h2&gt;
  &lt;p&gt;接下来先说下我以往在做MySQL DBA面试时的过程（套路）：&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;
    &lt;p&gt;1.先自我介绍后，再让候选人花2-5分钟做下     &lt;strong&gt;自我简介&lt;/strong&gt;&lt;/p&gt;
    &lt;blockquote&gt;     &lt;ul&gt;
      &lt;li&gt;
       &lt;p&gt;有不少人可能对自我简介这个环节嗤之以鼻，觉得多此一举，尤其是技术能力相对较好的更是如此。其实不然，通过短短2-5分钟的自我简介，很快就能考察出候选人是否有用心准备本次面试，其        &lt;strong&gt;归纳总结&lt;/strong&gt;能力，以及个人自信心等多方面信息。&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;因此，如果候选人看中这次面试机会的话，还请好好做下功课，做足准备。比如了解下目标公司的大致情况，主营业务，产品特色。可能的话，找同行打听可能的面试官背景信息，没准是校友、以前在同一家公司呆过、或者有其他共同点，这可能会使得面试过程更为顺利。&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;有心的候选人在面试官自我介绍时，就可以趁机也考察对方的情况。通常第一轮面试官很可能是你未来的直接主管，从面试过程中你和对方的沟通交流是否顺利也可预见到未来工作上配合的顺利程度。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;2.暖身完，就开始进入主题，从候选人的简历入手，挑选其中感兴趣的关键点     &lt;strong&gt;逐条交流&lt;/strong&gt;，有几个要点：&lt;/p&gt;
    &lt;blockquote&gt;     &lt;ul&gt;
      &lt;li&gt;
       &lt;p&gt;和应聘职位关联性较高的技术要素，需要逐个过一遍，大致了解候选人对于这些技术要素的掌握程度；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;挑选2-3个技术关键点，对候选人        &lt;strong&gt;穷追猛打深入探讨&lt;/strong&gt;，了解其真正的掌握程度，是泛泛的了解，还是知其所以然的那种，由此也可以考察候选人的        &lt;strong&gt;学习方法、心态&lt;/strong&gt;，是随波逐流抑或专精专注。&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;候选人每次        &lt;strong&gt;跳槽经历&lt;/strong&gt;也需要关注，究竟何种原因导致跳槽，每次跳槽是否其职业层次也跟着提高。由此考擦候选人的职业规划是否清晰，是否过于随性（任性）。否则的话，可能在下一家公司也待不了多久就会因为各种原因（最常见的就是薪资、或者对主管不服气）而跳槽。&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;候选人简历中特意提及的重点项目、事件、荣誉，也可以做深入的交流。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;3.重点技术要素考察完毕，可以聊聊     &lt;strong&gt;职业发展&lt;/strong&gt;等其他方面的话题，比如&lt;/p&gt;
    &lt;blockquote&gt;     &lt;ul&gt;
      &lt;li&gt;
       &lt;p&gt;为什么选择我司；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;如果还有其他公司的机会，如何权衡选择哪个offer，最主要的判断标准是什么；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;期望什么样的工作环境，团队环境，以及哪种风格的主管；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;对什么事情最在乎，或最不在乎；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;除了薪资福利，对公司、工作的期望是怎样的。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
  &lt;h2&gt;专业技术考察&lt;/h2&gt;
  &lt;p&gt;具体到   &lt;strong&gt;技术实力考查&lt;/strong&gt;上，通常可以关注几个要点：&lt;/p&gt;
  &lt;h3&gt;基础知识考察&lt;/h3&gt;
  &lt;ol&gt;
   &lt;li&gt;
    &lt;p&gt;基础知识，尤其是一些理论知识，例如：&lt;/p&gt;
    &lt;blockquote&gt;     &lt;ul&gt;
      &lt;li&gt;
       &lt;p&gt;MySQL有哪些索引类型，这是个半开放式命题；&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    &lt;p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
  &lt;ul&gt;
   &lt;li&gt;
    &lt;p&gt;从     &lt;strong&gt;数据结构&lt;/strong&gt;角度可分为     &lt;strong&gt;B+树索引&lt;/strong&gt;、     &lt;strong&gt;哈希索引&lt;/strong&gt;、以及不常用的     &lt;strong&gt;FULLTEXT索引&lt;/strong&gt;（现在MyISAM和InnoDB引擎都支持了）和     &lt;strong&gt;R-Tree索引&lt;/strong&gt;（用于对GIS数据类型创建SPATIAL索引）；&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;从物理存储角度可分为     &lt;strong&gt;聚集索引&lt;/strong&gt;     &lt;code&gt;（clustered index）&lt;/code&gt;、     &lt;strong&gt;非聚集索引&lt;/strong&gt;     &lt;code&gt;（non-clustered index）&lt;/code&gt;；&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;从逻辑角度可分为     &lt;strong&gt;主键索引&lt;/strong&gt;、     &lt;strong&gt;普通索引&lt;/strong&gt;，或者     &lt;strong&gt;单列索引&lt;/strong&gt;、     &lt;strong&gt;多列索引&lt;/strong&gt;、     &lt;strong&gt;唯一索引&lt;/strong&gt;、     &lt;strong&gt;非唯一索引&lt;/strong&gt;等等。需要掌握这些不同概念之间的区别，例如     &lt;code&gt;主键索引和唯一索引的区别是什么&lt;/code&gt;。&lt;/p&gt;
    &lt;blockquote&gt;     &lt;ul&gt;
      &lt;li&gt;
       &lt;p&gt;为什么InnoDB表最好要有自增列做主键；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;为什么需要设置双1才能保证主从数据的一致性；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;有几种        &lt;em&gt;binlog格式*&lt;/em&gt;，及其区别是什么；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;如何确认MySQL replication真正的        &lt;strong&gt;复制延迟&lt;/strong&gt;是多少；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;有过哪些印象深刻的实践经验。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    &lt;p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;通过考察候选人的基础知识掌握程度，可侧面反映候选人对学习的态度，是否仅浅层面的了解。&lt;/p&gt;
  &lt;h3&gt;核心技术能力考察&lt;/h3&gt;
  &lt;ol&gt;
   &lt;li&gt;
    &lt;p&gt;核心关键技术能力，例如：&lt;/p&gt;
    &lt;blockquote&gt;     &lt;ul&gt;
      &lt;li&gt;
       &lt;p&gt;怎么做的MySQL        &lt;strong&gt;备份恢复&lt;/strong&gt;方案及策略，为什么那么做，用什么工具；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;MySQL        &lt;strong&gt;主从复制&lt;/strong&gt;的具体原理是什么，实际使用过程中，遇到过哪些坑，怎么解决的；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;对一个        &lt;strong&gt;大表做在线DDL&lt;/strong&gt;，怎么进行实施的才能尽可能降低影响；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;MyISAM和InnoDB都有哪些不同之处；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;        &lt;strong&gt;InnoDB的体系结构&lt;/strong&gt;是否能讲的清楚，至少说出个大概；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;假设现在服务器负载很高，都有哪些性能问题        &lt;strong&gt;排查思路&lt;/strong&gt;，以及优化的方案；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;什么是        &lt;strong&gt;死锁&lt;/strong&gt;，什么是        &lt;strong&gt;锁等待&lt;/strong&gt;，如何优化；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;关于MySQL及InnoDB优化，讲讲自己的见解或者        &lt;strong&gt;实践经验&lt;/strong&gt;；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;如何确定及实施MySQL        &lt;strong&gt;高可用方案&lt;/strong&gt;，不同方案的优缺点对比；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;一定规模的MySQL        &lt;strong&gt;自动化运维&lt;/strong&gt;经验如何；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;在        &lt;strong&gt;SCHEMA设计&lt;/strong&gt;方面的经验如何；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;基于MySQL所做过的一些数据库        &lt;strong&gt;架构&lt;/strong&gt;方案设计、实施经验。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    &lt;p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
  &lt;p&gt;通过考察候选人对这些核心关键技术的掌握程度，可知晓候选人对深层次知识的掌握情况，除了实践，理论方面掌握了多少。&lt;/p&gt;
  &lt;h3&gt;潜力考察&lt;/h3&gt;
  &lt;ol&gt;
   &lt;li&gt;
    &lt;p&gt;发展潜力以及学习能力，例如：&lt;/p&gt;
    &lt;blockquote&gt;     &lt;ul&gt;
      &lt;li&gt;
       &lt;p&gt;对        &lt;strong&gt;Linux&lt;/strong&gt;的掌握程度，以及        &lt;code&gt;Shell&lt;/code&gt;、        &lt;code&gt;Python&lt;/code&gt;、        &lt;code&gt;Perl&lt;/code&gt;等常用运维开发语言的掌握程度；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;对服务器        &lt;strong&gt;硬件&lt;/strong&gt;设备，        &lt;strong&gt;存储&lt;/strong&gt;设备的了解程度；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;对        &lt;strong&gt;信息安全&lt;/strong&gt;，网络知识的了解程度；&lt;/p&gt;
&lt;/li&gt;
      &lt;li&gt;
       &lt;p&gt;其他语言，例如        &lt;code&gt;C&lt;/code&gt;、        &lt;code&gt;C++&lt;/code&gt;、        &lt;code&gt;JAVA&lt;/code&gt;、        &lt;code&gt;PHP&lt;/code&gt;、        &lt;code&gt;GO&lt;/code&gt;是否有所了解。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
    &lt;p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
  &lt;p&gt;这些知识对一般的DBA可能不太重要，但想要成为资深DBA或数据库架构师的话，这些知识是必不可少的。&lt;/p&gt;
  &lt;p&gt;先啰嗦说这么多吧，希望对有志成为DBA的同学有些帮助，加油加油   &lt;img alt="&amp;#8598;" src="https://s.w.org/images/core/emoji/72x72/2196.png"&gt;&lt;/img&gt;(^ω^)   &lt;img alt="&amp;#8599;" src="https://s.w.org/images/core/emoji/72x72/2197.png"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;/div&gt;
 &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;
 &lt;h2&gt;抱歉猜想失败，您看看下面的文章有用吗？&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;2013 年 5 月 15 日 --    &lt;a href="http://ourmysql.com/archives/1235" title="MySQL 5.6 &amp;#26032;&amp;#22686;&amp;#30340;&amp;#20004;&amp;#20010;&amp;#23494;&amp;#30721;&amp;#23433;&amp;#20840;&amp;#31574;&amp;#30053;&amp;#20307;&amp;#39564;"&gt;MySQL 5.6 新增的两个密码安全策略体验&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2008 年 10 月 9 日 --    &lt;a href="http://ourmysql.com/archives/127" title="MySQL &amp;#30340;&amp;#25968;&amp;#25454;&amp;#31867;&amp;#22411;&amp;#21644;&amp;#24314;&amp;#24211;&amp;#31574;&amp;#30053;"&gt;MySQL 的数据类型和建库策略&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2008 年 10 月 25 日 --    &lt;a href="http://ourmysql.com/archives/197" title="MySQL &amp;#24615;&amp;#33021;&amp;#20248;&amp;#21270;&amp;#65288;INNODB&amp;#19979;&amp;#20462;&amp;#25913;&amp;#34920;&amp;#32467;&amp;#26500;&amp;#65289;"&gt;MySQL 性能优化（INNODB下修改表结构）&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2016 年 1 月 26 日 --    &lt;a href="http://ourmysql.com/archives/1414" title="&amp;#22914;&amp;#20309;&amp;#25104;&amp;#20026;MySQL DBA"&gt;如何成为MySQL DBA&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2009 年 5 月 14 日 --    &lt;a href="http://ourmysql.com/archives/558" title="mysql&amp;#36830;&amp;#25509;&amp;#36807;&amp;#22810;&amp;#30340;&amp;#22788;&amp;#29702;"&gt;mysql连接过多的处理&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2011 年 1 月 28 日 --    &lt;a href="http://ourmysql.com/archives/908" title="&amp;#22788;&amp;#29702;Mysql&amp;#30340;MySql-bin.0000X&amp;#26085;&amp;#24535;&amp;#25991;&amp;#20214;"&gt;处理Mysql的MySql-bin.0000X日志文件&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2009 年 6 月 28 日 --    &lt;a href="http://ourmysql.com/archives/595" title="Xtrabackup&amp;#65306;MySQL DBA&amp;#30340;&amp;#24517;&amp;#22791;&amp;#24037;&amp;#20855;"&gt;Xtrabackup：MySQL DBA的必备工具&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 3 月 26 日 --    &lt;a href="http://ourmysql.com/archives/1090" title="Mysql&amp;#28304;&amp;#30721;&amp;#23398;&amp;#20064;&amp;#8212;&amp;#8212;&amp;#35789;&amp;#27861;&amp;#20998;&amp;#26512;MYSQLlex"&gt;Mysql源码学习——词法分析MYSQLlex&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2010 年 12 月 14 日 --    &lt;a href="http://ourmysql.com/archives/897" title="Handler-Socket Plugin for MySQL &amp;#8211; SQL&amp;#30340;&amp;#21151;&amp;#33021;&amp;#12289;NoSQL&amp;#30340;&amp;#24615;&amp;#33021;"&gt;Handler-Socket Plugin for MySQL – SQL的功能、NoSQL的性能&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2008 年 10 月 24 日 --    &lt;a href="http://ourmysql.com/archives/183" title="MySQL Replication(&amp;#22797;&amp;#21046;)&amp;#22522;&amp;#26412;&amp;#21407;&amp;#29702;"&gt;MySQL Replication(复制)基本原理&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MySQL基础知识 面试</category>
      <guid isPermaLink="true">https://itindex.net/detail/55185-mysql-dba-%E9%9D%A2%E8%AF%95</guid>
      <pubDate>Sun, 14 Feb 2016 06:29:05 CST</pubDate>
    </item>
    <item>
      <title>如何成为MySQL DBA</title>
      <link>https://itindex.net/detail/55127-mysql-dba</link>
      <description>&lt;p&gt;        互联网高速发展的成功，得益于MySQL数据库的给力支持。MySQL本身发展的速度较快，性能方面提升显著，让传统企业也有想法使用MySQL提供服务。目前看来MySQL DBA的缺口非常大。所以欢迎加入到MySQL DBA的团队中来。&lt;/p&gt;
 &lt;p&gt;        有同学一提到MySQL DBA或是DBA都把高难度入门联系到一块。我从事MySQL DBA差不多10几年了，在这里我也给大家讲述一下怎么成为一名MySQL DBA, 少走湾路，快速成为MySQL DBA。&lt;/p&gt;
 &lt;p&gt;        首先MySQL大多是跑在Linux环境上的，所以我们需要学习一下Linux的知识，最基础的需要了解：&lt;/p&gt;
 &lt;ul&gt;
  &lt;ul&gt;
   &lt;li&gt;
    &lt;p&gt;Linux的安装及目录结构意义&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;常用的Linux命令，大概20多个&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;网络基本知识，进一步了解网关及路由相关概念,dhcp&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;撑握一定的Linux网络服务，如果LAMP结构，dns,  ftp 等&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
 &lt;p&gt;      其实具备上面的条件，你就可以开始MySQL DBA的学习之路了，其它的知识，可以在学习在补。 MySQL DBA比较特殊，不象别的数据库一样，需要分为开发DBA，运维DBA，基本上MySQL DBA要把所有的活都干下来，因为MySQL太简单了，很好搞定。 那么下面给大家说一下MySQL DBA的学习路径：&lt;/p&gt;
 &lt;ul&gt;
  &lt;ul&gt;
   &lt;li&gt;
    &lt;p&gt;了解MySQL的版本意义&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;学习MySQL的安装方式，从根本下理解MySQL的启动原理&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;学习SQL语言，把高中数学中集合知识好好在学习一下，再来看SQL语言&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;学习SQL在MySQL使用中的一些规范（这些最好找有经验的请教一下，或是看看大会中别人分享的学习思考）&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;学习MySQL的高级特性： 触发器，事件，存储过程编程，分区等&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;学习MySQL的复制，了解复制原理及实现及相关高可用配合中有什么不足&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;学习PXC结构的实现，了解运维中的坑&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;系统的学习一下MySQL的体系结构，思想如何做MySQL优化，总结配置优化&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;学习MySQL的备份恢复，这块可能需要多一些时间好好的系统的学习一下&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;掌握几种压力测试的方法，方便优化后能做一个验证&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;深入理解Innodb的体系结构，全面理解一下事务及锁的实现，能解析出来一个update tb1 set col1=col1+1 where pk=10;  这样的语句在Innodb都干了什么了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
 &lt;p&gt;  可以说上面的东西并不是很难，基本上只要认真努力，都可以进入到MySQL DBA的大门了。当然如果想进入更高级的DBA工作环境，还是需要点更高级的知识：&lt;/p&gt;
 &lt;ul&gt;
  &lt;ul&gt;
   &lt;li&gt;
    &lt;p&gt;理解MySQL的特点，方便在实际环境决择&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;理解操作系统的IO调度，内存分配，CPU使用等&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;理解复制的优缺点及改善的方法&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;理解系统监控及系统瓶颈点分析&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;有一定的问题排查思路&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;可以全面的理解高可用的实现及自定义实现高可用架构&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;有一定的平台管理意识及实现能力&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;理解拆分的作用及实现的方法&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
 &lt;p&gt;     万里长征驶于足下，整体上来说踏上技术这条路，就要学会保持学习，能持续更新，敢于多关注现在的资源，利用现有的资源走上更的境界：&lt;/p&gt;
 &lt;ul&gt;
  &lt;ul&gt;
   &lt;li&gt;
    &lt;p&gt;了解现有硬件的特点&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;了解业务的实现及难点&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;了解常见NoSQL的使用 &lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;关注新技术&lt;/p&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;进入MySQL DBA架构师级别可以行容量的规划&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
 &lt;p&gt;     如果想系统提高一下MySQL DBA技术的，如果你是在职人员，可以利用周六时间来参加一下我的《MySQL DBA周末提高加班》，如果你现在没有工作，那可以考虑拼博3个月参加一下我组织的《MySQL DBA零基础就业脱产班》 一同走上MySQL DBA岗位。请联系我： QQ/weixin: 82565387. 2016年一起腾飞。&lt;/p&gt;
 &lt;p&gt;     在技术的成长之路，还有有一个比较重要的路径： 多参加同行业技术交流，关注行业技术实现。推荐： MySQL中国用户组， FireFlyClub 也欢迎各位加入。&lt;/p&gt;
 &lt;h2&gt;猜您喜欢&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;2013 年 5 月 1 日 --    &lt;a href="http://ourmysql.com/archives/1221" title="&amp;#20114;&amp;#32852;&amp;#32593;DBA&amp;#38656;&amp;#35201;&amp;#20570;&amp;#37027;&amp;#20123;&amp;#20107;"&gt;互联网DBA需要做那些事&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 11 月 29 日 --    &lt;a href="http://ourmysql.com/archives/1149" title="&amp;#38754;&amp;#23545;&amp;#19968;&amp;#20010;&amp;#20840;&amp;#26032;&amp;#30340;&amp;#29615;&amp;#22659;,&amp;#20316;&amp;#20026;&amp;#19968;&amp;#20010;Mysql DBA,&amp;#39318;&amp;#20808;&amp;#24212;&amp;#35813;&amp;#20102;&amp;#35299;&amp;#20160;&amp;#20040;?"&gt;面对一个全新的环境,作为一个Mysql DBA,首先应该了解什么?&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 3 月 21 日 --    &lt;a href="http://ourmysql.com/archives/1051" title="DBA&amp;#24212;&amp;#35813;&amp;#20855;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#26679;&amp;#30340;&amp;#32032;&amp;#36136;&amp;#65311;"&gt;DBA应该具有什么样的素质？&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2010 年 6 月 1 日 --    &lt;a href="http://ourmysql.com/archives/829" title="DBA&amp;#24037;&amp;#20316;&amp;#21021;&amp;#20307;&amp;#39564;&amp;#20043;&amp;#24515;&amp;#24778;&amp;#32966;&amp;#25112;"&gt;DBA工作初体验之心惊胆战&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2009 年 3 月 30 日 --    &lt;a href="http://ourmysql.com/archives/503" title="DBA&amp;#26410;&amp;#26469;&amp;#30340;&amp;#21457;&amp;#23637;&amp;#26041;&amp;#21521;"&gt;DBA未来的发展方向&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MySQL基础知识 DBA</category>
      <guid isPermaLink="true">https://itindex.net/detail/55127-mysql-dba</guid>
      <pubDate>Wed, 27 Jan 2016 07:44:54 CST</pubDate>
    </item>
    <item>
      <title>MySQL processlist中哪些状态要引起关注</title>
      <link>https://itindex.net/detail/55300-mysql-processlist-%E7%8A%B6%E6%80%81</link>
      <description>&lt;p&gt;    一般而言，我们在processlist结果中如果经常能看到某些SQL的话，至少可以说明这些SQL的频率很高，通常需要对这些SQL进行进一步优化。&lt;/p&gt;
 &lt;p&gt;    今天我们要说的是，在processlist中，看到哪些运行状态时要引起关注，主要有下面几个：&lt;/p&gt;
 &lt;table border="1" cellpadding="1" cellspacing="1"&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;状态&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;建议&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;copy to tmp table&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;执行ALTER TABLE修改表结构时    &lt;strong&gt;建议：&lt;/strong&gt;放在凌晨执行或者采用类似pt-osc工具&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Copying to tmp table&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;拷贝数据到内存中的临时表，常见于GROUP BY操作时    &lt;strong&gt;建议：&lt;/strong&gt;创建适当的索引&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Copying to tmp table on disk&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;临时结果集太大，内存中放不下，需要将内存中的临时表拷贝到磁盘上，形成 #sql***.MYD、#sql***.MYI(在5.6及更高的版本，临时表可以改成InnoDB引擎了，可以参考选项    &lt;strong&gt;default_tmp_storage_engine&lt;/strong&gt;)    &lt;strong&gt;建议：&lt;/strong&gt;创建适当的索引，并且适当加大    &lt;strong&gt;sort_buffer_size/tmp_table_size/max_heap_table_size&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Creating sort index&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;当前的SELECT中需要用到临时表在进行ORDER BY排序    &lt;strong&gt;建议：&lt;/strong&gt;创建适当的索引&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Creating tmp table&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;创建基于内存或磁盘的临时表，当从内存转成磁盘的临时表时，状态会变成：Copying to tmp table on disk    &lt;strong&gt;建议：&lt;/strong&gt;创建适当的索引，或者少用UNION、视图(VIEW)、子查询(SUBQUERY)之类的，确实需要用到临时表的时候，可以在session级临时适当调大     &lt;strong&gt;tmp_table_size/max_heap_table_size&lt;/strong&gt; 的值&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Reading from net&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;表示server端正通过网络读取客户端发送过来的请求    &lt;strong&gt;建议：&lt;/strong&gt;减小客户端发送数据包大小，提高网络带宽/质量&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Sending data&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;从server端发送数据到客户端，也有可能是接收存储引擎层返回的数据，再发送给客户端，数据量很大时尤其经常能看见备注：Sending Data不是网络发送，是从硬盘读取，发送到网络是Writing to net    &lt;strong&gt;建议：&lt;/strong&gt;通过索引或加上LIMIT，减少需要扫描并且发送给客户端的数据量&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Sorting result&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;正在对结果进行排序，类似Creating sort index，不过是正常表，而不是在内存表中进行排序    &lt;strong&gt;建议：&lt;/strong&gt;创建适当的索引&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;statistics&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;进行数据统计以便解析执行计划，如果状态比较经常出现，有可能是磁盘IO性能很差    &lt;strong&gt;建议：&lt;/strong&gt;查看当前io性能状态，例如iowait&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Waiting for global read lock&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;FLUSH TABLES WITH READ LOCK整等待全局读锁    &lt;strong&gt;建议：&lt;/strong&gt;不要对线上业务数据库加上全局读锁，通常是备份引起，可以放在业务低谷期间执行或者放在slave服务器上执行备份&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Waiting for tables,&lt;/strong&gt;    &lt;strong&gt;Waiting for table flush&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;FLUSH TABLES, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE, OPTIMIZE TABLE等需要刷新表结构并重新打开    &lt;strong&gt;建议：&lt;/strong&gt;不要对线上业务数据库执行这些操作，可以放在业务低谷期间执行&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Waiting for lock_type lock&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;等待各种类型的锁：• Waiting for event metadata lock• Waiting for global read lock• Waiting for schema metadata lock• Waiting for stored function metadata lock• Waiting for stored procedure metadata lock
    &lt;p&gt;    • Waiting for table level lock&lt;/p&gt;
    &lt;p&gt;    • Waiting for table metadata lock&lt;/p&gt;
    &lt;p&gt;    • Waiting for trigger metadata lock&lt;/p&gt;
    &lt;p&gt;         &lt;strong&gt;建议：&lt;/strong&gt;比较常见的是上面提到的global read lock以及table metadata lock，建议不要对线上业务数据库执行这些操作，可以放在业务低谷期间执行。如果是table level lock，通常是因为还在使用MyISAM引擎表，赶紧转投InnoDB引擎吧，别再老顽固了&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;    更多详情可参考官方手册：  &lt;a href="http://dev.mysql.com/doc/refman/5.6/en/general-thread-states.html" target="_blank"&gt;8.14.2 General Thread States&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;猜您喜欢&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;2016 年 2 月 21 日 --    &lt;a href="http://ourmysql.com/archives/1432" title="MySQL processlist&amp;#20013;&amp;#26368;&amp;#21738;&amp;#20123;&amp;#29366;&amp;#24577;&amp;#35201;&amp;#24341;&amp;#36215;&amp;#20851;&amp;#27880;"&gt;MySQL processlist中最哪些状态要引起关注&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 2 月 19 日 --    &lt;a href="http://ourmysql.com/archives/1008" title="mysql&amp;#25191;&amp;#34892;show processlist&amp;#20986;&amp;#29616;&amp;#8221;unauthenticated user&amp;#8221;"&gt;mysql执行show processlist出现”unauthenticated user”&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2009 年 2 月 26 日 --    &lt;a href="http://ourmysql.com/archives/467" title="&amp;#22914;&amp;#20309;&amp;#33719;&amp;#24713;mysql&amp;#30340;show processlist&amp;#26174;&amp;#31034;&amp;#30340;&amp;#36827;&amp;#31243;&amp;#29366;&amp;#24577;"&gt;如何获悉mysql的show processlist显示的进程状态&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MySQL优化设计 processlist</category>
      <guid isPermaLink="true">https://itindex.net/detail/55300-mysql-processlist-%E7%8A%B6%E6%80%81</guid>
      <pubDate>Thu, 10 Mar 2016 08:03:41 CST</pubDate>
    </item>
    <item>
      <title>MySQL DBA修炼秘籍</title>
      <link>https://itindex.net/detail/55237-mysql-dba-%E4%BF%AE%E7%82%BC</link>
      <description>&lt;h1&gt;0、导读&lt;/h1&gt;
 &lt;p&gt;本文主要写给那些立志成为MySQL DBA，以及正在学习MySQL的同行们，结合个人及业内其他同行的职业发展经历给大家一些参考，如何成为合格的MySQL DBA。&lt;/p&gt;
 &lt;h1&gt;1、什么是MySQL DBA&lt;/h1&gt;
 &lt;p&gt;首先，DBA是database administrator(数据库管理员)的简称，在一些招聘网站上，也可能会把职位写成数据库[管理]工程师，MySQL DBA是目前互联网企业中最为炙手可热的岗位需求之一，前(钱)景大好，快到碗里来吧。&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h1&gt;2、MySQL DBA的职责&lt;/h1&gt;
 &lt;p&gt;传统意义上的DBA基本上只要管好database system就可以，一般无需关注操作系统、硬件、网络、安全、NOSQL等相关技术细节，在一些传统企业或者操作集成商里的ORACLE DBA就是这样的，最多关注到主机(小机为主)及相应的存储设备。&lt;/p&gt;
 &lt;p&gt;而MySQL一般在互联网业务中使用，MySQL DBA需要关注的方面也相应更多了，主要就是上面提到的主机硬件、OS、网络、安全、NOSQL，以及一些MySQL运维自动化开发(这不奇怪，一个好平台，通常都要自己亲自开发才顺手)的工作。&lt;/p&gt;
 &lt;p&gt;此外，随着企业规模的变化，可能在公司初期是由一些比较资深的开发工程师负责所有服务器大小事宜，当然也包括MySQL的管理及优化。随着规模的扩大，可能改由运维工程师来负责这些事了。更进一步的话，就开始需要专职的MySQL DBA了，随着业务发展，形成DBA team，同时负责和数据及存储相关的事务，比如存储设备、NOSQL、日志存储&amp;amp;分析，甚至大数据平台。&lt;/p&gt;
 &lt;p&gt;在一线的大型互联网公司里，甚至还区分  &lt;strong&gt;运维DBA&lt;/strong&gt;、  &lt;strong&gt;开发DBA&lt;/strong&gt;、  &lt;strong&gt;DB架构师&lt;/strong&gt;等不同岗位，为的就是能做到  &lt;strong&gt;术业有专攻，让专业的人专注做专业的事&lt;/strong&gt;。&lt;/p&gt;
 &lt;p&gt;比较理想的MySQL DBA工作状态应该是这样的：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;p&gt;例如MySQL实例安装、备份&amp;amp;恢复、SLAVE搭建、权限管理、DDL&amp;amp;DML变更上线等基础的工作，通过DB平台鼠标点点点即可完成，大概占用10%的时间；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;关键业务的SQL审核也可采用DB平台来完成，尤其是一些常规的SQL规范规则，DDL相对好办，DML可能需要进一步完善的评估，个别SQL再采用人工审核，平时经常和业务部门进行沟通，了解下阶段的业务目标，预估DB端可能需要承载的压力，大概占用20-30%的时间；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;通过监控系统来完成可用性及性能监控，发现异常时，再进行人工干预处理，一些容易引发性能问题的常见情景，也可以固化到自动化处理机制中，比如自动探测超过N秒的纯SELECT查询，避免这种慢SQL产生连锁反应，或者自动杀掉一些有SQL注入风险的请求，大概占用10%的时间；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;数据库主动优化，一个有丰富经验的DBA，看到数据库的一些现场情况时，一般即可预感到是否需要进一步深入优化工作。而SQL开发规范推送也很重要，可以在开发阶段让程序猿做好基本的SQL优化，这样上线后不会手忙脚乱，反复的出现一些低级SQL性能问题，大概占用20-30%的时间；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;其余的时间可以用来充电学习，以及圈内的交流扯淡了，扩展知识面。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h1&gt;3、如何成为MySQL DBA&lt;/h1&gt;
 &lt;p&gt;事实上，MySQL DBA的入门并不难，但若想要成为高级、资深的DBA就有一定难度了。&lt;/p&gt;
 &lt;p&gt;如果是在校生，最起码要先把《数据库概论》那门课程给学一遍，其实如果是已经在职但对数据库还没什么概念的人，最好也要学习下这本书，对数据库基本概念有一定理解。&lt;/p&gt;
 &lt;p&gt;此外，最好还要对Linux有一定了解，现如今在互联网公司中，如果想从事和技术相关的岗位，你告诉面试官不懂Linux为何物的话，估计直接就被pass了。想学习Linux，可以买本《鸟哥linux私房菜》或者参加专业培训(花钱参加培训并不是什么丢人的事，关键是要找到一个靠谱的机构，靠谱的老师，学习效率会更高，知识也更为系统化，而自学毕竟要消耗更多时间，也可能比较零散，花钱买时间学会后，可以更快获得回报)。&lt;/p&gt;
 &lt;p&gt;有了基础概念后，可以再买一本MySQL相关的基础入门书籍，比如《MySQL必知必会》、《深入浅出MySQL》等，其实我更建议把MySQL官方手册中的关键章节完整看一遍(关注公众号  &lt;strong&gt;imysql_wx&lt;/strong&gt;，发送“  &lt;strong&gt;章节&lt;/strong&gt;”获得推荐)，并结合里面的案例进行测试，或者自己用wordpress搭一个博客站，平时可以自己做些实践演练。&lt;/p&gt;
 &lt;p&gt;如果能专注把上面的内容学习完毕，我相信你已经可以成为一个合格的初级MySQL DBA了。接下来就是找到一个合适的工作机会(可以把简历给我，我根据情况可帮忙进行推荐)，进行真正的实操，获得正式从业经验。&lt;/p&gt;
 &lt;h1&gt;4、MySQL DBA如何提升&lt;/h1&gt;
 &lt;p&gt;事实上，如果你已经获得了MySQL DBA工作机会之后，如何进行自我提升通常来说已经不需要我来说了，可以参考公司里的同事以及其他同行的提升发展道路模式。&lt;/p&gt;
 &lt;p&gt;通常来说，在这个阶段需要深入学习的是某些关键知识点，比如数据库原理、并发事务、锁控制、存储引擎、主机硬件优化等知识。有些不错的书可以推荐，比如：《高性能MySQL》、《数据库系统实现/概念》、《数据库与事务处理》、《数据库索引设计与优化》等等。&lt;/p&gt;
 &lt;h1&gt;5、后记&lt;/h1&gt;
 &lt;h2&gt;后记一&lt;/h2&gt;
 &lt;p&gt;从目前的行业情况来看，MySQL DBA还是个很热门的职位，现在加入还不算晚。除了自学成才外，还可以考虑  &lt;strong&gt;参加我和吴炳锡合作的“知数堂MySQL DBA实战优化”培训课程&lt;/strong&gt;，截止目前已经举办了六期两百多名同学，个别优秀学员成功加入支付宝、京东、去哪儿、畅游、美菜网、37游戏等各大互联网公司。&lt;/p&gt;
 &lt;h2&gt;后记二&lt;/h2&gt;
 &lt;p&gt;此前有朋友让我推荐一些MySQL相关的技术资源，自己收藏吧：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;p&gt;    &lt;strong&gt;官方MySQL手册&lt;/strong&gt;     &lt;a&gt;http://t.cn/zR9VXxB&lt;/a&gt; ，一有不清楚问题我都会尝试先从手册里找到答案。&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;MySQL Planet聚合     &lt;a&gt;http://t.cn/zWOqujX&lt;/a&gt; ，集合了几乎所有MySQL相关的技术站点&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;ORACLE MySQL官方     &lt;a&gt;http://t.cn/Ry6IUzQ&lt;/a&gt; ，官方团队的blog，&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;MySQL Server团队     &lt;a&gt;http://t.cn/R7vvhpw&lt;/a&gt; ，官方Server团队blog，比上面那个blog更实用，细节、技术型文章更多&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;Percona团队官方     &lt;a&gt;http://t.cn/aWUo1W&lt;/a&gt; ，无需多说&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;MySQL Planet中文聚合     &lt;a&gt;http://t.cn/Ry6IUz8&lt;/a&gt; ，集合了不错的中文MySQL技术站点&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;最后就是我自己的网站：    &lt;a&gt;http://imysql.com &lt;/a&gt;哈哈哈&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;猜您喜欢&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;2016 年 1 月 26 日 --    &lt;a href="http://ourmysql.com/archives/1414" title="&amp;#22914;&amp;#20309;&amp;#25104;&amp;#20026;MySQL DBA"&gt;如何成为MySQL DBA&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2013 年 5 月 1 日 --    &lt;a href="http://ourmysql.com/archives/1221" title="&amp;#20114;&amp;#32852;&amp;#32593;DBA&amp;#38656;&amp;#35201;&amp;#20570;&amp;#37027;&amp;#20123;&amp;#20107;"&gt;互联网DBA需要做那些事&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 11 月 29 日 --    &lt;a href="http://ourmysql.com/archives/1149" title="&amp;#38754;&amp;#23545;&amp;#19968;&amp;#20010;&amp;#20840;&amp;#26032;&amp;#30340;&amp;#29615;&amp;#22659;,&amp;#20316;&amp;#20026;&amp;#19968;&amp;#20010;Mysql DBA,&amp;#39318;&amp;#20808;&amp;#24212;&amp;#35813;&amp;#20102;&amp;#35299;&amp;#20160;&amp;#20040;?"&gt;面对一个全新的环境,作为一个Mysql DBA,首先应该了解什么?&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 3 月 21 日 --    &lt;a href="http://ourmysql.com/archives/1051" title="DBA&amp;#24212;&amp;#35813;&amp;#20855;&amp;#26377;&amp;#20160;&amp;#20040;&amp;#26679;&amp;#30340;&amp;#32032;&amp;#36136;&amp;#65311;"&gt;DBA应该具有什么样的素质？&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2010 年 6 月 1 日 --    &lt;a href="http://ourmysql.com/archives/829" title="DBA&amp;#24037;&amp;#20316;&amp;#21021;&amp;#20307;&amp;#39564;&amp;#20043;&amp;#24515;&amp;#24778;&amp;#32966;&amp;#25112;"&gt;DBA工作初体验之心惊胆战&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2009 年 3 月 30 日 --    &lt;a href="http://ourmysql.com/archives/503" title="DBA&amp;#26410;&amp;#26469;&amp;#30340;&amp;#21457;&amp;#23637;&amp;#26041;&amp;#21521;"&gt;DBA未来的发展方向&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MySQL基础知识 DBA</category>
      <guid isPermaLink="true">https://itindex.net/detail/55237-mysql-dba-%E4%BF%AE%E7%82%BC</guid>
      <pubDate>Sat, 20 Feb 2016 19:26:19 CST</pubDate>
    </item>
    <item>
      <title>老叶倡议：MySQL压力测试基准值</title>
      <link>https://itindex.net/detail/55214-%E5%80%A1%E8%AE%AE-mysql-%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95</link>
      <description>&lt;p&gt;    通常，我们会出于以下几个目的对MySQL进行压力测试：&lt;/p&gt;
 &lt;pre&gt;1、确认新的MySQL版本性能相比之前差异多大，比如从5.6变成5.7，或者从官方版本改成Percona分支版本；
2、确认新的服务器性能是否更高，能高多少，比如CPU升级了、阵列卡cache加大了、从机械盘换成SSD盘了；
3、确认一些新的参数调整后，对性能影响多少，比如 innodb_flush_log_at_trx_commit、sync_binlog 等参数；
4、确认即将上线的新业务对MySQL负载影响多少，是否能承载得住，是否需要对服务器进行扩容或升级配置；&lt;/pre&gt;
 &lt;p&gt;    针对上面这几种压测的目的，相应的测试方法也有所不同。&lt;/p&gt;
 &lt;p&gt;    先说第四种，需要和线上业务结合起来，这时候就需要自行开发测试工具，或者利用   &lt;strong&gt;   &lt;a href="https://github.com/session-replay-tools/tcpcopy" target="_blank"&gt;tcpcopy&lt;/a&gt;&lt;/strong&gt; 将线上实际用户请求导向测试环境，进行仿真模拟测试。&lt;/p&gt;
 &lt;p&gt;    对于前三种，我们通常采用基准测试就可以。比较常用的MySQL基准压力测试工具有   &lt;strong&gt;   &lt;a href="https://code.launchpad.net/~percona-dev/perconatools/tpcc-mysql" target="_blank"&gt;tpcc-mysql&lt;/a&gt;、   &lt;a href="https://code.launchpad.net/sysbench" target="_blank"&gt;sysbench&lt;/a&gt;、   &lt;a href="http://dev.mysql.com/doc/refman/5.6/en/mysqlslap.html" target="_blank"&gt;mysqlslap&lt;/a&gt;&lt;/strong&gt; 等几个。&lt;/p&gt;
 &lt;p&gt;    关于压力测试工具的使用，可以查看我之前在ORACLE技术嘉年华上的分享：  &lt;strong&gt;   &lt;a href="http://pan.baidu.com/s/1jGjELkq" target="_blank"&gt;MySQL压力测试经验&lt;/a&gt;&lt;/strong&gt;，在这里不再细说。&lt;/p&gt;
 &lt;p&gt;    基于促进同行间的交流，统一MySQL压测标准，并且可以相互分享、对比、借鉴测试结果的目的。因此老叶特别发起MySQL压力测试基准值倡议。建议大家采用以下几种压力测试基准值。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#20513;&amp;#35758;&amp;#65306;MySQL&amp;#21387;&amp;#21147;&amp;#27979;&amp;#35797;&amp;#24314;&amp;#35758;&amp;#22522;&amp;#20934;&amp;#20540;(2015&amp;#35797;&amp;#34892;&amp;#29256;)" border="0" height="489" hspace="0" src="http://imysql.com/wp-content/uploads/2015/07/weBENCH-spec-20150721.png" title="&amp;#20513;&amp;#35758;&amp;#65306;MySQL&amp;#21387;&amp;#21147;&amp;#27979;&amp;#35797;&amp;#24314;&amp;#35758;&amp;#22522;&amp;#20934;&amp;#20540;(2015&amp;#35797;&amp;#34892;&amp;#29256;)" vspace="0" width="650"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;倡议：MySQL压力测试建议基准值(2015试行版)&lt;/p&gt;
 &lt;p&gt;    也可以查看本文附件excel文档：  &lt;strong&gt;   &lt;a href="http://ourmysql.com/wp-content/uploads/2015/07/&amp;#21387;&amp;#21147;&amp;#27979;&amp;#35797;&amp;#22522;&amp;#20934;&amp;#24314;&amp;#35758;&amp;#21450;&amp;#25968;&amp;#25454;&amp;#37319;&amp;#38598;&amp;#27169;&amp;#26495;-20150722.xlsx" target="_blank"&gt;压力测试基准建议及数据采集模板&lt;/a&gt;&lt;/strong&gt;，里面已附带了压力测试相关的数据采集点建议，压测结果整理及自动生成对比图表。欢迎各位同行拍砖提出不同的见解和补充意见，先谢过大家。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;关于压力测试的其他几个方面：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;    1、如何避免压测时受到缓存的影响&lt;/p&gt;
 &lt;p&gt;    【老叶建议】有2点建议&lt;/p&gt;
 &lt;p&gt;    a、填充测试数据比物理内存还要大，至少超过   &lt;strong&gt;   &lt;a href="http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size" target="_blank"&gt;innodb_buffer_pool_size&lt;/a&gt;&lt;/strong&gt; 值，不能将数据全部装载到内存中，除非你的本意就想测试全内存状态下的MySQL性能。&lt;/p&gt;
 &lt;p&gt;    b、每轮测试完成后，都重启mysqld实例，并且用下面的方法删除系统cache，释放swap(如果用到了swap的话)，甚至可以重启整个OS。&lt;/p&gt;
 &lt;pre&gt;[root@imysql.com]# sync  -- 将脏数据刷新到磁盘
[root@imysql.com]# echo 3 &amp;gt; /proc/sys/vm/drop_cache  -- 清除OS Cache
[root@imysql.com]# swapoff -a &amp;amp;&amp;amp; swapon -a
&lt;/pre&gt;
 &lt;p&gt;    2、如何尽可能体现线上业务真实特点&lt;/p&gt;
 &lt;p&gt;    【老叶建议】有2点建议&lt;/p&gt;
 &lt;p&gt;    a、其实上面已经说过了，就是自行开发测试工具或者利用   &lt;strong&gt;   &lt;a href="https://github.com/session-replay-tools/tcpcopy" target="_blank"&gt;tcpcopy&lt;/a&gt;&lt;/strong&gt;(或类似交换机的mirror功能) 将线上实际用户请求导向测试环境，进行仿真模拟测试。&lt;/p&gt;
 &lt;p&gt;    b、利用   &lt;strong&gt;   &lt;a href="http://acme.com/software/http_load/" target="_blank"&gt;http_load&lt;/a&gt;&lt;/strong&gt; 或   &lt;strong&gt;   &lt;a href="https://github.com/JoeDog/siege" target="_blank"&gt;siege&lt;/a&gt;&lt;/strong&gt; 工具模拟真实的用户请求URL进行压力测试，这方面我不是太专业，可以请教企业内部的压力测试同事。&lt;/p&gt;
 &lt;p&gt;    3、压测结果如何解读&lt;/p&gt;
 &lt;p&gt;    【老叶建议】压测结果除了tps/TpmC指标外，还应该关注压测期间的系统负载数据，尤其是   &lt;strong&gt;iops、iowait、svcmtm、%util、每秒I/O字节数(I/O吞吐)、事务响应时间&lt;/strong&gt;(tpcc-mysql/sysbench 打印的测试记录中均有)。另外，如果I/O设备能提供设备级   &lt;strong&gt;IOPS、读写延时 &lt;/strong&gt;数据的话，也应该一并关注。&lt;/p&gt;
 &lt;p&gt;    加入两次测试的tps/TpmC结果一样的话，那么谁的   &lt;strong&gt;事务响应时间、iowait、svctm、%util、读写延时&lt;/strong&gt; 更低，就表示那个测试模式有更高的性能提升空间。&lt;/p&gt;
 &lt;p&gt;    4、如何加快tpcc_load加载数据的效率&lt;/p&gt;
 &lt;p&gt;    【老叶建议】tpcc_load其实是可以并行加载的，一方面是可以区分   &lt;strong&gt;ITEMS、WAREHOUSE、CUSTOMER、ORDERS&lt;/strong&gt; 四个维度的数据并行加载。&lt;/p&gt;
 &lt;p&gt;    另外，比如最终想加载1000个 warehouse的话，也可以分开成1000个并发并行加载的。看下 tpcc_load 工具的参数就知道了：&lt;/p&gt;
 &lt;pre&gt;usage: tpcc_load [server] [DB] [user] [pass] [warehouse]
OR
tpcc_load [server] [DB] [user] [pass] [warehouse] [part] [min_wh] [max_wh]
* [part]: 1=ITEMS 2=WAREHOUSE 3=CUSTOMER 4=ORDERS&lt;/pre&gt;
 &lt;p&gt;    本来想自己写个并行加载脚本的，后来发现万能的github上已经有人做好了，我就直接拿来用了，这是项目链接   &lt;strong&gt;   &lt;a href="https://gist.github.com/sh2/3458844" target="_blank"&gt;tpcc_load_parallel.sh&lt;/a&gt;&lt;/strong&gt;，加载效率至少提升10倍以上。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;延伸阅读：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;p&gt;    &lt;a href="http://imysql.com/2014/10/10/tpcc-mysql-full-user-manual.shtml" target="_blank"&gt;tpcc-mysql安装、使用、结果解读&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;    &lt;a href="http://imysql.com/2014/09/18/sysbench-and-tpcc-mysql-source-tarball-download.shtml" target="_blank"&gt;新增sysbench和tpcc-mysql源码包下载&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;    &lt;a href="http://imysql.com/2014/10/11/percona-tpcc-mysql-branch-release.shtml" target="_blank"&gt;发布基于percona的tpcc-mysql分支版本&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;    &lt;a href="http://imysql.com/2014/10/17/sysbench-full-user-manual.shtml" target="_blank"&gt;sysbench安装、使用、结果解读&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h2&gt;抱歉猜想失败，您看看下面的文章有用吗？&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;2015 年 6 月 2 日 --    &lt;a href="http://ourmysql.com/archives/1391" title="&amp;#27604;&amp;#36739;&amp;#20840;&amp;#38754;&amp;#30340;MySQL&amp;#20248;&amp;#21270;&amp;#21442;&amp;#32771;&amp;#65288;&amp;#19979;&amp;#31687;&amp;#65289;"&gt;比较全面的MySQL优化参考（下篇）&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2013 年 5 月 8 日 --    &lt;a href="http://ourmysql.com/archives/1231" title="MySQL&amp;#24320;&amp;#21457;&amp;#27969;&amp;#31243;&amp;#20171;&amp;#32461;"&gt;MySQL开发流程介绍&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 12 月 19 日 --    &lt;a href="http://ourmysql.com/archives/1183" title="MySQL5.5&amp;#37197;&amp;#32622;&amp;#25991;&amp;#20214;&amp;#27169;&amp;#26495;"&gt;MySQL5.5配置文件模板&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2016 年 2 月 6 日 --    &lt;a href="http://ourmysql.com/archives/1417" title=" B+&amp;#26641;&amp;#32034;&amp;#24341;&amp;#21644;&amp;#21704;&amp;#24076;&amp;#32034;&amp;#24341;&amp;#30340;&amp;#21306;&amp;#21035;"&gt; B+树索引和哈希索引的区别&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2008 年 11 月 8 日 --    &lt;a href="http://ourmysql.com/archives/257" title="mysql &amp;#25968;&amp;#25454;&amp;#24211;&amp;#30340;&amp;#21516;&amp;#27493;&amp;#38382;&amp;#39064; "&gt;mysql 数据库的同步问题 &lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2008 年 9 月 23 日 --    &lt;a href="http://ourmysql.com/archives/81" title="MySQL&amp;#20027;&amp;#20174;&amp;#21516;&amp;#27493;&amp;#20851;&amp;#38190;&amp;#21477;"&gt;MySQL主从同步关键句&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 2 月 19 日 --    &lt;a href="http://ourmysql.com/archives/1008" title="mysql&amp;#25191;&amp;#34892;show processlist&amp;#20986;&amp;#29616;&amp;#8221;unauthenticated user&amp;#8221;"&gt;mysql执行show processlist出现”unauthenticated user”&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2009 年 7 月 14 日 --    &lt;a href="http://ourmysql.com/archives/621" title="1266&amp;#19982;skip-innodb"&gt;1266与skip-innodb&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2015 年 1 月 12 日 --    &lt;a href="http://ourmysql.com/archives/1363" title="&amp;#27880;&amp;#24847;sql&amp;#35821;&amp;#21477;&amp;#20013;&amp;#30340;&amp;#36890;&amp;#37197;&amp;#31526;&amp;#65292;&amp;#21035;&amp;#25481;&amp;#22353;&amp;#37324;&amp;#38754;!"&gt;注意sql语句中的通配符，别掉坑里面!&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2010 年 3 月 9 日 --    &lt;a href="http://ourmysql.com/archives/811" title="InnoDB&amp;#32447;&amp;#31243;&amp;#24182;&amp;#21457;&amp;#26816;&amp;#26597;&amp;#26426;&amp;#21046;"&gt;InnoDB线程并发检查机制&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MySQL优化设计 压力测试</category>
      <guid isPermaLink="true">https://itindex.net/detail/55214-%E5%80%A1%E8%AE%AE-mysql-%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Wed, 17 Feb 2016 06:12:01 CST</pubDate>
    </item>
    <item>
      <title>MySQL怎么计算打开文件数?</title>
      <link>https://itindex.net/detail/55213-mysql-%E8%AE%A1%E7%AE%97-%E6%96%87%E4%BB%B6</link>
      <description>&lt;p&gt;    有时候，我们会遇到类似下面的报错信息：&lt;/p&gt;
 &lt;pre&gt;.....
[ERROR] /usr/local/mysql/bin/mysqld: Can&amp;apos;t open file: &amp;apos;./yejr/access.frm&amp;apos; (errno: 24)
[ERROR] /usr/local/mysql/bin/mysqld: Can&amp;apos;t open file: &amp;apos;./yejr/accesslog.frm&amp;apos; (errno: 24)
......
[ERROR] Error in accept: Too many open files
....&lt;/pre&gt;
 &lt;p&gt;    提示信息很明显，  &lt;strong&gt;打开文件数达到上限了，需要提高上限，或者释放部分已打开的表文件描述符&lt;/strong&gt;。&lt;/p&gt;
 &lt;p&gt;    在MySQL中，有几个地方会存在文件描述符限制：&lt;/p&gt;
 &lt;pre&gt;1、在Server层，整个mysqld实例打开文件总数超过用户进程级的文件数限制，需要检查内核   &lt;strong&gt;fs.file-max&lt;/strong&gt; 限制、进程级限制   &lt;strong&gt;ulimit -n&lt;/strong&gt; 及MySQL中的   &lt;strong&gt;open-files-limit&lt;/strong&gt; 选项，是否有某一个超限了。任何一个条件超限了，就会抛出错误。

2、虽然Server层总文件数没有超，但InnoDB层也有限制，所有InnoDB相关文件打开总数不能超过   &lt;strong&gt;innodb-open-files&lt;/strong&gt; 选项限制。否则的话，会先把最早打开的InnoDB文件描述符关闭，才能打开新的文件，但不会抛出错误，只有告警信息。
&lt;/pre&gt;
 &lt;p&gt;    相应地，如果提示超出限制，则可以使用下面方法提高上限：&lt;/p&gt;
 &lt;pre&gt;1、首先，提高内核级总的限制。执行：  &lt;strong&gt;sysctl -w fs.file-max=3264018&lt;/strong&gt;；

2、其次，提高内核对用户进程级的打开文件数限制。执行：  &lt;strong&gt;ulimit -n 204800&lt;/strong&gt;；

3、最后，适当提高MySQL层的几个参数：  &lt;strong&gt;open-files-limit、innodb-open-files、table-open-cache、table-definition-cache&lt;/strong&gt;。
&lt;/pre&gt;
 &lt;p&gt;    关于前面两个限制网上可以找到很多详细解释，我就不多说了，重点来说下MySQL相关的4个选项。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;1、open-files-limit&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;    它限制了mysqld进程可持有的最大打开文件数，相当于是一个小区的总电闸，一旦超限，小区里所有住户都得停电。&lt;/p&gt;
 &lt;p&gt;    5.6.7(含)以前，默认值0，最大和OS内核限制有关；&lt;/p&gt;
 &lt;p&gt;    5.6.8(含)以后，默认值会自动计算，最大和OS内核限制有关。&lt;/p&gt;
 &lt;p&gt;    在5.6.8及以后，其自动计算的几个限制规则见下，哪个计算结果最大就以哪个为上限：&lt;/p&gt;
 &lt;pre&gt;1) 10 + max_connections + (table_open_cache * 2)
2) max_connections * 5
3) open_files_limit value specified at startup, 5000 if none
&lt;/pre&gt;
 &lt;p&gt;      &lt;strong&gt;2、innodb-open-files&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;    限制InnoDB引擎中表空间文件最大打开的数量，相当于自己家中电箱里的某个电路保险，该电路短路的话，会自动跳闸，而不会影响其他电路，去掉短路源后重新按上去就可以使用。&lt;/p&gt;
 &lt;p&gt;    其值最低20，默认400，只计算了包含ibdata*、ib_logfile*、*.ibd 等三类文件，redo log不计算在内，5.6以后可独立undo log，我还未进行测试，应该也不会被计算在内，有兴趣的朋友可验证下。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;3、table-definition-cache&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;    该cache用于缓存 .frm 文件，该选项也预示着 .frm 文件同时可打开最大数量。&lt;/p&gt;
 &lt;p&gt;    5.6.7 以前默认值400；&lt;/p&gt;
 &lt;p&gt;    5.6.7 之后是自动计算的，且最低为400，自动计算公式：400 + (table-open-cache / 2)。&lt;/p&gt;
 &lt;p&gt;    对InnoDB而言，该选项只是软性限制，如果超过限制了，则会根据LRU原则，把旧的条目删除，加入新的条目。&lt;/p&gt;
 &lt;p&gt;    此外，innodb-open-files 也控制着最大可打开的表数量，和 table-definition-cache 都起到限制作用，以其中较大的为准。如果没配置限制，则通常选择 table-definition-cache 作为上限，因为它的默认值是 200，比较大。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;4、table-open-cache&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;    该cache用于缓存各种所有数据表文件描述符。&lt;/p&gt;
 &lt;p&gt;    5.6.7 以前，默认值400，范围：1 – 524288；&lt;/p&gt;
 &lt;p&gt;    5.6.8 – 5.6.11，默认值2000，范围：1 – 524288；&lt;/p&gt;
 &lt;p&gt;    5.6.12以后，默认值2000(且能自动计算)，范围：1 – 524288。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;补充说明1：&lt;/strong&gt;关于如何计算表文件描述符的建议：&lt;/p&gt;
 &lt;p&gt;    table-open-cache 通常和 max-connections 有关系，建议设置为 max_connections * N，N的值为平均每个查询中可能总共会用到的表数量，同时也要兼顾可能会产生临时表。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;补充说明2：&lt;/strong&gt;MySQL会在下列几种情况把表从table cache中删掉：&lt;/p&gt;
 &lt;pre&gt;1、table cache已满，并且正要打开一个新表时；
2、table cache中的条目数超过 table_open_cache 设定值，并且有某些表已经长时间未访问了；

3、执行刷新表操作时，例如执行 FLUSH TABLES，或者 mysqladmin flush-tables 或 mysqladmin refresh
&lt;/pre&gt;
 &lt;p&gt;      &lt;strong&gt;补充说明3：&lt;/strong&gt;MySQL采用下述方法来分配table cache：&lt;/p&gt;
 &lt;pre&gt;1、当前没在用的表会被释放掉，从最近最少使用的表开始；

2、当要打开一个新表，当前的cache也满了且无法释放任何一个表时，table cache会临时加大，临时加大的table cache中的表不用了之后，会被立刻释放掉。&lt;/pre&gt;
 &lt;h2&gt;抱歉猜想失败，您看看下面的文章有用吗？&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;2011 年 7 月 22 日 --    &lt;a href="http://ourmysql.com/archives/954" title="Heartbeat+DRBD+MySQL Replication&amp;#25925;&amp;#38556;&amp;#22788;&amp;#29702;"&gt;Heartbeat+DRBD+MySQL Replication故障处理&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 12 月 23 日 --    &lt;a href="http://ourmysql.com/archives/1185" title="MySQL TPCH&amp;#27979;&amp;#35797;&amp;#24037;&amp;#20855;&amp;#31616;&amp;#35201;&amp;#25163;&amp;#20876;"&gt;MySQL TPCH测试工具简要手册&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 2 月 13 日 --    &lt;a href="http://ourmysql.com/archives/978" title="MySQL&amp;#39640;&amp;#21487;&amp;#29992;&amp;#24615;&amp;#22823;&amp;#26432;&amp;#22120;&amp;#20043;MHA"&gt;MySQL高可用性大杀器之MHA&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2008 年 12 月 29 日 --    &lt;a href="http://ourmysql.com/archives/367" title="mysql&amp;#30340;10060&amp;#21644;10061&amp;#38169;&amp;#35823;&amp;#21407;&amp;#22240;"&gt;mysql的10060和10061错误原因&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2008 年 10 月 9 日 --    &lt;a href="http://ourmysql.com/archives/146" title="MySQL&amp;#25968;&amp;#25454;&amp;#24211;&amp;#22914;&amp;#20309;&amp;#20570;&amp;#22909;&amp;#20248;&amp;#21270;"&gt;MySQL数据库如何做好优化&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2012 年 3 月 26 日 --    &lt;a href="http://ourmysql.com/archives/1112" title="mysql hash &amp;#30772;&amp;#35299;&amp;#25552;&amp;#26435;"&gt;mysql hash 破解提权&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2010 年 12 月 14 日 --    &lt;a href="http://ourmysql.com/archives/895" title="DRBD+Heartbeat&amp;#35753;MySQL&amp;#25552;&amp;#20379;&amp;#30340;&amp;#26381;&amp;#21153;&amp;#26356;&amp;#21152;&amp;#31283;&amp;#23450;"&gt;DRBD+Heartbeat让MySQL提供的服务更加稳定&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2009 年 2 月 25 日 --    &lt;a href="http://ourmysql.com/archives/465" title="&amp;#20026;&amp;#20309;&amp;#25480;&amp;#26435;&amp;#19981;&amp;#23545;"&gt;为何授权不对&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2008 年 12 月 14 日 --    &lt;a href="http://ourmysql.com/archives/336" title="Mysql&amp;#26102;&amp;#38388;&amp;#20989;&amp;#25968;"&gt;Mysql时间函数&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2014 年 11 月 7 日 --    &lt;a href="http://ourmysql.com/archives/1331" title="[MySQL&amp;#20248;&amp;#21270;&amp;#26696;&amp;#20363;]&amp;#31995;&amp;#21015; &amp;#8212; &amp;#20998;&amp;#39029;&amp;#20248;&amp;#21270;"&gt;[MySQL优化案例]系列 — 分页优化&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MySQL基础知识 文件数</category>
      <guid isPermaLink="true">https://itindex.net/detail/55213-mysql-%E8%AE%A1%E7%AE%97-%E6%96%87%E4%BB%B6</guid>
      <pubDate>Wed, 17 Feb 2016 06:13:29 CST</pubDate>
    </item>
    <item>
      <title>MySQL安全策略</title>
      <link>https://itindex.net/detail/55367-mysql-%E5%AE%89%E5%85%A8%E7%AD%96%E7%95%A5</link>
      <description>&lt;h1&gt;0、导读&lt;/h1&gt;
 &lt;blockquote&gt;  &lt;p&gt;    MySQL被运用于越来越多的业务中，在关键业务中对数据安全性的要求也更高，如何保证MySQL的数据安全？&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;    MySQL被运用于越来越多的业务中，在关键业务中对数据安全性的要求也更高，如何保证MySQL的数据安全。&lt;/p&gt;
 &lt;p&gt;    数据安全如果只靠MySQL应用层面显然是不够的，是需要在多个层面来保护的，包括网络、系统、逻辑应用层、数据库层等。&lt;/p&gt;
 &lt;p&gt;    下面是我们可借鉴的一些安全策略。&lt;/p&gt;
 &lt;h1&gt;1、网络、系统层面&lt;/h1&gt;
 &lt;p&gt;    在这个层面可以做很多的事情，我们可以把这些安全要求作为新系统安装时的标准要求，放到自动化装机方案中。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;p&gt;把运行MySQL的服务器放在内网中，不要启用公网；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;迫不得已启用公网的话，修改sshd端口到10000以上；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;设置防火墙策略，只允许信任的服务器连接sshd和MySQL端口；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;修改idrac/imm密码，设置GRUB密码；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;设置密码安全策略，比如要求 PASS_MIN_LEN 不低于8位，其实最好是直接用一个复杂密码做MD5之后再作为正式密码，32位长度的安全程度够高吧；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;将操作日志记入syslog并且发送到远程log server上，坚决不能只存储在本地；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;除了必须的账号，其他的都设为无登入权限；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;尽量把运行MySQL的服务器独立出来，不要和web server、app server放一起。必须放一起的话，也要设置好权限分离，不允许web server、app server进程的属主有直接访问MySQL datadir的权限；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;禁用web server层的autoindex配置；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;可能的话，采用https代替http；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;关键应用保持更新，避免老版本的漏洞风险；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;设置nginx、php等应用服务的安全策略，禁用危险函数等；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;可以考虑购买运营商提供的一些安全防护、扫描器等产品；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;坚决杜绝二逼行为，把关键配置文件上传到公共网络上（如把公司项目代码放在github上作为个人项目，内含内网账号密码信息）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h1&gt;2、逻辑应用层&lt;/h1&gt;
 &lt;p&gt;    在这个层面，等多的是依赖运营及开发人员的安全意识，很多本可以避免的低级安全漏洞完全可以在这个层面处理掉，比如下面提到的XSS、CSRF、SQL注入等漏洞。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;p&gt;尽量不要在公网上使用开源的cms、blog、论坛等系统，除非做过代码安全审计，或者事先做好安全策略。这类系统一般都是黑客重点研究对象，很容易被搞；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;在web server层，可以用一些安全模块，比如nginx的WAF模块；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;在app server层，可以做好代码安全审计、安全扫描，防止XSS攻击、CSRF攻击、SQL注入、文件上传攻击、绕过cookie检测等安全漏洞；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;应用程序中涉及账号密码的地方例如JDBC连接串配置，尽量把明文密码采用加密方式存储，再利用内部私有的解密工具进行反解密后再使用。或者可以让应用程序先用中间账号连接proxy层，再由proxy连接MySQL，避免应用层直连MySQL；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;应用层启用关键日志记录，例如交易日志，方便后续对账什么的。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h1&gt;3、MySQL数据库层&lt;/h1&gt;
 &lt;p&gt;    前面几层如果都做的不够安全的话，在这层也几乎是岌岌可危了。但我们依然可以做些事情的。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;p&gt;启用 safe-update 选项，避免没有 WHERE 条件的全表数据被修改；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;将 binlog 的保存周期加长，便于后续的审计、审查；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;应用账号只赋予SELECT、UPDATE、INSERT权限，取消DELETE权限。把需要DELETE权限的逻辑改成用UPDATE实现，避免被物理删除；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;需要真正删除时，交由DBA先备份后再物理删除；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;可以采用Percona的SQL审计插件，据说还有macfee的插件；&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;还可以采用触发器来做一些辅助功能，比如防止黑客恶意篡改数据。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h1&gt;4、后记&lt;/h1&gt;
 &lt;p&gt;    数据安全可以做的事情很多，本文也只是罗列了一些比较简单可快速实施的方案。每个企业应有自己的安全策略规范，每一位参与者都应该心怀敬畏，努力遵守这些必要的规范，不使信息安全成为空谈。&lt;/p&gt;
 &lt;p&gt;    真正的数据安全，是靠所有人的意识安全作为支撑的，没有这个意识靠机制、制度、工具都是不靠谱。&lt;/p&gt;
 &lt;h2&gt;猜您喜欢&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;2013 年 5 月 15 日 --    &lt;a href="http://ourmysql.com/archives/1235" title="MySQL 5.6 &amp;#26032;&amp;#22686;&amp;#30340;&amp;#20004;&amp;#20010;&amp;#23494;&amp;#30721;&amp;#23433;&amp;#20840;&amp;#31574;&amp;#30053;&amp;#20307;&amp;#39564;"&gt;MySQL 5.6 新增的两个密码安全策略体验&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MySQL优化设计 安全</category>
      <guid isPermaLink="true">https://itindex.net/detail/55367-mysql-%E5%AE%89%E5%85%A8%E7%AD%96%E7%95%A5</guid>
      <pubDate>Wed, 23 Mar 2016 07:03:21 CST</pubDate>
    </item>
    <item>
      <title>MYSQL分页limit速度太慢优化方法</title>
      <link>https://itindex.net/detail/55354-mysql-%E5%88%86%E9%A1%B5-limit</link>
      <description>&lt;p&gt;    在mysql中limit可以实现快速分页，但是如果数据到了几百万时我们的limit必须优化才能有效的合理的实现分页了，否则可能卡死你的服务器哦。&lt;/p&gt;
 &lt;p&gt;    当一个表数据有几百万的数据的时候成了问题！&lt;/p&gt;
 &lt;p&gt;    如 * from table limit 0,10 这个没有问题 当 limit 200000,10 的时候数据读取就很慢，可以按照一下方法解决&lt;/p&gt;
 &lt;p&gt;     第一页会很快&lt;/p&gt;
 &lt;p&gt;    PERCONA PERFORMANCE CONFERENCE 2009上，来自雅虎的几位工程师带来了一篇”EfficientPagination Using MySQL”的报告&lt;/p&gt;
 &lt;p&gt;    limit10000,20的意思扫描满足条件的10020行，扔掉前面的10000行，返回最后的20行，问题就在这里。&lt;/p&gt;
 &lt;p&gt;    LIMIT 451350 , 30 扫描了45万多行，怪不得慢的都堵死了。&lt;/p&gt;
 &lt;p&gt;    但是&lt;/p&gt;
 &lt;p&gt;    limit 30 这样的语句仅仅扫描30行。&lt;/p&gt;
 &lt;p&gt;    那么如果我们之前记录了最大ID，就可以在这里做文章&lt;/p&gt;
 &lt;p&gt;    举个例子&lt;/p&gt;
 &lt;p&gt;    日常分页SQL语句&lt;/p&gt;
 &lt;p&gt;    select id,name,content from users order by id asc limit 100000,20&lt;/p&gt;
 &lt;p&gt;    扫描100020行&lt;/p&gt;
 &lt;p&gt;    如果记录了上次的最大ID&lt;/p&gt;
 &lt;p&gt;    select id,name,content from users where id&amp;gt;100073 order by id asc limit 20&lt;/p&gt;
 &lt;p&gt;    扫描20行。&lt;/p&gt;
 &lt;p&gt;    总数据有500万左右&lt;/p&gt;
 &lt;p&gt;    以下例子 当时候 select * from wl_tagindex where byname=’f’ order by id limit 300000,10 执行时间是 3.21s&lt;/p&gt;
 &lt;p&gt;    优化后：&lt;/p&gt;
 &lt;p&gt;    select * from (&lt;/p&gt;
 &lt;p&gt;                select id from wl_tagindex&lt;/p&gt;
 &lt;p&gt;                where byname=’f’ order by id limit 300000,10&lt;/p&gt;
 &lt;p&gt;    ) a&lt;/p&gt;
 &lt;p&gt;    left join wl_tagindex b on a.id=b.id&lt;/p&gt;
 &lt;p&gt;    执行时间为 0.11s 速度明显提升&lt;/p&gt;
 &lt;p&gt;    这里需要说明的是 我这里用到的字段是 byname ,id 需要把这两个字段做复合索引，否则的话效果提升不明显&lt;/p&gt;
 &lt;p&gt;    总结&lt;/p&gt;
 &lt;p&gt;    当一个数据库表过于庞大，LIMIT offset, length中的offset值过大，则SQL查询语句会非常缓慢，你需增加order by，并且order by字段需要建立索引。&lt;/p&gt;
 &lt;p&gt;    如果使用子查询去优化LIMIT的话，则子查询必须是连续的，某种意义来讲，子查询不应该有where条件，where会过滤数据，使数据失去连续性。&lt;/p&gt;
 &lt;p&gt;    如果你查询的记录比较大，并且数据传输量比较大，比如包含了text类型的field，则可以通过建立子查询。&lt;/p&gt;
 &lt;p&gt;    SELECT id,title,content FROM items WHERE id IN (SELECT id FROM items ORDER BY id limit 900000, 10);&lt;/p&gt;
 &lt;p&gt;    如果limit语句的offset较大，你可以通过传递pk键值来减小offset = 0，这个主键最好是int类型并且auto_increment&lt;/p&gt;
 &lt;p&gt;    SELECT * FROM users WHERE uid &amp;gt; 456891 ORDER BY uid LIMIT 0, 10;&lt;/p&gt;
 &lt;p&gt;    这条语句，大意如下:&lt;/p&gt;
 &lt;p&gt;    SELECT * FROM users WHERE uid &amp;gt;=  (SELECT uid FROM users ORDER BY uid limit 895682, 1) limit 0, 10;&lt;/p&gt;
 &lt;p&gt;    如果limit的offset值过大，用户也会翻页疲劳，你可以设置一个offset最大的，超过了可以另行处理，一般连续翻页过大，用户体验很差，则应该提供更优的用户体验给用户。&lt;/p&gt;
 &lt;p&gt;    limit 分页优化方法&lt;/p&gt;
 &lt;p&gt;    1.子查询优化法&lt;/p&gt;
 &lt;p&gt;    先找出第一条数据，然后大于等于这条数据的id就是要获取的数据&lt;/p&gt;
 &lt;p&gt;    缺点：数据必须是连续的，可以说不能有where条件，where条件会筛选数据，导致数据失去连续性&lt;/p&gt;
 &lt;p&gt;    实验下&lt;/p&gt;
 &lt;p&gt;     mysql&amp;gt; set profi=1;&lt;/p&gt;
 &lt;p&gt;    Query OK, 0 rows affected (0.00 sec)&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; select count(*) from Member;&lt;/p&gt;
 &lt;p&gt;    +———-+&lt;/p&gt;
 &lt;p&gt;    | count(*) |&lt;/p&gt;
 &lt;p&gt;    +———-+&lt;/p&gt;
 &lt;p&gt;    |   169566 |&lt;/p&gt;
 &lt;p&gt;    +———-+&lt;/p&gt;
 &lt;p&gt;    1 row in set (0.00 sec)&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; pager grep !~-&lt;/p&gt;
 &lt;p&gt;    PAGER set to ‘grep !~-‘&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; select * from Member limit 10, 100;&lt;/p&gt;
 &lt;p&gt;    100 rows in set (0.00 sec)&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; select * from Member where MemberID &amp;gt;= (select MemberID from Member limit 10,1) limit 100;&lt;/p&gt;
 &lt;p&gt;    100 rows in set (0.00 sec)&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; select * from Member limit 1000, 100;&lt;/p&gt;
 &lt;p&gt;    100 rows in set (0.01 sec)&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; select * from Member where MemberID &amp;gt;= (select MemberID from Member limit 1000,1) limit 100;&lt;/p&gt;
 &lt;p&gt;    100 rows in set (0.00 sec)&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; select * from Member limit 100000, 100;&lt;/p&gt;
 &lt;p&gt;    100 rows in set (0.10 sec)&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; select * from Member where MemberID &amp;gt;= (select MemberID from Member limit 100000,1) limit 100;&lt;/p&gt;
 &lt;p&gt;    100 rows in set (0.02 sec)&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; nopager&lt;/p&gt;
 &lt;p&gt;    PAGER set to stdout&lt;/p&gt;
 &lt;p&gt;    mysql&amp;gt; show profilesG&lt;/p&gt;
 &lt;p&gt;    *************************** 1. row ***************************&lt;/p&gt;
 &lt;p&gt;    Query_ID: 1&lt;/p&gt;
 &lt;p&gt;    Duration: 0.00003300&lt;/p&gt;
 &lt;p&gt;       Query: select count(*) from Member&lt;/p&gt;
 &lt;p&gt;    *************************** 2. row ***************************&lt;/p&gt;
 &lt;p&gt;    Query_ID: 2&lt;/p&gt;
 &lt;p&gt;    Duration: 0.00167000&lt;/p&gt;
 &lt;p&gt;       Query: select * from Member limit 10, 100&lt;/p&gt;
 &lt;p&gt;    *************************** 3. row ***************************&lt;/p&gt;
 &lt;p&gt;    Query_ID: 3&lt;/p&gt;
 &lt;p&gt;    Duration: 0.00112400&lt;/p&gt;
 &lt;p&gt;       Query: select * from Member where MemberID &amp;gt;= (select MemberID from Member limit 10,1) limit 100&lt;/p&gt;
 &lt;p&gt;    *************************** 4. row ***************************&lt;/p&gt;
 &lt;p&gt;    Query_ID: 4&lt;/p&gt;
 &lt;p&gt;    Duration: 0.00263200&lt;/p&gt;
 &lt;p&gt;       Query: select * from Member limit 1000, 100&lt;/p&gt;
 &lt;p&gt;    *************************** 5. row ***************************&lt;/p&gt;
 &lt;p&gt;    Query_ID: 5&lt;/p&gt;
 &lt;p&gt;    Duration: 0.00134000&lt;/p&gt;
 &lt;p&gt;       Query: select * from Member where MemberID &amp;gt;= (select MemberID from Member limit 1000,1) limit 100&lt;/p&gt;
 &lt;p&gt;    *************************** 6. row ***************************&lt;/p&gt;
 &lt;p&gt;    Query_ID: 6&lt;/p&gt;
 &lt;p&gt;    Duration: 0.09956700&lt;/p&gt;
 &lt;p&gt;       Query: select * from Member limit 100000, 100&lt;/p&gt;
 &lt;p&gt;    *************************** 7. row ***************************&lt;/p&gt;
 &lt;p&gt;    Query_ID: 7&lt;/p&gt;
 &lt;p&gt;    Duration: 0.02447700&lt;/p&gt;
 &lt;p&gt;       Query: select * from Member where MemberID &amp;gt;= (select MemberID from Member limit 100000,1) limit 100&lt;/p&gt;
 &lt;p&gt;     从结果中可以得知，当偏移1000以上使用子查询法可以有效的提高性能。&lt;/p&gt;
 &lt;p&gt;    2.倒排表优化法&lt;/p&gt;
 &lt;p&gt;    倒排表法类似建立索引，用一张表来维护页数，然后通过高效的连接得到数据&lt;/p&gt;
 &lt;p&gt;    缺点：只适合数据数固定的情况，数据不能删除，维护页表困难&lt;/p&gt;
 &lt;p&gt;    3.反向查找优化法&lt;/p&gt;
 &lt;p&gt;    当偏移超过一半记录数的时候，先用排序，这样偏移就反转了&lt;/p&gt;
 &lt;p&gt;    缺点：order by优化比较麻烦，要增加索引，索引影响数据的修改效率，并且要知道总记录数&lt;/p&gt;
 &lt;p&gt;    ，偏移大于数据的一半&lt;/p&gt;
 &lt;p&gt;    引用&lt;/p&gt;
 &lt;p&gt;    limit偏移算法：&lt;/p&gt;
 &lt;p&gt;    正向查找： (当前页 – 1) * 页长度&lt;/p&gt;
 &lt;p&gt;    反向查找： 总记录 – 当前页 * 页长度&lt;/p&gt;
 &lt;p&gt;    做下实验，看看性能如何&lt;/p&gt;
 &lt;p&gt;    总记录数：1,628,775&lt;/p&gt;
 &lt;p&gt;    每页记录数： 40&lt;/p&gt;
 &lt;p&gt;    总页数：1,628,775 / 40 = 40720&lt;/p&gt;
 &lt;p&gt;    中间页数：40720 / 2 = 20360&lt;/p&gt;
 &lt;p&gt;    第21000页&lt;/p&gt;
 &lt;p&gt;    正向查找SQL:&lt;/p&gt;
 &lt;p&gt;    Sql代码&lt;/p&gt;
 &lt;p&gt;    SELECT * FROM `abc` WHERE `BatchID` = 123 LIMIT 839960, 40&lt;/p&gt;
 &lt;p&gt;    时间：1.8696 秒&lt;/p&gt;
 &lt;p&gt;    反向查找sql:&lt;/p&gt;
 &lt;p&gt;    Sql代码&lt;/p&gt;
 &lt;p&gt;    SELECT * FROM `abc` WHERE `BatchID` = 123 ORDER BY InputDate DESC LIMIT 788775, 40&lt;/p&gt;
 &lt;p&gt;    时间：1.8336 秒&lt;/p&gt;
 &lt;p&gt;    第30000页&lt;/p&gt;
 &lt;p&gt;    正向查找SQL:&lt;/p&gt;
 &lt;p&gt;    Sql代码&lt;/p&gt;
 &lt;p&gt;    1.SELECT * FROM `abc` WHERE `BatchID` = 123 LIMIT 1199960, 40&lt;/p&gt;
 &lt;p&gt;    SELECT * FROM `abc` WHERE `BatchID` = 123 LIMIT 1199960, 40&lt;/p&gt;
 &lt;p&gt;    时间：2.6493 秒&lt;/p&gt;
 &lt;p&gt;    反向查找sql:&lt;/p&gt;
 &lt;p&gt;    Sql代码&lt;/p&gt;
 &lt;p&gt;    1.SELECT * FROM `abc` WHERE `BatchID` = 123 ORDER BY InputDate DESC LIMIT 428775, 40&lt;/p&gt;
 &lt;p&gt;    SELECT * FROM `abc` WHERE `BatchID` = 123 ORDER BY InputDate DESC LIMIT 428775, 40&lt;/p&gt;
 &lt;p&gt;     时间：1.0035 秒&lt;/p&gt;
 &lt;p&gt;    注意，反向查找的结果是是降序desc的，并且InputDate是记录的插入时间，也可以用主键联合索引，但是不方便。&lt;/p&gt;
 &lt;p&gt;    4.limit限制优化法&lt;/p&gt;
 &lt;p&gt;    把limit偏移量限制低于某个数。。超过这个数等于没数据，我记得alibaba的dba说过他们是这样做的&lt;/p&gt;
 &lt;p&gt;    5.只查索引法&lt;/p&gt;
 &lt;h2&gt;猜您喜欢&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;2014 年 11 月 25 日 --    &lt;a href="http://ourmysql.com/archives/1340" title="MySQL&amp;#19968;&amp;#20010;&amp;#24322;&amp;#24120;&amp;#26597;&amp;#35810;&amp;#38382;&amp;#39064;&amp;#36861;&amp;#26597;"&gt;MySQL一个异常查询问题追查&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2014 年 11 月 7 日 --    &lt;a href="http://ourmysql.com/archives/1331" title="[MySQL&amp;#20248;&amp;#21270;&amp;#26696;&amp;#20363;]&amp;#31995;&amp;#21015; &amp;#8212; &amp;#20998;&amp;#39029;&amp;#20248;&amp;#21270;"&gt;[MySQL优化案例]系列 — 分页优化&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2014 年 6 月 10 日 --    &lt;a href="http://ourmysql.com/archives/1322" title="SQL&amp;#24120;&amp;#35265;&amp;#30340;&amp;#21487;&amp;#20248;&amp;#21270;&amp;#28857;"&gt;SQL常见的可优化点&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;2009 年 1 月 23 日 --    &lt;a href="http://ourmysql.com/archives/404" title="mysql limit&amp;#26597;&amp;#35810;&amp;#20248;&amp;#21270;"&gt;mysql limit查询优化&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MySQL优化设计 limit 分页</category>
      <guid isPermaLink="true">https://itindex.net/detail/55354-mysql-%E5%88%86%E9%A1%B5-limit</guid>
      <pubDate>Tue, 22 Mar 2016 07:24:00 CST</pubDate>
    </item>
    <item>
      <title>B-Tree索引与Hash索引的比较</title>
      <link>https://itindex.net/detail/54241-tree-%E7%B4%A2%E5%BC%95-hash</link>
      <description>&lt;h2&gt;B-Tree索引与Hash索引的比较&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;翻译自   &lt;a href="http://dev.mysql.com/doc/refman/5.6/en/index-btree-hash.html"&gt;http://dev.mysql.com/doc/refman/5.6/en/index-btree-hash.html&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;理解B-Tree和Hash的数据结构能够帮助我们预测不同存储引擎下的查询性能差异。存储引擎在索引中使用这些数据结构，尤其是  &lt;code&gt;MEMORY&lt;/code&gt; 同时提供了B-Tree和Hash索引让你选择。&lt;/p&gt;
 &lt;h3&gt;B-Tree索引特性&lt;/h3&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;B-Tree索引可以在表达式中使用=, &amp;gt;, &amp;gt;=, &amp;lt;, &amp;lt;=用作列比较或者 BETWEEN 运算符。还能使用LIKE比较，如果参数是一个不以通配符开头的常量。举个例子，下面的SELECT语句使用了索引：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SELECT * FROM tbl_name WHERE key_col LIKE &amp;apos;Patrick%&amp;apos;;
SELECT * FROM tbl_name WHERE key_col LIKE &amp;apos;Pat%_ck%&amp;apos;;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;第一条语句中，只有&amp;apos;Patrick&amp;apos; &amp;lt;= key  &lt;em&gt;col &amp;lt; &amp;apos;Patricl&amp;apos; 的行会被考虑。第二条语句中，只有&amp;apos;Pat&amp;apos; &amp;lt;= key&lt;/em&gt;col &amp;lt; &amp;apos;Pau&amp;apos; 的行会被考虑。&lt;/p&gt;
 &lt;p&gt;下面的  &lt;code&gt;SELECT&lt;/code&gt;语句没有使用索引：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;
SELECT * FROM tbl_name WHERE key_col LIKE &amp;apos;%Patrick%&amp;apos;;
SELECT * FROM tbl_name WHERE key_col LIKE other_col;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;第一条语句是因为它以通配符开头，第二条语句是因为没有使用常量。&lt;/p&gt;
 &lt;p&gt;如果你使用... LIKE &amp;apos;%string%&amp;apos;而且  &lt;code&gt;string&lt;/code&gt;超过三个字符，那么MYSQL会使用  &lt;code&gt;Turbo Boyer-Moore algorithm&lt;/code&gt;算法来初始化查询表达式，然后用这个表达式来让查询更迅速。&lt;/p&gt;
 &lt;p&gt;查询中有col  &lt;em&gt;name IS NULL可以使用col&lt;/em&gt;name索引。&lt;/p&gt;
 &lt;p&gt;任何一个没有覆盖所有WHERE中AND级别条件的索引是不会被使用的。也就是说，要使用一个索引，这个索引中的第一列需要在每个AND组中出现。&lt;/p&gt;
 &lt;p&gt;下面的WHERE条件会使用索引：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;... WHERE index_part1=1 AND index_part2=2 AND other_column=3

    /* index = 1 OR index = 2 */
... WHERE index=1 OR A=10 AND index=2

    /* optimized like &amp;quot;index_part1=&amp;apos;hello&amp;apos;&amp;quot; */
... WHERE index_part1=&amp;apos;hello&amp;apos; AND index_part3=5

    /* Can use index on index1 but not on index2 or index3 */
... WHERE index1=1 AND index2=2 OR index1=3 AND index3=3;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;下面的WHERE条件不会使用索引：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;    /* index_part1 is not used */
... WHERE index_part2=1 AND index_part3=2

    /*  Index is not used in both parts of the WHERE clause  */
... WHERE index=1 OR A=10

    /* No index spans all rows  */
... WHERE index_part1=1 OR index_part2=10
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;有时候mysql不会使用索引，即使在可用的情况下。例如当mysql预估使用索引会读取大部分的行数据时。（在这种情况下，一次全表扫描可能比使用索引更快，因为它需要更少的检索）。然而，假如语句中使用LIMIT来限定返回的行数，mysql则会使用索引。因为当结果行数较少的情况下使用索引的效率会更高。&lt;/p&gt;
 &lt;h3&gt;Hash索引特性&lt;/h3&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;Hash类型的索引有一些区别于以上所述的特征：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;p&gt;它们只能用于对等比较，例如=和&amp;lt;=&amp;gt;操作符（但是快很多）。它们不能被用于像&amp;lt;这样的范围查询条件。假如系统只需要使用像“键值对”的这样的存储结构，尽量使用hash类型索引。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;优化器不能用hash索引来为ORDER BY操作符加速。（这类索引不能被用于搜索下一个次序的值）&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;MySQL不能判断出两个值之间有多少条数据（这需要使用范围查询操作符来决定使用哪个索引）。假如你将一个MyISAM表或InnoDB表转为一个依靠hash索引的MEMORY表，这可能会影响一些查询。&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;p&gt;只有完整的键才能被用于搜索一行数据（使用了B-Tree索引，那么任何一个键的前缀都可以用于查找）。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql index 索引</category>
      <guid isPermaLink="true">https://itindex.net/detail/54241-tree-%E7%B4%A2%E5%BC%95-hash</guid>
      <pubDate>Tue, 25 Aug 2015 23:10:17 CST</pubDate>
    </item>
  </channel>
</rss>

