<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[BIWEB开源PHP WMS系统创始人ArthurXF肖飞的blog]]></title> 
<link>http://www.bizeway.net/index.php</link> 
<description><![CDATA[网务通 - 网务公司发展之路]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[BIWEB开源PHP WMS系统创始人ArthurXF肖飞的blog]]></copyright>
<item>
<link>http://www.bizeway.net/read.php?</link>
<title><![CDATA[mysql分表的3种方法]]></title> 
<author>ArthurXF &lt;arthurxf@gmail.com&gt;</author>
<category><![CDATA[MySQL]]></category>
<pubDate>Sat, 27 Jul 2013 09:38:40 +0000</pubDate> 
<guid>http://www.bizeway.net/read.php?</guid> 
<description>
<![CDATA[ 
	一，先说一下为什么要分表<br/>当一张的数据达到几百万时，你查询一次所花的时间会变多，如果有联合查询的话，我想有可能会死在那儿了。分表的目的就在于此，减小数据库的负担，缩短查询时间。<br/><br/>根据个人经验，mysql执行一个sql的过程如下： <br/>1,接收到sql;<br/><br/>2,把sql放到排队队列中 ;<br/><br/>3,执行sql;<br/><br/>4,返回执行结果。<br/><br/>在这个执行过程中最花时间在什么地方呢？第一，是排队等待的时间，第二，sql的执行时间。其实这二个是一回事，等待的同时，肯定有sql在执行。所以我们要缩短sql的执行时间。<br/><br/>mysql中有一种机制是表锁定和行锁定，为什么要出现这种机制，是为了保证数据的完整性，我举个例子来说吧，如果有二个sql都要修改同一张表的同一条数据，这个时候怎么办呢，是不是二个sql都可以同时修改这条数据呢？很显然mysql对这种情况的处理是，一种是表锁定（myisam存储引擎），一个是行锁定（innodb存储引擎）。表锁定表示你们都不能对这张表进行操作，必须等我对表操作完才行。行锁定也一样，别的sql必须等我对这条数据操作完了，才能对这条数据进行操作。如果数据太多，一次执行的时间太长，等待的时间就越长，这也是我们为什么要分表的原因。<br/><br/>二，分表<br/>1，做mysql集群，例如：利用mysql cluster ，mysql proxy，mysql replication，drdb等等<br/>有人会问mysql集群，根分表有什么关系吗？虽然它不是实际意义上的分表，但是它启到了分表的作用，做集群的意义是什么呢？为一个数据库减轻负担，说白了就是减少sql排队队列中的sql的数量，举个例子：有10个sql请求，如果放在一个数据库服务器的排队队列中，他要等很长时间，如果把这10个sql请求，分配到5个数据库服务器的排队队列中，一个数据库服务器的队列中只有2个，这样等待时间是不是大大的缩短了呢？这已经很明显了。所以我把它列到了分表的范围以内<br/><br/>优点：扩展性好，没有多个分表后的复杂操作（php代码）<br/><br/>缺点：单个表的数据量还是没有变，一次操作所花的时间还是那么多，硬件开销大。<br/><br/>2，预先估计会出现大数据量并且访问频繁的表，将其分为若干个表<br/>这种预估大差不差的，论坛里面发表帖子的表，时间长了这张表肯定很大，几十万，几百万都有可能。 聊天室里面信息表，几十个人在一起一聊一个晚上，时间长了，这张表的数据肯定很大。像这样的情况很多。所以这种能预估出来的大数据量表，我们就事先分出个N个表，这个N是多少，根据实际情况而定。以聊天信息表为例：<br/><br/>我事先建100个这样的表，message_00,message_01,message_02……….message_98,message_99.然后根据用户的ID来判断这个用户的聊天信息放到哪张表里面，你可以用hash的方式来获得，可以用求余的方式来获得，方法很多，各人想各人的吧。下面用hash的方法来获得表名：<br/><br/>view sourceprint?1 <?php &nbsp;<br/><br/>2 function get_hash_table($table,$userid) { &nbsp;<br/><br/>3 &nbsp;$str = crc32($userid); &nbsp;<br/><br/>4 &nbsp;if($str<0){ &nbsp;<br/><br/>5 &nbsp;$hash = "0".substr(abs($str), 0, 1); &nbsp;<br/><br/>6 &nbsp;}else{ &nbsp;<br/><br/>7 &nbsp;$hash = substr($str, 0, 2); &nbsp;<br/><br/>8 &nbsp;} &nbsp;<br/><br/>9 &nbsp; &nbsp;<br/><br/>10 &nbsp;return $table."_".$hash; &nbsp;<br/><br/>11 } &nbsp;<br/><br/>12 &nbsp; &nbsp;<br/><br/>13 echo get_hash_table('message','user18991'); &nbsp; &nbsp; //结果为message_10 &nbsp;<br/><br/>14 echo get_hash_table('message','user34523'); &nbsp; &nbsp;//结果为message_13 &nbsp;<br/><br/>15 ?> <br/><br/>说明一下，上面的这个方法，告诉我们user18991这个用户的消息都记录在message_10这张表里，user34523这个用户的消息都记录在message_13这张表里，读取的时候，只要从各自的表中读取就行了。<br/><br/>优点：避免一张表出现几百万条数据，缩短了一条sql的执行时间<br/><br/>缺点：当一种规则确定时，打破这条规则会很麻烦，上面的例子中我用的hash算法是crc32，如果我现在不想用这个算法了，改用md5后，会使同一个用户的消息被存储到不同的表中，这样数据乱套了。扩展性很差。<br/><br/>—————PHP10086:根据userid，或者时间拆分，建立路由规则调度，确定聚合查询的时候麻烦。<br/><br/>3，利用merge存储引擎来实现分表<br/>我觉得这种方法比较适合，那些没有事先考虑，而已经出现了得，数据查询慢的情况。这个时候如果要把已有的大数据量表分开比较痛苦，最痛苦的事就是改代码，因为程序里面的sql语句已经写好了，现在一张表要分成几十张表，甚至上百张表，这样sql语句是不是要重写呢？举个例子，我很喜欢举子<br/><br/>mysql>show engines;的时候你会发现mrg_myisam其实就是merge。<br/><br/>view sourceprint?1 mysql> CREATE TABLE IF NOT EXISTS `user1` ( &nbsp;<br/><br/>2 &nbsp;-> &nbsp; `id` int(11) NOT NULL AUTO_INCREMENT, &nbsp;<br/><br/>3 &nbsp;-> &nbsp; `name` varchar(50) DEFAULT NULL, &nbsp;<br/><br/>4 &nbsp;-> &nbsp; `sex` int(1) NOT NULL DEFAULT '0', &nbsp;<br/><br/>5 &nbsp;-> &nbsp; PRIMARY KEY (`id`) &nbsp;<br/><br/>6 &nbsp;-> ) ENGINE=MyISAM &nbsp;DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; &nbsp;<br/><br/>7 Query OK, 0 rows affected (0.05 sec) &nbsp;<br/><br/>8 &nbsp; &nbsp;<br/><br/>9 mysql> CREATE TABLE IF NOT EXISTS `user2` ( &nbsp;<br/><br/>10 &nbsp;-> &nbsp; `id` int(11) NOT NULL AUTO_INCREMENT, &nbsp;<br/><br/>11 &nbsp;-> &nbsp; `name` varchar(50) DEFAULT NULL, &nbsp;<br/><br/>12 &nbsp;-> &nbsp; `sex` int(1) NOT NULL DEFAULT '0', &nbsp;<br/><br/>13 &nbsp;-> &nbsp; PRIMARY KEY (`id`) &nbsp;<br/><br/>14 &nbsp;-> ) ENGINE=MyISAM &nbsp;DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; &nbsp;<br/><br/>15 Query OK, 0 rows affected (0.01 sec) &nbsp;<br/><br/>16 &nbsp; &nbsp;<br/><br/>17 mysql> INSERT INTO `user1` (`name`, `sex`) VALUES('张映', 0); &nbsp;<br/><br/>18 Query OK, 1 row affected (0.00 sec) &nbsp;<br/><br/>19 &nbsp; &nbsp;<br/><br/>20 mysql> INSERT INTO `user2` (`name`, `sex`) VALUES('tank', 1); &nbsp;<br/><br/>21 Query OK, 1 row affected (0.00 sec) &nbsp;<br/><br/>22 &nbsp; &nbsp;<br/><br/>23 mysql> CREATE TABLE IF NOT EXISTS `alluser` ( &nbsp;<br/><br/>24 &nbsp;-> &nbsp; `id` int(11) NOT NULL AUTO_INCREMENT, &nbsp;<br/><br/>25 &nbsp;-> &nbsp; `name` varchar(50) DEFAULT NULL, &nbsp;<br/><br/>26 &nbsp;-> &nbsp; `sex` int(1) NOT NULL DEFAULT '0', &nbsp;<br/><br/>27 &nbsp;-> &nbsp; INDEX(id) &nbsp;<br/><br/>28 &nbsp;-> ) TYPE=MERGE UNION=(user1,user2) INSERT_METHOD=LAST AUTO_INCREMENT=1 ; &nbsp;<br/><br/>29 Query OK, 0 rows affected, 1 warning (0.00 sec) &nbsp;<br/><br/>30 &nbsp; &nbsp;<br/><br/>31 mysql> select id,name,sex from alluser; &nbsp;<br/><br/>32 +----+--------+-----+ &nbsp;<br/><br/>33 &#124; id &#124; name &nbsp; &#124; sex &#124; &nbsp;<br/><br/>34 +----+--------+-----+ &nbsp;<br/><br/>35 &#124; &nbsp;1 &#124; 张映 &#124; &nbsp; 0 &#124; &nbsp;<br/><br/>36 &#124; &nbsp;1 &#124; tank &nbsp; &#124; &nbsp; 1 &#124; &nbsp;<br/><br/>37 +----+--------+-----+ &nbsp;<br/><br/>38 2 rows in set (0.00 sec) &nbsp;<br/><br/>39 &nbsp; &nbsp;<br/><br/>40 mysql> INSERT INTO `alluser` (`name`, `sex`) VALUES('tank2', 0); &nbsp;<br/><br/>41 Query OK, 1 row affected (0.00 sec) &nbsp;<br/><br/>42 &nbsp; &nbsp;<br/><br/>43 mysql> select id,name,sex from user2 &nbsp;<br/><br/>44 &nbsp;-> ; &nbsp;<br/><br/>45 +----+-------+-----+ &nbsp;<br/><br/>46 &#124; id &#124; name &nbsp;&#124; sex &#124; &nbsp;<br/><br/>47 +----+-------+-----+ &nbsp;<br/><br/>48 &#124; &nbsp;1 &#124; tank &nbsp;&#124; &nbsp; 1 &#124; &nbsp;<br/><br/>49 &#124; &nbsp;2 &#124; tank2 &#124; &nbsp; 0 &#124; &nbsp;<br/><br/>50 +----+-------+-----+ &nbsp;<br/><br/>51 2 rows in set (0.00 sec) <br/><br/>从上面的操作中，我不知道你有没有发现点什么？假如我有一张用户表user，有50W条数据，现在要拆成二张表user1和user2，每张表25W条数据，<br/><br/>INSERT INTO user1(user1.id,user1.name,user1.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id <= 250000<br/><br/>INSERT INTO user2(user2.id,user2.name,user2.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id > 250000<br/><br/>这样我就成功的将一张user表，分成了二个表，这个时候有一个问题，代码中的sql语句怎么办，以前是一张表，现在变成二张表了，代码改动很大，这样给程序员带来了很大的工作量，有没有好的办法解决这一点呢？办法是把以前的user表备份一下，然后删除掉，上面的操作中我建立了一个alluser表，只把这个alluser表的表名改成user就行了。但是，不是所有的mysql操作都能用的<br/><br/>a，如果你使用 alter table 来把 merge 表变为其它表类型，到底层表的映射就被丢失了。取而代之的，来自底层 myisam 表的行被复制到已更换的表中，该表随后被指定新类型。<br/><br/>b，网上看到一些说replace不起作用，我试了一下可以起作用的。晕一个先<br/><br/>view sourceprint?1 mysql> UPDATE alluser SET sex=REPLACE(sex, 0, 1) where id=2; &nbsp; &nbsp;<br/><br/>2 &nbsp; &nbsp;<br/><br/>3 Query OK, 1 row affected (0.00 sec) &nbsp; &nbsp;<br/><br/>4 &nbsp; &nbsp;<br/><br/>5 Rows matched: 1 &nbsp;Changed: 1 &nbsp;Warnings: 0 &nbsp; &nbsp;<br/><br/>6 &nbsp; &nbsp;<br/><br/>7 &nbsp; &nbsp;<br/><br/>8 &nbsp; &nbsp;<br/><br/>9 mysql> select * from alluser; &nbsp; &nbsp;<br/><br/>10 &nbsp; &nbsp;<br/><br/>11 +----+--------+-----+ &nbsp; &nbsp;<br/><br/>12 &nbsp; &nbsp;<br/><br/>13 &#124; id &#124; name &nbsp; &#124; sex &#124; &nbsp; &nbsp;<br/><br/>14 &nbsp; &nbsp;<br/><br/>15 +----+--------+-----+ &nbsp; &nbsp;<br/><br/>16 &nbsp; &nbsp;<br/><br/>17 &#124; &nbsp;1 &#124; 张映 &#124; &nbsp; 0 &#124; &nbsp; &nbsp;<br/><br/>18 &nbsp; &nbsp;<br/><br/>19 &#124; &nbsp;1 &#124; tank &nbsp; &#124; &nbsp; 1 &#124; &nbsp; &nbsp;<br/><br/>20 &nbsp; &nbsp;<br/><br/>21 &#124; &nbsp;2 &#124; tank2 &nbsp;&#124; &nbsp; 1 &#124; &nbsp; &nbsp;<br/><br/>22 &nbsp; &nbsp;<br/><br/>23 +----+--------+-----+ &nbsp; &nbsp;<br/><br/>24 &nbsp; &nbsp;<br/><br/>25 3 rows in set (0.00 sec) <br/><br/>c，一个 merge 表不能在整个表上维持 unique 约束。当你执行一个 insert，数据进入第一个或者最后一个 myisam 表（取决于 insert_method 选项的值）。mysql 确保唯一键值在那个 myisam 表里保持唯一，但不是跨集合里所有的表。<br/><br/>d,当你创建一个 merge 表之时，没有检查去确保底层表的存在以及有相同的机构。当 merge 表被使用之时，mysql 检查每个被映射的表的记录长度是否相等，但这并不十分可靠。如果你从不相似的 myisam 表创建一个 merge 表，你非常有可能撞见奇怪的问题。<br/><br/>好困睡觉了，c和d在网上看到的，没有测试，大家试一下吧。<br/><br/>优点：扩展性好，并且程序代码改动的不是很大<br/><br/>缺点：这种方法的效果比第二种要差一点<br/><br/>三，总结一下<br/><br/>上面提到的三种方法，我实际做过二种，第一种和第二种。第三种没有做过，所以说的细一点。哈哈。做什么事都有一个度，超过个度就过变得很差，不能一味的做数据库服务器集群，硬件是要花钱买的，也不要一味的分表，分出来1000表，mysql的存储归根到底还以文件的形势存在硬盘上面，一张表对应三个文件，1000个分表就是对应3000个文件，这样检索起来也会变的很慢。我的建议是<br/><br/>方法1和方法2结合的方式来进行分表<br/><br/>方法1和方法3结合的方式来进行分表<br/><br/>我的二个建议适合不同的情况，根据个人情况而定，我觉得会有很多人选择方法1和方法3结合的方式<br/><br/>—–PHP10086手记:<br/>关于merge 表分表：<br/>聚合表alluser 要指定DEFAULT CHARSET=utf8，统一编码不然报错。 <br/>子表需要是MyISAM引擎，仅仅适合与myisam引擎的表 <br/>每个子表的结构必须一致，主表和子表的结构需要一致 <br/>每个子表的索引在merge表中都会存在，所以在merge表中不能根据该索引进行唯一性检索 <br/>REPLACE在merge表中不会工作 <br/>AUTO_INCREMENT 不会按照你所期望的方式工作- <br/>创建Mysql Merge表的参数 INSERT_METHOD有几个参数 ：<br/><br/>LAST &nbsp;如果你执行insert 指令来操作merge表时，插入操作会把数据添加到最后一个子表中。<br/><br/>FIRST &nbsp;同理，执行插入数据时会把数据添加到第一个子表中。<br/><br/>关于merge删除<br/>如果删除mrg表，那么各个子表间将不会有联系。但是如果删除其中的任一子表，对于GNU／LINUX来说，merge表结构及数据仍然存在。<br/><br/><br/>Tags - <a href="tag.php?tag=mysql" rel="tag">mysql</a> , <a href="tag.php?tag=%E5%88%86%E8%A1%A8" rel="tag">分表</a>
]]>
</description>
</item><item>
<link>http://www.bizeway.net/read.php?&amp;guid=0#topreply</link>
<title><![CDATA[[评论] mysql分表的3种方法]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.bizeway.net/read.php?&amp;guid=0#topreply</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>