------------------------------------------------- 引子 ----------------------------------------------------
在信息爆炸的当代,数据库需要存储的数据量也开始呈几倍数增长——这导致了数据库性能的降低。
如何解决?
一种思路是,提升硬件能力,比如增加存储容量,提升CPU性能等。这种方案成本很高但效果十分有限——因为,真正的瓶颈其实位于MySQL本身。
分库分表就是为了解决由于数据量过大而导致的数据库性能降低的问题。
垂直分表
▶ 定义:垂直分表是将一个表按字段分成多表,每个表存储其中一部分字段
▶ 提升:
- 减少锁表的概率(两个属性分离开来以后,对其的访问可以理解为“并行”)
- 避免了低IO效率的大字段拖累了高IO效率的小字段(小字段如通过id查姓名(varchar),大字段如通过id查个人简介(text))
▶ 局限:
- 垂直分表其实是破坏了表的结构的
- 垂直分表之后,数据归根结底依旧是被限制在一台服务器(一个机器)上的,多张表依旧在竞争着同一个物理机的CPU、内存、IO磁盘…
垂直分库
▶ 定义:垂直分库是指按照业务将表进行分类,分布到不同的数据库上面,而每个库可以放在不同的服务器上
▶ 提升:
- 解决业务层面的耦合,业务清晰
- 方便对不同业务的数据进行分级管理、维护、监控、扩展
- 将库分到不同的物理机上之后,就进入了高并发场景,突破单机的瓶颈
▶ 局限:
- 试想一下,现在垂直分库到业务只在不能再分了,也将多个数据库分到多个物理机了,但此时每个物理机上的数据仍在膨胀,怎么办?显然在垂直方向上,已然是穷途末路
水平分表,水平分库
▶ 为什么把水平分表分库放在一起说呢?
- 因为水平分表和水平分库的思路完全一致——把同一个表的数据在水平方向拦腰截断(其实一般是取模)——拆分后的数据如果仅仅分到同一个数据库中的多个表中,就是水平分表;如果拆分的数据被分到了多个数据库中,就是水平分库。它们,均没有破坏表本身的结构
- 反观垂直分表和垂直分库,其实思路相差较大——垂直分表的核心在于根据字段对表结构的切分;垂直分库的核心在于根据业务对表进行分类
▶ 定义:把一张表的数据拆分到不同的表/库中
▶ 提升:
- 减少锁定,减少IO冲突
- 水平分库还能通过将库放在不同物理机上,突破单机瓶颈
总结
- 垂直分表:把一个宽表的字段按访问频次、是否大字段的原则拆分为多个表,逻辑更清晰,性能也更高。拆分后,尽量从业务角度避免联查,毕竟我们已经把表结构给破坏了换取性能,不能反悔,得不偿失
- 垂直分库:把多个表按业务耦合松紧分类,分到不同的库中,这些库又可以分布到不同的服务器,从而使访问的压力被多个服务器负载,大大提升性能,同时业务逻辑无比清晰(专表专用)。但是我们又需要解决跨库带来的所有复杂问题
- 水平分表:把一个表的数据按数据行分到同一个数据库的多张表中,这仅仅能够小幅度提升性能,因此水平分表仅仅是水平分库的一个补充优化
- 水平分库:把一个表的数据按数据行分到多个不同的库,这些库又可以分布到不同的服务器,从而使访问的压力被多个服务器负载,大大提升性。但是我们不仅需要解决跨库带来的所有复杂问题,还需要解决数据路由的问题
分库分表共有四种思路,我们都需要使用吗? 其实,在实际中:
- 在系统设计阶段就应该根据业务耦合松紧来确定垂直分表、垂直分库方案
- 在数据访问压力不是特别大的情况下,优先考虑缓存(Redis等)、读写分离、索引技术
- 若数据量极大且不断膨胀,再考虑水平分库(水平分表作为补充)
分库分表带来的问题
▶ 事务的一致性问题
一个事务涉及若干个服务器,难免产生分布式事务问题
▶ 跨节点关联查询
两个表不在同一个数据库,甚至不在同一个服务器中,便无法进行关联查询(join in)—— 我们只能发出两次查询请求,再将查询结果进行拼装
▶ 跨节点分页、排序
在单数据库中这些根本不是事儿~但是当数据分布在了多个数据库中,就需要先汇总,再分页/排序
▶ 主键避重
我们熟悉的默认自增主键在多数据库环境中将失去用武之地——它在每个数据库中自增,在全局中就会重复。我们需要单独设计全局主键,以避免主键的重复
▶ 公共表
有些表数据量小,变动少,而且属于高频联合查询的依赖表——我们的思路是将其做成公共表,即每个数据库都拥有一份;当然,对公共表的修改会同步到每个数据库中
好在,上面的大部分问题,都可以通过中间件(Sharding-JDBC,MyCat)的层面上解决,甚至做到完全透明