MySQL基础架构

相关问题:一个SQL语句在MySQL的执行流程。

mysql基础架构

查询语句

1
2
select * from tb_student  A where A.age='18' and A.name=' 张三 ';

  1. 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 SQL 语句为 key 在内存中查询是否有结果
  2. 通过分析器进行词法分析,提取 SQL 语句的关键元素。比如提取上面这个语句是查询select,表名为tb_student,需要查询所有的列……然后判断这个 SQL 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
  3. 然后就是优化器进行确定执行方案。优化器根据自己的优化算法进行选择执行效率最好的一个方案,确认了执行计划后就准备开始执行了。
    1. 先查询姓名为张三的学生,然后判断年龄是否是18
    2. 先查询年龄18的学生,然后判断姓名是否是张三
  4. 进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。

更新语句

基本也是沿着查询的流程走,只不过执行更新的时候要记录日志。MySQL 自带的日志模块是 binlog(归档日志) ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 redo log(重做日志)

  1. 先查询到张三这一条数据,如果有缓存,也是会用到缓存。
  2. 然后拿到查询的语句,把 age 改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。
  3. 执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。
  4. 更新完成。

为什么要引入预提交状态?为了保证数据的一致性。

分析器 ----> 权限校验 ----> 执行器 —> 引擎 —redo log (prepare 状态)—>binlog—>redo log (commit 状态)

MySQL支持哪些存储引擎

通过show engines即可查看MySQL支持的所有引擎。MySQL当前默认的存储引擎是InnoDB。并且,所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。

MySQL 5.5.5 之前,MyISAM 是 MySQL 的默认存储引擎。5.5.5 版本之后,InnoDB 是 MySQL 的默认存储引擎。

MySQL存储引擎架构

MySQL 存储引擎采用的是 插件式架构 ,支持多种存储引擎,我们甚至可以为不同的数据库表设置不同的存储引擎以适应不同场景的需要。存储引擎是基于表的,而不是数据库。

MyISAM和InnoDB有什么区别

  • InnoDB 支持行级别的锁粒度,MyISAM 不支持,只支持表级别的锁粒度。
  • MyISAM 不提供事务支持。InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别。
  • MyISAM 不支持外键,而 InnoDB 支持。
  • MyISAM 不支持 MVCC,而 InnoDB 支持。
  • 虽然 MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是两者的实现方式不太一样。
  • MyISAM 不支持数据库异常崩溃后的安全恢复,而 InnoDB 支持。
  • InnoDB 的性能比 MyISAM 更强大。

是否支持行级锁

MyISAM 只有表级锁 (table-level locking),而 InnoDB 支持行级锁 (row-level locking) 和表级锁,默认为行级锁。这导致了InnoDB在并发写时的性能表现犹豫MyISAM。

是否支持事务

MyISAM不提供事务支持。InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别,具有提交 (commit) 和回滚 (rollback) 事务的能力。并且,InnoDB 默认使用的 REPEATABLE-READ(可重读)隔离级别是可以解决幻读问题发生的。

是否支持外键

MyISAM 不支持,而 InnoDB 支持。

外键对于维护数据一致性非常有帮助,但是对性能有一定的损耗。因此,通常情况下,我们是不建议在实际生产项目中使用外键的,在业务代码中进行约束即可!

是否支持数据库异常崩溃后的安全恢复

MyISAM 不支持,而 InnoDB 支持。

使用 InnoDB 的数据库在异常崩溃后,数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 redo log

索引实现不一样

性能有差别

InnoDB 的性能比 MyISAM 更强大,不管是在读写混合模式下还是只读模式下,随着 CPU 核数的增加,InnoDB 的读写能力呈线性增长。MyISAM 因为读写不能并发,它的处理能力跟核数没关系。

什么是索引

索引是一种用于快速查询和检索数据的数据结构,其本质可以看成是一种排序好的数据结构。

索引的作用就相当于书的目录。打个比方:我们在查字典的时候,如果没有目录,那我们就只能一页一页的去找我们需要查的那个字,速度很慢。如果有目录了,我们只需要先去目录里查找字的位置,然后直接翻到那一页就行了。

索引底层数据结构存在很多种类型,常见的索引结构有: B 树, B + 树 和 Hash、红黑树。在 MySQL 中,无论是 Innodb 还是 MyIsam,都使用了 B + 树作为索引结构。

索引的底层数据结构

Hash表

需要解决Hash冲突问题,并且Hash索引不支持顺序和范围查询。例如对表中的数据进行排序或者进行范围查询,Hash索引就不行了。

B树&B+树

B 树也称 B - 树。目前大部分数据库系统及文件系统都采用 B-Tree 或其变种 B+Tree 作为索引结构。

  • B 树的所有节点既存放键 (key) 也存放 数据 (data),而 B + 树只有叶子节点存放 key 和 data,其他内节点只存放 key。
  • B 树的叶子节点都是独立的;B + 树的叶子节点有一条引用链指向与它相邻的叶子节点。
  • B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B + 树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。

索引类型总结

按照数据结构维度划分:

  • BTree 索引:MySQL 里默认和最常用的索引类型。只有叶子节点存储 value,非叶子节点只有指针和 key。存储引擎 MyISAM 和 InnoDB 实现 BTree 索引都是使用 B+Tree,但二者实现方式不一样(前面已经介绍了)。
  • 哈希索引:类似键值对的形式,一次即可定位。
  • 全文索引:对文本的内容进行分词,进行搜索。目前只有 CHARVARCHARTEXT 列上可以创建全文索引。一般不会使用,效率较低,通常使用搜索引擎如 ElasticSearch 代替。

B+Tree 🆚 B Tree

B+Tree只在叶子结点存储数据,而B树的非叶子结点也要存储数据,所以B+Tree单个节点的数据量要更小。在相同的磁盘IO次数下,能查询更多的节点。

除此之外,B+Tree叶子结点用双链表链接,适合范围查找,而BTree则不行。

B+Tree 🆚 二叉树

即使数据达到千万级别时,B+Tree的高度依然维持在3~4层左右,而二叉树的高度就会很高。这就导致了二叉树检索到目标数据所经历的磁盘IO次数要更多。

B+Tree 🆚 Hash

哈希效率高, 但是不能做范围查询。

按照物理存储划分:

  • 聚簇索引(主键索引):叶子结点存放的是实际数据,所有完整的用户记录都存放在叶子结点里。
  • 二级索引(辅助索引):叶子结点存放的是主键值。获取主键值后通过主键索引中的B+Tree树查询到对应的叶子结点。这个过程叫做「回表」,也就是要查两个B+Tree才能查到数据。

按照字段特性划分:

  • 主键索引:加速查询 + 列值唯一(不可以有 NULL)+ 表中只有一个。
  • 普通索引:仅加速查询。
  • 唯一索引:加速查询 + 列值唯一(可以有 NULL)。

按字段个数分类:

  • 单列索引
  • 联合索引

将多个字段组合成一个索引,该索引就被称为联合索引。使用联合索引时,存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配。如果在使用联合索引进行查询时,不遵循「最左匹配原则」,联合索引就会失效。

使用索引的一些建议

  • 选择合适的字段创建索引

    • 不为NULL的字段
    • 被频繁查询的字段
    • 被作为条件查询的字段
    • 频繁需要排序的字段
    • 被经常用于连接的字段
  • 慎重考虑

    • 频繁更新的字段应该慎重建立索引
    • WHERE条件,GROUP BYORDER BY里用不到的字段
    • 存在大量重复数据
  • 限制每张表上的索引数量「索引会降低插入和更新的效率」

  • 尽可能的考虑联合索引而不是单列索引

索引优化

  • 前缀索引优化
  • 覆盖索引优化
  • 主键索引最好是自增的
  • 索引设置成NOT NULL
  • 避免索引失效
    • 使用select *进行查询
    • 创建了组合索引,但查询条件未遵守最左匹配原则
    • 在索引列上进行计算、函数、类型转换等操作
    • %开头的LIKE查询,比如like '%abc'
    • 查询条件使用了or,且or的前后条件中三有一个列没有索引
    • ……

MySQL三大日志

MySQL InnoDB 引擎使用 redo log (重做日志) 保证事务的持久性,使用 undo log (回滚日志) 来保证事务的原子性MySQL 数据库的数据备份、主备、主主、主从都离不开 binlog,需要依靠 binlog 来同步数据,保证数据一致性。

redo log

redolog是InnoDB存储引擎独有的,它让MySQL拥有了崩溃恢复能力。比如 MySQL 实例挂了或宕机了,重启时,InnoDB 存储引擎会使用 redo log 恢复数据,保证数据的持久性与完整性。

binlog

redo log 它是物理日志,记录内容是 “在某个数据页上做了什么修改”,是MySQL的Server层实现的日志,所有存储引擎都可以使用。

binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于 “给 ID=2 这一行的 c 字段加 1”,属于 MySQL Server 层。

不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。那 binlog 到底是用来干嘛的?

可以说 MySQL 数据库的数据备份、主备、主主、主从都离不开 binlog,需要依靠 binlog 来同步数据,保证数据一致性。

undo log

我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚,在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用 回滚日志 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。

回滚日志和重做日志的区别

  • 回滚日志「undo log」记录了此次事务「开始前」的数据状态,记录的是更新之前的值
  • 重做日志「redo log」记录了此次事务「完成后」的数据状态,记录的是更新之后的值

事务提交之前发生了崩溃,重启后会通过undo log回滚事务;事务提交之后发生了崩溃,重启后会通过redo log恢复事务

什么是事务

事务是逻辑上的一组操作,要么都执行,要么都不执行。

假如小明要给小红转账 1000 元,这个转账会涉及到两个关键操作,这两个操作必须都成功或者都失败。

  • 将小明的余额减少1000元
  • 将小红的余额增加1000元

事务会把这两个操作就可以看成逻辑上的一个整体,这个整体包含的操作要么都成功,要么都要失败。这样就不会出现小明余额减少而小红的余额却并没有增加的情况。

什么是数据库事务

简单来说,数据库事务可以保证多个对数据库的操作(也就是 SQL 语句)构成一个逻辑上的整体。构成这个逻辑上的整体的这些数据库操作遵循:要么全部执行成功,要么全部不执行

1
2
3
4
5
6
# 开启一个事务
START TRANSACTION;
# 多条 SQL 语句
SQL1,SQL2...
## 提交事务
COMMIT;

数据库事务示意图

另外,关系型数据库事务都有ACID特性:

  1. 原子性Atomicity) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 一致性Consistency): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
  3. 隔离性Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性Durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

原子性、隔离性、持久性是手段,一致性是目的。原子性通过回滚日志「undo log」来保证,持久性通过重做日志「redo log」来保证,隔离性通过MVCC或锁机制来保证。

并发事务带来了哪些问题

脏读

一个事务读取数据并且对数据进行了修改,这个修改对其他事务来说是可见的,即使当前事务没有提交。这时另外一个事务读取了这个还未提交的数据,但第一个事务突然回滚,导致数据并没有被提交到数据库,那第二个事务读取到的就是脏数据,这也就是脏读的由来。

例如:事务 1 读取某表中的数据 A=20,事务 1 修改 A=A-1,事务 2 读取到 A = 19, 事务 1 回滚导致对 A 的修改并未提交到数据库, A 的值还是 20。

丢失修改

在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。

例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 先修改 A=A-1,事务 2 后来也修改 A=A-1,最终结果 A=19,事务 1 的修改被丢失。

不可重复读

指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 修改 A=A-1,事务 2 再次读取 A =19,此时读取的结果和第一次读取的结果不同。

幻读

幻读与不可重复读类似。它发生在一个事务读取了几行数据,接着另一个并发事务插入了一些数据时。在随后的查询中,第一个事务就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

例如:事务 2 读取某个范围的数据,事务 1 在这个范围插入了新的数据,事务 2 再次读取这个范围的数据发现相比于第一次读取的结果多了新的数据。

  • 不可重复读的重点是前后数据内容不一致
  • 幻读的重点是前后记录数量不一致

并发事务的控制方式有哪些

MySQL 中并发事务的控制方式无非就两种:MVCC「多版本并发控制」

控制方式下会通过锁来显示控制共享资源而不是通过调度手段,MySQL 中主要是通过 读写锁 来实现并发控制。

  • 共享锁(S 锁) :又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
  • 排他锁(X 锁) :又称写锁 / 独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条记录加任何类型的锁(锁不兼容)。

读写锁可以做到读读并行,但是无法做到写读、写写并行。另外,根据根据锁粒度的不同,又被分为 表级锁 (table-level locking)行级锁 (row-level locking) 。InnoDB 不光支持表级锁,还支持行级锁,默认为行级锁。行级锁的粒度更小,仅对相关的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说, InnoDB 的性能更高。不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类。

MVCC 是多版本并发控制方法,即对一份数据会存储多个版本,通过事务的可见性来保证事务能看到自己应该看到的版本。通常会有一个全局的版本分配器来为每一行数据设置版本号,版本号是唯一的。

事务隔离级别

SQL标准定义了四个隔离级别:

  • READ-UNCOMMITTED (读取未提交) : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED (读取已提交) : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ (可重复读) : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE (可串行化) : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

MySQL InnoDB存储引擎的默认支持的隔离级别是可重复读。

MySQL的隔离级别是基于锁实现的吗

MySQL 的隔离级别基于锁和 MVCC 机制共同实现的。

SERIALIZABLE 隔离级别是通过锁来实现的,READ-COMMITTED「读提交」 和 REPEATABLE-READ「可重复读」 隔离级别是基于 MVCC 实现的。不过, SERIALIZABLE 之外的其他隔离级别可能也需要用到锁机制,就比如 REPEATABLE-READ 在当前读情况下需要使用加锁读来保证不会出现幻读。

表级锁和行级锁

  • 表级锁: MySQL 中锁定粒度最大的一种锁(全局锁除外),是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。不过,触发锁冲突的概率最高,高并发下效率极低。表级锁和存储引擎无关,MyISAM 和 InnoDB 引擎都支持表级锁。
  • 行级锁: MySQL 中锁定粒度最小的一种锁,是 针对索引字段加的锁 ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存储引擎有关,是在存储引擎层面实现的

MyISAM仅仅支持表级锁,InnoDB都支持。

⚠️InnoDB的行级锁是针对索引字段加的锁。当执行sql语句时,如果WHERE条件中字段没有命中唯一索引或者索引失效的话,就会导致扫描全表对表中的所有行记录进行加锁。

InnoDB有哪几类行锁

InnoDB 行锁是通过对索引数据页上的记录加锁实现的,MySQL InnoDB 支持三种行锁定方式:

  • 记录锁(Record Lock) :也被称为记录锁,属于单个行记录上的锁。
  • 间隙锁(Gap Lock) :锁定一个范围,不包括记录本身。
  • 临键锁(Next-Key Lock) :Record Lock+Gap Lock,锁定一个范围,包含记录本身,主要目的是为了解决幻读问题(MySQL 事务部分提到过)。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。

在 InnoDB 默认的隔离级别 REPEATABLE-READ 下,行锁默认使用的是 Next-Key Lock。但是,如果操作的索引是唯一索引或主键,InnoDB 会对 Next-Key Lock 进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。

共享锁和排他锁

不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类:

  • 共享锁(S 锁) :又称读锁,事务在读取记录的时候获取共享锁,允许多个事务同时获取(锁兼容)。
  • 排他锁(X 锁) :又称写锁 / 独占锁,事务在修改记录的时候获取排他锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那其他事务不能再对这条事务加任何类型的锁(锁不兼容)。

排他锁与任何的锁都不兼容,共享锁仅和共享锁兼容。

意向锁有什么作用

如果需要用到表锁的话,如何判断表中的记录没有行锁呢,一行一行遍历肯定是不行,性能太差。我们需要用到一个叫做意向锁的东东来快速判断是否可以对某个表使用表锁。

意向锁是表级锁,共有两种:

  • 意向共享锁(Intention Shared Lock,IS 锁):事务有意向对表中的某些记录加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁。
  • 意向排他锁(Intention Exclusive Lock,IX 锁):事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。

意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。

当前读和快照读

只有在事务隔离级别 RC (读取已提交) 和 RR(可重读)下,InnoDB 才会使用一致性非锁定读:

  • 在 RC 级别下,对于快照数据,一致性非锁定读总是读取被锁定行的最新一份快照数据。
  • 在 RR 级别下,对于快照数据,一致性非锁定读总是读取本事务开始时的行数据版本。

快照读比较适合对于数据一致性要求不是特别高且追求极致性能的业务场景。

当前读 (一致性锁定读)就是给行记录加 X 锁或 S 锁。

什么是读写分离

读写分离主要是为了将对数据库的读写操作分散到不同的数据库节点上。 这样的话,就能够小幅提升写性能,大幅提升读性能。

read-and-write-separation

一般情况下,我们都会选择一主多从,也就是一台主数据库负责写,其他的从数据库负责读。主库和从库之间会进行数据同步,以保证从库中数据的准确性。这样的架构实现起来比较简单,并且也符合系统的写少读多的特点。

读写分离会带来什么问题?

主库和从库的数据存在延迟。主库的数据同步到从库是需要时间的,这个时间差就导致了主库和从库的数据不一致性问题。这也就是我们经常说的主从同步延迟

解决方法:

1. 强制将读请求路由到主库处理

虽然会增加主库的压力,但是实现简单。对于这种方案,可以将那些必须获取最新数据的读请求都交给主库处理。

2. 延迟读取

还有一些朋友肯定会想既然主从同步存在延迟,那我就在延迟之后读取啊,比如主从同步延迟 0.5s, 那我就 1s 之后再读取数据。这样多方便啊!方便是方便,但是也很扯淡。

如何实现读写分离?

  1. 部署多台数据库,选择其中的一台作为主数据库,其他的一台或者多台作为从数据库
  2. 保证主数据库和从数据库之间的数据是实时同步的,这个过程也就是我们常说的主从复制
  3. 系统将写请求交给主数据库处理,读请求交给从数据库处理

第一种方式是用代理实现。我们可以在应用和数据中间加了一个代理层。应用程序所有的数据请求都交给代理层处理,代理层负责分离读写请求,将它们路由到对应的数据库中。提供类似功能的中间件有 MySQL Router(官方)、Atlas(基于 MySQL Proxy)、MaxscaleMyCat

或者是使用第三方组件来帮助我们读写请求,比如sharding-jdbc,直接引入jar包即可使用。

主从复制原理是什么

binlog主要记录了 MySQL 数据库中数据的所有变化 (数据库执行的所有 DDL 和 DML 语句)。因此,我们根据主库的 MySQL binlog 日志就能够将主库的数据同步到从库中。

  1. 主库将数据库中数据的变化写入到binlog
  2. 从库连接主库
  3. 从库会创建一个IO线程想主库请求更新的binlog
  4. 主库会创建一个binlog dump线程来发送binlog,从库中的IO线程负责接收
  5. 从库中的IO线程将接收的binlog写入到relay log中
  6. 从库的SQL线程读取relay log同步数据本地「也就是再执行一遍SQL」

简单总结:MySQL主从复制是依赖于binlog。另外,常见的一些同步MySQL数据到其他数据源的工具的底层一般也是依赖binlog。

分库分表

读写分离没有解决数据库存储问题。如何MySQL一张表的数据量过大怎么办?如何解决MySQL的存储压力呢?答案之一就是分库分表

什么是分库

分库就是将数据库中的数据分散到不同的数据库上。可以垂直分库,也可以水平分库。

垂直分库 就是把单一数据库按照业务进行划分,不同的业务使用不同的数据库,进而将一个数据库的压力分担到多个数据库。

举个例子:说你将数据库中的用户表、订单表和商品表分别单独拆分为用户数据库、订单数据库和商品数据库。

水平分库 是把同一个表按一定规则拆分到不同的数据库中,每个库可以位于不同的服务器上,这样就实现了水平扩展,解决了单表的存储和性能瓶颈的问题。

什么是分表

分表就是对单表的数据进行拆分,可以是垂直拆分,也可以是水平拆分。

垂直分表是对数据表列的拆分,把一张列比较多的表格分别多张表。比如我们可以讲用户信息表中的薪资列、公司列单独抽出来作为一个表。

水平分表是对数据表行的拆分,把一张行比较多的表拆分为多张表,可以解决单一表数据量过大的问题。

什么情况下需要分库分表

  • 单表的数据达到千万级别以上,数据库读写速度比较缓慢。
  • 数据库中的数据占用的空间越来越大,备份时间越来越长。
  • 应用的并发量太大

分库分表会带来什么问题

  • join操作:同一个数据库中的表分布在了不同的数据库中,导致无法使用 join 操作。这样就导致我们需要手动进行数据的封装,比如你在一个数据库中查询到一个数据之后,再根据这个数据去另外一个数据库中找对应的数据。
  • 事务问题:同一个数据库中的表分布在了不同的数据库中,如果单个操作涉及到多个数据库,那么数据库自带的事务就无法满足我们的要求了
  • 分布式ID:分库之后, 数据遍布在不同服务器上的数据库,数据库的自增主键已经没办法满足生成的主键唯一了。我们如何为不同的数据节点生成全局唯一主键呢?这个时候,我们就需要为我们的系统引入分布式 id 了。

参考文章:JavaGuide