1.1 问题分析
大型应用系统,影响数据筛选查询效率的关键因素之一,就是数据库保存的原始数据和用户期望的展示数据之间存在较大差异,后台需要执行复杂SQL语句,才能从原始数据中筛选出用户期望的数据;而通常情况下,随着SQL语句复杂度提高,其语句执行时间开销会成几何倍数递增;
1.2 解决方案
为了解决上述问题,提高数据查询时效,增强用户体验,可从以下几项作为切入点按需选择进行改进实施,研发人员可根据产品实际情况,选择其中的一种或几种技术来不断提高产品响应速度,进一步提升产品用户体验。
1、原始数据预处理(物化视图);
2、命令查询职责分离(数据库读写分离);
3、分布式数据库应用(分库/分表)等技术;
1.2.1 支持数据预处理
通常情况下,数据库保存的原始数据和用户期望的展示数据之间存在较大差异,后台需要执行复杂SQL语句,比如级联多张数据表,才能从原始数据中筛选出用户期望的数据。为了避免因拼写复杂SQL语句导致数据查询效率较低的问题,需要后台服务器提供数据二次预处理机制,即后台服务器可采用实时或定时方式,预先从原始数据中筛选出用户期望的数据,然后将其存储在特定数据库表(这里统称为报表)中。当用户通过系统客户端设定筛选条件查询业务数据时,后台服务器不需要执行复杂查询语句,而是直接从数据库报表中,将用户期望数据返回即可,从而有效提升数据查询时效。
1.2.1.1 数据预处理技术实现
接下来以关系型数据库MySQL为例,介绍下通过物化视图技术,实现数据预处理的实现方案,供研发人员参考实施。
1.2.1.1.1 物化视图介绍
物化视图是一种特殊的物理表,“物化”(Materialized)视图是相对普通视图而言的。普通视图是虚拟表,应用的局限性大,任何对视图的查询,数据库都实际上转换为视图SQL语句的查询。这样对整体查询性能的提高,并没有实质上的好处,只是方便了业务处理逻辑实现。而物化视图是查询结果的预运算,物化视图实际存储着用户数据,而这些用户数据通常是表连接或聚集等耗时较多的操作的结果,这样查询数据时,就不需要多表关联。物化视图主要用于需要对查询立即做出响应,而又不需要耗费长时间获得结果。物化视图的更新频率决定了内容的及时性,可根据项目实际需要采取实时更新和定时更新两种方式实现。
1.2.1.1.1.1 物化视图设计
物化视图表结构设计和用户期望展示的数据密切相关。物化视图表结构设计是否合理,直接影响到最终数据查询时效。理论上物化视图仅存储用户期望得到的数据,这样用户在查询数据时,就不需要多个原始表关联查询,而直接从物化视图中查询,从而大幅提升数据查询时效。
1.2.1.1.1.2 物化视图更新
物化视图的更新频率决定了内容的及时性,可根据项目实际需要采取实时更新和定时更新两种方式实现。
1.视图实时更新(ON COMMIT):实时更新是指当原始数据库表有数据修改时,需要同步更新物化视图中的内容,确保物化视图数据强一致性。通常可以借助数据库触发器来实现该功能,比如在原始表中按需创建触发器,当原始表中有数据修改时,会自动调用触发器来实现物化视图更新。
2.视图定时更新(ON DEMOND):应用程序或数据库本身创建定时任务,来周期性定时更新物化视图中的内容,确保物化视图数据最终一致性。比如以1小时为粒度进行物化视图自动更新。
1.2.1.1.2 物化视图实现
MySQL不支持物化视图技术,但可以通过触发器、存储过程或定时任务等多种技术,非常容易实现物化视图技术。具体实现细节可参考以下网站内容,供研发人员参考实施。http://www.fromdual.com/mysql-materialized-views
1.2.2 命令查询职责分离
在常规应用架构中,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的数据库实体。在一些业务逻辑简单的系统中可能没有什么问题,但是随着系统逻辑变得复杂,用户、数据量增多时,这种设计就会出现数据库读写性能问题;另外数据库系统中的读写频率比,是偏向读,还是偏向写,就如同一般的数据结构在查找和修改上时间复杂度不一样,在设计数据库系统结构时也需要考虑这样的问题。解决方法就是使用命令查询职责分离(CQRS)技术。
CQRS使用分离的接口将数据查询操作(Queries)和数据修改操作(Commands)分离开来,这也意味着在查询和更新过程中使用的数据模型是不一样的。这样读和写逻辑就隔离开来了。使用CQRS分离了读写职责之后,可以对数据进行读写分离操作来改进性能,同时提高可扩展性和安全。命令查询职责分离(CQRS)大致逻辑如下图所示:
主数据库处理CUD,从库处理R,从库的的结构可以和主库的结构完全一样,也可以不一样,从库主要用来进行只读的查询操作。在数量上从库的个数也可以根据查询的规模进行扩展,在业务逻辑上,也可以根据专题从主库中划分出不同的从库。从库也可以实现成ReportingDatabase(物化视图专属数据库),根据查询业务需求,从主库中抽取一些必要的数据生成一系列查询报表(物化视图表)来存储。
使用ReportingDatabase的一些优点通常可以使得查询变得更加简单高效:
1.ReportingDatabase的结构和数据表会针对常用的查询请求进行设计;
2.ReportingDatabase数据库通常会去正规化,存储一些冗余而减少必要的Join等联合查询操作,使得查询简化和高效,一些在主数据库中用不到的数据信息,在ReportingDatabase可以不用存储;
3.可以对ReportingDatabase单独重构优化,而不用去改变操作数据库;
4.对ReportingDatabase数据库的查询不会给操作数据库带来任何压力;
5.可以针对不同的查询请求建立不同的ReportingDatabase库。
1.2.3 分布式数据库应用
在常规应用架构中,通常都是通过数据访问层来修改或者查询数据,一般修改和查询使用的是相同的数据库实体。在一些业务逻辑简单的系统中可能没有什么问题,但是随着系统逻辑变得复杂,用户、数据量增多时,这种设计就会出现数据库读写性能问题,此时除了可以借助命令查询职责分离(CQRS)技术来解决性能问题外,还可以使用分布式数据库技术来解决性能问题。分布式数据库通常涉及到分库分表等特性,通过分库分表技术,将数据库读写压力从单节点分散到多节点,从而有效降低单节点数据库读写压力和避免长时间持锁操作等待问题。
1.2.3.1 分库
分库可以有效降低单节点数据读写压力,可以按照功能进行分库设计。目前MySQL仅支持主从数据库部署。该场景下,通常主库用来写操作,从库用来读操作。如果需要MySQL支持多节点分布式部署,则需要借助第三方组件来实现。我们可以使用Cobar、MyCat、Vitess等第三方开源组件,来实现MySQL分库、分表等特性,同时这些组件不同程度上有效屏蔽了分库、分表差异,从而简化了多节点分布式数据库场景下的应用业务逻辑实现。
1.2.3.2 分表
分表通常有两种方式,分别为水平分表和垂直分表。
1.水平分表:将数据库表中的数据分散到多表中存储,主要解决表行数过大的问题。比如一个表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询速度。其缺点是在查询时需要多个表名,查询所有数据需要union操作。
2.垂直分表:将一组逻辑相关的列分散到多表中存储,主要解决列过长的问题。比如一个表中某些列常用,而另外一些列不常用,则可以采用垂直分表。垂直分表可以使得数据行变小,一个数据页就能存放更多的数据,在查询时就会减少I/O次数。其缺点是查询所有数据时需要join操作。
分表会增加业务实现复杂度,但可以借助第三方组件来屏蔽分表差异,即通过第三方组件,使得底层数据库分表对上层应用不可见,上层应用仍可以像操作单表一样来操作多表。我们可以使用Cobar、MyCat、Vitess等第三方开源组件,来实现MySQL分库、分表等特性,同时这些组件不同程度上有效屏蔽了分库、分表差异,从而简化了多节点分布式数据库场景下的应用业务逻辑实现。