数据库schema设计与优化

 

原文地址前端

 

一、 前言

对于数据库而言,在平常开发中咱们主要的关注点有两块,一个是schema的结构设计,另外一个就是索引的优化,这两块是影响咱们最终系统结构和性能的关键部分,天然也是咱们花费精力最多的部分;mysql

本文主要介绍数据库设计中的通常原则和优化手段,包括数据库的一半范式、反范式设计、数据切分、数据路由与合并等等算法

 

二、 Schema设计的通常性原则

2.1 概述

范式理论是关系型数据库设计的黄金法则,它提供了数据结构化的理论基础,有效地保证了数据的一致性,应该说,关系型数据库就是在范式的基础上才成长起来的。sql

数据库的范式有不少种,可是咱们通常经常使用的只有第1、2、三范式和BC范式,这些范式直接在咱们的数据库schema设计中获得体现,虽然有时咱们根本就没有意识到。数据库

 

2.2 第一范式和第二范式

在关系型数据结构的实体-关系模型中,是容许实体集和关系集的属性具备某种程度的子结构的,好比多值属性和组合属性;而第一范式则限制了这种存在,他要求全部的字段都是不可再分的、原子的,不然就违反了第一范式;第一范式主要是为了不数据表结构过于复杂多样,使得上层操做的可抽象性和数据一致性遭到破坏。编程

第二范式简单的说,则是要求数据库中的每条记录都要有其对应的主键id存在,这样要求的主要目的是为了可以知足上层业务要求惟一标识每条记录的需求;其实数据库管理系统自己也有这种需求,部分数据库的索引结构就是基于此的,只不过这不是数据范式(data normal form)应该关心的东西;安全

 

2.3 第三范式

第三范式要求在在一个实体集中,不能存在一个非主属性能够做为该实体集中某个子集的候选主键,还能够表述为,不一样的关系集中不能存在除了主键字段外的其余相同字段;这二者是等价的。服务器

简单的说,第三范式主要是要求将实体集尽可能拆分,将不一样的业务单元属性字段拆分到不一样的表里,而后经过关系表进行关联。网络

第三范式必定程度上减小了数据的冗余,下降了数据不一致的风险;一般状况下,大部分的schema都应达到第三范式的要求。数据结构

 

2.4 BC范式

BC范式是在第三范式的一个子集,它在第三范式之上作了更强的约束,即实体集中的任何子集都只能依赖于主键(注意,不是主属性,这一点是BC范式和第三方范式的差异所在),不能存在一个非主属性或非主属性集能够做为某个子集的主键。

BC范式在定义上和第三方是差很少,他最大程度的减小了数据冗余,不过在实际应用中,两者基本是同样的,只有在表的主键包含多个字段时,才会产生差别。

 

三、 反范式化设计

3.1 数据冗余

这里介绍一个很经典的例子。咱们所常见的大部分网站都会有会员系统,用户须要注册会员帐号,而后登陆才能够享受进一步的功能或服务。对于这张会员表,咱们不妨命名为user_info表,通常会包括会员id,会员昵称,真实姓名,登陆密码,性别,教育经历、工做经历、电话、电子邮箱、我的简介、兴趣爱好等信息,其余可能还包括跟该网站会员业务相关的一些字段(好比积分、经验值、充值帐户余额等),通常的schema设计是这样的:

表名

user_info

数据量

100w

功能描述

主要存储用户的基本信息,如id、姓名、年龄、联系方式等;

字段名(新增)

数据类型(精度范围)

空/非空

缺省值

字段含义

id

BIGINT(20)

N

 

用户id

name

VARCHAR(32)

N

 

姓名

gender

TINYINT(4)

N

 

性别

age

INT(8)

N

 

年龄

tel

VARCHAR(16)

Y

 

联系电话

email

VARCHAR(64)

Y

 

电子邮箱

school

VARCHAR(32)

Y

 

就读学校

company

VARCHAR(32)

Y

 

供职机构

interest

VARCHAR(512)

Y

 

兴趣爱好

gmt_create

DATETIME

N

 

记录建立时间

gmt_modified

DATETIME

N

 

记录修改时间

这张表结构看起来是没什么问题的,并且在初期业务简单,用户数量少、访问量低的状况下也确实都ok的;

可是随着网站访问量不断增大,天天登陆的用户愈来愈多,user_info表的访问量愈来愈大,瓶颈慢慢出现;

咱们通过进一步分析发现,在处理用户登陆的过程当中,咱们须要的操做很简单,就是根据用户输入的会员昵称查询相应记录,进而判断密码是否正确;而会员登陆后,也仅会显示用户的昵称或真实姓名。

整个过程先后所涉及到的字段,只有会员id、昵称、密码和真实姓名等三、4个,而user_info表中其余绝大部分字段,诸如教育经历、兴趣爱好等,在这个过程当中是根本不须要的,但他们的体积却比较大,每次用户登陆都要读取,白白浪费时间和带宽。

那么咱们为何不把这部分字段单独拿出来放到一张新的表里呢?咱们不妨建一个login表,里边只有会员id、会员昵称、登陆密码以及会员真实姓名,每次用户登陆,都只读取这张login表,这样不但数据库读取记录的时间会缩短,也能够将应用和数据库之间网络传输量降到最低,彻底符合咱们前一篇文章里所提到的将结果集缩减到最小的原则。

表名

login

数据量

100w

功能描述

主要存储用户的登陆信息;

字段名(新增)

数据类型(精度范围)

空/非空

缺省值

字段含义

id

BIGINT(20)

N

 

用户id

name

VARCHAR(32)

N

 

姓名

password

VARCHAR(128)

N

 

登陆密码的md5值

可是有人不由要问,这样不是产生数据冗余了吗?是的,同一份数据在user_info表和login表中各存了一份,确实有可能存在不一致的状况,应用程序中每次新增或修改记录,均可能须要访问两张表,这无形中增长写操做的成本;因此这种优化方式也不是绝对的,要根据具体的场景区别对待,好比在上述这种读写比例很是高的场景中,咱们这样处理就是合适的;而在一个一样读写比例比较高,可是对数据一致性要求也很是高的系统中,对数据进行冗余存储就须要花费额外的精力去处理可能存在的数据不一致状况。

 

3.2 去关联化

Join是咱们在数据库操做时常常会使用的一个关键字,其做用就是将两张表拼接起来,而后过滤出符合条件的记录;可是在拼接的过程中,是采用的是笛卡尔积的方式,其原理图以下:

 

在MySQL中,Join的实现方式为Nested Loop Join ,主要以驱动表结果集做为基础数据进行循环,有点相似编程语言中的双层for循环嵌套;这种方式实现最最简单,性能也基本能够接受;其余数据库还提供Hash Join、Sort Merge Join等Join方式,都是针对不一样场景有性能提高,很难简单的说谁好谁坏。

从笛卡尔积的基本原理上来看,无论采用何种实现算法,表间join都是很是耗时耗力的操做,尤为是当其中一张表或两张表的数据量都很是大时更是如此!

因此咱们在作schema设计的时候,应该尽可能避免join的出现,经过必定的字段合并和数据冗余将这种需求降到最低。

 

3.3 去一致性约束

在传统应用中,数据一致性是很重要的一点,可是在不少互联网应用对数据的一致性要求并非很高;好比说数据库的外键约束、惟一性约束等等,当记录增删时,数据库都会去检查相关的条件是否知足,这些都是须要额外开销的,有时候其开销甚至会超过当前操做自己。

在数据库层去掉这些约束并不意味着系统中就不须要,咱们其实能够将这部分约束校验提早到应用程序中;前面咱们提到过,应用程序的扩展相对来讲是比较容易的,因此咱们未尝不充分利用一下来为数据库减负呢?

 

3.4 去SQL

3.4.1 数据库底层数据存储

咱们使用的关系型数据库虽然提供了表、字段、索引、sql等种种特性,可是归根到底,其最底层的数据存储仍然是<k,v>格式,好比MySQL,其数据的存储都是有由下层存储引擎来负责的,虽然在逻辑结构上咱们看到的是一张张表和其中一个个分散的字段,可是实际上每条记录在物理存储上都是一个总体;所谓的表结构、字段这些只是上层来维护的元数据,对底层来讲,其实没有字段的概念,就是一条条的物理记录;

好比说咱们前边的user_info表,咱们若是执行相似下边的一条sql:

select user_id,user_name from user_info where age =8;

虽然我只想返回user_id和user_name两个字段,可是存储引擎仍是要将全部知足的age=8的记录取出来,而后在分别扫描每条记录,取出咱们须要的两个字段数据返回;

因此咱们能够看到,所谓的关系数据库只不过是在<k,v>系统的上层作了进一步的封装,好比说权限验证、sql解析、二级索引等等;

 

3.4.2 MySQL数据库的层次结构

MySQL虽然是个开源数据库,体积也远小于DB二、Oracle这些商业数据库,可是一个RDBMS必备的结构MySQL却同样也很多,正所谓“麻雀虽小,五脏俱全”。

下边咱们就来看下MySQL到底有怎样的层次结构;

 

MySQL从上到下,能够分为三层,第一部分为客户端,中间部分为数据库管理系统(DBMS),最底层为存储引擎层;MySQL使用独立的存储引擎,能够方便切换,这一点和其余数据库有所不一样;

 

3.4.3 哪些是能够不要的

前面给出了MySQL数据库的层次结构,其余数据库结构也是相似的;那么不妨从上到下好好想想,整个体系中哪些东西其实不是咱们必需的?

首先,Client端确定是须要的,咱们总要链接数据库的,那么来看第二层DBMS层,其中的链接管理是须要的,无论什么样的数据库总要处理客户端链接;若是一个数据库在安全网络环境中,而且只有咱们本身在用的话,那么就不须要用户权限验证了,这一层能够去掉;为了减轻数据库负担,咱们也不使用sql,须要的逻辑能够直接使用API在应用层实现,那么这一层也能够省掉;而对于访问控制模块,由于数据库为咱们本身所独享,或者都是可信任的使用者,因此这一层也不那么重要了,或者说是能够进一步简化的;

咱们再来往下看存储引擎层。索引的话能够很好的提升数据的查询效率,这一点无论在什么类型的存储系统中都适用,那么这个功能咱们须要保留,可是若是咱们只想要个<k,v>存储的话,那就能够只保留主键索引好了;事务管理呢,最多咱们不用好了,不麻烦数据库了;锁的话即便咱们本身使用也会有不少的并发访问控制,那么须要保留;

通过这样的精简以后,咱们的数据库还剩如下这些东西;

 

3.4.4 NoSQL存储系统

前面咱们提到了去关联化、去一致性约束以及数据冗余等等;3.1中咱们讨论了关系型数据库的本质,其实就是在<k,v>存储之上又封装和增添了诸多的功能,而3.3中咱们又对关系型数据库进行了裁剪,去掉了一些咱们没必要需的;通过全部的这么步骤,剩下的部分(图1-1所示),基本上就是如今NoSQL存储了;

因此,NoSQL不是什么高级的东东,而是关系型数据库作了退化,回归到了其基础本源;而分布式的特性,也并非NoSQL独有的,关系型数据之因此难有分布式的架构,本质就是由于其选择了“向上生长”,上层的复杂特性制约了其“横向”生长的能力;而NoSQL只不过是在<k,v>之上,选择了另一个生长方向而已。

 

因为去掉了上层的“高级”特性,NoSQL系统的性能有了比较大的提高,同时因为横向生长,其存储能力也有了很大的加强;

因此当咱们的系统受制于关系型数据库的性能时,不妨放弃schema模式,来尝试一下“自由”的NoSQL数据库。

 

 

四、 数据扩展

4.1 Scale Up和Scale Out

Scale up主要是指加强数据库的单机处理能力,好比说提升CPU、内存、硬盘、网卡等硬件配置;其实scaleup这个概念包括咱们后便将要提到的scale out,不光对于数据库,对于大部分的软件系统都适用的,只不过具体的实施方案会有所差别;

由于scale up主要是增长机器硬件配置,相对来讲比较简单,也不须要迁移数据,对于技术人员来讲没有新的东西,因此对于中小型系统来讲很是合适;

scale out是指横向的扩展,就是增长数据库实例或节点来增长总体的处理能力,这里边还包括两种方式,一种常见的数据复制,好比MySQL的Replication,Oracle rac等;另外一种横向扩展的方式是进行数据切分,也就是说把本应该放在一台机器上的数据切成几部分,分别放在不一样的节点上(并非相同数据的备份),这样访问的压力就会分散到多个节点。

scale out的优势是成本低,若是整个系统都使用PC Server的话,能够用很低的成原本支撑海量的数据和高并发;并且通常来讲,这种扩展是线性的,即有多少机器,就能支撑多少的数据和多大的访问量,但一般这须要有个比较好的数据系统架构或中间件系统来支持;

以淘宝的交易库来讲,原来使用的都是IOE(I指的是IBM的小型机,O指Oracle数据库,E即EMC的高端存储),采用主备双机方式,用Orcle和高端存储来支撑天天的巨大访问量,可是整个系统的成本也很是高(听说一套下来要2000多万),而通过去IOE之后,经过使用MySQL和廉价的PC Server(线上16主16备,双11的时候扩展为32主32备),经过数据切分和Replication机制,不只整个数据库的在线处理能力提升了4倍,成本也降为原来的1 / 8不到,同时数据的安全性和容灾能力也获得了保证;

可是数据库技术不是本文关注的重点,下边将主要介绍和咱们平常开发联系更为紧密的数据切分知识。

 

4.2 数据切分

4.2.1 为何要切分数据

对于这个问题,可能有人会以为的是废话:你前边不是已经说过了吗,干吗还问?没错,扩展系统,支持更大访问和并发和替换商业数据库,下降成本这两个确实咱们进行数据切分的主要缘由;但这样来讲太笼统了

对于数据库来说,无论是商业的仍是开源的,其单库和单表的承载能力都是有限的;在一般的业务场景下(写操做和读操做比较均衡),普通的pc server上,MySQL数据库单表数据记录的承载能力在千万级(数据量在TB级别)左右,TPS大概在千这个级别(具体测试环境和数据可参考另外一篇文档);固然,咱们在这里没有必要苛求具体的数据,由于这和具体业务场景、实际读写比、服务器硬件配置、具体的数据引擎、MySQL的配置参数等相关,好比说,若是只是将MySQL做为日志数据库(基本只有写操做,不须要建索引),单表的支撑能力可达到上亿甚至是十亿的级别;但这毕竟只是种极限场景,不能拿来用做通常场景的参考。

另外须要说明的是,前面说的单库单表的承载能力有限,并非说当数据量超过这个上限时,数据库就会立刻崩溃或者拒绝服务,而是在这种状况下,数据库的总体的读写性能就会急剧降低,甚至于一条很简单的sql查询也可能会超时,若是原本就是负载很重的一张表,那与崩溃无异了!

 

4.2.2 数据切分基本原则

数据切分所要遵循的原则主要有两点:

第一就是将数据均衡分散在多个处理节点上,其实这里主要强调的是均衡,但这个均衡并不可简单的当作是每一个节点上库(表)数量相等或是记录条数同样;而更可能是要从数据访问和处理能力的均衡上去考虑,主要原则有如下几条。

a、 不一样节点业务关联度要低

b、 同一节点业务类型尽可能一致

c、 数据(访问量)要均衡

d、 数据的一致性和安全性

 

4.2.3 垂直切分

垂直切分主要是根据表中数据的业务类型,将不一样业务的数据放在不一样的表或者数据库中;

在系统结构设计中咱们会常常提到一个原则,叫作高内聚、低耦合,其实这个原则在数据库的垂直切分中一样适用;因此在作水平切分的时候要尽可能作到不一样功能的表关联尽量少,这不但能够减小SQL语句中的join出现的概率,同时后续表结构变动时也更容易;

对于中小型系统来说,不少人喜欢各类复杂功能一个sql搞定,甚至有些还使用存储过程,这样对前端程序来说确实方便了许多;可是这种方便也每每会给咱们带来伤害,尤为是当数据量和访问量都增加比较快的状况下,你会发现几条慢查询可能让你的整个系统直接失去响应!

 

4.2.4 水平切分

4.2.4.1 什么是水平切分

垂直切分主要是按照系统功能来切分的,因此一样是有瓶颈存在的,好比说,某一项功能比其余的要复杂许多,或者数据量要大不少,很难再进一步拆分,这样就不适合垂直切分了;这在实际的业务场景中是存在的;

好比说淘宝的订单系统,虽然功能看起来简单,可是因为交易类型复杂,中间状态繁多,耦合性很是强,加之订单数量巨大,其系统是很难再进行功能上的剥离的;可是这个系统属于底层基础系统,为了支撑巨大的访问量,只能采起水平数据切分方式来解决高并发问题。

水平切分就是咱们一般所说的分库分表,主要是将一张表中的数据按照某个字段(好比说用户id、商品id、订单id等)分散存储在多张结构相同的表中,这样访问的压力就会分散到多张表上;

在平时的开发中,数据的水平切分比垂直切分应用的更多,由于通常来说,须要进行垂直数据切分时,一般系统的规模和负载都已经很大了(尤为是使用oracle的时候),这时候咱们最早实施的,每每是经过RPC或者服务化将应用分红多个系统,底层数据表之间的依赖,很天然的会转化成系统间的接口依赖,因此这个时候,数据库固然也会跟着分开了,不须要太刻意其考虑垂直切分这个概念了。

 

4.2.4.2 水平切分优势

成本固定;只要在系统设计之初就指定好分表数量和分表字段,无论是要分红8个库1024张表,仍是16个库4096张表,其成本都是同样的;

解决了单表瓶颈问题;水平切分方式很好的解决了垂直切分时可能存在的单表瓶颈问题,只要在开始时作好容量预估,设定适当的切分数量,基本能够知足业务很长一段时期内的存储需求;

对事务透明;分表对于数据库来讲是透明的,因此原来的事务该怎么作还怎么作。

 

4.2.4.3 水平切分缺点

水平切分的虽然颇有效,可是其缺点也很多,主要以下:

sql路由变得复杂;每次在作了切分的库或表上执行sql时,都必需要明确指出目标分库或分表;这无形中增长业务方的成本。

分表字段单一;水平切分只能使用单一的分表字段;若是业务中有需求按照非分表字段进行查询,则会变得很困难,只能扫描全部的表;一个解决方案就是,按照不一样查询字段作多份分表;可是又要花费精力去解决数据冗余问题。

join操做变得困难;显而易见,之前单表间的join放到多表上是没法执行的,这时候咱们最好仍是选择放弃;

二次扩展比较麻烦;若是分表以后,咱们数据增加太快,又达到存储瓶颈了,就面临着二次拆分的命运;但由于路由规则发生了改变,迁移数据的麻烦是避免不了的;因此要有必要的手段去保证迁移数据时系统依然可以对外提供服务。

 

4.2.4.4 水平切分注意事项

在作水平切分后,咱们的部分业务实现方式或是开发方式可能须要随着改变;如下是咱们再作水平切分时须要注意的点,主要是针对水平切分的弱点而言的:

根据业务场景肯定切分字段;业务中根据什么字段去查询,就用什么字段去分表;

避免热点数据问题;一般切分时采用的hash算法理论上能够保证数据的分散性,但在实际应用中,仍可能遇到数据热点问题;理论是理论,实际归实际,没有绝对的,不要觉得分了表就万事大吉了。

分表宜多不宜少;这样作主要是为了尽可能避免后期可能遇到的二次拆分,由于前面咱们说过,拆成1024张表和拆成4096张表的操做成本是同样的。

避免分表上的join操做;在分表的缺点中咱们就提到过,join在水平切分场景下会很困难,因此在业务实现中,对这种状况能避免就避免,哪怕牺牲一些简洁性,多绕几步。

避免非分表字段查询;道理也是同样的,切分后只能按照切分字段进行查询;若是非要按其余字段查询,那就冗余数据吧。

 

4.2.5 其余切分方式

上边咱们提到的是咱们最多见的切分方式,其余还有一些切分方式不太“规矩”,它们具备部分水平切分或垂直切分的特色,但又很难直接纳入到某一分类中;正如那句老话所言:山无定势,水无定形。

 

4.2.5.1 逻辑切分

逻辑切分相似于垂直切分,也是将数据按照不一样业务逻辑拆分到不一样的表中;但这种方式追求的不是单纯的负载均衡,而是不一样业务的数据隔离;好比说某部分数据读多些少,某部分数据则可能读少写多;这样进行隔离后,能够充分的利用不一样的业务特色进行优化,好比说建立不一样的索引结构,使用不一样的查询方案等等;前面咱们提到的数据冗余其实就属于这类切分方式。

另外一个典型的案例就是数据倾斜;好比说对于淘宝上的卖家来讲,有些卖家比较小,可能他的店铺中的宝贝只有几十数百个,可是有些品牌卖家或热门店铺,他们的宝贝可能有上万甚至是数十万,再加上未上架的或是已下架的历史宝贝,数量会更多!这两类用户的处理,若是使用相同的逻辑,极可能会产生问题;可是若是咱们将这部分大卖家提出来放在另外的节点上来处理,效果可能会好不少。

 

4.2.5.2 时间切分

时间的切分主要是将记录按照建立时间的前后顺序,放在不一样的表中;mysql的分区表便提供了这样一种切分的机制;分区表对外逻辑上表现为一张表,可是实际物理存储上是多张表,不一样的表对应不一样的时间区段;用户能够设定建立新分区表的周期,好比说一天或者是一周,在某个时间点插入的记录便会写入对应的分区表;读取时,会从最近一个分区开始扫描,直到找到目标记录。

 

4.2.5.3 冷热切分

冷热切分主要是按照数据的访问频率对数据进行隔离,有点相似于前边咱们提到的逻辑切分。

淘宝的交易历史库就是典型的表明。淘宝的订单总量巨大,天天产生的订单数量也很是多;若是咱们为用户提供订单查询服务,那么巨大的订单量会使咱们的服务性能降低不少;可是实际中咱们会发现,用户不多会去查询本身三个月之前的订单,那咱们不妨将用户三个月之前的订单拿出来单独提供存储和查询服务,这样就可使用户访问频繁的订单表数据量变得很小,从而能够提供更高的处理性能。

 

4.2.5.1 体积切分

按体积切分主要是按照数据表的尺寸或是记录条数进行切分,这种切分通常适用于业务类型单一,对表的体积能够很好预估的状况,主要是为了不表的尺寸过大而是性能降低。

通常来讲,只有日志类型的表适用于这种切分方式,一般是以自增id做为判断标识,由于基本不存在删除和修改操做,因此能够很好的控制体积;不过这种状况下大部分系统更倾向于使用按时间切分的方式,因此按体积切分的实际应用不多。

 

4.3 数据路由与合并

当咱们进行了分库分表以后,一个咱们不得不面对的问题就是sql的路由。当咱们将数据分散存储在诸如名为test_0000、test_0001的分表中时,咱们会发现,必需要对原来的程序代码或数据库进行相应的改造,不然程序将找不到正确的库或表;这种状况下,一般的解决方案有三种:

 

4.3.1 修改程序

这种方案,只需将程序里涉及到数据库读写的代码按照分表逻辑进行改造便可,技术上比较简单,不须要额外的软件或者是技术的支持;缺点是对业务代码侵入性强,可能涉及到多个地方的修改,工做量较大,并且后期的修改和维护成本也比较高。

 

4.3.2 修改数据库

这种方法对程序透明,是的上层业务逻辑不须要考虑分库分表的读写规则,应用代码能够保持不变;

缺点是须要修改数据库系统,或者以模块、插件等形式对数据库进行加强,开发成本和后期维护成本都很高,部分商业数据库根本没法自行修改。

 

4.3.3 使用中间层代理

这个应该是目前采用最多的方式;其优势是对上层业务逻辑和底层数据库都透明,只须要对应用的访问层进行简单改造,便可快速切换到拆分以后的数据库,无论是开发人员仍是数据库管理人员都不须要增长多少工做成本;

其缺点是技术门槛较高,须要专门的人员来开发和维护;功能受限,部分在单库单表下的常见操做在这种中间层代理的方式下会变得麻烦,好比说跨库、跨表join,全局数据分组与排序等。

对于以上几种数据路由方案,不一样的场景可能会有不一样的选择,具体要看自身的业务需求,技术储备以及实施成本等。

 

4.4 Scale up之闪存存储

以上通篇几乎都在介绍如何对数据库进行水平扩展和数据切分,其实有时候咱们没必要搞得如此复杂,若是仅经过简单的硬件升级就能知足业务增加对数据库的要求,咱们何乐而不为呢?

咱们都知道,普通的服务器,甚至是不少高端存储,都是基于机械硬盘存放数据的;而机械硬盘最大的缺点就是速度上存在物理上限;

通常的机械硬盘读写数据都要通过寻道操做,主要由两个步骤组成:一是移动磁头,二是转动盘片;这二者都属于机械操做,前者通常在2ms~4ms,然后者,对于如今经常使用的SATA盘或SAS盘来讲,通常会在1ms之内,因此,一次寻道操做要耗费大约5ms左右的时间,而对于一些老式硬盘来讲,这个时间可能要达到10ms甚至更多!也就是说,单块磁盘随机读写的iops只有100左右,这简直低的不可忍受,再牛B的系统也被拖死了!

对于一个初级团队,再没有不少技术积累的状况下,盲目的进行水平扩容每每会给本身带来更高的维护成本和更差的系统稳定性,因此这个时候咱们不妨尝试一下数据库性能提高的又一利器——闪存存储;毕竟咱们不断对系统进行优化的目标就是为了得到更高的性能!

一、 前言

对于数据库而言,在平常开发中咱们主要的关注点有两块,一个是schema的结构设计,另外一个就是索引的优化,这两块是影响咱们最终系统结构和性能的关键部分,天然也是咱们花费精力最多的部分;

本文主要介绍数据库设计中的通常原则和优化手段,包括数据库的一半范式、反范式设计、数据切分、数据路由与合并等等

 

二、 Schema设计的通常性原则

2.1 概述

范式理论是关系型数据库设计的黄金法则,它提供了数据结构化的理论基础,有效地保证了数据的一致性,应该说,关系型数据库就是在范式的基础上才成长起来的。

数据库的范式有不少种,可是咱们通常经常使用的只有第1、2、三范式和BC范式,这些范式直接在咱们的数据库schema设计中获得体现,虽然有时咱们根本就没有意识到。

 

2.2 第一范式和第二范式

在关系型数据结构的实体-关系模型中,是容许实体集和关系集的属性具备某种程度的子结构的,好比多值属性和组合属性;而第一范式则限制了这种存在,他要求全部的字段都是不可再分的、原子的,不然就违反了第一范式;第一范式主要是为了不数据表结构过于复杂多样,使得上层操做的可抽象性和数据一致性遭到破坏。

第二范式简单的说,则是要求数据库中的每条记录都要有其对应的主键id存在,这样要求的主要目的是为了可以知足上层业务要求惟一标识每条记录的需求;其实数据库管理系统自己也有这种需求,部分数据库的索引结构就是基于此的,只不过这不是数据范式(data normal form)应该关心的东西;

 

2.3 第三范式

第三范式要求在在一个实体集中,不能存在一个非主属性能够做为该实体集中某个子集的候选主键,还能够表述为,不一样的关系集中不能存在除了主键字段外的其余相同字段;这二者是等价的。

简单的说,第三范式主要是要求将实体集尽可能拆分,将不一样的业务单元属性字段拆分到不一样的表里,而后经过关系表进行关联。

第三范式必定程度上减小了数据的冗余,下降了数据不一致的风险;一般状况下,大部分的schema都应达到第三范式的要求。

 

2.4 BC范式

BC范式是在第三范式的一个子集,它在第三范式之上作了更强的约束,即实体集中的任何子集都只能依赖于主键(注意,不是主属性,这一点是BC范式和第三方范式的差异所在),不能存在一个非主属性或非主属性集能够做为某个子集的主键。

BC范式在定义上和第三方是差很少,他最大程度的减小了数据冗余,不过在实际应用中,两者基本是同样的,只有在表的主键包含多个字段时,才会产生差别。

 

三、 反范式化设计

3.1 数据冗余

这里介绍一个很经典的例子。咱们所常见的大部分网站都会有会员系统,用户须要注册会员帐号,而后登陆才能够享受进一步的功能或服务。对于这张会员表,咱们不妨命名为user_info表,通常会包括会员id,会员昵称,真实姓名,登陆密码,性别,教育经历、工做经历、电话、电子邮箱、我的简介、兴趣爱好等信息,其余可能还包括跟该网站会员业务相关的一些字段(好比积分、经验值、充值帐户余额等),通常的schema设计是这样的:

表名

user_info

数据量

100w

功能描述

主要存储用户的基本信息,id、姓名、年龄、联系方式等

字段名(新增)

数据类型(精度范围)

/非空

缺省值

字段含义

id

BIGINT(20)

N

 

用户id

name

VARCHAR(32)

N

 

姓名

gender

TINYINT(4)

N

 

性别

age

INT(8)

N

 

年龄

tel

VARCHAR(16)

Y

 

联系电话

email

VARCHAR(64)

Y

 

电子邮箱

school

VARCHAR(32)

Y

 

就读学校

company

VARCHAR(32)

Y

 

供职机构

interest

VARCHAR(512)

Y

 

兴趣爱好

gmt_create

DATETIME

N

 

记录建立时间

gmt_modified

DATETIME

N

 

记录修改时间

这张表结构看起来是没什么问题的,并且在初期业务简单,用户数量少、访问量低的状况下也确实都ok的;

可是随着网站访问量不断增大,天天登陆的用户愈来愈多,user_info表的访问量愈来愈大,瓶颈慢慢出现;

咱们通过进一步分析发现,在处理用户登陆的过程当中,咱们须要的操做很简单,就是根据用户输入的会员昵称查询相应记录,进而判断密码是否正确;而会员登陆后,也仅会显示用户的昵称或真实姓名。

整个过程先后所涉及到的字段,只有会员id、昵称、密码和真实姓名等34个,而user_info表中其余绝大部分字段,诸如教育经历、兴趣爱好等,在这个过程当中是根本不须要的,但他们的体积却比较大,每次用户登陆都要读取,白白浪费时间和带宽。

那么咱们为何不把这部分字段单独拿出来放到一张新的表里呢?咱们不妨建一个login表,里边只有会员id、会员昵称、登陆密码以及会员真实姓名,每次用户登陆,都只读取这张login表,这样不但数据库读取记录的时间会缩短,也能够将应用和数据库之间网络传输量降到最低,彻底符合咱们前一篇文章里所提到的将结果集缩减到最小的原则。

表名

login

数据量

100w

功能描述

主要存储用户的登陆信息;

字段名(新增)

数据类型(精度范围)

/非空

缺省值

字段含义

id

BIGINT(20)

N

 

用户id

name

VARCHAR(32)

N

 

姓名

password

VARCHAR(128)

N

 

登陆密码的md5

可是有人不由要问,这样不是产生数据冗余了吗?是的,同一份数据在user_info表和login表中各存了一份,确实有可能存在不一致的状况,应用程序中每次新增或修改记录,均可能须要访问两张表,这无形中增长写操做的成本;因此这种优化方式也不是绝对的,要根据具体的场景区别对待,好比在上述这种读写比例很是高的场景中,咱们这样处理就是合适的;而在一个一样读写比例比较高,可是对数据一致性要求也很是高的系统中,对数据进行冗余存储就须要花费额外的精力去处理可能存在的数据不一致状况。

 

3.2 去关联化

Join是咱们在数据库操做时常常会使用的一个关键字,其做用就是将两张表拼接起来,而后过滤出符合条件的记录;可是在拼接的过程中,是采用的是笛卡尔积的方式,其原理图以下:

 

MySQL中,Join的实现方式为Nested Loop Join ,主要以驱动表结果集做为基础数据进行循环,有点相似编程语言中的双层for循环嵌套;这种方式实现最最简单,性能也基本能够接受;其余数据库还提供Hash JoinSort Merge JoinJoin方式,都是针对不一样场景有性能提高,很难简单的说谁好谁坏。

从笛卡尔积的基本原理上来看,无论采用何种实现算法,表间join都是很是耗时耗力的操做,尤为是当其中一张表或两张表的数据量都很是大时更是如此!

因此咱们在作schema设计的时候,应该尽可能避免join的出现,经过必定的字段合并和数据冗余将这种需求降到最低。

 

3.3 去一致性约束

在传统应用中,数据一致性是很重要的一点,可是在不少互联网应用对数据的一致性要求并非很高;好比说数据库的外键约束、惟一性约束等等,当记录增删时,数据库都会去检查相关的条件是否知足,这些都是须要额外开销的,有时候其开销甚至会超过当前操做自己。

在数据库层去掉这些约束并不意味着系统中就不须要,咱们其实能够将这部分约束校验提早到应用程序中;前面咱们提到过,应用程序的扩展相对来讲是比较容易的,因此咱们未尝不充分利用一下来为数据库减负呢?

 

3.4 SQL

3.4.1 数据库底层数据存储

咱们使用的关系型数据库虽然提供了表、字段、索引、sql等种种特性,可是归根到底,其最底层的数据存储仍然是<k,v>格式,好比MySQL,其数据的存储都是有由下层存储引擎来负责的,虽然在逻辑结构上咱们看到的是一张张表和其中一个个分散的字段,可是实际上每条记录在物理存储上都是一个总体;所谓的表结构、字段这些只是上层来维护的元数据,对底层来讲,其实没有字段的概念,就是一条条的物理记录;

好比说咱们前边的user_info表,咱们若是执行相似下边的一条sql

select user_id,user_name from user_info where age =8;

虽然我只想返回user_iduser_name两个字段,可是存储引擎仍是要将全部知足的age=8的记录取出来,而后在分别扫描每条记录,取出咱们须要的两个字段数据返回;

因此咱们能够看到,所谓的关系数据库只不过是在<k,v>系统的上层作了进一步的封装,好比说权限验证、sql解析、二级索引等等;

 

3.4.2 MySQL数据库的层次结构

MySQL虽然是个开源数据库,体积也远小于DB2Oracle这些商业数据库,可是一个RDBMS必备的结构MySQL却同样也很多,正所谓“麻雀虽小,五脏俱全”。

下边咱们就来看下MySQL到底有怎样的层次结构;

 

MySQL从上到下,能够分为三层,第一部分为客户端,中间部分为数据库管理系统(DBMS),最底层为存储引擎层;MySQL使用独立的存储引擎,能够方便切换,这一点和其余数据库有所不一样;

 

3.4.3 哪些是能够不要的

前面给出了MySQL数据库的层次结构,其余数据库结构也是相似的;那么不妨从上到下好好想想,整个体系中哪些东西其实不是咱们必需的?

首先,Client端确定是须要的,咱们总要链接数据库的,那么来看第二层DBMS层,其中的链接管理是须要的,无论什么样的数据库总要处理客户端链接;若是一个数据库在安全网络环境中,而且只有咱们本身在用的话,那么就不须要用户权限验证了,这一层能够去掉;为了减轻数据库负担,咱们也不使用sql,须要的逻辑能够直接使用API在应用层实现,那么这一层也能够省掉;而对于访问控制模块,由于数据库为咱们本身所独享,或者都是可信任的使用者,因此这一层也不那么重要了,或者说是能够进一步简化的;

咱们再来往下看存储引擎层。索引的话能够很好的提升数据的查询效率,这一点无论在什么类型的存储系统中都适用,那么这个功能咱们须要保留,可是若是咱们只想要个<k,v>存储的话,那就能够只保留主键索引好了;事务管理呢,最多咱们不用好了,不麻烦数据库了;锁的话即便咱们本身使用也会有不少的并发访问控制,那么须要保留;

通过这样的精简以后,咱们的数据库还剩如下这些东西;

 

3.4.4 NoSQL存储系统

前面咱们提到了去关联化、去一致性约束以及数据冗余等等;3.1中咱们讨论了关系型数据库的本质,其实就是在<k,v>存储之上又封装和增添了诸多的功能,而3.3中咱们又对关系型数据库进行了裁剪,去掉了一些咱们没必要需的;通过全部的这么步骤,剩下的部分(图1-1所示),基本上就是如今NoSQL存储了;

因此,NoSQL不是什么高级的东东,而是关系型数据库作了退化,回归到了其基础本源;而分布式的特性,也并非NoSQL独有的,关系型数据之因此难有分布式的架构,本质就是由于其选择了“向上生长”,上层的复杂特性制约了其“横向”生长的能力;而NoSQL只不过是在<k,v>之上,选择了另一个生长方向而已。

 

因为去掉了上层的“高级”特性,NoSQL系统的性能有了比较大的提高,同时因为横向生长,其存储能力也有了很大的加强;

因此当咱们的系统受制于关系型数据库的性能时,不妨放弃schema模式,来尝试一下“自由”的NoSQL数据库。

 

 

四、 数据扩展

4.1 Scale UpScale Out

Scale up主要是指加强数据库的单机处理能力,好比说提升CPU、内存、硬盘、网卡等硬件配置;其实scaleup这个概念包括咱们后便将要提到的scale out,不光对于数据库,对于大部分的软件系统都适用的,只不过具体的实施方案会有所差别;

由于scale up主要是增长机器硬件配置,相对来讲比较简单,也不须要迁移数据,对于技术人员来讲没有新的东西,因此对于中小型系统来讲很是合适;

scale out是指横向的扩展,就是增长数据库实例或节点来增长总体的处理能力,这里边还包括两种方式,一种常见的数据复制,好比MySQLReplicationOracle rac等;另外一种横向扩展的方式是进行数据切分,也就是说把本应该放在一台机器上的数据切成几部分,分别放在不一样的节点上(并非相同数据的备份),这样访问的压力就会分散到多个节点。

scale out的优势是成本低,若是整个系统都使用PC Server的话,能够用很低的成原本支撑海量的数据和高并发;并且通常来讲,这种扩展是线性的,即有多少机器,就能支撑多少的数据和多大的访问量,但一般这须要有个比较好的数据系统架构或中间件系统来支持;

以淘宝的交易库来讲,原来使用的都是IOEI指的是IBM的小型机,OOracle数据库,EEMC的高端存储),采用主备双机方式,用Orcle和高端存储来支撑天天的巨大访问量,可是整个系统的成本也很是高(听说一套下来要2000多万),而通过去IOE之后,经过使用MySQL和廉价的PC Server(线上1616备,双11的时候扩展为3232备),经过数据切分和Replication机制,不只整个数据库的在线处理能力提升了4倍,成本也降为原来的1 / 8不到,同时数据的安全性和容灾能力也获得了保证;

可是数据库技术不是本文关注的重点,下边将主要介绍和咱们平常开发联系更为紧密的数据切分知识。

 

4.2 数据切分

4.2.1 为何要切分数据

对于这个问题,可能有人会以为的是废话:你前边不是已经说过了吗,干吗还问?没错,扩展系统,支持更大访问和并发和替换商业数据库,下降成本这两个确实咱们进行数据切分的主要缘由;但这样来讲太笼统了

对于数据库来说,无论是商业的仍是开源的,其单库和单表的承载能力都是有限的;在一般的业务场景下(写操做和读操做比较均衡),普通的pc server上,MySQL数据库单表数据记录的承载能力在千万级(数据量在TB级别)左右,TPS大概在千这个级别(具体测试环境和数据可参考另外一篇文档);固然,咱们在这里没有必要苛求具体的数据,由于这和具体业务场景、实际读写比、服务器硬件配置、具体的数据引擎、MySQL的配置参数等相关,好比说,若是只是将MySQL做为日志数据库(基本只有写操做,不须要建索引),单表的支撑能力可达到上亿甚至是十亿的级别;但这毕竟只是种极限场景,不能拿来用做通常场景的参考。

另外须要说明的是,前面说的单库单表的承载能力有限,并非说当数据量超过这个上限时,数据库就会立刻崩溃或者拒绝服务,而是在这种状况下,数据库的总体的读写性能就会急剧降低,甚至于一条很简单的sql查询也可能会超时,若是原本就是负载很重的一张表,那与崩溃无异了!

 

4.2.2 数据切分基本原则

数据切分所要遵循的原则主要有两点:

第一就是将数据均衡分散在多个处理节点上,其实这里主要强调的是均衡,但这个均衡并不可简单的当作是每一个节点上库(表)数量相等或是记录条数同样;而更可能是要从数据访问和处理能力的均衡上去考虑,主要原则有如下几条。

a、不一样节点业务关联度要低

b、同一节点业务类型尽可能一致

c、数据(访问量)要均衡

d、数据的一致性和安全性

 

4.2.3 垂直切分

垂直切分主要是根据表中数据的业务类型,将不一样业务的数据放在不一样的表或者数据库中;

在系统结构设计中咱们会常常提到一个原则,叫作高内聚、低耦合,其实这个原则在数据库的垂直切分中一样适用;因此在作水平切分的时候要尽可能作到不一样功能的表关联尽量少,这不但能够减小SQL语句中的join出现的概率,同时后续表结构变动时也更容易;

对于中小型系统来说,不少人喜欢各类复杂功能一个sql搞定,甚至有些还使用存储过程,这样对前端程序来说确实方便了许多;可是这种方便也每每会给咱们带来伤害,尤为是当数据量和访问量都增加比较快的状况下,你会发现几条慢查询可能让你的整个系统直接失去响应!

 

4.2.4 水平切分

4.2.4.1 什么是水平切分

垂直切分主要是按照系统功能来切分的,因此一样是有瓶颈存在的,好比说,某一项功能比其余的要复杂许多,或者数据量要大不少,很难再进一步拆分,这样就不适合垂直切分了;这在实际的业务场景中是存在的;

好比说淘宝的订单系统,虽然功能看起来简单,可是因为交易类型复杂,中间状态繁多,耦合性很是强,加之订单数量巨大,其系统是很难再进行功能上的剥离的;可是这个系统属于底层基础系统,为了支撑巨大的访问量,只能采起水平数据切分方式来解决高并发问题。

水平切分就是咱们一般所说的分库分表,主要是将一张表中的数据按照某个字段(好比说用户id、商品id、订单id等)分散存储在多张结构相同的表中,这样访问的压力就会分散到多张表上;

在平时的开发中,数据的水平切分比垂直切分应用的更多,由于通常来说,须要进行垂直数据切分时,一般系统的规模和负载都已经很大了(尤为是使用oracle的时候),这时候咱们最早实施的,每每是经过RPC或者服务化将应用分红多个系统,底层数据表之间的依赖,很天然的会转化成系统间的接口依赖,因此这个时候,数据库固然也会跟着分开了,不须要太刻意其考虑垂直切分这个概念了。

 

4.2.4.2 水平切分优势

成本固定;只要在系统设计之初就指定好分表数量和分表字段,无论是要分红8个库1024张表,仍是16个库4096张表,其成本都是同样的;

解决了单表瓶颈问题;水平切分方式很好的解决了垂直切分时可能存在的单表瓶颈问题,只要在开始时作好容量预估,设定适当的切分数量,基本能够知足业务很长一段时期内的存储需求;

对事务透明;分表对于数据库来讲是透明的,因此原来的事务该怎么作还怎么作。

 

4.2.4.3 水平切分缺点

水平切分的虽然颇有效,可是其缺点也很多,主要以下:

sql路由变得复杂;每次在作了切分的库或表上执行sql时,都必需要明确指出目标分库或分表;这无形中增长业务方的成本。

分表字段单一;水平切分只能使用单一的分表字段;若是业务中有需求按照非分表字段进行查询,则会变得很困难,只能扫描全部的表;一个解决方案就是,按照不一样查询字段作多份分表;可是又要花费精力去解决数据冗余问题。

join操做变得困难;显而易见,之前单表间的join放到多表上是没法执行的,这时候咱们最好仍是选择放弃;

二次扩展比较麻烦;若是分表以后,咱们数据增加太快,又达到存储瓶颈了,就面临着二次拆分的命运;但由于路由规则发生了改变,迁移数据的麻烦是避免不了的;因此要有必要的手段去保证迁移数据时系统依然可以对外提供服务。

 

4.2.4.4 水平切分注意事项

在作水平切分后,咱们的部分业务实现方式或是开发方式可能须要随着改变;如下是咱们再作水平切分时须要注意的点,主要是针对水平切分的弱点而言的:

根据业务场景肯定切分字段;业务中根据什么字段去查询,就用什么字段去分表;

避免热点数据问题;一般切分时采用的hash算法理论上能够保证数据的分散性,但在实际应用中,仍可能遇到数据热点问题;理论是理论,实际归实际,没有绝对的,不要觉得分了表就万事大吉了。

分表宜多不宜少;这样作主要是为了尽可能避免后期可能遇到的二次拆分,由于前面咱们说过,拆成1024张表和拆成4096张表的操做成本是同样的。

避免分表上的join操做;在分表的缺点中咱们就提到过,join在水平切分场景下会很困难,因此在业务实现中,对这种状况能避免就避免,哪怕牺牲一些简洁性,多绕几步。

避免非分表字段查询;道理也是同样的,切分后只能按照切分字段进行查询;若是非要按其余字段查询,那就冗余数据吧。

 

4.2.5 其余切分方式

上边咱们提到的是咱们最多见的切分方式,其余还有一些切分方式不太“规矩”,它们具备部分水平切分或垂直切分的特色,但又很难直接纳入到某一分类中;正如那句老话所言:山无定势,水无定形。

 

4.2.5.1 逻辑切分

逻辑切分相似于垂直切分,也是将数据按照不一样业务逻辑拆分到不一样的表中;但这种方式追求的不是单纯的负载均衡,而是不一样业务的数据隔离;好比说某部分数据读多些少,某部分数据则可能读少写多;这样进行隔离后,能够充分的利用不一样的业务特色进行优化,好比说建立不一样的索引结构,使用不一样的查询方案等等;前面咱们提到的数据冗余其实就属于这类切分方式。

另外一个典型的案例就是数据倾斜;好比说对于淘宝上的卖家来讲,有些卖家比较小,可能他的店铺中的宝贝只有几十数百个,可是有些品牌卖家或热门店铺,他们的宝贝可能有上万甚至是数十万,再加上未上架的或是已下架的历史宝贝,数量会更多!这两类用户的处理,若是使用相同的逻辑,极可能会产生问题;可是若是咱们将这部分大卖家提出来放在另外的节点上来处理,效果可能会好不少。

 

4.2.5.2 时间切分

时间的切分主要是将记录按照建立时间的前后顺序,放在不一样的表中;mysql的分区表便提供了这样一种切分的机制;分区表对外逻辑上表现为一张表,可是实际物理存储上是多张表,不一样的表对应不一样的时间区段;用户能够设定建立新分区表的周期,好比说一天或者是一周,在某个时间点插入的记录便会写入对应的分区表;读取时,会从最近一个分区开始扫描,直到找到目标记录。

 

4.2.5.3 冷热切分

冷热切分主要是按照数据的访问频率对数据进行隔离,有点相似于前边咱们提到的逻辑切分。

淘宝的交易历史库就是典型的表明。淘宝的订单总量巨大,天天产生的订单数量也很是多;若是咱们为用户提供订单查询服务,那么巨大的订单量会使咱们的服务性能降低不少;可是实际中咱们会发现,用户不多会去查询本身三个月之前的订单,那咱们不妨将用户三个月之前的订单拿出来单独提供存储和查询服务,这样就可使用户访问频繁的订单表数据量变得很小,从而能够提供更高的处理性能。

 

4.2.5.1 体积切分

按体积切分主要是按照数据表的尺寸或是记录条数进行切分,这种切分通常适用于业务类型单一,对表的体积能够很好预估的状况,主要是为了不表的尺寸过大而是性能降低。

通常来讲,只有日志类型的表适用于这种切分方式,一般是以自增id做为判断标识,由于基本不存在删除和修改操做,因此能够很好的控制体积;不过这种状况下大部分系统更倾向于使用按时间切分的方式,因此按体积切分的实际应用不多。

 

4.3 数据路由与合并

当咱们进行了分库分表以后,一个咱们不得不面对的问题就是sql的路由。当咱们将数据分散存储在诸如名为test_0000test_0001的分表中时,咱们会发现,必需要对原来的程序代码或数据库进行相应的改造,不然程序将找不到正确的库或表;这种状况下,一般的解决方案有三种:

 

4.3.1 修改程序

这种方案,只需将程序里涉及到数据库读写的代码按照分表逻辑进行改造便可,技术上比较简单,不须要额外的软件或者是技术的支持;缺点是对业务代码侵入性强,可能涉及到多个地方的修改,工做量较大,并且后期的修改和维护成本也比较高。

 

4.3.2 修改数据库

这种方法对程序透明,是的上层业务逻辑不须要考虑分库分表的读写规则,应用代码能够保持不变;

缺点是须要修改数据库系统,或者以模块、插件等形式对数据库进行加强,开发成本和后期维护成本都很高,部分商业数据库根本没法自行修改。

 

4.3.3 使用中间层代理

这个应该是目前采用最多的方式;其优势是对上层业务逻辑和底层数据库都透明,只须要对应用的访问层进行简单改造,便可快速切换到拆分以后的数据库,无论是开发人员仍是数据库管理人员都不须要增长多少工做成本;

其缺点是技术门槛较高,须要专门的人员来开发和维护;功能受限,部分在单库单表下的常见操做在这种中间层代理的方式下会变得麻烦,好比说跨库、跨表join,全局数据分组与排序等。

对于以上几种数据路由方案,不一样的场景可能会有不一样的选择,具体要看自身的业务需求,技术储备以及实施成本等。

 

4.4 Scale up之闪存存储

以上通篇几乎都在介绍如何对数据库进行水平扩展和数据切分,其实有时候咱们没必要搞得如此复杂,若是仅经过简单的硬件升级就能知足业务增加对数据库的要求,咱们何乐而不为呢?

咱们都知道,普通的服务器,甚至是不少高端存储,都是基于机械硬盘存放数据的;而机械硬盘最大的缺点就是速度上存在物理上限;

通常的机械硬盘读写数据都要通过寻道操做,主要由两个步骤组成:一是移动磁头,二是转动盘片;这二者都属于机械操做,前者通常在2ms~4ms,然后者,对于如今经常使用的SATA盘或SAS盘来讲,通常会在1ms之内,因此,一次寻道操做要耗费大约5ms左右的时间,而对于一些老式硬盘来讲,这个时间可能要达到10ms甚至更多!也就是说,单块磁盘随机读写的iops只有100左右,这简直低的不可忍受,再牛B的系统也被拖死了!

对于一个初级团队,再没有不少技术积累的状况下,盲目的进行水平扩容每每会给本身带来更高的维护成本和更差的系统稳定性,因此这个时候咱们不妨尝试一下数据库性能提高的又一利器——闪存存储;毕竟咱们不断对系统进行优化的目标就是为了得到更高的性能!

相关文章
相关标签/搜索