数据库分库分表(浅谈)

数据库分表是为了解决单表海量数据的查询性能问题,分库是为了解决单台数据库的并发访问压力问题
分表的两种方案:

1.同库分表:所有的分表都在一个数据库中,由于数据库中表名不能重复,因此需要把数据库表名取成不同的名字。

   ●  优点:由于都在一个数据库中,公共表,不必进行复制,处理简单;

   ●  缺点:由于还在一个数据看中,CPU、内存、文件IO、网络IO等瓶颈还是无法解决,只能降低单表中的数据记录数。

   注:表名不一致也会导致后续的处理复杂

2.不同库分表:由于分表在不同的数据库中,这个时候就可以使用同样的表名。

   ●  优点:CPU、内存、文件IO、网络IO等瓶颈可以得到有效解决,表名相同,处理起来相对简单;

   ●  缺点:  公共表由于在所有的分表都要使用,因此要进行复制、同步。

分表策略的实现(利用用户ID简单实现):

因为大部分数据库设计和业务的操作都与用户的ID相关,所有可以使用用户ID实现分表的策略。(当然分表策略很多,这只是其中相对比较简单的一种)

电商平台假设,定点表order存放用户的订单数据,如下Sql:

CREATE TABLE `order` (
  `order_id` bigint(32) primary key auto_increment,
  `user_id` bigint(32),
   ...
) 

当数据比较大的时候,对数据进行分表操作,首先要确定需要将数据平均分配到多少张表中,也就是:表容量。

这里假设有100张表进行存储,则我们在进行存储数据的时候,首先对用户ID进行取模操作,根据 user_id%100 获取对应的表进行存储查询操作,示意图如下:


例如,user_id = 101 那么,我们在获取值的时候的操作,可以通过下边的sql语句:

select * from order_1 where user_id= 101

其中,order_1是根据 101%100 计算所得,表示分表之后的第一章order表。

MyBatis对数据库分表的支持:

接口定义:

/**
  * 获取用户相关的订单详细信息
  * @param tableNum 具体某一个表的编号
  * @param userId 用户ID
  * @return 订单列表
  */
public List<Order> getOrder(@Param("tableNum") int tableNum,@Param("userId") int userId);

xml配置映射文件:

<select id="getOrder" resultMap="BaseResultMap">
    select * from order_${tableNum}
    where user_id = #{userId}
  </select>

其中${tableNum} 含义是直接让参数加入到sql中,这是MyBatis支持的特性。

注意:在实际开发中,我们影虎ID更多的可能是通过UUID生成,这样的话,我们可以先将UUID进行hash获取到整数值,然后进行取模操作。

分库的策略:

数据库分表能够解决单表数据量很大的时候数据查询的效率问题,但是无法给数据库的并发操作带来效率上的提高,因为分表的实质还是在一个数据库上进行的操作,很容易受数据库IO性能的限制。

因此,如何将数据库IO性能的问题平均分配出来,很显然将数据进行分库操作可以很好地解决单台数据库的性能问题。

分库策略与分表策略的实现很相似,最简单的都是可以通过取模的方式进行。


分库分表实现策略:

上述的配置中,数据库分表可以解决单表海量数据的查询性能问题,分库可以解决单台数据库的并发访问压力问题。

有时候,我们需要同时考虑这两个问题,因此,我们既需要对单表进行分表操作,还需要进行分库操作,以便同时扩展系统的并发处理能力和提升单表的查询性能,就是我们使用到的分库分表。

分库分表的策略相对于前边两种复杂一些,一种常见的路由策略如下:

1、中间变量 = user_id%(库数量*每个库的表数量);
2、库序号 = 取整(中间变量/每个库的表数量);
3、表序号 = 中间变量%每个库的表数量;

例如:数据库有256 个,每一个库中有1024个数据表,用户的user_id=262145,按照上述的路由策略,可得:

1、中间变量 = 262145%(256*1024)= 1;
2、库序号 = 取整(1/1024)= 0;
3、表序号 = 1%1024 = 1;

这样的话,对于user_id=262145,将被路由到第0个数据库的第1个表中。


读写分离策略:

海量数据的存储即访问,通过对数据库进行读写分离,来提升数据的处理能力,读写分离它的方案特点是数据库产生多个副本,数据库的写操作都集中到一个数据库上,而一些读的操作,可以分解到其他数据库上。这样只要要付出数据复制的成本,就可以使得数据库的处理压力分解到多个数据库上,从而大大提升数据处理能力。

总结:

关于分库分表策略的选择有很多种,上文中根据用户ID应该是比较简单的一种。其他方式比如使用号段进行分区或者直接使用hash进行路由等。有兴趣的可以自行查找学习。

关于上文中提到的,如果用户的ID是通过UUID的方式生成的话,我们需要单独的进行一次hash操作,然后在进行取模操作等,其实hash本身就是一种分库分表的策略,使用hash进行路由策略的时候,我们需要知道的是,也就是hash路由策略的优缺点,优点是:数据分布均匀;缺点是:数据迁移的时候麻烦,不能按照机器性能分摊数据。

上述的分库和分表操作,查询性能和并发能力都得到了提高,但是还有一些需要注意的就是,例如:原本跨表的事物变成了分布式事物;由于记录被切分到不同的数据库和不同的数据表中,难以进行多表关联查询,并且不能不指定路由字段对数据进行查询。分库分表之后,如果我们需要对系统进行进一步的扩阵容(路由策略变更),将变得非常不方便,需要我们重新进行数据迁移。

最后需要指出的是,分库分表目前有很多中间件可供选择,最常见的是淘宝的Cobar;另外Spring也可以实现数据库读写分离操作。

中间件:


注:文章内容有很多借鉴之处,只是作为自我总结只用。如果对大家有帮忙,那就最好了。

猜你喜欢

转载自blog.csdn.net/huang_yx/article/details/79673718