1,什么是mysql分表,分区php
什么是分表,从表面意思上看呢,就是把一张表分红N多个小表,具体请看mysql分表的3种方法html
什么是分区,分区呢就是把一张表的数据分红N多个区块,这些区块能够在同一个磁盘上,也能够在不一样的磁盘上mysql
2. mysql分表。linux
一,先说一下为何要分表程序员
当一张的数据达到几百万时,你查询一次所花的时间会变多,若是有联合查询的话,我想有可能会死在那儿了。分表的目的就在于此,减少数据库的负担,缩短查询时间。算法
根据我的经验,mysql执行一个sql的过程以下:
1,接收到sql;2,把sql放到排队队列中 ;3,执行sql;4,返回执行结果。在这个执行过程当中最花时间在什么地方呢?第一,是排队等待的时间,第二,sql的执行时间。其实这二个是一回事,等待的同时,确定有sql在执行。因此咱们要缩短sql的执行时间。sql
mysql中有一种机制是表锁定和行锁定,为何要出现这种机制,是为了保证数据的完整性,我举个例子来讲吧,若是有二个sql都要修改同一张表的同一条数据,这个时候怎么办呢,是否是二个sql均可以同时修改这条数据呢?很显然mysql对这种状况的处理是,一种是表锁定(myisam存储引擎),一个是行锁定(innodb存储引擎)。表锁定表示大家都不能对这张表进行操做,必须等我对表操做完才行。行锁定也同样,别的sql必须等我对这条数据操做完了,才能对这条数据进行操做。若是数据太多,一次执行的时间太长,等待的时间就越长,这也是咱们为何要分表的缘由。数据库
二,分表服务器
1,作mysql集群,例如:利用mysql cluster ,mysql proxy,mysql replication,drdb等等架构
有人会问mysql集群,根分表有什么关系吗?虽然它不是实际意义上的分表,可是它启到了分表的做用,作集群的意义是什么呢?为一个数据库减轻负担,说白了就是减小sql排队队列中的sql的数量,举个例子:有10个sql请求,若是放在一个数据库服务器的排队队列中,他要等很长时间,若是把这10个sql请求,分配到5个数据库服务器的排队队列中,一个数据库服务器的队列中只有2个,这样等待时间是否是大大的缩短了呢?这已经很明显了。因此我把它列到了分表的范围之内,我作过一些mysql的集群:
linux mysql proxy 的安装,配置,以及读写分离
mysql replication 互为主从的安装及配置,以及数据同步
优势:扩展性好,没有多个分表后的复杂操做(php代码)
缺点:单个表的数据量仍是没有变,一次操做所花的时间仍是那么多,硬件开销大。
2,预先估计会出现大数据量而且访问频繁的表,将其分为若干个表
这种预估大差不差的,论坛里面发表帖子的表,时间长了这张表确定很大,几十万,几百万都有可能。 聊天室里面信息表,几十我的在一块儿一聊一个晚上,时间长了,这张表的数据确定很大。像这样的状况不少。因此这种能预估出来的大数据量表,咱们就事先分出个N个表,这个N是多少,根据实际状况而定。以聊天信息表为例:
我事先建100个这样的表,message_00,message_01,message_02..........message_98,message_99.而后根据用户的ID来判断这个用户的聊天信息放到哪张表里面,你能够用hash的方式来得到,能够用求余的方式来得到,方法不少,各人想各人的吧。下面用hash的方法来得到表名:
复制打印? <?php function get_hash_table($table,$userid) { $str = crc32($userid); if($str<0){ $hash = "0".substr(abs($str), 0, 1); }else{ $hash = substr($str, 0, 2); } return $table."_".$hash; } echo get_hash_table('message','user18991'); //结果为message_10 echo get_hash_table('message','user34523'); //结果为message_13 ?>
说明一下,上面的这个方法,告诉咱们user18991这个用户的消息都记录在message_10这张表里,user34523这个用户的消息都记录在message_13这张表里,读取的时候,只要从各自的表中读取就好了。
优势:避免一张表出现几百万条数据,缩短了一条sql的执行时间
缺点:当一种规则肯定时,打破这条规则会很麻烦,上面的例子中我用的hash算法是crc32,若是我如今不想用这个算法了,改用md5后,会使同一个用户的消息被存储到不一样的表中,这样数据乱套了。扩展性不好。
3,利用merge存储引擎来实现分表
我以为这种方法比较适合,那些没有事先考虑,而已经出现了得,数据查询慢的状况。这个时候若是要把已有的大数据量表分开比较痛苦,最痛苦的事就是改代码,由于程序里面的sql语句已经写好了,如今一张表要分红几十张表,甚至上百张表,这样sql语句是否是要重写呢?举个例子,我很喜欢举子
mysql>show engines;的时候你会发现mrg_myisam其实就是merge。
mysql> CREATE TABLE IF NOT EXISTS `user1` ( -> `id` int(11) NOT NULL AUTO_INCREMENT, -> `name` varchar(50) DEFAULT NULL, -> `sex` int(1) NOT NULL DEFAULT '0', -> PRIMARY KEY (`id`) -> ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; Query OK, 0 rows affected (0.05 sec) mysql> CREATE TABLE IF NOT EXISTS `user2` ( -> `id` int(11) NOT NULL AUTO_INCREMENT, -> `name` varchar(50) DEFAULT NULL, -> `sex` int(1) NOT NULL DEFAULT '0', -> PRIMARY KEY (`id`) -> ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; Query OK, 0 rows affected (0.01 sec) mysql> INSERT INTO `user1` (`name`, `sex`) VALUES('张映', 0); Query OK, 1 row affected (0.00 sec) mysql> INSERT INTO `user2` (`name`, `sex`) VALUES('tank', 1); Query OK, 1 row affected (0.00 sec) mysql> CREATE TABLE IF NOT EXISTS `alluser` ( -> `id` int(11) NOT NULL AUTO_INCREMENT, -> `name` varchar(50) DEFAULT NULL, -> `sex` int(1) NOT NULL DEFAULT '0', -> INDEX(id) -> ) TYPE=MERGE UNION=(user1,user2) INSERT_METHOD=LAST AUTO_INCREMENT=1 ; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> select id,name,sex from alluser; +----+--------+-----+ | id | name | sex | +----+--------+-----+ | 1 | 张映 | 0 | | 1 | tank | 1 | +----+--------+-----+ 2 rows in set (0.00 sec) mysql> INSERT INTO `alluser` (`name`, `sex`) VALUES('tank2', 0); Query OK, 1 row affected (0.00 sec) mysql> select id,name,sex from user2 -> ; +----+-------+-----+ | id | name | sex | +----+-------+-----+ | 1 | tank | 1 | | 2 | tank2 | 0 | +----+-------+-----+ 2 rows in set (0.00 sec)
从上面的操做中,我不知道你有没有发现点什么?假如我有一张用户表user,有50W条数据,如今要拆成二张表user1和user2,每张表25W条数据,
INSERT INTO user1(user1.id,user1.name,user1.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id <= 250000
INSERT INTO user2(user2.id,user2.name,user2.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id > 250000
这样我就成功的将一张user表,分红了二个表,这个时候有一个问题,代码中的sql语句怎么办,之前是一张表,如今变成二张表了,代码改动很大,这样给程序员带来了很大的工做量,有没有好的办法解决这一点呢?办法是把之前的user表备份一下,而后删除掉,上面的操做中我创建了一个alluser表,只把这个alluser表的表名改为user就好了。可是,不是全部的mysql操做都能用的
a,若是你使用 alter table 来把 merge 表变为其它表类型,到底层表的映射就被丢失了。取而代之的,来自底层 myisam 表的行被复制到已更换的表中,该表随后被指定新类型。
b,网上看到一些说replace不起做用,我试了一下能够起做用的。晕一个先
mysql> UPDATE alluser SET sex=REPLACE(sex, 0, 1) where id=2; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from alluser; +----+--------+-----+ | id | name | sex | +----+--------+-----+ | 1 | 张映 | 0 | | 1 | tank | 1 | | 2 | tank2 | 1 | +----+--------+-----+ 3 rows in set (0.00 sec)
c,一个 merge 表不能在整个表上维持 unique 约束。当你执行一个 insert,数据进入第一个或者最后一个 myisam 表(取决于 insert_method 选项的值)。mysql 确保惟一键值在那个 myisam 表里保持惟一,但不是跨集合里全部的表。
d,当你建立一个 merge 表之时,没有检查去确保底层表的存在以及有相同的机构。当 merge 表被使用之时,mysql 检查每一个被映射的表的记录长度是否相等,但这并不十分可靠。若是你从不类似的 myisam 表建立一个 merge 表,你很是有可能撞见奇怪的问题。
优势:扩展性好,而且程序代码改动的不是很大
缺点:这种方法的效果比第二种要差一点
三,总结一下
上面提到的三种方法,我实际作过二种,第一种和第二种。第三种没有作过,因此说的细一点。哈哈。作什么事都有一个度,超过个度就过变得不好,不能一味的作数据库服务器集群,硬件是要花钱买的,也不要一味的分表,分出来1000表,mysql的存储归根到底还以文件的形势存在硬盘上面,一张表对应三个文件,1000个分表就是对应3000个文件,这样检索起来也会变的很慢。个人建议是
方法1和方法2结合的方式来进行分表
方法1和方法3结合的方式来进行分表
个人二个建议适合不一样的状况,根据我的状况而定,我以为会有不少人选择方法1和方法3结合的方式。
3. mysql的表分区(5.1版本之后)
分表,能够根据id区间或者时间前后顺序等多种规则来分表。分表很容易,然而由此所带来的应用程序甚至是架构方面的改动工做却不>容小觑,还包括未来的扩展性等。
在之前,一种解决方案就是使用 MERGE
类型,这是一个很是方便的作饭。架构和程序基本上不用作改动,不过,它的缺点是显见的:
这个时候,MySQL 5.1 中新增的分区(Partition)功能的优点也就很明显了:
分区容许能够设置为任意大小的规则,跨文件系统分配单个表的多个部分。实际上,表的不一样部分在不一样的位置被存储为单独的表。
分区应该注意的事项:
一、 作分区时,要么不定义主键,要么把分区字段加入到主键中。
二、 分区字段不能为NULL,要否则怎么肯定分区范围呢,因此尽可能NOT NULL
能够经过使用SHOW VARIABLES命令来肯定MySQL是否支持分区,例如:
1. range分区
create table t_range( id int(11), money int(11) unsigned not null, date datetime )partition by range(year(date))( partition p2007 values less than (2008), partition p2008 values less than (2009), partition p2009 values less than (2010) partition p2010 values less than maxvalue )
2. list分区
create table t_list( a int(11), b int(11) )partition by list (b)( partition p0 values in (1,3,5,7,9), partition p1 values in (2,4,6,8,0) );
3. hash分区
hash分区的目的是将数据均匀的分布到预先定义的各个分区中,保证各分区的数据量大体一致。
create table t_hash( a int(11), b datetime )partition by hash(YEAR(b)) partitions 4;
其中year(b)对4取模若是是0因此这条数据被分配到了p0分区。
4. key分区
key分区和hash分区类似,KEY分区和HASH分区类似,可是KEY分区支持除text和BLOB以外的全部数据类型的分区,而HASH分区只支持数字分区,KEY分区不容许使用用户自定义的表达式进行分区,KEY分区使用系统提供的HASH函数进行分区,NDB cluster使用MD5函数来分区,对于其余存储引擎mysql使用内部的hash函数,这些函数基于password()同样的算法。
create table t_key( a int(11), b datetime) partition by key (b) partitions 4;
4. 分表和分区的做用
a),分表后,单表的并发能力提升了,磁盘I/O性能也提升了。并发能力为何提升了呢,由于查寻一次所花的时间变短了,若是出现高并发的话,总表能够根据不一样的查询,将并发压力分到不一样的小表里面。磁盘I/O性能怎么搞高了呢,原本一个很是大的.MYD文件如今也分摊到各个小表的.MYD中去了。
b),mysql提出了分区的概念,我以为就想突破磁盘I/O瓶颈,想提升磁盘的读写能力,来增长mysql性能。
在这一点上,分区和分表的测重点不一样,分表重点是存取数据时,如何提升mysql并发能力上;而分区呢,如何突破磁盘的读写能力,从而达到提升mysql性能的目的。
分表,将sql分散到多个表,多个sql同时执行,提升数据库并发能力,减小每一个表sql的处理时间。
分区,将数据分散到多个区块儿或者磁盘上,突破磁盘的I/o瓶颈,提升磁盘的读写能力
因此数据库的优化,要分析影响数据库性能的缘由在哪里(是cpu忙,仍是磁盘I/o瓶颈),而后对症下药,而不能糊里糊涂随便分表,分区。
查看sql是否使用分区
explain paititions select id from test where id =123