这是一篇在阅读《大规模分布式存储系统:原理解析与架构实战》时的阅读笔记,由于长时间碎片阅读的关系导致在做这种读书笔记的时候接近复制粘贴。虽然其中会有一小部分自己的想法但都十分零碎,希望后续能改进。
Microsoft SQL Azure是微软的云关系型数据库,后端存储又称为云SQL Server。它构建在SQL Server之上,通过分布式技术提升传统关系型数据库的可扩展性和容错能力。
数据模型
逻辑模型
云SQL Server将数据划分为多个分区,通过限制事务只能在一个分区执行来规避分布式事务。并通过主备复制(Primary-Copy)协议将数据复制到多个副本保证高可用。
云SQL Server中一个逻辑数据库称为表格组(table group),它既可以是有主键的也可以是无主键的。
如果一个表格组是有主键的,要求表格组中的所有表格都要有一个相同的列,称为划分主键(partitioning key)。划分主键可以当成表格主键使用,但不是强制的要求。如下所示:
上图两张表中【Id】是划分主键,且在Customers表中属于表的主键,而在Orders中不是,Orders表的主键为组合主键<Id, Oid>(<顾客Id,订单Id>)。
表格组就类似于Bigtable的对象组,即一个表格组是为某张表为主服务的,其他的表都属于这张主表的从表,抽象成一个对象来说,主表表示对象本体,从表表示对象附带的属性。因此图中的表格组的划分主键为Customers的Id(顾客Id)。其他所有的表都必须要有一个同样的字段。Customers与其他表之间的关系为has one或者has many。
如果表格组是有主键的,云SQL Server支持自动地水平拆分表格组并分散到整个集群。同一个行组总是被一台物理地SQL Server服务,从而避免分布式事务。
一个行组就相当于一个对象与和它相关地所有属性。这样拆分避免了与某个对象相关的事务是分布式事务,但由于一个对象由单机提供服务,当对象的数据量过大时又会出现“数据倾斜”问题。
这样的好处是避免了分布式事务的两个问题:阻塞及性能;缺点是限制了用户的使用模式。只读事务可以跨多个行组,但事务隔离级别最多支持读取已提交(read-committed)
RC:在一个事务中对同一个数据的读取会有两种或多种情况,这是因为事务执行过程中其他事务提交后对数据的修改被当前事务可见造成的。这就表示了分布式的只读事务会受到其他事务对数据提交修改的影响。
但这样的划分方式依旧避免不了查询多个用户时跨表、跨集群的情况
物理模型:
在物理层面,每个有主键的表格组根据划分主键列有序地分成多个数据分区(partition)。这些分区之间互相不重叠,并且覆盖了所有划分主键值。确保每一行组属于一个唯一分区。
分区是云SQL Server复制、迁移、负载均衡的基本单位。
物理层面的数据分区,能够通过程序设定保存的物理分区吗?这样的话要涉及到驱动或层面了吧。
每个分区包含多个副本(默认为3),每个副本存储在一台物理的SQL Server上。由于每个行组属于一个分区,这就意味着每个行组的数据量不能超过分区允许的最大值,即单台SQL Server的容量上限。
虽然在逻辑层面允许行组的数据量能够达到程序的理论上限,但是在物理层面由于行组被限定在数据分区里,因此行组的实际大小不能超过分区允许的最大容量。
一般来说,同一个交换机或者同一个机架内的机器出现故障的概率较大,因此他们属于同一个故障域,即出现故障的时候往往是同时宕机。
云SQL Server保证每个分区的多个副本分布在不同的故障域。同GFS或者HDFS一样,根据网络拓扑的层级进行备份:同一机架、同一机房、同一网络。
同一般的分布式存储系统一样,主备副本之中,只有主分区才会提供服务,副分区只接受主分区通过操作日志同步过来的操作。因为主备副本同步会有一个时间差,如果备副本提供读服务,就会读取到过期数据。
这里的同步时间差指的是备副本通过操作日志回放操作的时间,在强一致性的分布式系统中,通常要求主备副本在操作日志同步完成之后才认定此次写入成功。
如图所示,有四个逻辑分区PA、PB、PC、PD,每个分区有一个主副本和两个备副本。每台SQL Server混合存放了主副本和备副本。如果某台机器发生了故障,可以将服务快速地迁移到其他备副本节点上。
分区的划分是同台的,如果某个分区超过了允许的最大分区大小或者负载太高,这个分区将会被分裂为两个分区。同时它的备副本也将会被分裂为两个分区,同时为了负载均衡,所以主分区分裂后并不一定还是主分区,可能会让副分区分裂后的分区作为提供服务的新的主分区。
架构
云SQL Server分为四个主要部分:SQL Server实例、全局分区管理、协议网管、分布式基础部件。
- SQL Server实例:每个SQL Server实例是一个运行着SQL Server的物理进程。每个物理数据库包含多个子数据库,他们之间相互隔离。子数据库是一个分区,包含用户的数据以及schema信息。
- 全局分区管理器:它维护分区映射表信息,包括每个分区的主键范围,每个副本所在的服务器,以及每个副本的状态,包括副本当前是主还是备,前一次是主还是备,正在变成主,正在备拷贝或者正在被追赶。当服务器发生故障时,分布式基础部件检测并确保服务器故障后通知全局分区管理器。全局分区管理器接着执行重新配置操作。另外,全局分区管理器监控集群中的SQL Server工作集,负责负载均衡、副本拷贝等管理操作。
SQL Server实例就是存储节点,而全局分区管理器则是中心节点。从全局分区管理器管理每个分区的主键范围这点可以看到,SQL Server的扩容是按照通用的range+hash模式来进行分库操作。即分区时按照主键范围进行分区,分区内又按照hash值映射到对应表中。而全局分区管理器只需要负责维护分区的主键范围,后面的hash映射可以交给SQL Server实例完成。
- 协议网关:负责将用户的数据库连接请求转发到相应的主分区上。协议网管通过全局分区管理器获取分区所在SQL Server电视里,后续的读写事务操作都在网关与SQL Server实例之间进行。
为了防止全局分区管理器称为整个系统的性能瓶颈,因此通过将数据缓存到网关本地的方式减少读写流程对全局分区管理器的使用频率。
- 分布式基础组件:用于维护机器上下线状态,检测服务器故障并为集群你中的各种角色执行选举主节点操作。他在每台服务器上都运行了一个守护进程。
简单的说就是各个节点之间的通信模块,负责串联整个集群中的所有节点,并传递相关信息。
分布式存储系统的设计离不开客户端、存储节点、中心节点这三类,不同的系统会根据实际需要适当地添加新的组件或者将这三者的一部分功能抽离成一个新组件。在分布式系统中,中心节点需要及时地判断集群中各个节点的生存状态,因此各个节点通过心跳、互斥锁服务等方式将自身的存活状态告知中心节点;客户端在读写时需要根据读写数据内容获取对应的存储节点信息,同时要保证信息的及时性与一致性,因此这部分信息通常由中心节点负责维护,这就是集群元数据;由于中心节点需要和集群中所有节点保持通信、维护元数据修改,为了避免其称为整个系统的性能瓶颈,需要客户端缓存元数据信息以减轻读写过程对中心节点的使用频率。
复制与一致性
云SQL Server采用“Quorum Commit”的复制协议,用户数据存储三个副本,至少写成功两个副本才可以返回客户端成功。
这样在读取的时候只需要R + W > N,即可保证读取的结果中一定有正确值。
事务T的主副本分区生成操作日志并发送到备副本。乳沟事务T回滚,主副本会发送一个ABORT消息给备副本,备副本将删除接收到的T事务包含的修改操作。如果T事务提交,主副本会发送COMMIT消息给备副本,并带上事务提交序号(Commit Sequence Number,CSN),每个备副本会把事务T的修改操作应用到本地数据库并发送ACK消息回复主副本。如果主副本接收到一半以上的成功ACK,他将在本地提交事务并成功返回客户端。
从上面的操作来看,主副本执行事务的时候哪些修改操作发送到备副本之后,备副本并不会立即对这些操作进行恢复,而是等待主副本发送最终的命令确认这些操作是提交还是会滚。如果提交,则按照主副本提供的执行顺序对日志进行恢复。
如果备副本发生故障,在他们恢复后将向主副本发送本地已经提交的最后一个事务的提交序号。如果两者相差不多,主副本将直接发送操作日志给备副本;如果两者相差太多,主副本将首先把数据库快照传给备副本,再把快照点之后的操作日志传给备副本。
这个多于不多的判断在于备副本发送的最后一次提交的事务序号是否已经被保存为快照节点之后,如果已经被生成快照,则表示按照日志恢复记录的时间开销比直接传输数据要大。
主备副本之间传送逻辑操作日志,而不是对磁盘物理页的redo&undo日志。
数据库索引以及schema相关操作(如创建,删除表格)也通过操作日志发送。
因为在一致性中,主备副本之间的数据是一致的,但是数据保存的物理页不一定一致,如果要求这也要同步,那么开销将比单纯地同步逻辑操作要大,同时也没有太大意义。
同时,有些网卡会出现一些硬件问题,虽然TCP保证了数据传输过程中的准确性,但是网卡的问题依旧能够使得数据丢失或者被修改,因此对主备之间的所有消息都会做校验(checksum)。同样,某些磁盘会出现“位翻转“错误,因此,对写入到磁盘的数据也做校验。
校验在主副本的时候将数据与数据MD5码同时发送,如果传输过程中数据发生改变,无论是数据本身还是校验码,他们都将无法匹配。磁盘同理。
容错
每个SQL Server最多服务650个逻辑分区,这些分区可能是主副本,也可能是备副本。全局分区管理器统一调度,每次选择一个分区执行重新配置(Reconfiguration)。如果出现故障的分区是备副本,全局分区管理器首先选择一台负载比较轻的服务器,接着从相应的主副本分区拷贝数据来增加副本;如果出现故障的是主副本,首先需要从其他副本中选择一个最新的备副本作为新的主副本,接着选择一台负载比较轻的机器增加备副本。
由于云SQL Server采用“Quorum Commit”复制协议,不需要等待所有副本全部复制完成。
全局分区管理器控制重新配置任务的优先级,否则,用户的服务会收到影响。比如某个数据分片的主副本出现故障,需要尽快从其他副本中选择副本切换为主副本你;某个数据分片只有一个副本,需要优先复制。
针对某些服务器下线后重新上线也要配置相关策略:只有两个副本的状态持续较长一段时间(默认2小时)才开始复制第三个副本。
全局分区管理器也采用“Quorum Commit”实现高可用性。它包含七个副本,同一时刻只有一个副本为主,分区相关的元数据操作至少需要在四个副本上成功(即超过一半的副本写入成功)。如果全局分区管理器主副本出现故障,分布式基础部件将负责从其他副本中选择出最新的副本作为新的主副本。
负载均衡
负载均衡的相关操作包含三种:副本迁移一季主备副本切换。
新的服务器节点加入时,系统内的分区会逐步地迁移到新节点,为了避免过多的分区同时迁入新节点,全局分区管理器需要控制迁移的频率,否则系统整体性能可能会下降。
如果同时迁入,会使得新机器的网络资源同时被多台节点占用,这样的后果就是所有节点的网络资源都少得可怜,使得数据迁移的时间被拉长;同时由于迁移过程中也会占用原先节点的网络资源,如果长时间被占用,将会导致这些迁移过程中的节点对外提供服务的效率变慢。
如果主副本所在服务器负载过高,可以选择负载较低的备副本替换为主副本提供读写服务。这个过程称为主备切换,不涉及数据拷贝。
如果将流量分流到备副本,那么将会导致两个同时提供服务的节点直接数据的不一致性。
影响服务器节点负载的因素包括:读写次数,磁盘/内存/CPU/IO使用量。
多租户
云存储系统中多个用户的操作互相干扰,因此需要限制每个SQL Azure逻辑实例使用的系统资源:
1. 操作系统资源限制,比如CPU、内存、写入速度等,如果超过限制将在10秒内内拒绝相应的用户请求
2. SQL Azure逻辑数据库容量限制。每个逻辑数据库都预先设置了最大的容量,超过限制时拒绝更新请求,允许删除操作
3. SQL Server物理数据库大小限制。超过该限制时返回客户端系统错误,此时需要人工介入
讨论
Microsoft SQL Azure将传统的关系型数据库SQL Server搬到云环境中,比较符合用户过去的使用习惯。但还是有一些区别:
- 不支持的操作:Mucrosoft Azure作为一个针对企业级应用的平台,景观尝试支持尽量多的SQL特性,仍然有一些特性无法支持。比如USE操作:SQL Server可以通过USE切换数据库,不过在SQL Azure不支持,这时因为不同的逻辑数据库可能位于不同的物理机器。切换过程中需要切换网络连接等相关配置。
- 观念转变:对于开发人员,需要用分布式系统的思维开发程序,比如一个连接除了成功、失败还有第三种不确定状态:云端没有返回操作结果,操作是否成功无从得知;对于DBA,数据库日常维护,比如升级、数据备份等工作都交给了微软,可能会有更多的精力关注业务系统架构。
项目Azure Table Storage,SQL Azure在扩展性上有一些劣势,例如,单个SQL Azure实例大小限制。Azure Table Storage单个用户表格的数据可以分布到多个存储节点,数据总量几乎没有限制;而单个SQL Azure实例最大限制为50GB,如果用户的数据量大于最大值,需要用户在应用层对数据库进行水平或者垂直拆分,使用比较麻烦。
这里可以看到分布式表格与分布式数据库的一些区别:分布式表格的灵活性比分布式数据库大,因为分布式数据库需要保证用户在使用的时候与平时使用的单机数据库没有多大区别,因此为了支持大部分的事务操作,对数据的分布做了限制,不允许将同一个用例的数据分布到多台服务器上,对一个用例的事务只会在单机层面完成,不会扩展为分布式事务。
分布式表格系统中没有数据库引擎部分,所有数据都统一由存储节点维护,相关信息由中心节点维护,可以十分灵活地对数据分布进行重新排列。