面试问懵了?分库分表的细节你真的懂了吗?

本文详细介绍了分库分表的概念、方法及其难点,包括垂直和水平切分的具体应用场景和技术细节,并推荐了sharding-jdbc这款轻量级Java框架。

之前有不少朋友留言,想系统的学习一下分库分表相关技术,可我一直没下定决心搞,眼下赶上公司项目在使用 sharding-jdbc 对现有 MySQL 架构做分库分表的改造,所以借此机会出一系分库分表落地实践的文章,也算是自己对架构学习的一个总结。

为了让新手也能看得懂,有些知识点我可能会用更多的篇幅加以描述,希望大家不要嫌我啰嗦,如果发现文中有哪些错误或不严谨之处,欢迎大家交流指正。

具体实践分库分表之前再啰嗦几句,回头复习下分库分表的基础概念。

在这里我也为大家整理了各个知识点模块整理文档(微服务、数据库、mysql、jvm、Redis等都有)和大厂面试真题,有需要的朋友可以点一点下方链接免费领取

链接:1103806531暗号:优快云

在这里插入图片描述

什么是分库分表

其实 分库 和 分表 是两个概念,只不过通常分库与分表的操作会同时进行,以至于我们习惯性的将它们合在一起叫做分库分表。

分库分表是为了解决由于库、表数据量过大,而导致数据库性能持续下降的问题。按照一定的规则,将原本数据量大的数据库拆分成多个单独的数据库,将原本数据量大的表拆分成若干个数据表,使得单一的库、表性能达到最优的效果(响应速度快),以此提升整体数据库性能。

如何分库分表

分库分表的核心理念就是对数据进行切分(Sharding),以及切分后如何对数据的快速定位与查询结果整合。而分库与分表都可以从:垂直(纵向)和 水平(横向)两种纬度进行切分。
在这里插入图片描述
下边我们就以订单相关的业务举例,看看如何做库、表的 垂直 和 水平 切分。

垂直切分

垂直切分有 垂直 分库 和 垂直分表。

1、垂直分库

垂直分库相对来说是比较好理解的,核心理念就四个字:专库专用。

按业务类型对表进行分类,像订单、支付、优惠券、积分等相应的表放在对应的数据库中。开发者不可以跨库直连别的业务数据库,想要其他业务数据,对应业务方可以提供 API 接口,这就是微服务的初始形态。

垂直分库很大程度上取决于业务的划分,但有时候业务间的划分并不是那么清晰,比如:订单数据的拆分要考虑到与其他业务间的关联关系,并不是说直接把订单相关的表放在一个库里这么简单。

在一定程度上,垂直分库似乎提升了一些数据库性能,可实际上并没有解决由于单表数据量过大导致的性能问题,所以就需要配合水平切分方式来解决。

在这里插入图片描述

2、垂直分表

垂直分表是基于数据表的列(字段)为依据切分的,是一种大表拆小表的模式。

例如:一张 order 订单表,将订单金额、订单编号等访问频繁的字段,单独拆成一张表,把 blob 类型这样的大字段或访问不频繁的字段,拆分出来创建一个单独的扩展表 work_extend ,这样每张表只存储原表的一部分字段,再将拆分出来的表分散到不同的库中。

在这里插入图片描述
我们知道数据库是以行为单位将数据加载到内存中,这样拆分以后核心表大多是访问频率较高的字段,而且字段长度也都较短,因而可以加载更多数据到内存中,来增加查询的命中率,减少磁盘IO,以此来提升数据库性能。

垂直切分的优点:

  • 业务间数据解耦,不同业务的数据进行独立的维护、监控、扩展。
  • 在高并发场景下,一定程度上缓解了数据库的压力。

垂直切分的缺点:

  • 提升了开发的复杂度,由于业务的隔离性,很多表无法直接访问,必须通过接口方式聚合数据。
  • 分布式事务管理难度增加。
  • 数据库还是存在单表数据量过大的问题,并未根本上解决,需要配合水平切分。

水平切分

前边说了垂直切分还是会存在单库、表数据量过大的问题,当我们的应用已经无法在细粒度的垂直切分时,依旧存在单库读写、存储性能瓶颈,这时就要配合水平切分一起了,水平切分能大幅提升数据库性能。

1、水平分库

水平分库是把同一个表按一定规则拆分到不同的数据库中,每个库可以位于不同的服务器上,以此实现水平扩展,是一种常见的提升数据库性能的方式。

这种方案往往能解决单库存储量及性能瓶颈问题,但由于同一个表被分配在不同的数据库中,数据的访问需要额外的路由工作,因此系统的复杂度也被提升了。

例如下图,订单DB_1、订单DB_1、订单DB_3 三个数据库内有完全相同的表 order,我们在访问某一笔订单时可以通过对订单的订单编号取模的方式 订单编号 mod 3 (数据库实例数) ,指定该订单应该在哪个数据库中操作。

在这里插入图片描述

2、水平分表

水平分表是在同一个数据库内,把一张大数据量的表按一定规则,切分成多个结构完全相同表,而每个表只存原表的一部分数据。

例如:一张 order 订单表有 900万数据,经过水平拆分出来三个表,order_1、order_2、order_3,每张表存有数据 300万,以此类推。

在这里插入图片描述
水平分表尽管拆分了表,但子表都还是在同一个数据库实例中,只是解决了单一表数据量过大的问题,并没有将拆分后的表分散到不同的机器上,还在竞争同一个物理机的CPU、内存、网络IO等。要想进一步提升性能,就需要将拆分后的表分散到不同的数据库中,达到分布式的效果。
在这里插入图片描述

水平切分的优点:

  • 解决高并发时单库数据量过大的问题,提升系统稳定性和负载能力。
  • 业务系统改造的工作量不是很大。

水平切分的缺点:

  • 跨分片的事务一致性难以保证。
  • 跨库的join关联查询性能较差。
  • 扩容的难度和维护量较大,(拆分成几千张子表想想都恐怖)。

一定规则是什么

我们上边提到过很多次 一定规则 ,这个规则其实是一种路由算法,就是决定一条数据具体应该存在哪个数据库的哪张表里。

常见的有 取模算法 和 范围限定算法

1、取模算法

按字段取模(对hash结果取余数 (hash() mod N),N为数据库实例数或子表数量)是最为常见的一种切分方式。

还拿 order 订单表举例,先对数据库从 0 到 N-1进行编号,对 order 订单表中 work_no 订单编号字段进行取模,得到余数 i,i=0存第一个库,i=1存第二个库,i=2存第三个库…以此类推。

这样同一笔订单的数据都会存在同一个库、表里,查询时用相同的规则,用 work_no 订单编号作为查询条件,就能快速的定位到数据。

优点:

数据分片相对比较均匀,不易出现请求都打到一个库上的情况。

缺点:

这种算法存在一些问题,当某一台机器宕机,本应该落在该数据库的请求就无法得到正确的处理,这时宕掉的实例会被踢出集群,此时算法变成hash(userId) mod N-1,用户信息可能就不再在同一个库中了。

2、范围限定算法

按照 时间区间 或 ID区间 来切分,比如:我们切分的是用户表,可以定义每个库的 User 表里只存10000条数据,第一个库只存 userId 从1 ~ 9999的数据,第二个库存 userId 为10000 ~ 20000,第三个库存 userId 为 20001~ 30000…以此类推,按时间范围也是同理。

优点:

  • 单表数据量是可控的
  • 水平扩展简单只需增加节点即可,无需对其他分片的数据进行迁移
  • 能快速定位要查询的数据在哪个库

缺点:

由于连续分片可能存在数据热点,比如按时间字段分片,可能某一段时间内订单骤增,可能会被频繁的读写,而有些分片存储的历史数据,则很少被查询。

分库分表的难点

1、分布式事务

由于表分布在不同库中,不可避免会带来跨库事务问题。一般可使用 "三阶段提交 "和 “两阶段提交” 处理,但是这种方式性能较差,代码开发量也比较大。通常做法是做到最终一致性的方案,如果不苛求系统的实时一致性,只要在允许的时间段内达到最终一致性即可,采用事务补偿的方式。

这里我应用阿里的分布式事务框架Seata 来做分布式事务的管理,后边会结合实际案例。

2、分页、排序、跨库联合查询

分页、排序、联合查询是开发中使用频率非常高的功能,但在分库分表后,这些看似普通的操作却是让人非常头疼的问题。将分散在不同库中表的数据查询出来,再将所有结果进行汇总整理后提供给用户。

3、分布式主键

分库分表后数据库的自增主键意义就不大了,因为我们不能依靠单个数据库实例上的自增主键来实现不同数据库之间的全局唯一主键,此时一个能够生成全局唯一ID的系统是非常必要的,那么这个全局唯一ID就叫 分布式ID。

4、读写分离

不难发现大部分主流的关系型数据库都提供了主从架构的高可用方案,而我们需要实现 读写分离 + 分库分表,读库与写库都要做分库分表处理,后边会有具体实战案例。

5、数据脱敏

数据脱敏,是指对某些敏感信息通过脱敏规则进行数据转换,从而实现敏感隐私数据的可靠保护,如身份证号、手机号、卡号、账号密码等个人信息,一般这些都需要进行做脱敏处理。

分库分表工具

我还是那句话,尽量不要自己造轮子,因为自己造的轮子可能不那么圆,业界已经有了很多比较成熟的分库分表中间件,我们根据自身的业务需求挑选,将更多的精力放在业务实现上。

  • sharding-jdbc(当当)
  • TSharding(蘑菇街)
  • Atlas(奇虎360)
  • Cobar(阿里巴巴)
  • MyCAT(基于Cobar)
  • Oceanus(58同城)
  • Vitess(谷歌)

为什么选 sharding-jdbc

sharding-jdbc 是一款轻量级 Java 框架,以 jar 包形式提供服务,是属于客户端产品不需要额外部署,它相当于是个增强版的 JDBC 驱动;相比之下像 Mycat 这类需要单独的部署服务的服务端产品,就稍显复杂了。况且我想把更多精力放在实现业务,不想做额外的运维工作。

  • sharding-jdbc的兼容性也非常强大,适用于任何基于 JDBC 的 ORM 框架,如:JPA,
    Hibernate,Mybatis,Spring JDBC Template 或直接使用的 JDBC。

  • 完美兼容任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP,Druid, HikariCP等,几乎对所有关系型数据库都支持。

不难发现确实是比较强大的一款工具,而且它对项目的侵入性很小,几乎不用做任何代码层的修改,也无需修改 SQL 语句,只需配置待分库分表的数据表即可。

总结

简单的回顾一下分库分表的基础知识,接下来的文章会配合实战项目介绍 sharding-jdbc 在分库分表中的各个功能点。

需要更多知识点和面试资料、面试真题的朋友可以点一点下方链接免费领取

链接:1103806531暗号:优快云

在这里插入图片描述

### 分库分表相关面试题与答案 #### 1. 什么是分库分表?为什么需要进行分库分表分库分表是一种数据库扩展技术,用于解决单库单表在高并发、大数据量场景下的性能瓶颈题。垂直分库是按照业务功能维度对数据进行拆分,将不同业务的数据分别存放到不同的数据库中[^1]。水平分库则是将同一个表的数据按照某种规则(如ID范围、哈希、时间等)拆分到多个不同的数据库中[^3]。通过分库分表可以有效降低单库的压力,提升系统性能扩展性。 #### 2. 垂直分库水平分库区别是什么? 垂直分库按照业务类型对数据进行分离,剥离为多个数据库,例如订单、支付、会员等表分别存放在对应的订单库、支付库、会员库中。这种方式的核心理念是“专库专用”,能够缓解单库的压力,但无法解决单表数据量过大的题。水平分库则是将同一张表的数据按照某种规则拆分到多个数据库中,每个数据库具有相同的表结构,但存储不同的数据子集。 #### 3. 分库分表后如何进行分页查询分库分表后进行分页查询需要考虑数据的分布情况。可以通过在各个分表中分别执行分页查询,然后将结果合并起来。另一种方法是在总表中进行分页查询,根据分表规则将查询结果路由到相应的分表中获取具体数据[^2]。需要注意的是,这种操作可能会带来额外的性能开销,因此需要合理设计分页逻辑。 #### 4. 数据库分片策略有哪些?如何选择合适的分片策略? 常见的分片策略包括: - **基于范围的分片**:按照某个字段的值范围进行分片,例如按用户ID范围划分。 - **基于哈希的分片**:通过对某个字段的值进行哈希运算,将数据分配到不同的分片中。 - **基于列表的分片**:根据某些固定值列表进行分片,例如按地区划分。 - **基于时间的分片**:按照时间维度进行分片,适用于日志或历史数据存储。 选择分片策略时需要综合考虑业务特点、数据访模式以及潜在的热点题。例如,为了避免数据热点题,可以选择基于哈希的分片策略,确保数据均匀分布[^5]。 #### 5. 分库分表后如何处理跨库事务? 分库分表后,跨库事务的处理是一个复杂的题。通常可以通过以下几种方式解决: - **分布式事务管理框架**:使用如Seata、TCC等分布式事务管理框架来保证跨库事务的一致性。 - **补偿机制**:通过实现补偿逻辑来处理事务失败的情况。 - **消息队列**:利用消息队列实现最终一致性,通过异步方式完成跨库操作。 #### 6. 分区分表的区别是什么? 分区分表虽然都用于分解表,但本质上有很大区别。分表是将大表分解为若干个独立的实体表,每个表都是独立的物理存在。而分区是将数据分段划分在多个位置存放,分区后表仍然是一张表,只是数据分散到不同的物理位置。此外,分区表对分区键有严格要求,并且在表变大后执行DDL、SHARDING、单表恢复等操作会变得更加困难[^4]。 #### 7. 数据库中间件在分库分表中的作用是什么? 数据库中间件是连接应用程序数据库之间的桥梁,它可以提供数据访、事务管理、数据安全等功能,使得应用程序可以更加高效、安全地访数据库。不同的数据库中间件具有不同的特点优势,例如Sharding-JDBC提供了分库分表、读写分离等功能,帮助开发者简化分布式数据库的开发维护工作[^5]。 ```python # 示例代码:基于哈希的分片策略 def get_shard_id(user_id, total_shards): return user_id % total_shards user_id = 12345 total_shards = 10 shard_id = get_shard_id(user_id, total_shards) print(f"User ID {user_id} belongs to shard {shard_id}") ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值