Mysql分库分表方案

Mysql分区:【还是在一个表里】,分表则在多张表了

说白就是sql语句,1.可以建静态的分区,2.也可以建动态的,3.还可以后来补充分区【下面的注释部分其实是执行的,尽管是灰色】

 CREATE TABLE `us_active_user_111111111111111` (
  `visitdate` date NOT NULL,
  `us_id` int(11) NOT NULL,
  PRIMARY KEY (`us_id`,`visitdate`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY RANGE (EXTRACT(YEAR_MONTH FROM `visitdate`))
SUBPARTITION BY HASH (to_days(`visitdate`))
SUBPARTITIONS 31
(PARTITION p201610 VALUES LESS THAN (201611) ENGINE = MyISAM,
 PARTITION p201611 VALUES LESS THAN (201612) ENGINE = MyISAM,
 PARTITION p201612 VALUES LESS THAN (201701) ENGINE = MyISAM,
 PARTITION p201701 VALUES LESS THAN (201702) ENGINE = MyISAM,
 PARTITION p201702 VALUES LESS THAN (201703) ENGINE = MyISAM,
 PARTITION p201703 VALUES LESS THAN (201704) ENGINE = MyISAM,
 PARTITION p201704 VALUES LESS THAN (201705) ENGINE = MyISAM,
 PARTITION p201705 VALUES LESS THAN (201706) ENGINE = MyISAM,
 PARTITION p201706 VALUES LESS THAN (201707) ENGINE = MyISAM,
 PARTITION p201707 VALUES LESS THAN (201708) ENGINE = MyISAM,
 PARTITION p201708 VALUES LESS THAN (201709) ENGINE = MyISAM,
 PARTITION p201709 VALUES LESS THAN (201710) ENGINE = MyISAM,
 PARTITION p201710 VALUES LESS THAN (201711) ENGINE = MyISAM,
 PARTITION p201711 VALUES LESS THAN (201712) ENGINE = MyISAM,
 PARTITION p201712 VALUES LESS THAN (201801) ENGINE = MyISAM,
 PARTITION p201801 VALUES LESS THAN (201802) ENGINE = MyISAM,
 PARTITION p201802 VALUES LESS THAN (201803) ENGINE = MyISAM,
 PARTITION p201803 VALUES LESS THAN (201804) ENGINE = MyISAM) */;

分区的几种方式:【也可以自己定义】

1.Range(范围)–这种模式允许将数据划分不同范围。例如可以将一个表通过年份划分成若干个分区。
2.Hash(哈希)–这中模式允许通过对表的一个或多个列的Hash Key 进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区。例如可以建立一个对表主键进行分区的表。
3.Key (键值)-上面Hash模式的一种延伸,这里的Hash Key 是MySQL系统产生的。
4.List(预定义列表)–这种模式允许系统通过预定义的列表的值来对数据进行分割。
5.Composite(复合模式) –以上模式的组合使用 

-------------------------------------

range分区

按照RANGE分区的表是通过如下一种方式进行分区的,每个分区包含那些分区表达式的值位于一个给定的连续区间内的行

  1. //创建range分区表  
  2. mysql> CREATE TABLE IF NOT EXISTS `user` (  
  3.  ->   `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',  
  4.  ->   `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',  
  5.  ->   `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0为男,1为女',  
  6.  ->   PRIMARY KEY (`id`)  
  7.  -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1  
  8.  -> PARTITION BY RANGE (id) (  
  9.  ->     PARTITION p0 VALUES LESS THAN (3),  
  10.  ->     PARTITION p1 VALUES LESS THAN (6),  
  11.  ->     PARTITION p2 VALUES LESS THAN (9),  
  12.  ->     PARTITION p3 VALUES LESS THAN (12),  
  13.  ->     PARTITION p4 VALUES LESS THAN MAXVALUE  
  14.  -> );  
  15. Query OK, 0 rows affected (0.13 sec)  
  16.   
  17. //插入一些数据  
  18. mysql> INSERT INTO `test`.`user` (`name` ,`sex`)VALUES ('tank''0')  
  19.  -> ,('zhang',1),('ying',1),('张',1),('映',0),('test1',1),('tank2',1)  
  20.  -> ,('tank1',1),('test2',1),('test3',1),('test4',1),('test5',1),('tank3',1)  
  21.  -> ,('tank4',1),('tank5',1),('tank6',1),('tank7',1),('tank8',1),('tank9',1)  
  22.  -> ,('tank10',1),('tank11',1),('tank12',1),('tank13',1),('tank21',1),('tank42',1);  
  23. Query OK, 25 rows affected (0.05 sec)  
  24. Records: 25  Duplicates: 0  Warnings: 0  
  25.   
  26. //到存放数据库表文件的地方看一下,my.cnf里面有配置,datadir后面就是  
  27. [root@BlackGhost test]# ls |grep user |xargs du -sh  
  28. 4.0K    user#P#p0.MYD  
  29. 4.0K    user#P#p0.MYI  
  30. 4.0K    user#P#p1.MYD  
  31. 4.0K    user#P#p1.MYI  
  32. 4.0K    user#P#p2.MYD  
  33. 4.0K    user#P#p2.MYI  
  34. 4.0K    user#P#p3.MYD  
  35. 4.0K    user#P#p3.MYI  
  36. 4.0K    user#P#p4.MYD  
  37. 4.0K    user#P#p4.MYI  
  38. 12K    user.frm  
  39. 4.0K    user.par  
  40.   
  41. //取出数据  
  42. mysql> select count(id) as count from user;  
  43. +-------+  
  44. count |  
  45. +-------+  
  46. |    25 |  
  47. +-------+  
  48. 1 row in set (0.00 sec)  
  49.   
  50. //删除第四个分区  

  1. mysql> alter table user drop partition p4;  

  1. Query OK, 0 rows affected (0.11 sec)  
  2. Records: 0  Duplicates: 0  Warnings: 0  
  3.   
  4. /**存放在分区里面的数据丢失了,第四个分区里面有14条数据,剩下的3个分区 
  5. 只有11条数据,但是统计出来的文件大小都是4.0K,从这儿我们可以看出分区的 
  6. 最小区块是4K 
  7. */  
  8. mysql> select count(id) as count from user;  
  9. +-------+  
  10. count |  
  11. +-------+  
  12. |    11 |  
  13. +-------+  
  14. 1 row in set (0.00 sec)  
  15.   
  16. //第四个区块已删除  
  17. [root@BlackGhost test]# ls |grep user |xargs du -sh  
  18. 4.0K    user#P#p0.MYD  
  19. 4.0K    user#P#p0.MYI  
  20. 4.0K    user#P#p1.MYD  
  21. 4.0K    user#P#p1.MYI  
  22. 4.0K    user#P#p2.MYD  
  23. 4.0K    user#P#p2.MYI  
  24. 4.0K    user#P#p3.MYD  
  25. 4.0K    user#P#p3.MYI  
  26. 12K    user.frm  
  27. 4.0K    user.par  
  28.   
  29. /*可以现有表进行分区,并且会按規则自动的将表中的数据分配相应的分区 
  30. 中,这样就比较好了,可以省去很多事情,看下面的操作*/  
  31. mysql> 
  32. alter table aa partition by RANGE(id)  
  33.  -> (PARTITION p1 VALUES less than (1),  
  34.  -> PARTITION p2 VALUES less than (5),  
  35.  -> PARTITION p3 VALUES less than MAXVALUE);  
  36. Query OK, 15 rows affected (0.21 sec)   //对15数据进行分区  
  37. Records: 15  Duplicates: 0  Warnings: 0  
  38.   
  39. //总共有15条  
  40. mysql> select count(*) from aa;  
  41. +----------+  
  42. count(*) |  
  43. +----------+  
  44. |       15 |  
  45. +----------+  
  46. 1 row in set (0.00 sec)  
  47.   
  48. //删除一个分区  

  1. mysql> alter table aa drop partition p2;  

  1. Query OK, 0 rows affected (0.30 sec)  
  2. Records: 0  Duplicates: 0  Warnings: 0  
  3.   
  4. //只有11条了,说明对现有的表分区成功了  
  5. mysql> select count(*) from aa;  
  6. +----------+  
  7. count(*) |  
  8. +----------+  
  9. |       11 |  
  10. +----------+  
  11. 1 row in set (0.00 sec) 

list分区

LIST分区中每个分区的定义和选择是基于某列的值从属于一个值列表集中的一个值,而RANGE分 区是从属于一个连续区间值的集合。

  1. //这种方式失败  
  2. mysql> CREATE TABLE IF NOT EXISTS `list_part` (  
  3.  ->   `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',  
  4.  ->   `province_id` int(2) NOT NULL DEFAULT 0 COMMENT '省',  
  5.  ->   `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',  
  6.  ->   `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0为男,1为女',  
  7.  ->   PRIMARY KEY (`id`)  
  8.  -> ) ENGINE=INNODB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1  
  9.  -> PARTITION BY LIST (province_id) (  
  10.  ->     PARTITION p0 VALUES IN (1,2,3,4,5,6,7,8),  
  11.  ->     PARTITION p1 VALUES IN (9,10,11,12,16,21),  
  12.  ->     PARTITION p2 VALUES IN (13,14,15,19),  
  13.  ->     PARTITION p3 VALUES IN (17,18,20,22,23,24)  
  14.  -> );  
  15. ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function 
  16.  
  17. //这种方式成功 
  18. mysql> CREATE TABLE IF NOT EXISTS `list_part` ( 
  19.  ->   `id` int(11) NOT NULL  COMMENT '用户ID', 
  20.  ->   `province_id` int(2) NOT NULL DEFAULT 0 COMMENT '', 
  21.  ->   `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称', 
  22.  ->   `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0为男,1为女'  
  23.  -> ) ENGINE=INNODB  DEFAULT CHARSET=utf8  
  24.  -> PARTITION BY LIST (province_id) (  
  25.  ->     PARTITION p0 VALUES IN (1,2,3,4,5,6,7,8),  
  26.  ->     PARTITION p1 VALUES IN (9,10,11,12,16,21),  
  27.  ->     PARTITION p2 VALUES IN (13,14,15,19),  
  28.  ->     PARTITION p3 VALUES IN (17,18,20,22,23,24)  
  29.  -> );  
  30. Query OK, 0 rows affected (0.33 sec)  

上面的这个创建list分区时,如果有主銉的话,分区时主键必须在其中,不然就会报错

如果我不用主键,分区就创建成功了,一般情况下,一个张表肯定会有一个主键,这算是一个分区的局限性吧。

如果对数据进行测试,请参考range分区的测试来操作

hash分区

HASH分区主要用来确保数据在预先确定数目的分区中平均分布,你所要做的只是基于将要被哈希的列值指定一个列值或表达式,以 及指定被分区的表将要被分割成的分区数量。

  1. mysql> CREATE TABLE IF NOT EXISTS `hash_part` (  
  2.  ->   `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '评论ID',  
  3.  ->   `comment` varchar(1000) NOT NULL DEFAULT '' COMMENT '评论',  
  4.  ->   `ip` varchar(25) NOT NULL DEFAULT '' COMMENT '来源IP',  
  5.  ->   PRIMARY KEY (`id`)  
  6.  -> ) ENGINE=INNODB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1  
  7.  -> PARTITION BY HASH(id)  
  8.  -> PARTITIONS 3;  
  9. Query OK, 0 rows affected (0.06 sec)  

key分区

按照KEY进行分区类似于按照HASH分区,除了HASH分区使用的用 户定义的表达式,而KEY分区的 哈希函数是由MySQL 服务器提供。

  1. mysql> CREATE TABLE IF NOT EXISTS `key_part` (  
  2.  ->   `news_id` int(11) NOT NULL  COMMENT '新闻ID',  
  3.  ->   `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '新闻内容',  
  4.  ->   `u_id` varchar(25) NOT NULL DEFAULT '' COMMENT '来源IP',  
  5.  ->   `create_time` DATE NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '时间'  
  6.  -> ) ENGINE=INNODB  DEFAULT CHARSET=utf8  

  1.  -> PARTITION BY LINEAR HASH(YEAR(create_time))  
  1.  -> PARTITIONS 3;  

  1. Query OK, 0 rows affected (0.07 sec)  

-------------------------------------







Range:
create table range(//这里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 
)

List:

create table 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) 
 );


Hash:

create table hash( 
  a int(11), 
  b datetime 
  )partition by hash (YEAR(b) 
  partitions 4;


Key:
create table t_key( 
  a int(11), 
  b datetime) 
  partition by key (b) 
  partitions 4;


分区管理

新增分区

ALTER TABLE sale_data
ADD PARTITION (PARTITION p201010 VALUES LESS THAN ( 201011 ));

删除分区
--当删除了一个分区,也同时删除了该分区中所有的数据。
ALTER TABLE sale_data DROP PARTITION p201010;

分区的合并
下面的SQL,将p201001 - p201009 合并为3个分区p2010Q1 - p2010Q3

ALTER TABLE sale_data
REORGANIZE PARTITION p201001,p201002,p201003,
p201004,p201005,p201006,
p201007,p201008,p201009 INTO
(
PARTITION p2010Q1 VALUES LESS THAN ( 201004 ),
PARTITION p2010Q2 VALUES LESS THAN ( 201007 ),
PARTITION p2010Q3 VALUES LESS THAN ( 201010 )
);

分区优点
1,分区可以分在多个磁盘,存储更大一点
2,根据查找条件,也就是where后面的条件,查找只查找相应的分区不用全部查找了
3,进行大数据搜索时可以进行并行处理。
4,跨多个磁盘来分散数据查询,来获得更大的查询吞吐量


Mysql分库分表方案

http://wentao365.iteye.com/blog/1740874

1.为什么要分表:


当一张表的数据达到几千万时,你查询一次所花的时间会变多,如果有联合查询的话,我想有可能会死在那儿了。分表的目的就在于此,减小数据库的负担,缩短查询时间。


mysql中有一种机制是表锁定和行锁定,是为了保证数据的完整性。

表锁定表示你们都不能对这张表进行操作,必须等我对表操作完才行。

行锁定也一样,别的sql必须等我对这条数据操作完了,才能对这条数据进行操作。



2. mysql proxy:amoeba


做mysql集群,利用amoeba。


从上层的java程序来讲,不需要知道主服务器和从服务器的来源,即主从数据库服务器对于上层来讲是透明的。可以 通过amoeba来配置。


 3.大数据量并且访问频繁的表,将其分为若干个表


比如对于某网站平台的数据库表-公司表,数据量很大,这种能预估出来的大数据量表,我们就事先分出个N个表,这个N是多少,根据实际情况而定。


     某网站现在的数据量至多是5000万条,可以设计每张表容纳的数据量是500万条,也就是拆分成10张表,


那么如何判断某张表的数据是否容量已满呢?可以在程序段对于要新增数据的表,

在插入前先做统计表记录数量的操作,当<500万条数据,就直接插入,

当已经到达阀值,可以在程序段新创建数据库表(或者已经事先创建好),再执行插入操作。



 4. 利用merge存储引擎来实现分表


如果要把已有的大数据量表分开比较痛苦,最痛苦的事就是改代码,因为程序里面的sql语句已经写好了。用merge存储引擎来实现分表, 这种方法比较适合.


举例子:


 


 ------------------- ----------华丽的分割线--------------------------------------


 数据库架构


1、简单的MySQL主从复制:


MySQL的主从复制解决了数据库的读写分离,并很好的提升了读的性能,其图如下:


 


其主从复制的过程如下图所示:


 


但是,主从复制也带来其他一系列性能瓶颈问题:


1. 写入无法扩展


2. 写入无法缓存


3. 复制延时


4. 锁表率上升


5. 表变大,缓存率下降


那问题产生总得解决的,这就产生下面的优化方案,一起来看看。






2、MySQL垂直分区


   如果把业务切割得足够独立,那把不同业务的数据放到不同的数据库服务器将是一个不错的方案,而且

万一其中一个业务崩溃了也不会影响其他业务的正常进行,

并且也起到了负载分流的作用,大大提升了数据库的吞吐能力。

经过垂直分区后的数据库架构图如下:



 


然而,尽管业务之间已经足够独立了,但是有些业务之间或多或少总会有点联系,如用户,基本上都会和每个业务相关联,况且这种分区方式,也不能解决单张表数据量暴涨的问题,因此为何不试试水平分割呢?


3、MySQL水平分片(Sharding)


这是一个非常好的思路,将 用户按一定规则(按id哈希)分组,并把该组用户的数据存储到一个数据库分片中,即一个sharding,这样随着用户数量的增加,只要简单地配置一台服务器即可,原理图如下:


 


如何来确定某个用户所在的shard呢,可以

建一张用户和shard对应的数据表,

每次请求先从这张表找用户的shard id,再从对应shard中查询相关数据,如下图所示:


不太严格的讲,对于海量数据的数据库,

如果是因为表多而数据多,这时候适合使用垂直切分,即把关系紧密(比如同一模块)的表切分出来放在一个server上。

如果表并不多,但每张表的数据非常多,这时候适合水平切分,即把表的数据按某种规则(比如按ID散列)切分到多个数据库(server)上。

当然,现实中更多是这两种情况混杂在一起,这时候需要根据实际情况做出选择,也可能会综合使用垂直与水平切分,从而将原有数据库切分成类似矩阵一样可以无限扩充的数据库(server)阵列。下面分别详细地介绍一下垂直切分和水平切分.


垂直切分的最大特点就是规则简单,实施也更为方便,

尤其适合各业务之间的耦合度非常低,相互影响很小,业务逻辑非常清晰的系统。

在这种系统中,可以很容易做到将不同业务模块所使用的表分拆到不同的数据库中。

根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也会比较简单清晰。(这也就是所谓的”share nothing”)。

 


不太严格的讲,对于海量数据的数据库,如果是因为表多而数据多,这时候适合使用垂直切分,即把关系紧密(比如同一模块)的表切分出来放在一个server上。如果表并不多,但每张表的数据非常多,这时候适合水平切分,即把表的数据按某种规则(比如按ID散列)切分到多个数据库(server)上。当然,现实中更多是这两种情况混杂在一起,这时候需要根据实际情况做出选择,也可能会综合使用垂直与水平切分,从而将原有数据库切分成类似矩阵一样可以无限扩充的数据库(server)阵列。下面分别详细地介绍一下垂直切分和水平切分.


水平切分于垂直切分相比,相对来说稍微复杂一些。因为要
将同一个表中的不同数据拆分到不同的数据库中,对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂
后期的数据维护也会更为复杂一些
数系统会将垂直切分和水平切分联合使用,先对系统做垂直切分,再针对每一小搓表的情况选择性地做水平切分。从而将整个数据库切分成一个分布式矩阵。

二、切分策略

      如前面所提到的,切分是按先垂直切分再水平切分的步骤进行的。

     垂直切分的结果正好为水平切分做好了铺垫。

直切分的思路就是分析表间的聚合关系,把关系紧密的表放在一起。多数情况下可能是同一个模块,或者是同一“聚集”。这里的“聚集”正是领域驱动设计里所说的聚集。在垂直切分出的表聚集内,找出“根元素”(这里的“根元素”就是领域驱动设计里的“聚合根”),按“根元素”进行水平切分,也就是从“根元素”开始,把所有和它直接与间接关联的数据放入一个shard里。这样出现跨shard关联的可能性就非常的小。应用程序就不必打断既有的表间关联。比如:对于社交网站,几乎所有数据最终都会关联到某个用户上,基于用户进行切分就是最好的选择。再比如论坛系统,用户和论坛两个模块应该在垂直切分时被分在了两个shard里,对于论坛模块来说,Forum显然是聚合根,因此按Forum进行水平切分,把Forum里所有的帖子和回帖都随Forum放在一个shard里是很自然的。

      对于共享数据数据,如果是只读的字典表,每个shard里维护一份应该是一个不错的选择,这样不必打断关联关系。如果是一般数据间的跨节点的关联,就必须打断。


      需要特别说明的是:当同时进行垂直和水平切分时,切分策略会发生一些微妙的变化。比如:在只考虑垂直切分的时候,被划分到一起的表之间可以保持任意的关联关系,因此你可以按“功能模块”划分表格,但是一旦引入水平切分之后,表间关联关系就会受到很大的制约,通常只能允许一个主表(以该表ID进行散列的表)和其多个次表之间保留关联关系,也就是说:当同时进行垂直和水平切分时,在垂直方向上的切分将不再以“功能模块”进行划分,而是需要更加细粒度的垂直切分,而这个粒度与领域驱动设计中的“聚合”概念不谋而合,甚至可以说是完全一致,每个shard的主表正是一个聚合中的聚合根!这样切分下来你会发现数据库分被切分地过于分散了(shard的数量会比较多,但是shard里的表却不多),为了避免管理过多的数据源,充分利用每一个数据库服务器的资源,可以考虑将业务上相近,并且具有相近数据增长速率(主表数据量在同一数量级上)的两个或多个shard放到同一个数据源里,每个shard依然是独立的,它们有各自的主表,并使用各自主表ID进行散列,不同的只是它们的散列取模(即节点数量)必需是一致的。(

本文着重介绍sharding的基本思想和理论上的切分策略,关于更加细致的实施策略和参考事例请参考我的另一篇博文:数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示 


1.事务问题:
解决事务问题目前有两种可行的方案:分布式事务和通过应用程序与数据库共同控制实现事务下面对两套方案进行一个简单的对比。
方案一:使用分布式事务
    优点:交由数据库管理,简单有效
    缺点:性能代价高,特别是shard越来越多时
方案二:由应用程序和数据库共同控制
     原理:将一个跨多个数据库的分布式事务分拆成多个仅处
           于单个数据库上面的小事务,并通过应用程序来总控
           各个小事务。
     优点:性能上有优势
     缺点:需要应用程序在事务控制上做灵活设计。如果使用  
           了spring的事务管理,改动起来会面临一定的困难。
2.跨节点Join的问题
      只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。

3.跨节点的count,order by,group by以及聚合函数问题
      这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。


参考资料:

《MySQL性能调优与架构设计》



比如日志文章(article)表有如下字段:

面对这样的一个表,我们怎样切分呢?怎样将这样的数据分布到不同的数据库中的表中去呢?我们可以这样做,将user_id为1~10000的所有的文章信息放入DB1中的article表中,将user_id为10001~20000的所有文章信息放入DB2中的 article表中,以此类推,一直到DBn。这样一来,文章数据就很自然的被分到了各个数据库中,达到了数据切分的目的。

接下来要解决的问题就是怎样找到具体的数据库呢?其实问题也是简单明显的,既然分库的时候我们用到了区分字段user_id,那么很自然,数据库路由的过程当然还是少不了user_id的。就是我们知道了这个blog的user_id,就利用这个user_id,利用分库时候的规则,反过来定位具体的数据库。比如user_id是234,利用刚才的规则,就应该定位到DB1,假如user_id是12343,利用该才的规则,就应该定位到DB2。以此类推,利用分库的规则,反向的路由到具体的DB,这个过程我们称之为“DB路由”。


平常我们会自觉的按照范式来设计我们的数据库,考虑到数据切分的DB设计,将违背这个通常的规矩和约束。为了切分,我们不得不在数据库的表中出现冗余字段,用作区分字段或者叫做分库的标记字段。比如上面的article的例子中的user_id这样的字段(当然,刚才的例子并没有很好的体现出user_id的冗余性,因为user_id这个字段即使就是不分库,也是要出现的,算是我们捡了便宜吧)。当然冗余字段的出现并不只是在分库的场景下才出现的,在很多大型应用中,冗余也是必须的,这个涉及到高效DB的设计,本文不再赘述。



<think>首先,用户的问题是关于MySQL分库分表架构设计方案及实现方法的。我需要基于系统级指令来构建回答。 系统级指令关键点: - 所有行内数学表达式必须用$...$格式。 - 独立公式必须用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考提供的引用内容,但用户意图是了解MySQL分库分表方案。 提供的引用: - [^1]: 关于MySQL分库分表的概述,强调数据分片、路由规则、分布式事务等。 - [^2]: 介绍分库分表的基本概念,如水平分表、垂直分表、水平分库。 - [^3]: 关于基于SpringCloudAlibaba的微服务架构设计,但似乎不直接相关,可能是个错误或无关引用。 用户意图:了解或实现MySQL分库分表方案,处理数据库水平扩展、性能优化或大数据量场景。 我的回答结构: 1. **介绍分库分表的概念和必要性**:解释为什么需要分库分表,基于大数据量和高并发。 2. **分库分表的设计方案**:详细描述水平分表、垂直分表、水平分库等。 3. **实现方法**:包括路由规则、分布式事务处理、ID生成等。 4. **实际示例**:使用Java或伪代码展示如何实现。 5. **优化和注意事项**:讨论性能、数据迁移等。 6. **生成相关问题**:在最后添加相关问题部分。 确保使用中文回答。 数学表达式:虽然用户问题不涉及数学,但指令说所有行内数学表达式必须用$...$。如果提到任何数学概念,如取模运算,应该用$...$。例如,路由规则中可能用到取模,写成$user_id \mod N$。 独立公式:如果有,用$$...$$,但这里可能不需要。 引用标识:在回答中,如果引用提供的参考内容,在末尾添加[^1]等。例如,提到数据分片时,参考引用[^1]。 逐步构建回答: - **步骤1:介绍**:简述分库分表是什么,为什么需要它。参考引用[^1]和[^2]。 - **步骤2:设计方案**:详细解释水平分表、垂直分表、水平分库。 - **步骤3:实现方法**:讨论路由规则(如基于用户ID取模),分布式事务,ID生成。 - **步骤4:示例代码**:提供简单Java代码或伪代码展示实现。 - **步骤5:优化**:谈数据迁移、跨分片查询等。 - **步骤6:总结**。 - **步骤7:相关问题**:生成3-5个相关问题。 生成相关问题: - 基于主题,如性能分析、优化方法、应用场景。 - 示例:1. 如何选择分片键?2. 如何处理分布式事务?3. 分库分表的常见问题? 确保回答真实可靠:基于我的知识和提供的引用。引用[^1]和[^2]是相关的,[^3]似乎不直接相关,所以少用或忽略。 最后,添加引用标识:在回答中,当提到特定点时,添加[^1]等。例如,“在实施分库分表时,需要考虑数据分片和路由规则[^1]。” 完整回答结构: - 标题:MySQL分库分表架构设计方案及实现方法 - 内容: 1. 引言 2. 分库分表的基本概念 3. 设计方案 4. 实现方法 5. 优化和挑战 6. 总结 - 相关问题部分</think>### MySQL分库分表架构设计方案及实现方法 MySQL分库分表是一种应对大数据量和高并发访问的关键技术,通过将数据分散到多个数据库或表中,提升系统的性能、扩展性和可用性。以下我将基于实际架构设计经验,逐步介绍方案和实现方法。回答参考了相关技术文档和最佳实践[^1][^2]。 #### 1. **分库分表的基本概念与必要性** - **为什么需要分库分表?** 当单库单表面临数据量过大(如TB级)或高并发(如每秒数千次查询)时,会出现性能瓶颈(如I/O阻塞、锁竞争)。分库分表通过水平或垂直拆分数据,减轻单点压力,提高吞吐量。例如: - 水平分表:将一张大表按规则(如用户ID取模)拆分成多个物理小表。 - 垂直分表:根据业务模块拆分字段(如用户表拆分为基本信息表和扩展信息表)。 - 水平分库:将不同表分布到不同数据库实例上,实现资源隔离。 引用[^2]指出,分库分表的核心目标是提升并发处理能力和存储容量。 #### 2. **分库分表的设计方案** 设计方案需结合业务场景,常见策略包括: - **分片键选择**:选择高频查询字段作为分片键(如用户ID、订单ID)。例如,用户表按$user_id \mod N$(N为分片数)路由到不同分片。 - **分片策略**: - **范围分片**:按字段范围(如时间戳)分配数据,适合时间序列数据。 - **哈希分片**:使用哈希函数(如$hash(key) \mod N$)确保数据均匀分布,避免热点问题。 - **自定义路由**:基于业务规则(如地域)动态路由。 - **架构模式**: - **共享数据库**:多个应用共享一个数据库集群,适合微服务架构。 - **独立数据库**:每个分片部署独立数据库实例,提高隔离性。 设计时需考虑数据一致性(如通过分布式事务)和扩展性(支持动态添加分片)[^1]。 #### 3. **实现方法与代码示例** 实现分库分表涉及路由、事务、ID生成等。以下以Java和MySQL为例,结合Spring框架演示水平分表实现。 - **路由规则实现**: 使用分片键计算目标表。例如,用户表按ID取模: ```java // 分片计算工具类 public class ShardingUtil { public static String getTableSuffix(String userId, int shardCount) { int hash = Math.abs(userId.hashCode()); int suffix = hash % shardCount; // 计算分片后缀,如 _0, _1 return String.valueOf(suffix); } } ``` - **SQL执行与数据源路由**: 借助Spring的AbstractRoutingDataSource实现动态数据源切换: ```java // 动态数据源路由器 public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // 从线程上下文获取分片键 return ShardingContext.getShardKey(); } } // 在Service层设置分片上下文 @Service public class UserService { @Autowired private UserMapper userMapper; public User getUserById(String userId) { // 设置当前分片键 ShardingContext.setShardKey(userId); return userMapper.selectById(userId); } } ``` - **分布式ID生成**: 避免主键冲突,使用Snowflake算法生成全局唯一ID: ```java public class SnowflakeIdGenerator { public long nextId() { // 实现Snowflake逻辑(时间戳+机器ID+序列号) return ...; } } ``` - **分布式事务处理**: 采用柔性事务(如Seata框架)保证最终一致性: ```yml # Spring配置示例 seata: enabled: true application-id: user-service tx-service-group: my_tx_group ``` #### 4. **优化与挑战** - **性能优化**: - **跨分片查询**:通过汇总中间件(如ShardingSphere)聚合结果,减少多次查询。 - **数据迁移**:使用双写策略(旧新库同时写入)逐步迁移,避免停机。 - **常见挑战**: - **分布式事务**:优先使用本地事务或消息队列(如RocketMQ)解耦。 - **ID生成**:确保全局唯一性(如用UUID或Snowflake算法)。 - **监控与扩展**:集成Prometheus监控分片负载,支持动态扩容。 引用[^1]强调,合理设计能显著提高系统可用性,但需平衡复杂性和业务需求。 #### 5. **总结** MySQL分库分表是处理大数据场景的有效手段,核心在于分片设计、路由规则和事务管理。通过水平分表或分库,结合Java中间件(如ShardingSphere),可构建高可用架构。实际实施时,建议从简单场景入手,逐步优化。随着技术发展,新工具(如TiDB)也值得关注[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值