在现代的软件系统中,几乎全部的系统都使用到了数据库,不管是关系型数据,例如MySql、SQLite、Oracle、SQLServer等,仍是非关系性数据,例如mongoDB、redis等。本文已web系统为例来阐述为何要下降数据库的压力,在提出具体方案以前先大体讲解一下如今web系统的架构,要了解web系统的架构和演变过程具体能够参考大型网站架构演变和知识体系这片文章。web
如今的大型web系统多采用分布式的架构,分布式系统面临的最大挑战就是如何在复杂的、并发的状况下保证数据的一致性问题。一般为了不因为保证数据一致性问题而带来的困难,一般状况下都是采用多个实例,单个数据源的架构模式,简化模式如图。
在这个架构中,经过不断的增长实例(webserver)能够下降应用服务器的压力,因此只要保证应用代码的质量、应用之间的低耦合性、可扩展性和可维护性等,应用服务器的压力就再也不会成为总体架构中性能瓶颈,可是随着业务量的不断增长增加,或者时间的积累,沉淀下来的数据变得愈来愈来多,随着而来的数据库的压力变得愈来愈大,慢慢的性能的瓶颈主要集中在数据库上。redis
表索引能够加快对表中数据的检索速度,可是会下降表中数据的更新速度,因此增长表的索引必定控制在合理范围内,过多的索引不但不会下降数据库的压力,反而可能增大数据库的压力,表索引的创建通常要从具体业务场景出发,对于读多写少的场景,能够经过适当的增长索引来提升效率,对表的那些列创建索引?创建单独索引仍是创建复合索引?要根据具体的业务场景来决定,创建索引以后能够针对索引对业务逻辑中使用的SQL进行优化,创建索引是最基础的手段,这里不错过多的介绍。sql
通常状况下,业务中所处理的数据的都具备必定的时间间隔,因此能够经过对业务进行梳理,将当前时间间隔以外的数据进行截转,截转到历史数据库中,经过对业务进行拆分,当须要历史数据时,能够转到历史数据库中进行查询,或者修改,经过减小当前数据库的数据量,来减轻当前业务数据的压力。数据截转通常状况下是按照时间来进行,因此在业务员数据库设计的时候就要考虑到时间这个因素。
数据截转能够进行间隔一段时间作一次手工的数据截转,也能够启动一个定时器,每一个一段时间进行一次数据截转,推荐的方式是准实时截转,及天天在业务量较小的时间,启动任务实时截转。
数据截转须要注意的几个问题:(1)外键关联关系(特别是有主键ID的关联的)注意在截转的历史数据库中的关联关系是否正确。(2)保证生产库和历史库的业务关联关系,从而避免历史库的数据须要关联生产库中的数据。数据库
缓存是下降数据压力一个强有力的手段,基本是全部系统大型web系统中都会使用到,因此现代的大型web系统的架构通常如图。
请求1到达webserver以后,首先执行2访问缓存,若是hit则返回,miss则执行3访问数据库,在执行4同步到缓存中,再返回。可是不是缓存并非万能的,缓存也有其使用的业务场景,通常在读多写少,数据重复查询比较集中的场景下,缓存能够大大提升性能,缓存操做顺序很是重要,不合理的操做顺序,在并发场景下经常会致使数据的不一致,缓存的具体操做能够参考缓存架构设计细节二三事这边文章。缓存
有些业务例如报表、数据汇总等须要数据量较多,此时可能须要进行多表联合查询,联合查询操做很是消耗数据库的性能,因此在这种业务场景下为了不过大的性能消耗,每每须要将查询时的多个表按照关联条件进行关联,生成一张含有冗余信息的包含全部表的多个字段的大宽表,这样在进行查询时,只在一张表中进行查询,性能明显获得提高。大宽表的生成是在业务流程中生成仍是经过异步化任务来生成,根据具体的业务逻辑来定。服务器
非关系型数据库,也就是咱们一般说的NoSQL数据,最多见就是key/value类型的数据库,这类数据库不强调表的关系,可是查询速度很是快,所在某些具体场景下,咱们应该优先选择NoSQL数据库,例如字典信息表的查询。架构
若是采用单点数据数据库,就算对数据进行上述的相关优化,可是因为其自己的单点性,因此随着流量的激增,数据库仍然会成为系统的瓶颈,如何对数据进行拆分来解决这个问题了,读写分离就是最经常使用的方法,读写分离的原理以下图。
读写分离技术如今已经应用的很成熟,经过将数据拆分为两个实例,读写分离操做改善了数据单点的瓶颈,分摊了数据库压力,并且当主数据库宕机以后能够迅速的切换到从库,而不会致使业务不可用,同时也起到数据备份的做用,因为存在两个数据实例,因此数据怎么由主库同步到从库、主从之间延迟引起的数据不一致问题,以及怎么来分离业务中读和写操做成为要解决的问题成为要解决的问题。主从同步能够参考Mysql主从架构的复制原理及配置这篇文章,从主数据一致能够参考DB主从一致性的几种解决方法这篇文章。并发
采用读写分离以后,数据库已经变为两份实例,数据库的压力已经获得分摊,若是数据库的压力仍是过大时,这是就要从业务方面着手,将具体业务细分,将业务对应的表分拆到不一样的数据库当中,以下图。
业务变更较大,同时要对系统内部之间的相互调用提供接口,调用方式能够选用RPC、Restful、JMQ消息等方式。通常状况下,数据库垂直拆分作的足够细分的话,加上读写分离技术,加上适当的数据截转就能够知足通常的大型业务系统对性能的需求。异步
数据库能够进行垂直拆分,固然也能够对数据库中的表进行垂直拆分,对表进行拆分就是对数据拆分的再拆分,如图。种解决方法只适用于一些特定的场景,例如对表进行垂直拆分,经过异步化调用将全部任务异步化,前提是总的任务能够进行分布的异步化操做,在实际应用比较少,由于设计的表只要复合三范式的要求,通常是很难在进行拆分的,应用较可能是对表进行水平拆分。 数据库设计
若是已经作了数据库拆分,而且进行了读写分离,数据压力仍是过大,主要缘由就是数据库表中的记录太多,或者对数据进行了截转,可是对历史数据的操做仍是比较频繁的,且随着截转的历史数据愈来愈多,历史数据库的压力也边的也变的愈来愈大,这时有两种解决方案:第一种方案就是对数据库中的表进行垂直拆分,从而不用在截转数据,经过不断对表进行水平拆分,保证数据数据库中单表的记录数保持在一个高性能合理的范围之类,经过扩容将不一样分配到不一样的数据中(分库分表)来保证数据库的压力,应用在访问时,经过分库分表的条件进行路由,就能够取到数据。第二种就是仍旧对数据进行截转,当历史数据信息过多从而致使数据库压力过大时,采用搜索引擎的方式来解决。相比于第一种操做第二种方案适用于读操做上,对与写操做,具备必定的局限性,第一种方案具备必定的通用性。对表进行水平拆分的过程如图所示。
在进行进行具体水平拆分以前,咱们须要考虑这样几个问题