赞
踩
**Greenplum Database(GPDB)**是一款基于开源 PostgreSQL 扩展的 MPP
(massively parallel processing),可支持大规模水平扩展的分布式数据库。 GPDB 采用的是 master-worker
模式,每个 worker process 运行在不同的机器上,拥有各自的存储和运算资源。**客户端通过 master 把查询语句分发到各个机器上,以达到并行计算来处理海量数据。**集群节点(无论是master还是segemnt)上的每个实例都是一个物理上独立的PostgrepSQL数据库。
Greenplum数据库是一种shared nothing的分析型MPP数据库。这种模型与高度规范化的/事务型的SMP数据库有显著区别。Greenplum数据库使用非规范化的模式设计会工作得最好,非规范化的模式适合于MPP分析型处理,例如带有大型事实表和较小维度表的星形模式或者雪花模式。
Greenplum 在 PostgreSQL 之上还添加了大量其他功能,例如 Append-Optimized 表、列存表、外部表、多级分区表、细粒度资源管理器、ORCA 查询优化器、备份恢复、高可用、故障检测和故障恢复、集群数据迁移、扩容、MADlib 机器学习算法库、容器化执行 UDF、PostGIS 扩展、GPText 套件、监控管理、集成 Kubernetes 等。
使用标准的 INSERT
SQL 语句可以将数据自动按照用户定义的策略分布到合适的节点,然而 INSERT 性能较低,仅适合插入少量数据。Greenplum 提供了专门的并行化数据加载工具以实现高效数据导入,详情可以参考 gpfdist 和 gpload 的官方文档。
在数据分布方面,Greenplum 在这方面不单单做到了基本的分布式数据存储,还提供了很多更高级灵活的特性,譬如多级分区、多态存储。Greenplum 6 进一步增强了这一领域,实现了一致性哈希和复制表,并允许用户根据应用干预数据分布方法。有这么多种手段,可见Greenplum用户肯定时遇到了很多数据倾斜的问题
Greenplum支持的分区方法有:
范围分区:根据某个列的时间范围或者数值范围对数据分区。譬如以下 SQL 将创建一个分区表,该表按天分区,从 2016-01-01 到 2017-01-01 把全部一年的数据按天分成了 366 个分区:
CREATE TABLE sales (id int, date date, amt decimal(10,2))
DISTRIBUTED BY (id)
PARTITION BY RANGE (date)
( START (date '2016-01-01') INCLUSIVE
END (date '2017-01-01') EXCLUSIVE
EVERY (INTERVAL '1 day') );
列表分区:按照某个列的数据值列表,将数据分不到不同的分区。譬如以下 SQL 根据性别创建一个分区表,该表有三个分区:一个分区存储女士数据,一个分区存储男士数据,对于其他值譬如 NULL,则存储在单独 other 分区。
CREATE TABLE rank (id int, rank int, year int, gender char(1), count int )
DISTRIBUTED BY (id)
PARTITION BY LIST (gender)
( PARTITION girls VALUES ('F'),
PARTITION boys VALUES ('M'),
DEFAULT PARTITION other );
Greenplum 支持多态存储,即单张用户表,可以根据访问模式的不同使用不同的存储方式存储不同的分区。通常不同年龄的数据具有不同的访问模式,不同的访问模式有不同的优化方案。多态存储以用户透明的方式为不同数据选择最佳存储方式,提供最佳性能。Greenplum 提供以下存储方式:
存储方式和分区方式相组合,可以对一张表不同的数据区域有不同的存储方式。
数据分布是任何 MPP 数据库的基础,也是 MPP 数据库是否高效的关键之一。通过把海量数据分散到多个节点上,一方面大大降低了单个节点处理的数据量,另一方面也为处理并行化奠定了基础,两者结合起来可以极大的提高整个系统的性能。譬如在一百个节点的集群上,每个节点仅保存总数据量的百分之一,一百个节点同时并行处理,性能会是单个配置更强节点的几十倍。如果数据分布不均匀出现数据倾斜,受短板效应制约,整个系统的性能将会和最慢的节点相同。因而数据分布是否合理对 Greenplum 整体性能影响很大。
Greenplum 6 提供了以下数据分布策略。
PostgreSQL 生成的查询计划只能在单节点上执行,Greenplum 需要将查询计划并行化,以充分发挥集群的优势。
Greenplum 引入 Motion 算子(操作符)实现查询计划的并行化。Motion 算子实现数据在不同节点间的传输,它为其他算子隐藏了 MPP 架构和单机的不同,使得其他大多数算子不用关心是在集群上执行还是在单机上执行。每个 Motion 算子都有发送方和接收方。此外 Greenplum 还对某些算子进行了分布式优化,譬如聚集。
CdbDispatchUtilityStatement
vectoring中的分发语法树(一些语法信息只有QE上有)),协调控制(控制、等待下发的任务的状态)special
中存储了页面级的元信息:
要点总结
相邻 Gang 之间的数据传输称为数据洗牌(Data Shuffling)。数据洗牌和 Slice 的层次相吻合,从下到上一层一层通过网络进行数据传输,不能跨层传输数据。根据 Motion 类型的不同有不同的实现方式,譬如广播和重分布。
Greenplum 实现数据洗牌的技术称为 interconnect,它为 QEs 提供高速并行的数据传输服务,不需要磁盘 IO 操作,是 Greenplum 实现高性能查询执行的重要技术之一。interconnect 只用来传输数据(表单的元组),调度、控制和错误处理等信息通过 QD 和 QE 之间的 libpq 连接传输。
Interconnect 有 TCP 和 UDP 两种实现方式,TCP interconnect 在大规模集群中会占用大量端口资源,因而扩展性较低。Greenplum 默认使用 UDP 方式。UDP interconnect 支持流量控制、网络包重发和确认等特性。
Greenplum 使用两阶段提交(2PC)协议实现分布式事务。2PC 是数据库经典算法,此处不再赘述。本节概要介绍两个 Greenplum 分布式事务的实现细节:
(更正:应该不是2pc,应该就是MVCC)
在 QD 开始一个新的事务(StartTransaction)时,它会创建一个新的分布式事务 id、设置时间戳及相应的状态信息;在获取快照(GetSnapshotData)时,QD 创建分布式快照并保存在当前快照中。和单节点的快照类似,分布式快照记录了 xmin/xmax/xip 等信息。这些信息被用于确定元组的可见性(HeapTupleSatisfiesMVCC)。
和 PostgreSQL 的提交日志 clog 类似,Greenplum 需要保存全局事务的提交日志,以判断某个事务是否已经提交。这些信息保存在共享内存中并持久化存储在 distributedlog 目录下。
为了提高判断本地 xid 可见性的效率,避免每次访问全局事务提交日志,Greenplum 引入了本地事务-分布式事务提交缓存
greenplum基于PG,虽然PG没有分布式事务管理器,但是支持2阶段提交2pc
PREPARE TRANSACTION
COMMIT PREPARED
ROLLBACK PREPARED
prepare之后持有的行锁不会被释放,就算宕机,重启pg节点之后锁还是在的(数据库会将prepare写进事务日志,需要进行负责恢复),方便后续协调者cordinator继续对该事务进行操作
gp在pg的基础上,实现了:
Writer QE
和Reader QE
共享本地快照信息理论上,如果参与者只有1个,2pc可以简化为1pc(Bernstein称为协调权的转移,因为只有一个参与者的话,就不需要协调者了)
主流数据库三大并发控制方法:
事务的本质就是将多个步骤捆绑为原子的步骤,要么都成功,要么都不成功,事务的中间状态不应该被其他事务看到(隔离isolation)。
greenplum只实现了read commited和repeatable read.
MVCC主要为了解决读写冲突。
|t_xmin|t_xmax|t_cid|t_ctid|t_infomaskt2|t_infomask|NULL_bitmap|userdata|
Xmin
:创建tuple的事务IDXmax
:删除tuple的事务ID,有时用于行锁(有事务在更新该行,通过配合infomask中的HEAP_XMAX_EXCL_LOCK完成)cid
:事物内的查询命令编号,用户跟踪事务内部的可见性,创建cursor(游标)和更改cursor中的内容,这两步需要保证看到的事务是一样的。ctid
:指向下一个版本tuple的指针,由两个成员blocknumber
和offset
组成t_infomask
用以加速可见性的查询,标记优化,比用每次都去看mvcc快照或者pg_clog
Xmin
:所有小于Xmin的事务都已经提交Running
:正在执行的事务列表,这里面保存的所有事务都是未提交的,不在这里面,并且处于水位之间的id就是已经提交的Xmax
:所有大于等于Xmax的事务都未提交pg_clog
(事务日志)。(事务在abort的时候不需要更改行中的t_xmax
)22:50
讲解的很详细一个transaction共有三种状态, committed,running,abort
2021 sigmod论文《Greenplum: A Hybrid Database for Transactional and Analytical Workloads》
针对 Hadoop 存储的 SQL 执行引擎。HAWQ 通过数据接口可以直接读取 Hive 表里的数据(也支持原生存储格式),然后用 SQL 执行引擎来计算得到查询结果。与 HiveQL 通过把 SQL 解析成一连串的 MapReduce job 的执行模式相比,速度要快好几个量级。HAWQ 虽然在开发执行引擎过程中借鉴了很多 GPDB 的东西,但毕竟是一款不同的数据库引擎,Pivotal 因此希望有一款兼容的优化器能够服务于它。由此,研发了开源优化器 ORCA。
除了使用了 vec-exec(毕竟,联合创始人 Marcin 的博士毕业论文就是关于 vec-exec 的),Snowflake 也是一款 100%计算和存储分离,面向云原生的数据仓库系统。本文内容主要参考他们发表于 SIGMOD-16 的 paper: The Snowflake Elastic Data Warehouse
。
Snowlake 是 2012 年成立的,2015 年正式推出商用版本。2012 年,正是云服务起步不久,大数据热火朝天的时候。当时,数据仓库的主流趋势是 SQL On Hadoop。Cloudera, Hontornworks, MapR, Greenplum HAWQ, Facebook 的 Presto,算是百花齐放。但主创团队认为,RDBMS 不会消失,用户们会因为上云的趋势,想要一款完全适配云端的数据仓库。
文章简单介绍了市面上通常的 on-prem 分布式数据仓库的一些缺点。首先就是计算和存储硬件是耦合的,即每个服务器同时负责存储数据,并且执行 SQL 语句得到结果。耦合的劣势在于,不能针对不同的 workloads 做优化。二就是服务器的 node membership 改变(无论是因为服务器损坏,或者是因为数据量提升需要扩容)对用户来说都不友善。一,就是要进行大量数据的 reshuffle。二是,为了做到高可用,可能会保留一部分 node 作为 stand-by replica,当主节点有问题时,马上接替主节点,这相当于变相提高了数据成本。总结来说,on-prem 的数据仓库要做到同时保持可伸缩性(elasticity)和高可用性(availability)并兼顾成本,是很难鱼与熊掌兼得的。三就是对服务进行升级比较麻烦。
由于云服务的出现,很多上述的问题,变得不再是问题了。一就是,云服务通常会提供多种类型的服务器来针对特定的 usecase;二,服务器的下线,上线,扩容在云服务上都属于基本操作;三是,云上有高可用,低成本的存储系统;四是,服务更新非常方便。基于这些原因,Snowflake 选择了完完全全的计算和存储分离的架构设计。整个架构分成三个大模块:
在设计存储系统的时候,Snowflake 有纠结过,是应该使用 AWS 的 S3,还是自行设计类似于 HDFS 的存储系统。最终,在经过了各种比较,利弊权衡后,决定使用 S3。虽然,S3 的性能并不是最快;并且,由于是网络接入,也不是最稳定。但是,胜在高可用性和高可靠性上。团队决定基于 S3 打造数据存储系统,同时,可以把精力放在优化 local caching 和数据倾斜(skew resilience)上。
相对于本地文件系统,S3 的 access latency 会更高,并且,由于是网络接入(尤其是用 https),CPU 使用率也更高。而且,S3 本身就是一个简单的 blob 存储,支持的主要创建,删除和读取文件,即,不能对现有文件进行更新,更新相当于重新创建一个更新过的文件。但是,S3 的读取有一大好处在于,可以读取部分文件。
S3 的这些属性,对于整个 Snowflake 的数据存储和并行控制设计有重大的影响。首先,表数据被水平(horizontally partitioned)地切分成多个不可变的 blob 文件;每个文件通过列存(column-store)的形式保存数据,Snowflake 具体使用的存储格式是 PAX 的 Hybrid-column store(挖个坑,可以单独讲一期这个)。每个数据文件包含数据头用来存储元数据。基于 S3 的下载部分文件的 API,对于运行的 SQL 语句,优化器会选择只下载必须用到的数据 block 即可。这也就意味着所有snowflake的事务都是基于快照隔离Snapshot Isolation(SI)
值得一提的是,Snowflake 不单单使用 S3 来存储表数据文件,也用 S3 来存储临时生成的 intermediate result(语句执行中,某个 operator 产生的临时结果集)。一旦这些结果集的大小超过了本地磁盘空间,spill 到磁盘上的文件就会以 S3 的形式存储。这样的好处在于,可以让 Snowflake 真正可以处理巨大的数据而不用担心内存或者本地磁盘空间吃紧。另一个好处在于,这些临时结果集也可能被利用作为 cache 使用。
最后文中还提到了数据库的其他元数据存储,包括有哪些 caching 文件,每个表存在了哪些 S3 文件中,等等,都是存储在一个 transactional 的 key-value store 中,并不在 S3 里。
执行 SQL 语句:每个语句 instance 都只会运行在一个 VW 上;每个 VW 有多个 WN;每个 WN 只隶属于一个 VW,不会被共享。(这边有注解说,WN 变成共享的会是一个未来的工作,因为可以更好地提升使用率并且会进一步降低用户成本)。当一个语句被运行时,所有的 WN 在这个 VW 上,(或者也可能是一部分 WN,如果优化器认为这是一个非常轻量级的语句),都会起一个 worker process,这个进程的生命周期就是这句语句的执行周期。worker process ,在执行的过程中,不会对外部资源造成任何变化,换言之,no side effect,即使是 update 语句。为什么这么说呢,因为所有的表数据文件都是 immutable 的。这样带来的好处就是,如果 worker process 由于各种原因崩溃了, 通常只是需要 retry 即可,没有其他善后事宜要做。现在 VW 里还不支持 partial retry,这也在未来计划的工作中。
由于 VW 的可伸缩性(elasticity),通常情况下,可以通过起一个更大 size 的 VW 来提升语句的性能,但保持一样的使用成本。例如,**一个复杂的分析语句在一个 4 节点 VW 上需要运行 15 个小时,但在一个 32 节点 VW 上只需要 2 小时。**因为是云原生,用户只需要支付运行 VW 时的费用即可。因此,在价格不变的情况下,用户体验和查询速度却大幅度提升。这也是 Snowflake 云原生数据仓库的一大卖点。
数据库带来的新挑战:
Oracle RAC --> Greenplum --> HBase --> AnalyticDB
ADB主要是OLAP系统,同时要顾及各种点查询、优化的速度。底层采用盘古,所以数据库主要的创新点在数据格式、优化器、执行器等等
AnalyticDB主要分为以下几个部分:
为便于大规模分析处理,AnalyticDB对数据表进行分区。AnalyticDB数据表有两个分区级别:一级分区和二级分区。
选择具有较高基数(cardinality)的列作为一级分区键,以保证数据行能均匀地分布到每个一级分区,最大化并行。用户还可以根据需要定义二级分区,以便进行数据的自动管理。二级分区拥有最大分区数,当二级分区的实际数目超过了这个最大分区数后,最老的二级分区会被自动删除。通常,选择时间列(天、周或月)作为二级分区列,这样,包含相同时间序列的数据行,会被划分到同一个二级分区中。
传统OLAP系统在同一个链路上同时处理读写请求,因此,所有的并发读写请求都共享同一个资源池,也会互相影响。但是当读写并发同时非常大时,这种设计会由于过度的资源竞争而导致不好的性能。如图5所示,为了解决这个问题,同时确保读和写的高性能,AnalyticDB采用的架构为读写分离架构,即AnalyticDB有独立的读写节点各自处理读写请求,且写节点和读节点完全互相隔离。
AnalyticDB存储层采用Lambda架构,读节点上的数据包括基线数据和增量数据两部分。增量数据又分为Incremental Data和Deleted bitset,按照行列混存的架构存放在读节点的SSD上。真正读取是,basline数据要和增量数据做UNION和MINUS之后,才能输出有效数据。
对于每张表,每k行的数据组成一个Row Group。Row Group中的数据连续存放在磁盘中。整个Row Group中,又将数据按照列(聚集列)分别顺序存放。AnalyticDB会对每列构建一份元数据,用于维护列数据的统计信息(包括Cardinality、Sum和Min/Max等)、字典数据(采用字典编码)以及物理映射等。AnalyticDB默认会对每一列数据建立索引,索引中的Key是列的值,Value是值出现的所有行号集合,采用后台异步构建模式。由于增量数据部分没有索引,随着数据的不断实时写入,增量数据的查询性能会越来越慢。AnalyticDB采用后台任务来合并基线数据和增量数据形成一个新的基线数据,并基于新的基线数据构建全量索引。
使用copy on write技术(OLAP读多写少)来支持MVCC,delete数据被转化在Deleted bitset上,而update操作则被分为Incremental Data和Deleted bitset分别存放。每个写操作都会分配独立的LSN,从而达到MVCC
由于建立了全列倒排索引,所以执行引擎处理返回结果的时候用到了多路归并
由于没有全局索引,随着数据的不断实时写入,增量数据的查询性能会越来越慢。因此ADB会在后台通过伏羲启动一个MapReduce 任务来合并基线数据和增量数据(同时去掉标记为删除的数据)形成一个新的基线数据,并基于新的基线数据构建全量索引。
在合并任务开始时,一部分增量数据会标记为immutable,并执行合并,合并完成之后,之前的baseline data和immutable会被删除
在海量数据分析场景下,数据分析业务主要有以下三类workload:
在ADB的实现中,每K行数据实现了Row Group,每个row group中的每个列存放在自己的block中,Row group按照索引排列
为了应对ad-hoc,ADB对每列建立了倒排索引,从而提高复杂数据的查询效率。(每列都建立索引,不就是倒排索引了)
为了加速查询,AnalyticDB对每列构建一份元数据,并保存在一个叫detail_meta的单独文件中。detail_meta文件通常较小(小于1MB),首次查询时被加载在内存中。如图8左边所示,元数据主要包括4部分:
AnalyticDB设计和实现了一个新的索引引擎,在不影响写入性能的情况下,支持结构化和非结构化数据类型索引。它将构建过程从写入链路中移除,采用后台异步构建模式,支持对所有列构建索引,从而解决了OLAP任意查询的性能问题。
AnalyticDB默认对所有列构建索引,并保存在一个单独的文件中。与传统的数据库不同,AnalyticDB索引中的key是列的值,value是该值出现的所有行号集合,并支持所有的条件同时走索引查询。多个列的操作去做union或者intersect
AnalyticDB在索引引擎是实现上也做了大量的优化,包括:多路流式归并、索引选择CBO和索引结果缓存。
where id = 1 and 0 < x < 1000000
的情况下,id = 1
这个条件的选择率已经很高,则0<x<1000000
条件不走索引查询效率会更高。为了支持每秒千万的实时数据写入,避免同步构建索引影响实时写入的性能,AnalyticDB并没有采用同步构建索引的策略,而是采用异步后台进程构建索引的方式。索引引擎会根据时间或增量数据的大小来决定是否启动后台进程来构建索引。该后台进程读取Pangu上的历史全量数据和新写入的增量日志数据,完成数据合并形成新的全量数据,并对该全量数据重新构建索引。该过程通过伏羲的MapReduce任务执行,选择负载较低的机器执行,对用户完全透明。
创新性引入了两个关键功能:存储感知的优化和高效实时采样。因为ADB独特的索引结构和分布式的数据存储
在优化器之下,AnalyticDB在MPP架构基础上,采用流水线执行的DAG架构,构建了一个适用于低延迟和高吞吐量工作负载的执行器。AnalyticDB的列式执行引擎能够充分利用底层的行列混合存储。与行式执行引擎相比,当前的向量化执行引擎更加缓存友好,能避免将不必要的数据加载到内存中。
与许多 OLAP 系统一样,AnalyticDB在运行时利用代码生成器(CodeGen) 来提高 CPU 密集型计算的性能。AnalyticDB的CodeGen基于 ANTLR ASM来动态生成表达式的代码树。同时此 CodeGen 引擎还将运行时因素纳入考虑,让AnalyticDB能在Task级别利用异构新硬件的能力。例如,如果集群中CPU支持 AVX-512指令集,我们通过生成字节码使用SIMD来提高性能。在此之外,通过整合内部数据表示形式,在存储层和执行引擎之间,AnalyticDB是能够直接对序列化二进制数据进行操作,而不是Java 对象。这有助于消除序列化和去序列化的开销,这在大数据量shuffle时可能会节约20%以上的时间。
得益于流水线处理、全列索引、行列混存、运行时索引路径选择、K路归并、向量化执行引擎、CodeGen等优化机制,AnalyticDB获得了最优的TCP-H测试运行时间,并比Greenplum快了近2倍。
使用共享存储解决MySQL主从结构遇到的一系列问题
系统结构:
存储资源管理单元:
ParallelRaft与Raft最根本的不同在于,当某个entry提交成功时,并不意味着之前的所有entry都已成功提交。因此我们需要保证:
有了这两点,结合数据库或其他应用普遍存在的对存储I/O乱序完成的默认容忍能力,就可以保证它们在PolarFS上的正常运转,并获得PolarFS提供的数据可靠性。
ParallelRaft的乱序执行遵循如下原则:
容易知道,依照此原则完成的I/O不会违反传统存储语义的正确性。
后面说了一大堆,反正就是paxos,因为同一个raft上面,可能会有多个并行的事务,所以一定要乱序提交,乱序确认
PolarFS设计中采用了如下技术以充分发挥I/O性能:
PolarFS是共享访问的分布式文件系统,每个文件系统实例都有相应的Journal文件和与之对应的Paxos文件。Journal文件记录了metadata的修改历史,是共享实例之间元数据同步的中心。Journal文件逻辑上是一个固定大小的循环buffer。PolarFS会根据水位来回收journal。Paxos文件基于Disk Paxos实现了分布式互斥锁(文件锁,文件系统里的悲观锁,性能如何?)。
由于journal对于PolarFS非常关键,它们的修改必需被Paxos互斥锁保护。如果一个节点希望在journal中追加项,其必需使用DiskPaxos算法来获取Paxos文件中的锁。通常,锁的使用者会在记录持久化后马上释放锁。但是一些故障情况下使用者不释放锁。为此在Paxos互斥锁上分配有一个租约lease。其他竞争者可以重启竞争过程。当PolarFS当节点开始同步其他节点修改的元数据时,它从上次扫描的位置扫描到journal末尾,将新entry更新到memory cache中。
PolarFS的上述共享机制非常适合POLARDB一写多读的典型应用扩展模式。一写多读模式下没有锁争用开销,只读实例可以通过原子I/O无锁获取Journal信息,从而使得POLARDB可以提供近线性的QPS性能扩展。
由于PolarFS支持了基本的多写一致性保障,当可写实例出现故障时,POLARDB能够方便地将只读实例升级为可写实例,而不必担心底层存储产生不一致问题,因而方便地提供了数据库实例Failover的功能。(DBFS,单机高可用)
感觉这个系统从db到libpfs、到后端存储chunkserver,都有WAL…所以最底层做快照,libpfs可以恢复,然后上层的PolarDB也可以恢复。
对底层盘做快照而不是对上层db做快照有一个问题,就是对盘做快照的时候,当时正在执行的IO,其是否真正落盘了是UB的。PolarDB管这种快照叫做disk outage consistency snapshot
,在具体的实现上,如果做快照,PolarCtrl会通知PolarSwitch,在某个时间点的IO上打Tag,chunkserver收到对应的tag之后,说明这个tag时间的时间位点就是一个快照点。所以会先做快照,然后再处理打上tag的IO。这样,做快照的时间就和上层对应的某个事务的LSN联系起来了。
一、MySQL/InnoDB
通过Undo日志来实现事务的MVCC,由于只读节点跟读写节点属于不同的mysqld进程,读写节点在进行Undo日志Purge的时候并不会考虑此时在只读节点上是否还有事务要访问即将被删除的Undo Page,这就会导致记录旧版本被删除后,只读节点上事务读取到的数据是错误的。
针对该问题,PolarDB提供两种解决方式:
二、还有个问题,由于InnoDB BP刷脏页有多种方式,其并不是严格按照oldest modification来的,这就会导致有些事务未提交的页已经写入共享存储,只读节点读到该页后需要通过Undo Page来重建可见的版本,但可能此时Undo Page还未刷盘,这就会出现只读上事务读取数据的另一种错误。
针对该问题,PolarDB解决方法是:
如果读写节点把一个表删了,反映到存储上就是把文件删了。对于mysqld进程来说,它会确保删除期间和删除后不再有事务访问该表。但是在只读节点上,可能此时还有事务在访问,PolarFS在完成文件系统元数据同步后,就会导致只读节点的事务访问存储出错。
PolarDB目前的解决办法是:如果主库对一个表进行了表结构变更操作(需要拷表),在操作返回成功前,必须通知到所有的ReadOnly节点(有一个最大的超时时间),告诉他们,这个表已经被删除了,后续的请求都失败。当然这种强同步操作会给性能带来极大的影响,有进一步的优化的空间。
Change Buffer本质上是为了减少二级索引带来的IO开销而产生的一种特殊缓存机制。当对应的二级索引页没有被读入内存时,暂时缓存起来,当数据页后续被读进内存时,再进行应用,这个特性也带来的一些问题,该问题仅存在于StandBy中。例如Primary节点可能因为数据页还未读入内存,相应的操作还缓存在Change Buffer中,但是StandBy节点则因为不同的查询请求导致这个数据页已经读入内存,可以直接将二级索引修改合并到数据页上,无需经过Change Buffer了。但由于复制的是Primary节点的redo,且需要保证StandBy和Primary在存储层的一致性,所以StandBy节点还是会有Change Buffer的数据页和其对应的redo日志,如果该脏页回刷到存储上,就会导致数据不一致。
为了解决这个问题,PolarDB引入shadow page的概念,把未修改的数据页保存到其中,将Change Buffer记录合并到原来的数据页上,同时关闭该Mtr的redo,这样修改后的Page就不会放到Flush List上。也就是StandBy实例的存储层数据跟Primary节点保持一致。
ClickHouse拥有多种表引擎类型,在这众多的表引擎中,MergeTree是比较有代表性的引擎之一,被广泛使用。
MergeTree采用列式存储,类似LSM Tree的架构组织数据。数据导入时被划分为多个Part,每个Part对应一个目录。Part中包含各个列的数据,每个列都有独立的文件。后台会调度合并任务,将多个小的Part合并成更大的Part,类似LSM Tree的合并过程。 Part中包含几类文件:
MergeTree对于批量导入支持较好,对OLTP级事务更新仅有限支持。MergeTree存储引擎对数据实时可见要求非常高的场景是不太友好的。
TiFlash | AnalyticDB | ClickHouse | SqlServer | |
---|---|---|---|---|
存储结构 | Delta Tree,磁盘行列混存 | 增量 + 基线,磁盘行列混存 | MergeTree,磁盘列存 | Hekaton列存索引,内存行列混存 |
索引结构 | 主键索引 | 全列倒排索引 | 主键索引 + 二级索引 | 本身是行存的索引,可以利用行存的其他索引 |
数据更新方式 | MVCC事务隔离,支持TP型事务和批量导入 | MVCC事务隔离,支持TP型事务 | 批量导入友好,有限支持更新 | 与行存保持一致 |
数据压缩 | 通用压缩 | 字典压缩 | 通用压缩 | RLE等专用压缩 |
clickhouse极致的列存、查询优化,但是并发查询性能不佳,不支持事务等等,相比其他竞品(hadoop、impala。。。)做到了极致的查询性能
minmax
: 以index granularity
为单位,存储指定表达式计算后的min、max值;在等值和范围查询中能够帮助快速跳过不满足要求的块,减少IO。set(max_rows)
:以index granularity为单位,存储指定表达式的distinct value集合,用于快速判断等值查询是否命中该块,减少IO。ngrambf_v1
(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed):将string进行ngram分词后,构建bloom filter,能够优化等值、like、in等查询条件。tokenbf_v1
(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed): 与ngrambf_v1类似,区别是不使用ngram进行分词,而是通过标点符号进行词语分割。bloom_filter([false_positive])
:对指定列构建bloom filter,用于加速等值、like、in等查询条件的执行。PARTITION BY
子句,在建表时可以指定按照任意合法表达式进行数据分区操作,比如通过toYYYYMM()
将数据按月进行分区、toMonday()
将数据按照周几进行分区、对Enum类型的列直接每种取值作为一个分区等。总结来说,极致压缩,稀疏索引、数据分片
ClickHouse在计算层做了非常细致的工作,竭尽所能榨干硬件能力,提升查询速度。它实现了单机多核并行、分布式计算、向量化执行与SIMD指令、代码生成等多种重要技术。
Vectorized execution engine
),对内存中的列式数据,一个batch调用一次SIMD指令(而非每一行调用一次),不仅减少了函数调用次数、降低了cache miss,而且可以充分发挥SIMD指令的并行能力,大幅缩短了计算耗时。向量执行引擎,通常能够带来数倍的性能提升。runtime codegen
,动态地根据当前SQL直接生成代码,然后编译执行。如下图例子所示,对于Expression直接生成代码,不仅消除了大量的虚函数调用(即图中多个function pointer
的调用),而且由于在运行时表达式的参数类型、个数等都是已知的,也消除了不必要的if-else
分支判断。近年来ClickHouse发展趋势迅猛,社区和大厂都纷纷跟进使用。本文尝试从OLAP场景的需求出发,介绍了ClickHouse存储层、计算层的主要设计。ClickHouse实现了大多数当前主流的数据分析技术,具有明显的技术优势:
相比于开源社区的其他几项分析型技术,如Druid、Presto、Impala、Kylin、ElasticSearch等,ClickHouse更是一整套完善的解决方案,它自包含了存储和计算能力(无需额外依赖其他存储组件),完全自主实现了高可用,而且支持完整的SQL语法包括JOIN等,技术上有着明显优势。相比于hadoop体系,以数据库的方式来做大数据处理更加简单易用,学习成本低且灵活度高。当前社区仍旧在迅猛发展中,相信后续会有越来越多好用的功能出现。
shared-nothing,raft,很多mysql实现的功能还没实现。底层KV存储,TiBD主要负责和client对接,然后做优化,很多执行计划会下推到TiKV
我在想TiDB还是有很多问题的,首先TiDB的底座不是云原生的基础组件(类比snowflake polarDB ADB),很多问题上云之后就没法解决了
TiDB目前有两种存储节点,分别是 TiKV 和 TiFlash。TiKV 采用了行式存储,更适合 TP 类型的业务;而 TiFlash 采用列式存储,擅长 AP 类型的业务。TiFlash 通过 raft 协议从 TiKV 节点实时同步数据,拥有毫秒级别的延迟,以及非常优秀的数据分析性能。它支持实时同步 TiKV 的数据更新,以及支持在线 DDL。我们把 TiFlash 作为 Raft Learner 融合进 TiDB 的 raft 体系,将两种节点整合在一个数据库集群中,上层统一通过 TiDB 节点查询,使得 TiDB 成为一款真正的 HTAP 数据库。
TiFlash的列式存储引擎Delta Tree参考了B+ Tree和LSM Tree的设计思想。
大多数DBMS都是为写优化,C-store是第一个为读优化的OLTP数据库。C-Store 的主要贡献有以下几点:通过精心设计的 projection 同时实现列数据的多副本和多种索引方式;用读写分层的方式兼顾了(少量)写入的性能。此外,C-Store 可能是第一个现代的列式存储数据库实现,其的设计启发了无数后来的商业或开源数据库,就比如 Vertica。
在 C-Store 内部,逻辑表被纵向拆分成 projections,每个 projection 可以包含一个或多个列,甚至可以包含来自其他逻辑表的列(构成索引)。当然,每个列至少会存在于一个 projections 上。
Projection 内是以列式存储的:里面的每个列分别用一个数据结构存放。为了避免列太长引起问题,也支持每个 projection 以 sort key 的值做横向切分。
Projection 是有冗余性的,常常 1 个列会出现在多个 projection 中,但是它们的顺序也就是 sort key 并不相同,因此 C-Store 在查询时可以选用最优的一组 projections,使得查询执行的代价最小。
Apache ORC 最初是为支持 Hive 上的 OLAP 查询开发的一种文件格式,如今在 Hadoop 生态系统中有广泛的应用。ORC 支持各种格式的字段,包括常见的 int、string 等,也包括 struct、list、map 等组合字段;字段的 meta 信息就放在 ORC 文件的尾部(这被称为自描述的)。
ORC 里的 Stripe 就像传统数据库的页,它是 ORC 文件批量读写的基本单位。这是由于分布式储存系统的读写延迟较大,一次 IO 操作只有批量读取一定量的数据才划算。这和按页读写磁盘的思路也有共通之处。
Apache ORC 提供有限的 ACID 事务支持。受限于分布式文件系统的特点,文件不能随机写,那如何把修改保存下来呢?
类似于 LSM-Tree 中的 MVCC 那样,writer 并不是直接修改数据,而是为每个事务生成一个 delta 文件,文件中的修改被叠加在原始数据之上。当 delta 文件越来越多时,通过 minor compaction 把连续多个 delta 文件合成一个;当 delta 变得很大时,再执行 major compaction 将 delta 和原始数据合并。这种保持基线数据不变、分层叠加 delta 数据的优化方式在列式存储系统中十分常见,是一种通用的解决思路。
Dremel 是 Google 研发的用于大规模只读数据的查询系统,用于进行快速的 ad-hoc 查询,弥补 MapReduce 交互式查询能力的不足。为了避免对数据的二次拷贝,Dremel 的数据就放在原处,通常是 GFS 这样的分布式文件系统,为此需要设计一种通用的文件格式。
Dremel 的系统设计和大多 OLAP 的列式数据库并无太多创新点,但是其精巧的存储格式却变得流行起来,Apache Parquet 就是它的开源复刻版。注意 Parquet 和 ORC 一样都是一种存储格式,而非完整的系统。
Impala是Cloudera公司主导开发的新型查询系统,它提供SQL语义,能查询存储在Hadoop的HDFS和HBase中的PB级大数据。已有的Hive系统虽然也提供了SQL语义,但由于Hive底层执行使用的是MapReduce引擎,仍然是一个批处理过程,难以满足查询的交互性。相比之下,Impala的最大特点也是最大卖点就是它的快速。Impala完全抛弃了MapReduce这个不太适合做SQL查询的范式,而是像Dremel一样借鉴了MPP并行数据库的思想另起炉灶,因此可做更多的查询优化,从而省掉不必要的shuffle、sort等开销。
Impala与Hive类似不是数据库而是数据分析工具,集群有以下几类节点
Druid可以对多列数据构建倒排索引(bitmap-based inverted indexes)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。