池化技术
相比于 SQL 的执行,MySQL 建立连接的过程是比较耗时的。所以我们会用连接池预先建立数据库连接。
这是一种常见的软件设计思想,叫做池化技术,它的核心思想是空间换时间,期望使用预先创建好的对象来减少频繁创建对象的性能开销。
(同样的,可以建立线程池来节省频繁创建线程的性能开销)
读写分离
由于大部分系统的访问模型是读多写少,读写请求量的差距可能达到几个数量级。因此,我们优先考虑数据库如何抗住更高的查询,那么就需要把读写流量区分开,以方便针对读流量做单独的扩展,这就是主从读写分离。
读写分离带来的两个问题。
1. 一个是数据的拷贝,我们称为主从复制。
MySQL 的主从复制是依赖于 binlog 的,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上二进制日志文件。主从复制就是将 binlog 中的数据从主库传输到从库上,一般这个过程是异步的,即主库上的操作不会等待 binlog 同步的完成。
2. 在主从分离的情况下,如何屏蔽主从分离带来的访问数据库方式的变化,像是在使用单一数据库一样呢?
为了降低实现的复杂度,业界涌现了很多数据库中间件来解决数据库的访问问题,这些中间件可以分为两类。
添加应用程序的方案
第一类以淘宝的 TDDL为代表,以代码形式内嵌运行在应用程序内部。它像是一种数据源的代理,它的配置管理着多个数据源,每个数据源对应一个数据库,可能是主库,可能是从库。当有一个数据库请求时,中间件将 SQL 语句发给某一个指定的数据源来处理,然后将处理结果返回。
它的优点是简单易用,缺点是只支持java语言。
代理层方案
另一类是单独部署的代理层方案,这一类方案代表比较多,如早期阿里巴巴开源的 Cobar,基于 Cobar 开发出来的 Mycat,360 开源的 Atlas,美团开源的基于 Atlas 开发的 DBProxy 等等。
这一类中间件部署在独立的服务器上,业务代码如同在使用单一数据库一样使用它,实际上它内部管理着很多的数据源,当有数据库请求时,它会对 SQL 语句做必要的改写,然后发往指定的数据源。
它一般使用标准的 MySQL 通信协议,所以可以很好地支持多语言。由于它是独立部署的,所以也比较方便进行维护升级,比较适合有一定运维能力的大中型团队使用。它的缺陷是所有的 SQL 语句都需要跨两次网络:从应用到代理层和从代理层到数据源,所以在性能上会有一些损耗。
分库分表
随着业务增长,数据库的写入请求量大造成的性能和可用性方面的问题,要解决这些问题,你所采取的措施就是对数据进行分片。这样可以很好地分摊数据库的读写压力,也可以突破单机的存储瓶颈,而常见的一种方式是对数据库做“分库分表”。
如何对数据库做垂直拆分
垂直拆分的原则一般是按照业务类型来拆分,核心思想是专库专用,将业务耦合度比较高的表拆分到单独的库中。把不同的业务的数据分拆到不同的库,这样还有一个好处,一旦数据库发生故障时只会影响到某一个模块的功能,不会影响到整体功能,从而实现了数据层面的故障隔离。
如何对数据库做水平拆分
垂直拆分的关注点在于业务相关性,而水平拆分指的是将单一数据表按照某一种规则拆分到多个数据库和多个数据表中,关注点在数据的特点。
拆分的规则有下面这两种:
1. 按照某一个字段的哈希值做拆分,这种拆分规则比较适用于实体表,比如说用户表。
2. 另一种比较常用的是按照某一个字段的区间来拆分,比较常用的是时间字段,这样在查询时就可以根据创建时间先定位数据存储在哪个表里面,再按照查询条件来查询。
解决分库分表引入的问题
1.数据访问问题
数据库在分库分表之后,数据的访问方式也有了极大的改变,原先只需要根据查询条件到从库中查询数据即可,现在则需要先确认数据在哪一个库表中,再到那个库表中查询数据。这种复杂度也可以通过数据库中间件来解决。
如上面那两种添加应用程序或代理层的方案。
2.分区键的问题
分库分表引入的一个最大的问题就是引入了分库分表键,也叫做分区键,也就是我们对数据库做分库分表所依据的字段。
无论是哈希拆分还是区间段的拆分,我们首先都需要选取一个数据库字段,这带来一个问题是:我们之后所有的查询都需要带上这个字段,才能找到数据所在的库和表,否则就只能向所有的数据库和数据表发送查询命令。
最合适的思路是要建立一个昵称和 ID 的映射表,在查询的时候要先通过昵称查询到 ID,再通过 ID 查询完整的数据,这个表也可以是分库分表的,也需要占用一定的存储空间,但是因为表中只有两个字段,所以相比重新做一次拆分还是会节省不少的空间的。
3.主键的全局唯一性的问题
在分库分表后如何生成全局唯一的并且自增的数据库主键。
为什么UUID 不能满足需求?
首先 ID 要是有序的,因为在系统设计时,ID 有可能成为排序的字段。另一个原因在于 ID 有序也会提升数据的写入性能。UUID 不能作为 ID 的另一个原因是它不具备业务含义。
那么怎么去生成满足需求的主键?
有一种很好的方案是基于 Snowflake 算法搭建发号器来解决
…………
使用 NoSQL与传统数据库互补
当业务增长特别快,数据量达到几亿的级别后,传统数据库无论怎么扩展都很难从根本解决存储瓶颈了。这时可能需要使用NoSQL数据库。
NoSQL 数据库在性能、扩展性上的优势,以及它的一些特殊功能特性,主要有以下几点:
1. 在性能方面,NoSQL 数据库使用一些算法将对磁盘的随机写转换成顺序写,提升了写的性能;
2. 在某些场景下,比如全文搜索功能,关系型数据库并不能高效地支持,需要 NoSQL 数据库的支持;
3. 在扩展性方面,NoSQL 数据库天生支持分布式,支持数据冗余和数据分片的特性。
NoSQL 数据库发展到现在,十几年间,出现了多种类型,举几个例子:
Redis、LevelDB 这样的 KV 存储。这类存储相比于传统的数据库的优势是极高的读写性能,一般对性能有比较高的要求的场景会使用。
Hbase、Cassandra 这样的列式存储数据库。这种数据库的特点是数据不像传统数据库以行为单位来存储,而是以列来存储,适用于一些离线数据统计的场景。
像 MongoDB、CouchDB 这样的文档型数据库。这种数据库的特点是 Schema Free(模式自由),数据表中的字段可以任意扩展,比如说电商系统中的商品有非常多的字段,并且不同品类的商品的字段也都不尽相同,使用关系型数据库就需要不断增加字段支持,而用文档型数据库就简单很多了。
网友评论