34

【架构入门 - 高性能篇】数据库高性能

 5 years ago
source link: http://www.wangtianyi.top/blog/2019/05/05/jia-gou-ru-men-gao-xing-neng-pian-shu-ju-ku/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

单机高性能与集群高性能都是针对负责计算的服务器而言的,负责存储的数据库服务器因为处理的是数据而不是计算,架构和使用方式又有所不同

YfyAzab.jpg!web

SQL - 读写分离

基本原理是将数据库读写操作分散到不同节点上,从而分散读写压力到不同的节点上

IrAJb2u.jpg!web

一主一从或一主多从都可以;主机负责读写操作,从机负责读操作,每个机器都需要同时工作;主机通过复制将数据同步到从机,每个节点都存储了所有的数据

复制延迟

MySQL的主从复制延迟可能达到1秒,如果有大量数据,1分钟也可能,那么这样很容易造成用户注册了但是无法登陆

一般的解决方法是将关键业务全部指向主机,非关键业务进行读写分离,比如注册登陆都指向主机,修改个人信息就读写分离,即使查询出来的是旧的数据,业务上的影响也不会太大

分配机制

如何将读写操作分开,访问不同的数据库呢?

【1】 程序代码封装

在程序中抽象出一个数据访问层,实现读写操作分离和数据库服务连接的管理,比如

MJjiumY.jpg!web

实现简单,但无法通用,每个语言都需要写一个,主从数据库发生切换则需要修改所有系统并重启

【2】 中间件封装

独立出一套系统来,实现读写操作分离和数据库服务连接的管理,中间件对业务服务器提供SQL兼容的协议。业务服务器访问中间件与数据库无差异,如图

zmeUfyQ.jpg!web

能够支持多种语言,且能够探测服务器主从状态,比如向某个测试表里写个数据,成功就是主机,失败就是从机。但是对应的实现极其复杂,容易出BUG,所有数据库请求都走中间件的话性能是个大问题。一般使用成熟的中间件方案,如MySQL Proxy,Atlas

SQL - 分库分表分区

为什么需要分这么多东西,主要是因为如果在单库或者单表上:

  • 数据量太大,即使加了索引,索引也非常大,读写性能太差
  • 数据量太大,备份和恢复耗时太长
  • 数据库文件太大,导致物理上受影响的可能更大(机房火灾烧了一块,恰好是这个数据库)

分库

为了解决数据量太大,且读写操作太多的问题,分库是按不同的业务模块将数据分散到不同的数据库服务器,比如:

aUvYbyV.jpg!web

但会存在以下问题:

  1. 事务问题。在执行分库之后,由于数据存储到了不同的库上,数据库事务管理出现了困难。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担
  2. 跨库跨表的join问题。在执行了分库分表之后,难以避免会将原本逻辑关联性很强的数据划分到不同的表、不同的库上,我们无法join位于不同分库的表,也无法join分表粒度不同的表,结果原本一次查询能够完成的业务,可能需要多次查询才能完成
  3. 额外的数据管理负担和数据运算压力。额外的数据管理负担,最显而易见的就是数据的定位问题和数据的增删改查的重复执行问题,这些都可以通过应用程序解决,但必然引起额外的逻辑运算
  4. 成本问题。本来是1台服务器,现在是3台,所以一开始是不需要考虑分库的,除非一开始就是业务特别多,用户量特别大,否则一定要避免初期在开发上的无端开销,以后业务起来之后再分库也不迟

分表

【1】 垂直分表

为了解决表的宽度问题,同时还能分别优化每张单表的处理能力。所以将表结构根据数据的活跃度拆分成多个表,把不常用的字段单独放到一个表、把大字段单独放到一个表、把经常使用的字段放到一个表

复杂度的增加在于操作表的数量会增加,比如以前一条SQL需要查一个表,现在需要查两次,分别查两个表

【2】 水平分表

为了解决单表数据量过大(数据量达到千万级别)问题。所以需要将数据按一定的规则划分到不同表中,需要在读写的时候通过规则路由到具体的数据上

从而引起的复杂度有:

  • 路由

范围路由:根据有序的数据列,如时间戳/ID作为路由条件,不同的分段分散到不同的表中。比如按ID分段

复杂度在于分段大小,太小则导致表太多影响以后维护难度,太大导致单表性能问题,一般在100万到2000万之间

范围路由的优点在于可以跟随数据的增长而平滑地去扩充新的表,而不影响原有表的数据,但缺点在于数据分布不均匀,可能0-100万的用户表已经填满了,但100-200万的用户还没几个,并且还需要维护表的增加逻辑

Hash路由:通过选取某个列的值进行Hash运算,然后根据Hash结果分散到不同的表中。比如ID字段用ID%n(n代表分表的数量)计算,从而路由到对应表中

复杂度一是在于表的数量选取,二是在于路由字段的选取,因为还需要考虑到日后的增删改查,如果数据分散到100张表里,那么不得不查100张表才能拿到所有数据,所以还需要在一定程度上将数据聚合,比如根据用户组的ID进行分表,那么就不会影响到用户组的查询效率,当然到底通过哪个字段分,是根据业务需要来取舍的

Hash路由的优点在于实现简单,不需要维护扩表逻辑,一般也不需要扩表,缺点在于一旦要扩展表的时候,所以数据都需要重新计算分配,导致大量数据的变动

配置路由:用一张独立的路由表来记录路由信息。比如以用户ID为路由字段,创建一张维护用户ID-表ID信息的表,做到根据用户ID,查到这个用户信息所在的表

配置路由的优点在于实现简单,扩充表的时候只需要指定迁移的数据,修改路由表就行了,缺点在于必须多查询一次路由表,而且路由表数据量太大的话又会成为一个瓶颈

  • 查询操作

在水平分表之后,一旦涉及到查询全部表数据的SQL(比如多表查询join/查询总数count/全数据order by),都需要进行多次查询然后将结果汇集到一起,无法避免地需要在原来的基础上重写大量业务读写逻辑的代码

分区

为了解决单表数据量过大的另外的一种方式,不是把一张表在逻辑上拆分,而是把一张表的数据在物理上分成N个区块,在逻辑上看最终只是一张表,但底层是由N个物理区块组成的,通过将不同数据按一定规则放到不同的区块中提升表的查询效率

比如在MySQL中,在创建表时可以指定分区表的类型以及按哪个字段进行分区,完成后对业务是透明的,原来的SQL也能基本照常使用(可能需要针对分区表做优化,条件中要带上分区条件的列,从而使查询定位到少量的分区上,否则就会扫描全部分区),在MySQL内部会负责路由到具体哪个分区

但分区的缺点在于:分区表中无法使用外键、分区键必须是主键或唯一索引、分区数量有限制

NOSQL

参考

号外号外

最近在总结一些针对 Java 面试相关的知识点,感兴趣的朋友可以一起维护~

地址: https://github.com/xbox1994/2018-Java-Interview


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK