• 产品与解决方案
  • 行业解决方案
  • 服务
  • 支持
  • 合作伙伴
  • 关于我们

H3C SeaSQL DWS 最佳实践-5W102

01-正文

本章节下载  (777.99 KB)

01-正文

  录

1 简介

1.1 SeaSQL DWS概述

1.2 SeaSQL DWS应用场景

2 SeaSQL DWS最佳实践摘要

2.1 数据模型

2.2 硬件资源与集群规模

2.3 Heap vs Append-Optimized存储

2.4 行存VS列存

2.5 数据库/表数量规格

2.6 压缩

2.7 分布

2.8 内存管理

2.9 分区

2.10 索引

2.11 资源队列

2.12 监控和维护

2.13 ANALYZE

2.14 Vacuum

2.15 数据加载

2.16 安全性

2.17 数据加密

2.18 高可用性

3 系统配置

3.1 配置时区

3.2 文件系统

3.3 端口配置

3.4 I/O配置

3.5 OS内存配置

3.6 共享内存设置

3.7 验证操作系统

3.8 每台主机上的Segment数量

3.9 Segment内存配置

3.10 资源队列语句内存配置

3.11 资源队列溢出文件配置

4 模式设计

4.1 数据类型

4.2 存储模型

4.3 压缩

4.4 分布

4.5 分区

5 采用资源组管理内存和资源

5.1 配置SeaSQL DWS数据库内存

5.2 配置资源组

5.3 低内存查询

5.4 管理工具和admin_group并行

6 采用资源队列管理内存和资源

6.1 解决内存不足错误

6.2 低内存查询

6.3 SeaSQL DWS数据库配置内存

6.4 配置资源队列

7 系统监控和维护

7.1 概述

7.1.1 监控

7.1.2 gpstate

7.1.3 gpcheckperf

7.1.4 用操作系统工具监控

7.1.5 最佳实践

7.2 用ANALYZE更新统计信息

7.2.1 有选择地生成统计信息

7.2.2 提升统计信息质量

7.2.3 何时运行ANALYZE

7.2.4 配置统计信息自动收集

7.3 在数据库中管理膨胀

7.3.1 检测膨胀

7.3.2 从数据库表移除膨胀

7.3.3 从索引移除膨

7.3.4 从系统目录移除膨胀

7.3.5 从追加优化表移除膨胀

8 装载数据

8.1 带列值的INSERT语句

8.2 COPY语句

8.3 外部表

8.4 使用gpfdist外部表

8.5 gpload

8.6 最佳实践

9 安全性

9.1 基础安全最佳实践

9.2 口令强度指导

10 加密数据和数据库连接

10.1 最佳实践

10.2 密钥管理

10.3 加密gpfdist连接

11 SQL查询调优

11.1 如何生成执行计划

11.2 如何阅读执行计划

11.3 优化SeaSQL DWS查询

11.4 SeaSQL DWS分组扩展

11.5 窗口函数

12 高可用性

12.1 磁盘存储

12.2 Master镜像

12.3 Segment镜像

12.4 双集群

12.5 备份和恢复

12.6 检测故障的Master和Segment实例


1 简介

本文介绍H3C SeaSQL DWS数据库的最佳实践。

最佳实践是指能持续产生比其他方法更好结果的方法或者技术,它来自于实战经验,并被证实了遵循这些方法可以获得可靠的预期结果。

·     本文最佳实践旨在通过利用所有可能的知识和技术为正确使用SeaSQL DWS提供有效参考,在设计、实现和使用SeaSQL DWS数据库时遵循哪些最佳实践。关于如何使用和实现SeaSQL DWS数据库特性,请参考《H3C SeaSQL DWS用户手册(独立形态)》。

·     本文概述SeaSQL DWS实践中最重要的特性。不涉及依赖于SeaSQL DWS具体特性的边缘用例,后者需要精通数据库特性和环境,包括SQL访问、查询执行、并发、负载和其他因素。

通过掌握这些最佳实践知识,会增加SeaSQL DWS集群在维护、支持、性能和可扩展性等方面的成功率。

1.1  SeaSQL DWS概述

SeaSQL DWS是一款基于PostgreSQL的分布式数据库,采用Shared-Nothing架构,其主机、操作系统、内存和存储都是自我控制,不存在共享。该数据库架构具有很强的横向扩展能力,并能够提供高效的并行处理能力,并支持SQL 92、SQL 99以及SQL 2003,同时支持窗口函数、cube、rollup等函数。因其具备PB级数据量的快速分析能力,现在金融、医疗、教育、交通等领域被广泛应用。

1.2  SeaSQL DWS应用场景

·     OLAP

SeaSQL DWS主要应用于AP分析型场景,对报表分析、未来预测等相关复杂查询有优异的表现。其自带ORCA优化器,可以满足客户各种复杂的即席查询业务。

·     低并发

由于天生的架构设计,SeaSQL DWS无法处理高并发业务,特别对于频繁的DML操作的并发业务,相对于传统的TP数据库,有较大的差距。也就是说SeaSQL DWS的强项在于分析,适合作为TP数据库的后端数据集市,虽然新版本对于简单的TP场景有较大的提升,但不建议其使用于频繁的DML并发业务场景中。

·     应用架构图

一般情况下,SeaSQL DWS的在解决方案中的使用角色如下:

图1-1 SeaSQL DWS使用场景

 


2 SeaSQL DWS最佳实践摘要

2.1  数据模型

SeaSQL DWS数据库是一个基于大规模并行处理(MPP)和无共享架构的分析型数据库。这种数据库的数据模式与高度规范化的事务性SMP数据库显著不同。

·     通过使用非规范化数据库模式,例如具有大事实表和小维度表的星型或者雪花模式,SeaSQL DWS在处理MPP分析型业务时表现优异。

·     跨表关联(JOIN)时字段使用相同的数据类型。

2.2  硬件资源与集群规模

为达到更高的性能,硬件资源对SeaSQL DWS非常重要,为了做到投入和性能的均衡,当进行集群规划时,考虑以下因素:

·     数据量

首先,查看集群规划总的原始数据量,然后应用合理的压缩比。在未使用压缩的情况下,需要具备两倍的原始数据;如果大表使用压缩存储方式,若想计算得到一个更精确的压缩率,请尝试以下方法之一:

¡     使用之前的经验值。

¡     在现有系统上安装SeaSQL DWS,导入一部分数据(约10%即可),确保导入的数据能够很好的代表所有数据。导入数据后,通过计算原有数据量大小及实际存储大小,得到一个压缩比。

·     数据增长

请根据实际的数据增长速率(比如每天产生数据量的多少)和数据的保留策略(数据库中保留数据的时间)来进行集群的配置设计。

·     负载

¡     查询的并发量:并发量越大,需要越多的内存。

¡     查询将运行的平均数据量。

以下表格中的服务器配置建议是基于2:1的压缩比来进行的,若有更好的压缩比,可以相应的减少节点数目。   

表2-1 服务器配置建议

原始数据大小

服务器配置建议

小于5TB

4个节点,每个节点配置:

磁盘:数据分区至少需要8块15k RPM 磁盘(每个节点需要1-2T的空间大小)。

处理器:8-15核双插槽CPU,主频不低于2.6GHz。

内存:128G。

5-10TB

7个节点,每个节点配置:

磁盘:数据分区至少需要8块15k RPM 磁盘(每个节点需要1-2T的空间大小)。

处理器:8-15核双插槽CPU,主频不低于2.6GHz。

内存:256G。

10-40 TB

4~5个节点,每个节点配置:

磁盘:22块10k RPM 磁盘。

处理器:12核双插槽CPU,主频2.6GHz。

内存:256-512G。

40 TB–1PB

5~100节点,每个节点配置:

磁盘:22块10k RPM 磁盘。

处理器:12核双插槽CPU,主频2.6GHz。

内存:256-512G。

大于1PB

联系技术支持获取配置建议。

 

说明

为获得更好的数据库性能,建议使用的最小内存为256G。

 

2.3  Heap vs Append-Optimized存储

·     若表和分区表需要进行迭代式的批处理或者频繁执行单个UPDATE、DELETE或INSERT操作,使用堆存储。

·     若表和分区表需要并发执行UPDATE、DELETE或INSERT操作,使用堆存储。

·     若表和分区表在数据初始加载后更新不频繁,且仅以批处理方式插入数据,则使用AO存储。

·     不要对AO表执行单个INSERT、UPDATE或者DELETE操作。

·     不要对AO表执行并发批量UPDATE或DELETE操作,但可以并发执行批量INSERT操作。

2.4  行存VS列存

·     若数据需要经常更新或者插入,则使用行存储。

·     若需要同时访问一个表的很多字段,则使用行存储。

·     对于通用或者混合型业务,建议使用行存储。

·     若查询访问的字段数目较少,或者仅在少量字段上进行聚合操作,则使用列存储。

·     若常常修改表的某一字段而不修改其他字段,则使用列存储。

2.5  数据库/表数量规格

·     只要数据库总容量没有超过1PB,数据库数量理论上没有数量限制,但是也不适宜创建过多,因为创建一个空的数据库本身也会占用部分存储资源。

·     数据库表的数量最多不要超过5W张表,因为每张表都会在系统表(比如pg_class)中插入一条该表的记录,过多数量的表会导致系统表变的比较大,会影响该数据库的查询速度;同时每张表都会在每个segment的数据目录下生成一个数据文件(如果是列存的话,文件数量会更多),过多数量的小文件会严重影响数据库的性能。

2.6  压缩

·     对于大AO表和分区表使用压缩,以提高系统I/O。

·     在字段级别配置压缩。

·     考虑压缩比和压缩性能之间的平衡。

2.7  分布

·     为所有表定义分布策略:要么定义分布键,要么使用随机分布。不建议使用缺省分布方式。

·     优先选择可均匀分布数据的单个字段做分布键。

·     不要选择经常用于WHERE子句的字段做分布键。

·     不要使用日期或时间字段做分布键。

·     分布键和分区键不建议使用同一字段。

·     对经常执行JOIN操作的大表,优先考虑使用关联字段做分布键,尽量做到本地关联,以提高性能。

·     尽可能避免数据倾斜,数据初始加载后或者每次增量加载后,检查数据分布是否均匀。

·     对于数据量较小的维度表,建议使用复制表,减少后续查询过程中的数据广播或者重分布代价。

2.8  内存管理

·     设置vm.overcommit_memory为2。

·     不要为操作系统的页设置过大的值。

·     使用gp_vmem_protect_limit,设置每个Segment实例可以分配的最大内存。

·     通过下面的公式计算gp_vmem_protect_limit的值:

gp_vmem–单个Segment host上SeaSQL DWS数据库可用的总内存

gp_vmem = ((SWAP + RAM) – (7.5GB + 0.05 * RAM)) / 1.7

其中SWAP是该主机的交换空间(以GB为单位),RAM是该主机的RAM(以GB为单位)

max_acting_primary_Segments–当Mirror Segment由于主机或者Segment失效而被激活时,能在一台主机上运行的Primary Segment的最大数量

gp_vmem_protect_limit(单位:MB)

gp_vmem_protect_limit = gp_vmem / acting_primary_Segments

·     在有大量工作文件(workfiles)被生成的场景下,用下面的公式计算gp_vmem:

gp_vmem = ((SWAP + RAM) – (7.5GB + 0.05 * RAM - (300KB * total_#_workfiles))) / 1.7

·     绝不能将gp_vmem_protect_limit设置得过高或者比系统的物理RAM大。

·     使用计算出的gp_vmem值来计算操作系统参数vm.overcommit_ratio。

vm.overcommit_ratio = (RAM - 0.026 * gp_vmem) / RAM

·     使用statement_mem控制每个Segment实例数据库中单个查询分配的内存量。

·     使用资源队列设置队列允许的当前最大查询数(ACTIVE_STATEMENTS)和允许使用的内存大小(MEMORY_LIMIT)。

·     不要使用默认的资源队列,为所有用户都分配资源队列。

·     根据负载和时间段,设置和队列实际需求相匹配的优先级(PRIORITY)。

·     保证资源队列的内存配额不超过gp_vmem_protect_limit。

·     动态更新资源队列配置以适应日常工作需要。

2.9  分区

·     只为大表设置分区,不要为小表设置分区。

·     仅在根据查询条件可以实现分区裁剪时使用分区表。

·     建议优先使用范围(Range)分区,否则使用列表 (List) 分区。

·     根据查询特点合理设置分区。

·     不要使用相同的字段即做分区键又做分布键。

·     不要使用默认分区。

·     不要使用多级分区,尽量创建少量的分区,每个分区的数据更多些。

·     通过查询计划的 EXPLAIN 结果来验证查询对分区表执行的是选择性扫描 (分区裁剪)

·     对于列存储的表,不要创建过多的分区,否则会造成物理文件过多:Physical files = Segments * Columns * Partitions。

2.10  索引

·     一般来说SeaSQL DWS数据库中索引不是必需的,但是不排除例外情况。

·     对高基数(cardinality)的列存表,如果需要遍历且查询选择性较高,则创建单列索引。

·     频繁更新的列不需要创建索引。

·     总是在加载数据前删除索引。在加载数据后,重新为该表创建索引。

·     优先使用B树索引。

·     不要为需要频繁更新的字段创建位图索引。

·     不要对唯一性列、基数(cardinality)非常高或者非常低的列创建位图索引。当列的基数(cardinality)较小时(100-100000个不同数据),位图索引效果最佳。

·     不要为事务性负载创建位图索引。

·     一般来说不要为分区表创建索引。如果需要创建索引,选择与分区键不同的字段。

2.11  资源队列

·     使用资源队列管理集群的负载。

·     将所有的角色都与一个用户定义的资源队列关联。

·     使用ACTIVE_STATEMENTS参数限制队列的成员可以并发运行的查询总数。

·     使用MEMORY_LIMIT参数限制资源队列中查询可以使用的内存总量。

·     根据负载及时间段动态调整资源队列。

2.12  监控和维护

·     安装SeaSQL DWS数据库前建议运行gpcheckperf,安装后定期运行。保存输出结果,随着时间推移对系统性能进行比较。

·     使用所有可用的工具,以了解系统不同负载下的表现。

·     检查任何不寻常的事件并确定原因。

·     通过定期运行执行计划监控系统查询活动,以确保查询处于最佳运行状态。

·     检查查询计划,以确定是否按预期使用了索引和进行了分区裁剪。

·     了解系统日志文件的位置和内容,定期监控日志文件,而不是在出现问题时才查看。

2.13  ANALYZE

·     确定是否真的需要执行ANALYZE。当gp_autostats_mode参数被设置为on_no_stats(缺省值)并且表没有分区时,不需要执行ANALYZE。

·     如果需要对大量的表执行ANALYZE,建议使用analyzedb命令。analyzedb可以并行并且增量地更新统计信息。

·     建议数据加载后即刻运行ANALYZE,但是大表ANALYZE会比较慢。

·     如果INSERT、UPDATE和DELETE等操作修改大量数据,建议运行ANALYZE。

·     执行CREATE INDEX操作后建议运行ANALYZE。

·     如果对大表ANALYZE耗时很久,则只对JOIN字段、WHERE、SORT、GROUP BY或HAVING字句的字段运行ANALYZE。

2.14  Vacuum

·     批量UPDATE和DELETE操作后建议执行VACUUM。

·     不建议使用VACUUM FULL。建议使用CTAS(CREATE TABLE...AS)操作,然后重命名表名,并删除原来的表。

·     对系统表定期运行 VACUUM,以避免系统表膨胀和在系统表上执行VACUUM FULL操作,目前每天凌晨3点30分进行系统表膨胀回收。

·     禁止杀死系统表的 VACUUM 任务。

2.15  数据加载

·     使用gpfdist进行数据的加载和导出。

·     随着段数据库个数的增加,并行性增加。

·     尽量将数据均匀地分布到多个ETL节点上。

¡     将非常大的数据文件切分成相同大小的块,并放在尽量多的文件系统上。

¡     一个文件系统运行两个gpfdist实例。

¡     在尽可能多的网络接口上运行gpfdist。

¡     使用gp_external_max_segs控制访问每个gpfdist服务器的段数据库的个数。

¡     建议gp_external_max_segs的值和gpfdist进程个数为偶数。

·     数据加载前删除索引;加载完后重建索引。

·     若数据加载失败,使用VACUUM回收空间。

2.16  安全性

·     妥善保护ssadmin账号,只有在必要的时候才能允许系统管理员访问它。

·     仅当执行系统维护任务(例如升级或扩容)时,管理员才能以ssadmin登录SeaSQL DWS集群。

·     限制具有SUPERUSER角色属性的用户数。SeaSQL DWS中,身为超级用户的角色会跳过所有访问权限检查和资源队列限制。

·     仅有系统管理员具有数据库超级用户权限。

·     严禁数据库用户以ssadmin身份登录,严禁以ssadmin身份执行ETL或者生产任务。

·     为有登录需求的每个用户都分配一个不同的角色。

·     考虑为每个应用或者网络服务分配一个不同的角色。

·     使用用户组管理访问权限。

·     保护好ROOT的密码。

·     对于操作系统密码,强制使用强密码策略。

·     确保保护好操作系统的重要文件。

2.17  数据加密

·     加密和解密数据会影响性能,仅加密需要加密的数据。

·     在生产系统中实现任何加密解决方案之前都要做性能测试。

·     如果需要加密SeaSQL DWS数据库磁盘文件,在部署数据库的时候开启透明加密。

·     SeaSQL DWS生产系统使用的服务器证书应由证书签名颁发机构(CA)签名,这样客户端可以验证服务器。如果所有客户端都是本地的,则可以使用本地CA。

·     如果客户端与SeaSQL DWS的连接会经过不安全的链路,则使用SSL加密。

·     数据的加密和解密都由数据库进程完成,为了避免传输明文数据,需要使用SSL加密客户端和数据库间的连接。

·     数据加载和导出时,使用gpfdists协议保护ETL数据安全。

2.18  可用性

·     使用8到24个磁盘的硬件RAID存储解决方案。

·     使用RAID1、RAID5或RAID6,以使磁盘阵列可以容忍磁盘故障。

·     为磁盘阵列配备热备磁盘,以便在检测到磁盘故障时自动开始重建。

·     在重建时通过RAID卷镜像防止整个磁盘阵列故障和性能下降。

·     定期监控磁盘利用率,并在需要时增加额外的空间。

·     定期监控段数据库倾斜,以确保在所有段数据库上数据均匀分布,存储空间均匀消耗。

·     配置备用主服务器(Standby Master),当主服务器发生故障时由备用主服务器接管,默认情况下安装过程中必须有备用主服务器。

·     建立监控系统,当主服务器发生故障时,可以通过系统监控应用或电子邮件发送通知。

·     为所有的Segment数据库配置镜像,默认安装情况下都会进行。

·     分配主段数据库和其镜像到不同的主机上,以防止主机故障。

·     使用gprecoverseg工具及时恢复故障段,并使系统返回最佳平衡状态。

·     考虑双集群配置,提供额外的冗余和查询处理能力。

·     除非SeaSQL DWS数据库的数据很容易从数据源恢复,否则定期备份。

·     如果备份保存在集群的本地存储系统上,则备份结束后,将文件迁移到其他的安全存储系统上。


3 系统配置

本章节主要描述配置SeaSQL DWS数据库集群主机的要求和最佳实践。通常使用root用户配置SeaSQL DWS数据库集群。

3.1  配置时区

SeaSQL DWS数据库会从存储在PostgreSQL内部的一个时区集合中选择一个时区使用。PostgreSQL中存储的可用时区全部取自于Internet Assigned Numbers Authority (IANA)时区数据库,一旦PostgreSQL的IANA数据库发生改变,SeaSQL DWS数据库也会随之更新它的可用时区列表。

SeaSQL DWS通过将用户定义的时区与PostgreSQL的时区进行匹配来选择自身的时区,如果用户时区没配置,则会采用操作系统主机时区。例如,当选择默认时区时,SeaSQL DWS会基于主机操作系统时区文件并根据算法来选择PostgreSQL的时区。如果系统时区包含闰秒信息,SeaSQL DWS数据库便不能用PostgreSQL的时区匹配到系统时区。这种情形下,SeaSQL DWS数据库会基于主机系统的相关信息来计算一个最佳的PostgreSQL时区匹配值。

作为最佳实践,应该配置SeaSQL DWS数据库和主机系统采用已知的被支持的时区。采用当前系统时区和SeaSQL DWS数据库时区文件(该信息可能自上次重启后已经从IANA数据库更新)来匹配,这样做可以设置好SeaSQL DWS数据库Master和Segment实例的时区,防止SeaSQL DWS数据库每次重启后都重新计算这个最佳匹配值。使用gpconfig工具设置和显示SeaSQL DWS数据库时区。例如,以下命令显示SeaSQL DWS数据库时区并设置时区为US/Pacific。

# gpconfig -s TimeZone

# gpconfig -c TimeZone -v 'US/Pacific'

修改时区后必须重启SeaSQL DWS数据库。重启SeaSQL DWS数据库的命令为gpstop -ra。系统视图 pg_timezone_names提供SeaSQL DWS数据库时区相关的信息。

3.2  文件系统

XFS是SeaSQL DWS数据库数据目录的最佳实践文件系统。XFS应该用下列选项挂载:

rw,nodev,noatime,nobarrier,inode64

3.3  端口配置

ip_local_port_range应该被设置为不与SeaSQL DWS数据库端口范围冲突。例如,/etc/sysctl.conf文件中的设置:

net.ipv4.ip_local_port_range = 20000  65535

客户可以设置SeaSQL DWS数据库基础端口号为下列值。

PORT_BASE = 16000

MIRROR_PORT_BASE = 17000

3.4  I/O配置

在含有数据目录的设备上,blockdev预读尺寸应该被设置为16384。下列命令设置/dev/sdb的预读值大小。

# /sbin/blockdev --setra 16384 /dev/sdb

下列命令显示/dev/sdb的预读值大小。

/sbin/blockdev --getra /dev/sdb

16384

应该为所有数据目录设备设置死线IO调度器。

# cat /sys/block/sdb/queue/scheduler

noop anticipatory [deadline] cfq

应该在/etc/security/limits.conf文件中增加OS文件和进程的最大数量。

* soft  nofile 65536

* hard  nofile 65536

* soft  nproc 131072

* hard  nproc 131072

让内核文件输出到一个已知的位置并且确保limits.conf允许输出内核文件。

kernel.core_pattern = /var/core/core.%h.%t

# grep core /etc/security/limits.conf 

* soft  core unlimited

3.5  OS内存配置

Linux中sysctl的变量vm.overcommit_memory和vm.overcommit_ratio反映了操作系统管理内存分配的方式。这些变量应该按照下面的方式设置:

·     vm.overcommit_memory决定OS用于确定为进程可以分配多少内存的方法。这个变量应该总是被设置为2,它是对数据库唯一安全的设置。

·     vm.overcommit_ratio是被用于应用进程的RAM的百分数。这个变量可设置成95为这一变量计算最优值的公式可见Segment内存配置。

说明

设置过程中,不要启用操作系统的中的大页。

 

3.6  共享内存设置

SeaSQL DWS数据库使用共享内存在postgres进程之间通信,这些进程是同一个postgres实例的组成部分。下面的共享内存设置应该在sysctl中设定并且很少会被修改。

kernel.shmmax值可通过下面命令获取:

echo $(expr $(getconf _PHYS_PAGES) / 2)

kernel.shmmni 一般设置为 4096

kernel.shmall的值可通过下面命令获得:

echo $(expr $(getconf _PHYS_PAGES) / 2 \* $(getconf PAGE_SIZE))

3.7  验证操作系统

运行gpcheckperf以验证操作系统配置。

3.8  每台主机上的Segment数量

每台Segment主机上执行的Segment数量的确定对总体系统性能有着巨大的影响。这些Segment以及主机上的其他进程共享该主机的CPU核心、内存和网络接口。过高估计一台服务器能容纳的Segment数量是导致非最优性能的常见原因。

在选择每台主机上运行多少Segment时必须要考虑的因素包括:

·     核心数量

·     安装在该服务器上的物理RAM容量

·     NIC数量

·     附加到服务器的存储容量

·     主Segment和镜像Segment的混合

·     将在主机上运行的ETL进程

·     运行在主机上的非SeaSQL DWS进程

3.9  Segment内存配置

gp_vmem_protect_limit服务器配置参数指定单个Segment的所有活动postgres进程在任何给定时刻能够消耗的内存量。查询一旦超过该值则会失败。可使用下面的计算方法为gp_vmem_protect_limit估计一个安全值。

(1)     使用这个公式计算gp_vmem(SeaSQL DWS数据库可用的主机内存):

gp_vmem = ((SWAP + RAM) – (7.5GB + 0.05 * RAM)) / 1.7

其中SWAP是主机的交换空间(以GB为单位)而RAM是主机上安装的 内存(以GB为单位)。

(2)     计算max_acting_primary_Segments。当镜像Segment由于集群中其他主机上的 Segment或者主机故障而被激活时,这是能在一台主机上运行的主Segment的最大数量。例如,对于布置在每台主机有8个主Segment的四主机块中的镜像来说,单一Segment主机失效将会在其所在块中剩余每台主机上激活2个或者3个镜像Segment。这一配置的max_acting_primary_Segments值是11(8个主Segment外加3个故障时激活的镜像)。

通过将总的SeaSQL DWS数据库内存除以活动主Segment的最大数量来计算gp_vmem_protect_limit:

gp_vmem_protect_limit = gp_vmem / max_acting_primary_Segments

转换成兆字节就是gp_vmem_protect_limit系统配置参数的设置。

对于有大量工作文件产生的场景,可调整gp_vmem的计算以增加工作文件条件:

gp_vmem = ((SWAP + RAM) – (7.5GB + 0.05 * RAM - (300KB * total_#_workfiles))) / 1.7

有关监控和管理工作文件使用的信息请见SeaSQL DWS数据库的联机帮助。

用户可以根据gp_vmem的值计算操作系统参数 vm.overcommit_ratio的值:

vm.overcommit_ratio = (RAM - 0.026 * gp_vmem) / RAM

3.10  资源队列语句内存配置

注意

·     在集群中增加Segment主机无助于内存不足错误,除非用户使用额外的主机来减少每台主机上的Segment数量。

·     当不能提供足够的内存来映射所有的输出时,才会创建溢出文件。通常发生在缓存空间占据达到80%以上。

 

statement_mem服务器配置参数是分配给Segment数据库中任何单个查询的内存量。如果一个语句要求额外的内存,它将溢出到磁盘。用下面的公式计算statement_mem的值:

(gp_vmem_protect_limit * .9) / max_expected_concurrent_queries

例如:如果gp_vmem_protect_limit被设置为8GB(8192MB),对于40个并发查询, statement_mem的计算可以是:(8192MB * .9) / 40 = 184MB。在每个查询被溢出到磁盘之前,它被允许使用184MB内存。

实现安全地增加statement_mem的值,用户必须增加gp_vmem_protect_limit或者减少并发的查询数量。要增加gp_vmem_protect_limit,用户必须增加物理RAM或者交换空间,也可以减少每台主机上的Segment数量。

3.11  资源队列溢出文件配置

如果查询没有被分配足够的内存,SeaSQL DWS数据库会在磁盘上创建溢出文件(也被称为工作文件)。默认单个查询可以创建不超过100,000个溢出文件,这对大部分查询来说都是足够的。

用户可以用配置参数gp_workfile_limit_files_per_query控制每个查询和每个Segment创建的溢出文件最大数量。设置该参数为0将允许查询创建无限个溢出文件。限制允许的溢出文件数量可以防止失控的查询损坏系统。

如果一个查询没有被分配足够的内存或者被查询数据中存在数据倾斜,查询可能会生成大量溢出文件。如果查询创建超过指定数量的溢出文件,SeaSQL DWS数据库会返回这个错误:

ERROR: number of workfiles per query limit exceeded

在增加gp_workfile_limit_files_per_query的值之前,尝试通过更改查询、改变数据分布或者更改内存配置来降低溢出文件的数量。

gp_toolkit模式包括一些视图可以允许用户查看所有正在使用溢出文件的查询的信息。这些信息可以被用来排查故障以及查询调优:

·     gp_workfile_entries:视图中包含当前在某个Segment上使用工作文件的操作。

·     gp_workfile_usage_per_query:视图包含当前在某个Segment上使用工作文件的查询。

·     gp_workfile_usage_per_Segment:视图为包含Segment信息。每一行显示当前在该Segment上用于工作文件的磁盘空间总量。

·     gp_workfile_compression:该配置参数指定是否压缩溢出文件。默认设置为off。启用压缩可以提高文件溢出时的性能。


4 模式设计

本章节主要介绍设计SeaSQL DWS数据库模式的最佳实践。

SeaSQL DWS数据库是一种分析型的shared-nothing数据库,它和高度规范化的事务型数据库有很大不同。SeaSQL DWS数据库使用非规范化的模式设计会工作得最好,例如带有大型事实表和多个较小维度表的星形模式或者雪花模式。

4.1  数据类型

·     使用一致的数据类型

为表之间的连接列使用相同的数据类型。如果数据类型不同,SeaSQL DWS数据库必须动态地转换其中一列的数据类型,这样数据值才能被正确地比较。出于这种考虑,用户可能需要增加数据类型尺寸以便与其他常用对象进行连接操作。

·     选择使用最小空间的数据类型

通过选择最有效的数据类型存储数据,用户可以增加数据库容量并且改进查询执行能力。

使用TEXT或者VARCHAR而不是CHAR。在这些字符数据类型之间没有性能差异,但使用TEXT或VARCHAR 能够降低使用的存储空间。

使用能容纳数据的最小数字数据类型。对适合于INT或SMALLINT 的数据使用BIGINT会浪费存储空间。

4.2  存储模型

在创建表时,SeaSQL DWS数据库提供了一些存储选项。非常有必要了解何时使用堆存储或追加优化(AO)存储,还有何时使用行存储或列存储。在堆和AO以及行和列之间做出正确的选择对于大型的事实表来说极其重要,但是对于小的维度表就不那么重要。

确定存储模型的最佳实践是:

(1)     设计并且构建一种只插入的模型,在加载前截断每天的分区。

(2)     对于大型的分区事实表,为不同的分区评估并且使用最优的存储选项。一种存储选项对于整个分区大表来说并不一定都是合适的。例如,我们在使用分区表的过程中,可以设置有些分区为行存而其他分区为列存。

(3)     在使用面向列的存储时,每一列在每个SeaSQL DWS数据库Segment上都是一个单独的文件。对于具有大量列的表,对经常访问的数据(热数据)考虑列存,对不经常访问的数据(冷数据)考虑行存储。

(4)     存储选项应该在分区级别或者数据被存储的级别设置。

(5)     如果需要,压缩大型表以提升I/O性能并且在集群中腾出空间。

1. 堆存储还是追加优化存储

堆存储是默认存储模型,并且是PostgreSQL为所有数据库表使用的模型。为频繁进行UPDATE、 DELETE以及单个INSERT操作的表和分区使用堆存储。为将收到并发UPDATE、DELETE以及INSERT操作的表和分区使用堆存储。

为初始装载后就很少被更新并且后续只会以批操作执行插入的表和分区使用追加优化存储。绝不能在追加优化表上执行单个INSERT、UPDATE或者DELETE操作。并发的批量INSERT操作可以被执行但是绝不执行并发的批量UPDATE或者DELETE操作。

追加优化表中被更新和删除的行所占用的空间不会像堆表那样被有效地回收及重用,因此追加优化存储模型不适合频繁更新的表。它的设计目标是用于一次装载、很少更新且频繁进行分析查询处理的大型表。

2. 行存还是列存

按行存储数据是传统的存储数据库元组的方式。组成一行的列被连续地存储在磁盘上,因此整个行可以被以单次I/O的形式从磁盘上读出。

·     面向列的存储方式把列值在磁盘上存在一起。对每一列都会创建一个单独的文件。如果表被分区,则会对每个列和分区的组合创建一个单独的文件。当从一个有很多列的列存表中查询少量列时,I/O代价会比行存表要减少很多,因为不必从磁盘上检索没有被引用的列。

¡     对于包含要求更新并且频繁执行插入的事务的交易型负载,推荐使用面向行的存储。当对表的选择很宽(即查询中需要单个行的很多列)时,应该使用面向行的存储。

¡     如果大部分列出现在查询的SELECT列表或者WHERE子句中,请使用行存储。对一般目的或者混合负载使用面向行的存储,因为它能提供灵活性和性能的最佳组合。

·     面向列的存储是为了读操作而优化,但它并未对写操作优化,一行的列值必须被写入到磁盘上的不同位置。对于有很多列的大型表,当查询中只访问列的一个小集合时,列存表可以提供最优查询性能。

·     列存的另一个好处是,同一种数据类型的值集合可以用比混合类型值集合更少的空间存储在一起,因此列存表比行存表使用的磁盘空间更少(进而导致需要更少的磁盘I/O)。列存表的压缩效果也比行存表更好。

对于数据仓库的分析型负载,其中的选择很窄或者在少量列上计算数据聚集,请使用面向列的存储。对于定期更新单个列但不修改行中其他列的表,使用面向列的存储。在一个很宽的列存表中读取一个完整的行比在行存表中读取同样一行需要更多时间。每个列都是SeaSQL DWS数据库中每个Segment上的一个单独物理文件。

4.3  压缩

SeaSQL DWS数据库提供了多种选项以压缩追加优化表和分区。压缩存储允许在每次从磁盘中读取更多的数据,这样就能提高系统的I/O。

【注意】被增加到分区表的新分区不会自动继承表级定义的压缩,在增加新分区时,用户必须明确地定义压缩。

Run-length encoding (RLE)压缩提供了最好的压缩级别。较高的压缩级别通常会使数据在磁盘上更加紧凑的存储,但是写入时的数据压缩和读取时的数据解压会要求额外的时间和CPU周期。排序数据并且结合多种压缩选项可以实现最高的压缩级别。

绝不要对存储在压缩文件系统上的数据使用数据压缩。

测试不同的压缩类型和排序方法以确定对用户特定数据的最佳压缩方式。例如,客户可以从ZSTD 8级或9级压缩开始,然后调整参数达到最理想的结果。RLE压缩在存储文件中包含大量重复数据时工作效果最好。

4.4  分布

能让数据被均匀分布的最优分布方式是SeaSQL DWS数据库使用过程中的一个重要因素。在一个MPP无共享环境中,一个查询的总体响应时间由所有Segment的完成时间度量。整个系统的响应速度和最慢的Segment正相关。如果数据发生倾斜,拥有更多数据的Segment将需要更多时间完成,因此每一个Segment必须有大约相同数据量的行并且执行大概相同量级的处理。如果一个Segment比其他Segment有明显更多的数据要处理,将会导致很差的性能和内存不足的情况。

在决定分布策略时,考虑下列最佳实践:

·     为所有的表明确定义一个分布列或者随机分布。不要使用默认分布。

·     理想情况下,使用单个将数据在所有Segment之间均匀分布的列作为分布列。

·     不要将查询的WHERE子句中将要使用的列作为分布列。

·     不要在日期或者时间戳上分布。

·     分布键列数据最好含有唯一值或者非常高的可辨别性。

·     如果单个列无法实现均匀分布,则使用多列分布键,但最好不要超过两列。额外的列值通常不会得到更均匀的分布,而且它们要求额外的哈希处理时间。

·     如果两个列的分布键无法实现数据的均匀分布,则使用随机分布。大部分情况中的多列分布键都要求移动操作来连接表,因此它们对于随机分布来说没有优势。

SeaSQL DWS数据库的随机分布不是循环的,因此无法保证每个Segment上的记录数相等。随机分布通常会落在变化低于10个百分点的目标范围中。

在连接大型表时,最优分布非常关键。为了执行连接,匹配的行必须位于同一个Segment上。如果数据没有按照同一个连接列分布,其中一个表中需要的行会被动态重新分布到其他Segment上。在一些情况下会执行一次广播移动而不是执行重新分布移动,在这种情况下每个Segment都会重新对数据进行哈希操作并根据哈希键将对应的行发送到合适的Segment上。

1. 本地(局内)连接

使用在所有Segment之间均匀分布的表,这样哈希分布情况下的本地连接能够提供非常好的性能。当被连接的行在同一个Segment上时,很多处理都可以在该Segment实例内完成,这被称为本地或者局内连接。本地连接能使得数据移动的最小化,每一个Segment都独立于其他Segment操作,不需要Segment之间的网络流量或通信。

为了在常被连接在一起的大型表上实现本地连接,请在相同的列上分布这些表。本地连接要求连接的两边都被按照相同的列(以及相同的顺序)分布并且连接表时使用分布子句中的所有列。分布列最好是同样的数据类型——虽然一些具有不同数据类型的值看起来有相同的表现形式,但它们的存储方式不同并且会被哈希为不同的值,因此它们会被存放在不同的Segment上。

2. 数据倾斜

数据倾斜通常是慢查询和内存不足的根源。倾斜的数据会影响扫描(读取)性能,从而它会影响所有其他相关的执行操作,例如执行的连接和分组。

有必要验证分布以确保数据在初始装载之后被均匀地分布。在增量装载之后继续验证分布也同等重要。

下列查询显示每个Segment的行数以及与最大、最小行数之间的差异:

SELECT 'Example Table' AS "Table Name", max(c) AS "Max Seg Rows", min(c) AS "Min Seg Rows",    (max(c)-min(c))*100.0/max(c) AS "Percentage Difference Between Max & Min" FROM (SELECT count(*) c, gp_Segment_id FROM facts GROUP BY 2) AS a;

facts: 要查询的用户表名称

gp_toolkit模式有两个可以用来检查倾斜的视图。

·     gp_toolkit.gp_skew_coefficients视图通过计算存储在每个Segment上的数据的变异系数(CV)来显示数据分布倾斜。skccoeff列显示变异系数(CV),它由标准偏差除以均值算出。它同时考虑均值和围绕一个数据序列的均值的变化性。值越低,情况就越好。值越高表明数据倾斜越严重。

·     gp_toolkit.gp_skew_idle_fractions视图通过计算一次表扫描期间系统空闲的百分数来显示数据分布倾斜,这种数据是计算性倾斜的指示器。siffraction列显示在一次表扫描期间系统处于空闲的百分数。这是一种非均匀数据分布或者查询处理倾斜的指示器。例如,值为0.1表示10%的倾斜,值为0.5表示50%的倾斜等等。如果表的倾斜超过10%,就应该重新评估其分布策略。

3. 处理倾斜

当不成比例的数据量流入一个或者少数Segment并被它们处理时,处理倾斜就会发生。它常常就是SeaSQL DWS数据库性能和稳定性问题的根本原因。它可能随着连接、排序、聚集和多种OLAP操作而发生。查询倾斜在查询执行时才会发生,因此并不如数据倾斜那么容易检测,数据倾斜由于错误的分布键选择导致的非均匀数据分布而产生。数据倾斜存在于表 级别,因此它可以被很容易地被检测到并且通过选择最优的分布键来解决。

如果单个Segment故障(也就是说并非主机上所有Segment失效),可能就会是一个处理倾斜问题。当前确定处理倾斜还是一种手工处理。首先查看溢出文件,如果有倾斜但还不足以导致溢出,这将不会成为一种性能问题。如果使用者确定倾斜存在,接着查找对该倾斜负责的查询。下面是这个处理过程要使用的步骤和命令(请相应地更改传递给gpssh 的主机文件名之类的东西):

(1)     查找要在其中监控倾斜处理的数据库的OID:

SELECT oid, datname FROM pg_database;

其输出的例子:

  oid  |  datname

-------+-----------

 17088 | ssadmin

 10899 | postgres

     1 | template1

 10898 | template0

 38817 | pws

 39682 | gpperfmon

(6 rows)

(2)     运行一个gpssh命令以在系统中所有的Segment节点间检查文件尺寸。把<OID>用前一个命令中得到的数据库OID替换:

[ssadmin@mdw kend]$ gpssh -f ~/ hostfile_segments -e \

    "du -b /opt/ss_mpp/data /primary/gpseg*/base/<OID>/pgsql_tmp/*" | \

    grep -v "du -b" | sort | awk -F" " '{ arr[$1] = arr[$1] + $2 ; tot = tot + $2 }; END \

    { for ( i in arr ) print "Segment node" i, arr[i], "bytes (" arr[i]/(1024**3)" GB)"; \

    print "Total", tot, "bytes (" tot/(1024**3)" GB)" }' -

其输出的例子:

Segment node[sdw1] 2443370457 bytes (2.27557 GB)

Segment node[sdw2] 1766575328 bytes (1.64525 GB)

Segment node[sdw3] 1761686551 bytes (1.6407 GB)

Segment node[sdw4] 1780301617 bytes (1.65804 GB)

Segment node[sdw5] 1742543599 bytes (1.62287 GB)

Segment node[sdw6] 1830073754 bytes (1.70439 GB)

Segment node[sdw7] 1767310099 bytes (1.64594 GB)

Segment node[sdw8] 1765105802 bytes (1.64388 GB)

Total 14856967207 bytes (13.8366 GB)

如果在磁盘使用上有显著且持续的差别,那么应该研究正在被执行的查询看看有没有倾斜(上面的输出例子并未表明明显的倾斜)。在被监控的系统中,总是会有一点倾斜,但通常它们是短暂的并且只会持续很短的时间。

(3)     如果显著且持久的倾斜出现,下一个任务就是确定导致问题的查询。

前一步的命令已经摘要了整个节点。这一次,要找到实际的Segment目录。使用者可以从Master或者通过登入前一步的特定节点来做这些操作。下面是一个从Master运行的例子。

这个例子专门地查找排序文件。并非所有的溢出文件或者倾斜情况都由排序文件导致,因此使用者需要自定义这个命令:

$ gpssh -f ~/hostfile_segments -e \

    "ls -l /opt/ss_mpp/data/primary/gpseg*/base/19979/pgsql_tmp/*" \

    | grep -i sort | awk '{sub(/base.*tmp\//, ".../", $10); print $1,$6,$10}' | sort -k2 -n  

下面是来自这个命令的输出:

[sdw1] 288718848      /opt/ss_mpp/data/primary/gpseg2/.../pgsql_tmp_slice0_sort_17758_0001.0[sdw1] 291176448     /opt/ss_mpp/data/primary/gpseg5/.../pgsql_tmp_slice0_sort_17764_0001.0[sdw8] 924581888     /opt/ss_mpp/data/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0010.9[sdw4] 980582400     /opt/ss_mpp/data/primary/gpseg18/.../pgsql_tmp_slice10_sort_29425_0001.0[sdw6] 986447872     /opt/ss_mpp/data/primary/gpseg35/.../pgsql_tmp_slice10_sort_29602_0001.0...[sdw5] 999620608     /opt/ss_mpp/data/primary/gpseg26/.../pgsql_tmp_slice10_sort_28637_0001.0[sdw2] 999751680     /opt/ss_mpp/data/primary/gpseg9/.../pgsql_tmp_slice10_sort_3969_0001.0[sdw3] 1000112128     /opt/ss_mpp/data/primary/gpseg13/.../pgsql_tmp_slice10_sort_24723_0001.0[sdw5] 1000898560     /opt/ss_mpp/data/primary/gpseg28/.../pgsql_tmp_slice10_sort_28641_0001.0...[sdw8] 1008009216     /opt/ss_mpp/data/primary/gpseg44/.../pgsql_tmp_slice10_sort_15671_0001.0[sdw5] 1008566272     /opt/ss_mpp/data/primary/gpseg24/.../pgsql_tmp_slice10_sort_28633_0001.0[sdw4] 1009451008     /opt/ss_mpp/data/primary/gpseg19/.../pgsql_tmp_slice10_sort_29427_0001.0[sdw7] 1011187712     /opt/ss_mpp/data/primary/gpseg37/.../pgsql_tmp_slice10_sort_18526_0001.0[sdw8] 1573741824     /opt/ss_mpp/data/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0001.0[sdw8] 1573741824     /opt/ss_mpp/data/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0002.1[sdw8] 1573741824     /opt/ss_mpp/data/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0003.2[sdw8] 1573741824     /opt/ss_mpp/data/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0004.3[sdw8] 1573741824     /opt/ss_mpp/data/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0005.4[sdw8] 1573741824

/opt/ss_mpp/data/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0007.6[sdw8] 1573741824     /opt/ss_mpp/data/primary/gpseg45/.../pgsql_tmp_slice10_sort_15673_0008.7[sdw8] 1573741824

扫描这一输出将会揭示出主机sdw8上的名为gpseg45的Segment是罪魁祸首。

(4)     用ssh登入导致问题的节点并且成为root。使用lsof命令查找 拥有排序文件的进程的PID:

[root@sdw8 ~]# lsof /opt/ss_mpp/data/primary/gpseg45/base/19979/pgsql_tmp/pgsql_tmp_slice10_sort_15673_0002.1

COMMAND  PID    USER    FD   TYPE DEVICE  SIZE        NODE        NAME

postgres 15673  ssadmin 11u  REG  8,48    1073741824  64424546751 /opt/ss_mpp/data/primary/gpseg45/base/19979/pgsql_tmp/pgsql_tmp_slice10_sort_15673_0002.1

The PID 15673也是文件名的一部分,但并不总是这样。

(5)     用该PID作为参数运行ps命令以确定数据库和连接信息:

[root@sdw8 ~]# ps -eaf | grep 15673

ssadmin  15673 27471 28 12:05 ?        00:12:59 postgres: port 16003, sbaskin bdw

        172.28.12.250(21813) con699238 seg45 cmd32 slice10 MPPEXEC SELECT

root     29622 29566  0 12:50 pts/16   00:00:00 grep 15673

(6)     在Master上,在pg_log日志文件中查找上一个命令中的用户(sbaskin)、连接(con699238)以及命令编号(cmd32)。日志文件中含有这三个值的行应该是包含该查询的行,但偶尔命令编号可能会略有不同。例如,ps输出可能显示cmd32,但在日志文件中是cmd34。如果该查询仍在运行,该用户和连接的最后一个查询就是导致该问题的原因。

对于处理倾斜的纠正几乎都是重写该查询。创建临时表可以消除倾斜。临时表可以被随机地分布以强制一种两阶段的聚集。

4.5  分区

一种好的分区策略可以通过只读取满足查询所需的分区来降低被扫描的数据量。

每个分区在每一个Segment上都是一个单独的物理文件或文件集合(这种情况出现在列存表上)。就像在宽列存表中读取一整行比从堆表读取同一行需要更多时间一样,在分区表中读取所有分区比从非分区表中读取相同的数据要求更多的时间。

下面是分区的最佳实践:

·     只分区大型表,不要分区小型表。

·     仅当可以基于查询条件实现分区消除(分区裁剪)并且可以基于查询谓词对表分区来完成分区消除时才在大型表上使用分区。无论何时,优先使用范围分区而不是列表分区。

·     只有当查询中where选择条件包含表的分区列使用不可变操作符(例如=、<、<=、>、>=以及<>)时,查询规划器才能有选择地扫描分区表。

·     选择性扫描会识别STABLE和IMMUTABLE函数,但是不识别查询中的VOLATILE函数。例如,

date > CURRENT_DATE

之类的WHERE子句会导致查询规划器选择性地扫描分区表,但time > TIMEOFDAY之类的WHERE子句却不行。有必要通过EXPLAIN检查执行计划来验证查询是否选择性地扫描分区表(分区被裁剪)。

·     不要使用默认分区。默认分区总是会被扫描,但是更重要的是,在很多情况下它们会被填得太满导致糟糕的性能。

·     绝不在相同的列上对表分区和分布。

·     不要使用多级分区。虽然支持子分区,但并不推荐使用这种特性,因为通常子分区包含很少的数据或者不包含数据。分区或者子分区数量增加时性能也增加简直就是天方夜谭。维护很多分区和子分区的管理工作很可能将会超过得到的性能收益。为了性能、可扩展性以及可管理性,请在分区扫描性能和总体分区数量之间做出平衡。

·     谨防对列式存储使用太多分区。

·     考虑负载并发性以及为所有并发查询打开并且扫描的平均分区数。

1. 分区和列存文件的数量

SeaSQL DWS数据库支持的最大文件数量的唯一硬限制是操作系统的文件句柄数。但是,有必要考虑集群中文件的总数、 每个Segment上的文件数以及一台主机上的文件总数。在一个MPP无共享环境中,每一个节点都独立于其他节点操作。每个节点受到其磁盘、CPU和内存的约束。CPU和I/O约束对SeaSQL DWS数据库并不常见,但内存常常是一种限制因素,因为查询执行模型会在内存中优化查询性能。

每个Segment上的最优文件数也基于该节点上的Segment数量、集群的大小、SQL访问、并发、负载和倾斜等因素而变化。通常在每台主机上有六到八个Segment,大的集群中每台主机可能有很少的Segment。当使用分区和列存时,更重要的是考虑每个Segment的文件数和节点上的文件总数。

例子,DCA V2每节点64GB内存

·     节点数:16

·     每节点的Segment数:8

·     每个Segment的平均文件数:10,000

每节点上的文件总数是8*10,000 = 80,000,而该集群的文件总数是 8*16*10,000 = 1,280,000。随着分区数和列数的增加,文件数会快速增加。

作为最佳实践最推荐的设置,请把每节点的文件总数限制为低于100,000。如上一个例子所示,每个Segment的最优文件数和每节点的文件总数取决于节点的硬件配置(主要是内存)、集群的大小、SQL 访问、并发性、负载以及倾斜。

2. 索引

在SeaSQL DWS数据库中通常不需要索引。大部分分析型查询会在大体量数据上操作,而索引是用于从多行数据中定位某一行或某几行。在SeaSQL DWS数据库中,顺序扫描是一种读取数据的有效方法,因为每个Segment都含有数据同等大小的一部分并且所有的Segment都并行工作以读取数据。

·     如果增加索引不能获得性能提升,马上删掉它。验证创建的每个索引都被优化器使用到。

·     对于具有高选择性的查询,索引会提升查询性能。对于选择性查询所要求的高基数表,在一个列式表的单列上创建用于钻透目的的索引。

·     不要在频繁更新的列上创建索引。在频繁被更新的列上创建索引会增加更新时所需的写次数。

·     只有当表达式被频繁地使用在查询中时,才应该在表达式上建立索引。

·     带有谓词的索引会创建一个部分索引,它可以被用来从大型表中选择少量行。

·     避免重叠的索引。具有相同前导列的索引是冗余的。

·     对于返回一个定向行集合的查询来说,索引能够提高在压缩追加优化表上的性能。对于压缩数据,采用索引访问方法意味着只有必要的页面会被解压缩。

·     创建有选择性的B-树索引。索引选择度是一列中的唯一值数量除以表中的行数。例如,如果一个表有1000行并且有一列中有800个唯一值,那么该索引的选择度就是0.8,这是不错的索引使用情形。

·     在向表中装载数据前删除索引。这样装载的运行速度将会比在带有索引的表中装载数据快一个数量级。在装载之后,重新创建索引。

·     位图索引适合于查询但不适合于更新。当列具有较低的基数(100到100,000个唯一值)时位图索引表现得最好。不要为唯一列、基数非常高或者非常低的数据使用位图索引。不要为事务性负载使用位图索引。

通常,不要索引分区表。如果需要索引,索引列必须不同于分区列。索引分区表的一个好处是因为当B-树尺寸增长时其性能呈指数下降,在分区表上创建索引可以得到很多较小的B-树,其性能比未分区表上的B-树更好。

3. 列顺序与字节对齐

为了最优性能,请布置表列以实现数据类型的字节对齐。以下面的顺序布置堆表中的列:

(1)     分布列和分区列

(2)     固定的数字类型

(3)     可变的数据类型

从大到小布置数据类型,BIGINT和TIMESTAMP会在INT和DATE的前面,而所有这些类型都在TEXT、VARCHAR或者NUMERIC(x,y)之前。例如,首先是8字节类型(BIGINT、TIMESTAMP),然后是4字节类型(INT、DATE),再后面是2字节类型(SMALLINT),最后是数据类型(VARCHAR)。

·     不要以这种顺序定义列:

Int, Bigint, Timestamp, Bigint, Timestamp, Int(分布键), Date(分区键), Bigint, Smallint

·     以这种顺序定义列:

Int(分布键), Date(分区键), Bigint, Bigint, Timestamp, Bigint, Timestamp, Int, Smallint


5 采用资源组管理内存和资源

采用资源组管理SeaSQL DWS数据库资源。

在SeaSQL DWS数据库集群中,内存、CPU和并发事务管理对性能影响非常巨大。资源组是SeaSQL DWS数据库提供的用来强制限制内存、CPU和并发事务的全新资源管理模式。

5.1  配置SeaSQL DWS数据库内存

不可能一直增加系统内存,客户可以通过配置资源组来管理可预期的工作负载,这样能够避免内存的溢出。

以下操作系统和SeaSQL DWS数据库内存设置,对采用资源组来管理日常工作是非常有用的:

·     vm.overcommit_memory

该Linux内核参数在/etc/sysctl.conf文件中设置,用来指定操作系统分配给系统进程使用多少内存的方法。vm.overcommit_memory在SeaSQL DWS数据库所在的机器上必须设置为2。

·     vm.overcommit_ratio

该Linux内核参数在/etc/sysctl.conf文件中设置,用来执行应用进程可以使用的内存百分比;剩余的内存留给操作系统。操作系统默认值(H3Linux上默认是95)对于部署SeaSQL DWS数据库集群基于资源组的管理方式是一个不错的初始值。如果感觉内存利用率太低,便可以提高该值;如果内存或交换分区使用太高,就减少该设置。

·     gp_resource_group_memory_limit

系统分配给SeaSQL DWS数据库的内存百分比。默认值为0.7(70%)。

·     gp_workfile_limit_files_per_query

设置gp_workfile_limit_files_per_query以限制每个查询允许使用的临时溢出文件(工作文件)的最大数量。当查询要求的内存比它能分配的更多时,它将创建溢出文件。当上述限制被超过时,查询会被中止。默认值为100000,允许无限多的溢出文件并且可能会填满文件系统。

·     gp_workfile_compression

如果有很多溢出文件,则设置gp_workfile_compression来压缩这些溢出文件。压缩溢出文件可能有助于避免IO操作导致磁盘子系统过载。

其他考虑因素:

·     不要启用操作系统大页配置。

·     当配置资源组内存时,提前考虑出现Segment实例或Segment主机宕机时,镜像Segment变成主Segment对系统内存的占用。

5.2  配置资源组

SeaSQL DWS数据库资源组能提供管理集群负载的强有力手段。当在系统中配置资源组时,考虑以下常规指导方法:

·     任何具有SUPERUSER权限的用户提交的事务都在默认资源组 admin_group下运行。在调度和运行任何SeaSQL DWS管理工具时都要牢记这一点。

·     确保为每一个非管理员用户分配一个用户组。如果不给用户分配资源组,那么该用户提交的查询会被默认资源组default_group处理。

·     采用资源组参数CONCURRENCY来限制某个资源组可以并发运行的活动查询的数量。

·     采用MEMORY_LIMIT和MEMORY_SHARED_QUOTA参数控制运行在资源组中的查询可以申请的最大内存数量。

·     SeaSQL DWS数据库会将无保留内存(100-(所有资源组MEMORY_LIMIT总和))全部分配给全局共享内存池。该内存本着一视同仁的原则,先到先得。

·     基于实时需求和负载的变化来动态调整资源组满足业务要求。

·     采用gptoolkit视图检查资源组使用情况,来监控资源组工作良好。

5.3  低内存查询

memory_spill_ratio设置为较低值时(例如,在0-2%之间)能够提升低内存要求查询的性能。我们可以在每个查询之前让memory_spill_ratio生效来覆盖系统默认设置。例如:

SET memory_spill_ratio=0;

5.4  管理工具和admin_group并行

SeaSQL DWS数据库用户SUPERUSERs的默认资源组是admin_group。admin_group资源组的默认CONCURRENCY值为10。

某些SeaSQL DWS数据库管理工具可能会同时使用多个CONCURRENCY槽,例如使用gpbackup带有--jobs选项时。如果客户运行的工具要求的并发事务数比admin_group的多,可以考虑临时增加该资源组的MEMORY_LIMIT和CONCURRENCY,以满足工具的要求,但一定要记得在工具执行完后及时将这些设置恢复原样。

【注意】通过修改ALTER RESOURCE GROUP达到的内存改变并不能立刻影响到正在运行的 查询。所以尽量选择在维护窗口时间修改资源组参数。


6 采用资源队列管理内存和资源

注意

资源组是SeaSQL DWS数据库提供的用来强制限制内存、CPU和并发事务的全新资源管理模式。有关资源管理模式的配置和使用方法请见5 采用资源组管理内存和资源

 

资源组可以有效管理数据库资源,避免内存和CPU使用率过高导致的问题。

SeaSQL DWS数据库集群中,内存管理对性能有显著的影响。默认设置适合于大部分环境。不要更改默认设置,除非理解系统上的内存特点和使用方法。

6.1  解决内存不足错误

内存不足错误消息表明SeaSQL DWS的Segment、主机和进程遇到了内存不足错误。例如:

Out of memory (seg27 host.example.com pid=47093)

VM Protect failed to allocate 4096 bytes, 0 MB available

SeaSQL DWS数据库中一些常见的导致内存不足的情况有:

·     集群上可用的系统内存(RAM)不足

·     内存参数配置不当

·     Segment级别有数据倾斜

·     查询级别有操作性倾斜

下面是内存不足情况的可用解决方案:

·     调优查询以要求较少的内存

·     使用资源队列降低查询并发

·     在数据库级别上验证gp_vmem_protect_limit配置参数。最大安全设置的计算请见6.3  SeaSQL DWS数据库配置内存

·     在资源队列上设置内存限额以限制资源队列中执行的查询所使用的内存

·     使用会话设置来降低特定查询使用的statement_mem

·     在数据库级别降低statement_mem

·     降低SeaSQL DWS集群中每台主机上的Segment数量。该操作要求重新初始化集群并重新加载数据。

·     增加主机上的内存(要求增加额外的硬件)。

向集群中增加Segment主机本身不会缓解内存不足问题。每个查询使用的内存由statement_mem 参数决定,并且在查询被调用时会设置它。但是,如果增加更多的主机允许每台主机上的Segment数量降低,那么gp_vmem_protect_limit中分配的内存量就可以上升。

6.2  低内存查询

较低的statement_mem设置(例如,在1-3MB之间)可以提升低内存查询的性能。可以在语句级别设置statement_mem配置参数来覆盖系统默认值。例如:

SET statement_mem='2MB';

6.3  SeaSQL DWS数据库配置内存

如果合理地管理内存,大部分内存不足的情况是可以避免的。

不可能不断增加系统内存,所以客户可以通过配置资源组来管理可预期的工作负载,这样能够避免内存溢出。

当配置资源组内存时,提前考虑出现Segment实例或Segment主机宕机时,镜像Segment变成主Segment对系统内存的占用。

推荐的操作系统以及SeaSQL DWS数据库内存设置如下:

·     不要启用操作系统大页配置

·     vm.overcommit_memory

该Linux内核参数在/etc/sysctl.conf文件中设置,用来指定操作系统分配给系统进程使用多少内存的方法。SeaSQL DWS数据库所在的机器上vm.overcommit_memory必须设置为2。

·     vm.overcommit_ratio

该Linux内核参数在/etc/sysctl.conf文件中设置,用来执行应用进程可以使用的内存百分比;剩余的内存留给操作系统。操作系统默认值(H3Linux上默认是95)。

把vm.overcommit_ratio设置得太高可能会导致没有为操作系统保留足够的内存,进而导致Segment主机故障或者数据库故障。将这个值设置得太低会降低并发量和通过降低SeaSQL DWS 数据库可用内存量能运行的查询复杂度。当增加这一设置时,有必要记住总是为操作系统活动保留一些内存。

有关计算vm.overcommit_ratio值的步骤,参见3.9  Segment内存配置

·     gp_vmem_protect_limit

使用gp_vmem_protect_limit设置能够为每个Segment实例数据库分配的最大内存。不要设置该值高于系统上的物理RAM。

¡     如果gp_vmem_protect_limit太高,有可能耗尽系统上的内存并且正常的操作可能会失败,导致Segment故障。

¡     如果gp_vmem_protect_limit被设置为一个安全的较低值,系统上真正的内存耗尽就能避免。查询可能会因为达到限制而失败,但是可以避免系统崩溃和Segment故障。

有关计算gp_vmem_protect_limit安全值的步骤,参见3.9  Segment内存配置

·     runaway_detector_activation_percent

失控查询终止能防止内存不足的问题。runaway_detector_activation_percent系统参数控制触发查询终止的gp_vmem_protect_limit内存利用率。默认被设置为90%。如果一个Segment利用的gp_vmem_protect_limit内存的百分比超过指定的值,SeaSQL DWS数据库会基于内存使用终止查询,从消耗内存量最大的查询开始。查询会被挨个终止直至gp_vmem_protect_limit的利用率重新低于指定的百分比。

·     statement_mem

使用statement_mem分配每个Segment数据库中一个查询所使用的内存。如果要求额外的内存,将会溢出到磁盘。按照下面的方式为statement_mem设置最优值:

(vmprotect * .9) / max_expected_concurrent_queries

为要求额外内存来完成的特定查询,可以在会话级别上设置statement_mem。在并发性低的集群上这种设置可以很好地管理查询内存。对于高并发的集群还需要使用资源队列来控制内存的使用。

·     gp_workfile_limit_files_per_query

设置gp_workfile_limit_files_per_query以限制每个查询允许使用的临时溢出文件 (工作文件)的最大数量。当查询要求的内存比它能分配的更多时,它将创建溢出文件。当上述限制被超过时,查询会被中止。默认值为100000,允许无限多的溢出文件并且可能会填满文件系统。

·     gp_workfile_compression

如果有很多溢出文件,则设置gp_workfile_compression来压缩这些溢出文件。压缩溢出文件可能有助于避免IO操作导致磁盘子系统过载。

6.4  配置资源队列

·     SeaSQL DWS数据库的资源队列为管理集群负载提供了一种强有力的机制。队列可以被用来限制活动查询的数量以及队列中查询可使用的内存量。当查询被提交给SeaSQL DWS数据库时,它会被加入一个资源队列,资源队列会决定该查询是否应该被接受并且何时有资源可用来执行它。

·     为所有用户分配预定义的资源队列。

·     每个登录用户(角色)都被关联到单个资源队列,任何该用户提交的查询都由关联的资源队列处理。如果没有为用户的查询明确地分派一个队列,则会由默认队列pg_default处理。

·     不要用ssadmin角色或者其他超级用户角色运行查询。

·     超级用户会被从资源队列限制中排除,因此超级用户的查询运行不会考虑在其所属队列上设置的限制。

·     使用ACTIVE_STATEMENTS资源队列参数来限制特定队列成员能够并发运行的活动查询数量。

·     使用MEMORY_LIMIT参数控制通过队列运行的查询可以利用的内存总量。通过组合 ACTIVE_STATEMENTS和MEMORY_LIMIT属性,管理员可以完全控制从一个给定资源队列发出的活动。

分配按如下方式进行:假定资源队列sample_queue的ACTIVE_STATEMENTS被设置为10,而MEMORY_LIMIT被设置为2000MB。这限制该队列在每个Segment上使用大约2GB内存。对于每台服务器有8个Segment的集群,sample_queue在每台服务器上的总用量是16GB(2GB * 8 Segment/服务器)。如果Segment服务器有64GB内存,系统中可以有不超过四个这种资源队列,再多就会内存不足(4队列 * 16GB/队列)。

【注意】通过使用STATEMENT_MEM,运行在队列中的个体查询能够分配超过其内存“份额”的内存,从而降低队列中其他查询可用的内存。

·     资源队列优先级可以被用来排列具有预期结果的负载。带有MAX优先权的队列会扼杀所有其他队列中的活动,直至MAX队列完成所有查询的运行。

·     根据负载和现状动态修改资源队列以匹配队列的实际需求。用户可以把这些更改写成脚本并且增加crontab项来执行这些脚本。

·     使用gp_toolkit查看资源队列使用以及理解队列如何工作


7 系统监控和维护

7.1  概述

本章节主要介绍日常运维相关的最佳实践,确保SeaSQL DWS数据库日常高可用性和性能保持最佳状态。

7.1.1  监控

SeaSQL DWS数据库提供了一些对监控系统非常有用的工具。

gp_toolkit模式包含多个可以用SQL命令访问的视图,通过它们可以查询系统目录、日志文件以及操作环境来获得系统状态信息。

gp_stats_missing视图展示没有统计信息且要求运行ANALYZE的表。

7.1.2  gpstate

gpstate工具显示SeaSQL DWS系统的状态,包括宕掉的Segment宕掉、Master和Segment 的配置信息(hosts、数据目录等)、系统使用的端口以及主Segment与它们对应的镜像Segment之间的映射。

·     运行gpstate -Q可以得到一个Segment的列表,这个列表列出了那些在Master的系统目录中被标记为"down"的Segment。

·     运行gpstate -s可以要得到SeaSQL DWS系统的详细状态信息。

7.1.3  gpcheckperf

gcheckperf工具能用来测试主机硬件的基线性能。其结果可以帮助发现硬件问题。它会执行下列检查:

·     磁盘I/O测试:通过使用操作系统命令dd读写一个大型文件来测量I/O性能。它报告以兆字节每秒为单位的读写速率。

·     内存带宽测试:使用STREAM基准测量以兆字节每秒为单位的可持续的内存带宽。

·     网络性能测试:运行gpnetbench网络基准程序(也可以选netperf)来测试网络性能。这种测试可以运行在三种模式中:并行结对测试(-r N)、串行结对测试(-r n)或者全矩阵测试(-r M)。报告的最小、最大、平均和中值传输率将以兆字节每秒为单位。

为了从gpcheckperf获得有效的数字,数据库系统必须被停止。即使系统仍在运行且没有运行查询活动,从gpcheckperf得到的数字可能也不准确。

gpcheckperf要求在参与性能测试的主机之间提前进行访问互信设置。因为工具会调用gpssh以及gpscp,因此这些工具必须也位于用户的PATH中。可以个别指定要检查的主机(-h host1 -h host2)或者使用-f hosts_file,其中hosts_file是一个包含要检查的主机列表的文本文件。如果用户有多个子网,为每个子网都创建一个单独的主机文件,这样用户可以单独测试子网。

gpcheckperf默认会运行磁盘I/O测试、内存测试和串行结对网络性能测试。对于磁盘I/O测试,用户必须使用-d选项指定要测试的文件系统。下面的命令在subnet_1_hosts文件中列出的主机上测试磁盘I/O和内存带宽:

$ gpcheckperf -f subnet_1_hosts -d /data1 -d /data2 -r ds

-r选项选择要运行的测试:磁盘I/O(d)、内存带宽(s)、网络并行结对(N)、网络串行结对测试(n)、网络全矩阵测试(M)。每次执行只能选择一种网络模式。

7.1.4  用操作系统工具监控

下面的Linux/UNIX工具可以被用来评估主机性能:

·     iostat允许用户监控Segment主机上的磁盘活动。

·     top显示操作系统进程的动态视图。

·     vmstat显示内存使用统计信息。

用户可以使用gpssh在多台主机上运行工具。

7.1.5  最佳实践

·     在安装时运行gpcheckperf,并且在安装之后定期运行它,将其输出保存起来,用以对比不同时刻的系统性能。

·     使用所有能支配的工具来理解系统在不同负载下的行为。

·     检查异常事件以确定原因。

·     通过定期运行执行计划监控系统上的查询活动,以确保查询以最优的方式运行。

·     检查执行计划以确定索引是否被使用以及分区裁剪是否按照预期发生。

7.2  用ANALYZE更新统计信息

良好查询性能的最重要先决条件是从表的正确统计信息开始。用ANALYZE语句更新统计信息让查询规划器能生成最优的查询计划。当表被分析时,有关数据的信息被存储在系统目录表中。如果存储的信息过时,规划器可能会生成低效的执行计划。

7.2.1  有选择地生成统计信息

不带参数运行ANALYZE会为数据库中所有的表更新统计信息。这样操作运行时间可能会非常长,因此不推荐这样做。当数据被改变时,应该有选择地使用ANALYZE表或者analyzedb工具。

在大型表上运行ANALYZE可能需要很长时间。如果在非常大的表的所有列上运行ANALYZE行不通,可以只使用ANALYZE table(column,...)为选择的列生成统计信息。确保包括用在连接、WHERE子句、SORT子句、GROUP BY子句或者HAVING子句中的列都被收集了统计信息。

对于一个分区表,使用者可以只在更改过的分区(例如,使用者增加一个分区)上运行ANALYZE。 【注意】对于分区表,可以在父(主)表上或者叶子节点(实际存储数据和统计信息的分区文件)上运行ANALYZE。子分区表的中间文件没有存储数据或统计信息,因此在其上运行ANALYZE没有效果。使用者可以在pg_partitions系统目录中寻找分区表的名字:

SELECT partitiontablename from pg_partitions WHERE tablename='parent_table';

7.2.2  提升统计信息质量

在生成统计信息所用的时间和统计信息的质量或者准确性之间存在着权衡。

为了允许大型表能在合理的时间内被分析完,ANALYZE会对表内容做随机采样而不是检查每一行。 要对所有表列增加采样,可调整default_statistics_target配置参数。其目标值取值范围从1到1000,默认的目标值是100。default_statistics_target变量默认会被应用到所有的列。更大的目标值会增加执行ANALYZE所需的时间,但是可以提升查询规划器的评估质量。对于带有不规则数据模式的列尤其如此。default_statistics_target可以在Master或者会话级别设置,并且要求重新载入配置。

7.2.3  何时运行ANALYZE

在下列时机运行ANALYZE:

·     装载数据后

·     CREATE INDEX操作后

·     在显著更改底层数据的INSERT、UPDATE以及DELETE操作之后

ANALYZE仅在表上要求一个读锁,因此它可以与其他数据库活动并行运行。但不要在执行装载、INSERT、UPDATE、DELETE以及CREATE INDEX操作期间运行ANALYZE。

7.2.4  配置统计信息自动收集

·     gp_autostats_mode配置参数与gp_autostats_on_change_threshold参数一起决定何时触发自动分析操作。当自动统计信息收集被触发时,规划器会为查询增加一个ANALYZE步骤。

·     gp_autostats_mode默认为on_no_stats,这会为任何没有统计信息的表上的CREATE TABLE AS SELECT、INSERT或者COPY 操作触发统计信息收集。

·     gp_autostats_mode设置为on_change时,只有当受影响的行数超过由 gp_autostats_on_change_threshold定义的阈值时才会触发统计信息收集,该阈值参数的默认值为2147483647。on_change设置下能触发自动统计信息收集的操作有:CREATE TABLE AS SELECT、UPDATE、DELETE、INSERT以及COPY。CREATE TABLE AS SELECT、UPDATE、DELETE、 INSERT以及COPY。

·     gp_autostats_mode设置为none时,会禁用自动统计信息收集。

对于分区表,如果数据从分区表的顶层父表插入,则自动统计信息收集不会被触发。但是如果数据直接被插入在分区表的叶子表(存储数据的地方)中,则自动统计信息收集会被触发。

7.3  在数据库中管理膨胀

SeaSQL DWS数据库的堆表使用PostgreSQL多版本并发控制(MVCC)存储实现。被删除或更新的行被从数据库逻辑删除,但是该行的一个不可见映像仍然保留在表中。这些被删除的行(也被称为过期行)被存储在一个空闲空间映射文件中。运行VACUUM会把过期行标记为可以被后续插入重用的空闲空间。

如果空闲空间映射不足以容纳所有的过期行,VACUUM命令就不能从导致空闲空间映射溢出的过期行回收空间。磁盘空间只能通过运行VACUUM FULL恢复,这个操作会锁住表,逐行拷贝到文件的开头,然后截断文件。这是一种昂贵的操作,对于大型的表,它可能需要超乎想象的时间来完成。所以一般情况下只在较小的表上使用这种操作。如果使用者尝试杀死VACUUM FULL操作,系统可能会损坏。

·     业务运行过程中,如果存在大量的UPDATE以及DELETE操作,非常有必要对该表运行VACUUM操作,这样可以避免运行VACUUM FULL。

·     如果空闲空间映射溢出并且需要恢复空间,推荐使用CREATE TABLE...AS SELECT命令把该表拷贝为一个新表,这将会创建一个新的紧凑的表。然后删除原始表并且重命名拷贝的表为原始表名。

对于频繁更新的表来说,有少量或者中等数量的过期行以及空闲空间很正常,空闲空间将随着新数据的加入而被重用。但是当表被允许增长得非常大以至于活动数据只占空间的一小部分时,该表就明显地“膨胀”了。膨胀的表要求更多磁盘存储以及可能拖慢查询执行的额外I/O。

·     膨胀影响堆表、系统目录和索引。

在表上定期运行VACUUM语句可以防止它们长得过大。如果表确实出现了明显的膨胀,必须使用VACUUM FULL语句(或者可替代的过程)来紧缩文件。如果一个大型表变得明显膨胀,更好的方法是使用从数据库表移除膨胀中描述的方法之一来移除膨胀。

【注意】禁止运行VACUUM FULL <database_name>,并且不要在SeaSQL DWS 数据库中的大型表上运行VACUUM FULL。

7.3.1  检测膨胀

ANALYZE语句所收集的统计信息可以被用来计算存储一个表所要求的磁盘页面的预计数量。页面的预计数量和实际数量之间的差别就是膨胀的度量。gp_toolkit模式提供了一个ss_视图,它可以获取表的膨胀信息:

# select * from gp_toolkit.ss_bloat_tables;

   tablename   | live_tuples | dead_tuples | ratio

---------------+-------------+-------------+-------

 public.player | 2           | 2           | 0.50

 (1 row)

其结果只包括发生了中度或者明显膨胀的表。当实际页面数和预期页面的比率超过0.32,就会报告为膨胀。

7.3.2  从数据库表移除膨胀

VACUUM命令会把过期行加入到共享的空闲空间映射中,这样这些空间能被重用。当在被频繁更新的表上定期运行VACUUM时,过期行所占用的空间可以被迅速地重用,从而防止表文件长得更大。在空闲空间映射被填满之前运行VACUUM也很重要。对于更新密集的表,用户可能需要每天运行VACUUM至少一次来防止表膨胀。

·     当表出现显著膨胀时,在运行ANALYZE之前先运行VACUUM会更好。如果采样包含空的数据页,分析膨胀表会生成不合适的统计信息,所以在分析表之前先做VACUUM是最好的选择。

·     当表积累了显著的膨胀时,运行VACUUM命令并不能起到明显作用。对于小型表,运行VACUUM FULL <table_name>能够回收导致空闲空间映射溢出的行所使用的空间并且减小表文件的尺寸。但是,VACUUM FULL语句是一种昂贵的操作,它要求一个ACCESS EXCLUSIVE锁并且可能需要异常长的时间完成。比起在一个大型表上运行VACUUM FULL,采用另一种方法从大型文件中移除膨胀会更好。

【注意】每一种从大型表中移除膨胀的方法都是资源密集型的,并且只应该在极端情况下完成。

·     第一种从大型表中移除膨胀的方法是创建一个将过期行排除在外的表拷贝,删掉原始的表并且把这个拷贝重命名为原来的表名。这种方法使用CREATE TABLE <table_name> AS SELECT语句创建新表,例如:

# CREATE TABLE mytable_tmp AS SELECT * FROM mytable;

# DROP TABLE mytable;

# ALTER TABLE mytabe_tmp RENAME TO mytable;

·     第二种从表移除膨胀的方法是重新分布该表,该操作会把表重建为不含过期行的表。参考步骤如下:

a.     把表的分布列记下来。

b.     把该表的分布策略改为随机分布:

# ALTER TABLE mytable SET WITH (REORGANIZE=false)

                DISTRIBUTED randomly;

上述操作会为该表更改分布策略,但不会移除任何数据。该命令应该会立即完成。

c.     将分布策略改回其初始设置:

# ALTER TABLE mytable SET WITH (REORGANIZE=true)

DISTRIBUTED BY (<original distribution columns>);

这一步会重新分布数据。因为表之前是用同样的分布键分布的,表中的行只需要简单地在同一Segment上重写即可,同时排除过期行。

7.3.3  从索引移除膨胀

VACUUM命令只会从表中恢复空间。要从索引中恢复空间,需要使用REINDEX命令重建索引。

要在一个表上重建所有的索引,可运行REINDEX table_name。要重建一个特定的索引,可运行REINDEX index_name。REINDEX会将该索引相关reltuples和relpages的值设置为0(零),如果要更新统计信息,则需要在重建索引后运行ANALYZE。

7.3.4  从系统目录移除膨胀

SeaSQL DWS数据库系统表也是堆表,并且会随着时间推移变得膨胀。随着数据库对象被创建、修改或者删除,过期行会留在系统目录中。使用gpload装载数据会加剧膨胀,因为gpload会创建并且删除外部表。(为了避免使用gpload,推荐使用gpfdist装载数据。)

系统目录中的膨胀会导致扫描表所需的时间增加,例如在创建执行计划时需要扫描系统目录。系统目录会被频繁扫描,所以如果它们变得膨胀,整体的系统性能都会下降。

推荐每晚在系统目录上运行VACUUM,或者至少每周运行一次(新版本的SeaSQL DWS默认每天凌晨3点30分进行一次系统表的VACUUM操作)。同时,运行REINDEX SYSTEM从索引中移除膨胀。此外,还可以使用带-s(--system)选项的reindexdb工具对系统目录重建索引。在移除系统目录膨胀后,还有必要运行ANALYZE以更新系统目录表的统计信息。

以下是SeaSQL DWS数据库系统目录维护步骤。

(1)     在系统目录表上执行REINDEX操作用于重建系统目录索引。该操作可以移除索引膨胀并提高VACUUM性能。

【注意】当在系统目录表上执行REINDEX操作时,会锁住相应的表,进而影响到当前正在执行的查询性能。用户可以在系统的非活动窗口时间来调用REINDEX命令重建索引,以避免打扰正常业务操作的进行。

(2)     在系统目录表上执行VACUUM命令。

(3)     在系统目录表上执行ANALYZE操作来更新表的统计信息。

如果在维护窗口期内,由于时间限制需要停止目前正在进行的系统目录维护,可以运行SeaSQL DWS数据库函数pg_cancel_backend(<PID>)来安全的停止该任务。

如果系统目录膨胀得很厉害,使用者就必须执行一次大强度的系统目录维护过程。采用CREATE TABLE AS SELECT移除膨胀的方法以及重新分布数据的方法均不能被用于系统目录。使用者必须转而在计划的停机时段运行VACUUM FULL。在此期间,停止系统上所有的目录活动,VACUUM FULL会对系统目录加排他锁。定期运行VACUUM能够预防最终不得不采用上面的高代价方法。

以下为彻底解决系统目录膨胀的步骤。

(1)     停止SeaSQL DWS数据库上所有系统目录操作。

(2)     在系统目录表上执行REINDEX操作来重建系统目录索引。该操作可以移除索引膨胀并提高VACUUM性能。

(3)     在系统目录表上执行VACUUM FULL操作。注意关注下面提到的注意事项。

(4)     在系统目录表上执行ANALYZE操作来更新系统目录表的统计信息。

【注意】系统目录表pg_attribute通常是这里面最大的表。如果pg_attribute表明显膨胀,在该表上的VACUUM FULL操作会占用很长时间,此时可能需要将操作分解。以下两种情形表明pg_attribute表存在明显膨胀并可能需要运行长时间的VACUUM FULL操作:

·     pg_attribute表包含大量记录。

·     gp_toolkit.gp_bloat_diag视图中有关pg_attribute表的诊断信息上显示该表存在明显膨胀。

7.3.5  从追加优化表移除膨胀

对追加优化表(AO表)的处理与堆表有很大不同。尽管追加优化表允许更新、插入和删除,但它们并非为这些操作而优化,因此不推荐对追加优化表使用这些操作。如果使用者采纳这一建议并且为一次装载/多次读取负载使用追加优化,追加优化表上的VACUUM几乎会即刻运行。

如果使用者确实在追加优化表上运行了UPDATE或者DELETE命令,过期行会由一个辅助位图而不是空闲空间映射来跟踪。

·     VACUUM是唯一能恢复空间的方式。

在有过期行的追加优化表上运行VACUUM会通过把整个表重写成没有过期行的表以紧缩该表。不过,如果表中过期行的百分数超过了gp_appendonly_compaction_threshold 配置参数的值,则不会执行任何操作,该参数的默认值是10(10%)。每个Segment上都会检查该阈值,因此VACUUM语句可能会在某些Segment上对追加优化表进行紧缩而在其他Segment上不做紧缩。通过将gp_appendonly_compaction参数设置为no可以禁用对追加表的紧缩。


8 装载数据

本章节主要描述将数据装载到SeaSQL DWS数据库的不同方式。

8.1  带列值的INSERT语句

带有值的单个INSERT语句会向表中加入一行。这个行会流过Master并且被分布到一个Segment上。这是最慢的方法并且不适合装载大量数据。

8.2  COPY语句

PostgreSQL的COPY语句从外部文件拷贝数据到数据表中。它比INSERT语句插入多行的效率更高,但是行仍需流过Master。所有数据都在一个命令中被拷贝,它并不是一种并行处理。

COPY命令的数据输入来自于一个文件或者标准输入。

例如:

COPY table FROM '/data/mydata.csv' WITH CSV HEADER;

使用COPY适合于增加相对较小的数据集合(例如多达上万行的维度表)或者一次性数据装载。

在编写脚本处理装载少于1万行的少量数据时使用COPY。

因为COPY是一个单一命令,在使用这种方法填充表时没有必要禁用自动提交。使用者可以运行多个并发的COPY命令以提高性能。

8.3  外部表

外部表提供了对SeaSQL DWS数据库之外的数据来源的访问。可以用SELECT语句访问它们,外部表通常被用于抽取、装载、转换(ELT)模式,这是一种抽取、转换、装载(ETL)模式的变种,这种模式可以利用SeaSQL DWS数据库的快速并行数据装载能力。

通过ETL工具,在数据库外部使用外部转换工具(Informatica、Datastage或者DI)转换,然后数据被装载到数据库中。

通过ELT工具,SeaSQL DWS外部表提供对外部来源中数据的访问,外部来源可以是只读文件(例如文本、CSV或者XML文件)、Web服务器、Hadoop文件系统、可执行的OS程序或者SeaSQL DWS gpfdist文件服务器,详情参见8.4  使用gpfdist外部表

·     外部表支持选择、排序和连接这样的SQL操作,这样数据可以被同时装载和转换,或者被装载到一个装载表并且在数据库内被转换成目标表。

·     外部表使用CREATE EXTERNAL TABLE语句定义,该语句有一个LOCATION子句定义数据的位置以及一个FORMAT子句定义源数据的格式,这样系统才能够解析输入数据。文件使用file://协议,并且文件必须位于一台Segment主机上由SeaSQL DWS超级用户可访问的位置。数据可以被分散在Segment主机上,并且每台主机上的每个主Segment有不超过一个文件。LOCATION子句中列出的文件的数量是将并行读取该外部表的Segment的数量。

8.4  使用gpfdist外部表

装载大型事实表的最快方式是使用基于gpdist的外部表。gpfdist是一个使用HTTP协议的文件服务器程序,它以并行的方式向SeaSQL DWS数据库的Segment供应外部数据文件。一个gpfdist实例每秒能供应200MB(操作系统允许的情况下),并且很多gpfdist进程可以同时运行,每一个进程供应被装载的数据的一部分。

当使用者用INSERT INTO <table> SELECT * FROM <external_table>这样的语句开始装载时

·     INSERT语句会被Master解析并且分布给主SegmentSegment连接到gpfdist服务器并且并行检索数据,解析并验证数据,从分布键数据计算一个哈希值,并且基于哈希键把行发送给它的目标Segment。每个gpfdist实例默认将接受最多64个来自Segment的连接。通过让更多的Segmentgpfdist服务器参与到装载处理中,可以以非常高的速度进行数据装载。

·     在使用gpfdist数量达到配置参数gp_external_max_segs最大值时,主Segment会并行访问外部文件。在优化gpfdist的性能时,随着Segment的数量增加会最大化并行性。在尽可能多的ETL节点上均匀地散布数据。将非常大的数据文件分解成相等的部分,并且把数据分散在尽可能多的文件系统上。

·     需要在所有这些资源上保持工作处于均衡状态,因为装载的速度受制于最慢的节点。装载文件布局上的倾斜将导致整体装载受制于资源瓶颈。

·     gp_external_max_segs配置参数控制每个gpfdist进程能服务的Segment数量。默认值是64。使用者可以在master上的postgresql.conf配置文件中设置一个不同的值。尽量保持gp_external_max_segs和gpfdist进程的数量为一个偶因子,也就是说gp_external_max_segs值应该是gpfdist 进程数的倍数。

例如,如果有12个Segment和4个gpfdist进程,规划器会按照下面的方式循环分配Segment连接:

Segment 1  - gpfdist 1

Segment 2  - gpfdist 2

Segment 3  - gpfdist 3

Segment 4  - gpfdist 4

Segment 5  - gpfdist 1

Segment 6  - gpfdist 2

Segment 7  - gpfdist 3

Segment 8  - gpfdist 4

Segment 9  - gpfdist 1

Segment 10 - gpfdist 2

Segment 11 - gpfdist 3

Segment 12 - gpfdist 4

在装载到已有表之前删除索引,并且在装载之后重建索引。在装载完数据后重新创建索引比装载每行时增量更新索引更快。

装载后在表上运行ANALYZE。在装载期间通过设置gp_autostats_mode为NONE来禁用自动统计信息收集。如果在装载出错,则需要后台运行VACUUM来回收空间。

对重度分区的列存表执行少量高频的数据装载可能会对系统有很大影响,因为在每个时间间隔内被访问的物理文件会很多。

8.5  gpload

gpload是一种数据装载工具,它扮演着SeaSQL DWS外部表并行装载特性的接口的角色。

要当心对gpload的使用,因为它会创建并且删除外部表,从而可能会导致系统目录膨胀。可转而使用gpfdist,因为它能提供最好的性能。

gpload使用定义在一个YAML格式的控制文件中的规范来执行一次装载。它会执行下列操作:

·     调用gpfdist进程

·     基于定义的源数据创建一个临时的外部表定义

·     执行INSERT、UPDATE或者MERGE操作,将源数据载入数据库中的目标表

·     删除临时外部表

·     清除gpfdist进程

装载会在单个事务中完成。

8.6  最佳实践

·     在装载数据之前删掉现有表上的任何索引,并且在装载之后重建那些索引。新创建索引比装载每行时增量更新索引更快。

·     在装载期间通过将gp_autostats_mode配置参数设置为NONE禁用自动统计信息收集。

·     外部表并非为频繁访问或者ad hoc访问而设计。

·     在使用gpfdist时,通过为ETL服务器上的每一块NIC运行一个gpfdist实例以最大化网络带宽。在gpfdist实例之间均匀地划分源数据。

·     在使用gpload时,在资源允许的情况下同时运行尽可能多的gpload实例。利用可用的CPU、内存和网络资源以增加能从ETL服务器传输到SeaSQL DWS数据库的数据量。

·     使用COPY语句的SEGMENT REJECT LIMIT子句设置在COPY FROM命令被中止之前,可以出现错误的行的百分数限制。这个拒绝限制是针对每个Segment的,当任意一个Segment超过该限制时,命令将被中止且不会有行被增加。使用LOG ERRORS 子句可以保存错误行。如果有一行在格式上有错误(例如缺少值或者有多余的值,或者数据类型不对),SeaSQL DWS数据库会在内部存储错误信息和行。使用内建SQL函数gp_read_error_log(‘<external_tablename>’)可以访问这种存储下来的信息。

·     如果装载出现错误,在该表上运行VACUUM以恢复空间。

·     在用户装载数据到表中后,在堆表(包括系统目录)上运行VACUUM,并且在所有的表上运行ANALYZE。没有必要在追加优化表上运行VACUUM。如果表已经被分过区,用户可以只清理和分析受数据装载影响的分区。这些步骤会清除被中止的装载、删除或者更新中的行,并且为表更新统计信息。

·     在装载大量数据之后重新检查表中的Segment倾斜。用户可以使用下面这样的查询来检查倾斜:

SELECT gp_Segment_id, count(*) FROM schema.table GROUP BY gp_Segment_id ORDER BY 2;

·     gpfdist默认假定最大记录尺寸为32K。要装载大于32K的数据记录,用户必须通过在 gpfdist命令行上指定-m <max_length>选项来增加最大行尺寸参数。如果用户使用的是gpload,在gpload控制文件中设置 MAX_LINE_LENGTH参数。

【注意】与Informatica Power Exchange的集成当前被限制为默认的32K记录长度。


9 安全性

最佳实践可以确保最高级别的系统安全性。

9.1  基础安全最佳实践

·     保护好ssadmin系统用户。SeaSQL DWS要求一个UNIX用户ID来安装和初始化SeaSQL DWS数据库系统。这个系统用户在SeaSQL DWS文档中被称作ssadmin。ssadmin 用户是SeaSQL DWS数据库中的默认数据库超级用户,也是SeaSQL DWS安装及其底层数据文件的文件系统拥有者。默认的管理员账户是SeaSQL DWS数据库设计的根本。没有它系统无法运行,并且也没有办法限制ssadmin用户ID的访问。这个ssadmin用户可以绕过SeaSQL DWS数据库的所有安全性特性。任何人通过该用户ID登录到SeaSQL DWS主机,就可以读取、修改或者删除任何数据,包括系统目录数据和数据库访问权限。因此,非常有必要保护好ssadmin用户ID并且只允许必要的系统管理员可以接触到它。只有在执行特定系统维护任务(例如升级或扩展)时,管理员才应该作为ssadmin登录到SeaSQL DWS。数据库用户绝不应作为ssadmin登录,并且也绝不应以ssadmin运行ETL或者生产负载。

·     为每个登录的用户分配一个不同的角色。为了日志和审计目的,每个被允许登录SeaSQL DWS数据库的用户应该被给定其自己的数据库角色。对于应用或者Web服务,考虑为每种应用或者服务创建一个不同的角色。详情请见《SeaSQL DWS用户手册》。

·     使用组来管理访问特权。详情请见《SeaSQL DWS用户手册》。

·     限制拥有SUPERUSER角色属性的用户。作为超级用户的角色会绕过SeaSQL DWS数据库中的所有访问特权检查,也会绕过资源队列。只有系统管理员才应该被给予超级用户权利,详情请见《SeaSQL DWS用户手册》。

9.2  口令强度指导

为了保护网络不受侵入,系统管理员应该验证组织中使用的口令是强口令。下面的建议可以增强口令:

·     最小口令强度推荐:至少9个字符。MD5口令应该为至少15个字符。

·     混合大小写字母。

·     混合字母和数字。

·     包括非字母数字字符。

·     选择一个用户可以记住的口令。

整个系统的安全性依赖于root口令的强度。该口令应该至少长达12个字符并且包括大写字母、小写字母、特殊字符和数字的组合。它不能基于任何词典中的词。

确保下面的命令不会产生输出。通过这一命令列出的任何账号都应该被锁定。

grep "^+:" /etc/passwd /etc/shadow /etc/group

【注意】强烈推荐客户在部署完成后更改口令。

cd /etc

chown root:root passwd shadow group gshadow

chmod 644 passwd group

chmod 400 shadow gshadow


10 加密数据和数据库连接

该章节主要描述有关实现加密和管理密钥的最佳实践。

在SeaSQL DWS数据库系统中可以用下面的方式通过加密来保护数据:

·     客户端和Master数据库之间的连接可以用SSL加密。这种方式可以通过设置ssl服务器配置参数为on并且配置好pg_hba.conf文件来启用。有关在SeaSQL DWS数据库中启用SSL的信息请c参见《H3C SeaSQL DWS用户手册(独立形态)》。

·     SeaSQL DWS数据库允许在SeaSQL DWS的并行文件分发服务器、gpfdist和Segment主机之间传输SSL加密数据。详见10.3  加密gpfdist连接

·     SeaSQL DWS数据库集群中主机之间的网络通信可以使用IPsec加密。集群中的每一对主机之间会建立一个认证过的加密的VPN。对IPsec的支持请检查操作系统文档,或者考虑Zettaset等组织提供的第三方解决方案。

10.1  最佳实践

·     加密和解密数据会影响性能,建议加密需要加密的数据。

·     在生产系统中实现任何加密解决方案之前都要做性能测试。

·     如果需要加密SeaSQL DWS数据库磁盘文件,在部署数据库的时候开启透明加密。

·     SeaSQL DWS生产系统使用的服务器证书应由证书签名颁发机构(CA)签发这样客户端可以认证务器。如果所有客户端都是本地的,则可以使用本地CA。

·     如果客户端与SeaSQL DWS的连接会经过不安全的链路,则使用SSL加密。

·     数据的加密和解密都由数据库进程完成,为了避免传输明文数据,需要使用SSL加密客户端和数据库间的连接。

·     数据加载和导出时,使用gpfdists协议保护ETL数据安全。

10.2  密钥管理

只要使用对称(单私钥)或者非对称(公钥和私钥)加密,就有必要安全地存储主密钥或者私钥。存储加密密钥有很多选项,例如在文件系统上保存、密钥保管库、加密的USB、可信平台模块(TPM)或者硬件安全模块(HSM)。

在规划密钥管理时考虑下列问题:

·     密钥将被存在哪里?

·     密钥何时过期?

·     如何保护密钥?

·     如何访问密钥?

·     如何恢复和收回密钥?

开放Web应用安全性项目(OWASP)提供了一套非常全面的保护加密密钥指南。

10.3  加密gpfdist连接

gpfdists协议是gpfdist协议的一个安全版本,它能安全地标识文件服务器和SeaSQL DWS数据库并且加密它们之间的通信。使用gpfdists可以防止窃听和中间人攻击。

gpfdists协议利用下列值得关注的特性实现客户端/服务器的SSL安全性:

·     要求客户端证书。

·     不支持多语言证书。

·     不支持证书撤销列表(CRL)。

·     TLSv1协议被用于TLS_RSA_WITH_AES_128_CBC_SHA加密算法。这些SSL参数不能被更改。

·     不支持SSL再协商。

·     SSL忽略主机失配参数被设置为false。

·     gpfdist文件服务器(server.key)或SeaSQL DWS数据库(client.key)不支持含有口令的私钥。

·     为使用的操作系统颁发合适的证书是用户的责任。通常,支持将证书转换成所要求的格式,例如可使用https://www.sslshopper.com/ssl-converter.html的SSL转换器。

用--ssl选项启动的gpfdist服务器只能用gpfdists 协议通信。没有用--ssl选项启动的gpfdist服务器只能用gpfdist协议通信。

有两种方式启用gpfdists协议:

·     用--ssl选项运行gpfdist,然后在CREATE EXTERNAL TABLE语句的LOCATION子句中使用gpfdists协议。

·     在YAML控制文件中将SSL选项设置为true,然后用它来运行gpload。运行的gpload 会用--ssl选项启动gpfdist服务器,然后使用gpfdists协议。

在使用gpfdists时,下列客户端证书必须位于每个Segment的$PGDATA/gpfdists目录中:

·     客户端证书文件,client.crt

·     客户端私钥文件,client.key

·     受信证书发布机构,root.crt

【注意】不要用口令保护私钥。服务器不会为私钥提示要求口令,并且数据装载会在要求口令时失败报错。

在使用带SSL的gpload时,用户要在YAML控制文件中指定服务器证书的位置。在使用带SSL 的gpfdist时,用户用--ssl选项指定服务器证书的位置。

下面的例子展示了如何安全地装载数据到外部表中。这个例子从所有带txt扩展名的文件使用 gpfdists协议创建一个可读外部表ext_expenses。这些文件被格式化为用一个竖线(|)作为列定界符,并且用空格表示空。

(1)     在Segment主机上用--ssl选项运行gpfdist。

(2)     登录数据库并执行下列命令:

=# CREATE EXTERNAL TABLE ext_expenses

   ( name text, date date, amount float4, category text, desc1 text )

LOCATION ('gpfdists://etlhost-1:8081/*.txt', 'gpfdists://etlhost-2:8082/*.txt')

FORMAT 'TEXT' ( DELIMITER '|' NULL ' ') ;


11 SQL查询调优

SeaSQL DWS数据库基于代价的优化器会权衡所有执行查询的策略并选择代价最小的策略去执行。

和其他RDBMS的优化器类似,在计算可选执行计划的代价时,SeaSQL DWS的优化器会考虑诸如要连接的表中的行数、索引的可用性以及列数据的基数等因素。优化器还会考虑数据的位置、倾向于在Segment上做尽可能多的工作以及最小化完成查询必须在Segment之间传输的数据量。

当查询运行的比预期慢时,用户可以查看优化器选择的计划以及它为计划的每一步计算出的代价。这将帮助用户确定哪些步骤消耗了最多的资源,然后修改查询或者模式来为优化器提供更加有效的执行方法。用户可以使用SQL语句EXPLAIN来查看查询的执行计划。

优化器基于为表生成的统计信息产生计划。精确的统计信息对于产生最好的执行计划非常重要。有关更新统计信息的方法参见7.2  用ANALYZE更新统计信息

11.1  如何生成执行计划

EXPLAIN和EXPLAIN ANALYZE语句是查询原因并改进查询性能的有用工具。

·     EXPLAIN会为查询显示其查询计划和估算的代价,但是不执行该查询。

·     EXPLAIN ANALYZE除了显示查询的查询计划之外,还会执行该查询。

EXPLAIN ANALYZE会丢掉任何来自SELECT语句的输出,但是其他语句中的操作会被执行(例如INSERT,UPDATE或DELETE)。要在DML语句上使用EXPLAIN ANALYZE却不让该命令影响数据,可以明确地把EXPLAIN ANALYZE用在一个事务中(BEGIN; EXPLAIN ANALYZE ...; ROLLBACK;)。

EXPLAIN ANALYZE语句运行后除了显示执行计划外,还有以下额外信息:

¡     运行该查询消耗的总时间(以毫秒计)

¡     计划节点操作中涉及的worker(Segments)数量

¡     操作中产生最多行的Segment返回的最大行数(及其Segment ID)

¡     操作所使用的内存

¡     从产生最多行的Segment中检索到第一行所需的时间(以毫秒计),以及从该Segment中检索所有行花费的总时间。

11.2  如何阅读执行计划

执行计划是一份报告,它详细描述了SeaSQL DWS数据库优化器确定的执行查询要遵循的步骤。查询计划是一棵由节点构成的树,应该从下向上阅读,每一个节点都会将其结果传递给其上一级节点。每个节点表示执行计划中的一个步骤,每个节点对应的那一行标识了在该步骤中执行的操作。例如,一个扫描、一个连接、一个聚集或者排序操作。节点还标识了用于执行该操作的方法。例如,扫描操作的方法可能是顺序扫描或者索引扫描。而连接操作可以执行哈希连接或者嵌套循环连接。

下面是一个简单查询的执行计划。查询存储在每一个Segment分布表中的行数。

# EXPLAIN SELECT gp_Segment_id, count(*)

                  FROM contributions

                  GROUP BY gp_Segment_id;

                                 QUERY PLAN                       

--------------------------------------------------------------------------------

 Gather Motion 2:1  (slice2; Segments: 2)  (cost=0.00..431.00 rows=2 width=12)

   ->  GroupAggregate  (cost=0.00..431.00 rows=1 width=12)

         Group By: gp_Segment_id

         ->  Sort  (cost=0.00..431.00 rows=1 width=12)

               Sort Key: gp_Segment_id

               ->  Redistribute Motion 2:2  (slice1; Segments: 2)  (cost=0.00..431.00 rows=1 width=12)

                     Hash Key: gp_Segment_id

                     ->  Result  (cost=0.00..431.00 rows=1 width=12)

                           ->  GroupAggregate  (cost=0.00..431.00 rows=1 width=12)

                                 Group By: gp_Segment_id

                                 ->  Sort  (cost=0.00..431.00 rows=7 width=4)

                                       Sort Key: gp_Segment_id

                                       ->  Seq Scan on table1  (cost=0.00..431.00 rows=7 width=4)

 Optimizer status: Pivotal Optimizer (GPORCA) version 2.56.0

(14 rows)

 

这个执行计划有八个节点–Seq Scan、Sort、GroupAggregate、Result、Redistribute Motion、Sort、 GroupAggregate和最后的Gather Motion。每一个节点包含三个代价估值:代价估值(以顺序页面读取的方式)-cost、行数-rows、以及行宽度-width。

代价评估由两部分构成。1.0的代价等于一次顺序磁盘页面读取。

·     第一部分是启动代价,它是获取第一行的代价。

·     第二部分是总代价,它是得到所有行的代价。

行数评估是由计划节点输出的行数。这个数字可能会小于执行计划节点实际处理或者扫描的行数,它反映了WHERE子句条件的选择度评估。总代价代表假设所有的行将被检索出来的代价评估,但并非总是这样(例如,如果用户使用LIMIT子句,情况可能不一样)。

宽度评估是计划节点输出的所有列的以字节计的总宽度。

节点中的代价评估包括了其所有子节点的代价总和,因此执行计划中最顶层节点(通常是一个Gather Motion)具有对计划总体执行代价的评估。这就是查询规划器想要最小化的那个数字。

扫描操作符扫描表中的行以寻找一个行的集合。对于不同种类的存储有不同的扫描操作符。它们包括:

·     Seq Scan:扫描表中的所有行。

·     Index Scan:遍历一个索引以从表中取得行。

·     Bitmap Heap Scan:从索引中收集表中行的指针并且按照磁盘上的位置进行排序。(无论是否是AO表,该操作都会调用一个Bitmap Heap Scan)

·     Dynamic Seq Scan:使用一个分区选择函数来选择分区。

Join操作符包括以下这些:

·     Hash Join:从较小的表构建一个哈希表,用连接列作为哈希键。然后扫描较大的表,为连接列计算哈希键 并且探索哈希表寻找具有相同哈希键的行。哈希连接通常是SeaSQL DWS数据库中最快的连接方式。执行计划中的Hash Cond显示列出要被连接的列。

·     Nested Loop:在较大数据集的行上迭代,在每次迭代时从较小的数据集中扫描行。嵌套循环连接要求广播其中的一个表,这样一个表中的所有行才能与其他表中的所有行进行比较。它在较小的表或者通过使用索引约束的表上性能表现的更好。它还被用于笛卡尔积和范围连接。在使用Nested Loop连接大型表时会有性能影响。对于包含Nested Loop连接操作符的执行计划节点,应该验证SQL并且确保结果是想要的结果。设置服务器配置参数enable_nestloop 为OFF(默认)能够让优化器更倾向于使用Hash Join。

·     Merge Join:排序两个数据集并且将它们合并起来。归并连接对预排序好的数据很快,但是在现实世界中很少见。为了更倾向于使用Merge Join而不是Hash Join,可以把系统配置参数enable_mergejoin设置为ON。

一些查询计划节点指定数据移动操作。在处理查询操作时,数据移动操作在Segment之间移动行。该节点标识执行移动操作使用的方法。Motion操作符包括以下这些:

·     Broadcast motion:每一个Segment将自己的行发送给所有其他Segment,这样每一个Segment实例都有表的一份完整的本地拷贝。Broadcast motion可能不如Redistribute motion那么好,因此优化器通常只在小表上选择 Broadcast motion。对大表来说,Broadcast motion是不可接受的。在数据没有按照连接键分布的情况下,将把一个表中所需的行动态重分布到另一个Segment。

·     Redistribute motion:每一个Segment重新哈希数据并且把行发送到对应于哈希键的合适的Segment上。

·     Gather motion:来自所有Segment的结果数据被组装成一个单一的流。对大部分执行计划来说这是最后的操作。

查询计划中出现的其他操作符包括:

·     Materialize:规划器将一个子查询物化一次,这样就不用为顶层行重复该工作。

·     InitPlan:一个预查询,被用在动态分区裁剪中,当执行时还不知道规划器需要用来标识要扫描分区的值时,会执行这个预查询。

·     Sort:为另一个要求排序数据的操作(例如Aggregation或者Merge Join)准备排序数据。

·     Group By:通过一个或者更多列分组行。

·     Group/Hash Aggregate:使用哈希聚集行。

·     Append:串接数据集,例如在整合从分区表中各分区扫描的行时会用到。

·     Filter:使用来自于一个WHERE子句的条件选择行。

·     Limit:限制返回的行数。

11.3  优化SeaSQL DWS查询

该小节主要描述在某些情况下,提高系统性能的SeaSQL DWS数据库特性和编程实践。

为了分析执行计划,首先找出评估代价非常高的计划节点。判断估计的行数和代价是不是和该操作执行的行数相关。

如果使用分区,验证是否实现了分区裁剪。要实现分区裁剪,查询谓词(WHERE子句)必须与分区条件相同。还有WHERE子句不能包含显式值,并且不能含有子查询。

用户一般想要执行顺序构建在较小的表,最优情况下,最大的表被用于最后的连接,以减少传递到树最顶层计划节点的行数。如果分析结果显示,构建的执行顺序不是最优的,应确保数据库统计信息为最新,可以运行ANALYZE去更新数据库统计信息,进而产生一个最优的查询计划。

查找计算性倾斜的迹象。当Hash Aggregate和Hash Join之类的操作导致Segment上执行的不平均时,查询执行过程中会发生计算性倾斜。在一些Segment上会使用比其他Segment耗费更多的CPU和内存,导致非最优化执行。原因可能出现在具有低基数或者非一致分布的列上使用连接、排序或者聚集操作。用户可以在查询的EXPLAIN ANALYZE 语句中检测计算性倾斜。每个节点包括任一Segment所处理的最大行数以及所有Segment处理的平均行数。如果最大行数远大于平均数,那么至少有一个Segment执行了比其他Segment更多的工作,此时该操作符可能出现了计算性倾斜。

确定执行Sort或者Aggregate操作的执行计划节点。Aggregate操作隐藏了一个Sort。如果Sort或者Aggregate 操作涉及到大量行,这就是改进查询性能的机会。在需要排序大量行时,HashAggregate操作会比Sort和Aggregate操作可以得到一个更好的性能。通常优化器会因为SQL结构(也就是编写SQL的方式)而选择Sort操作。在查询重写时,大部分的Sort操作可以用HashAggregate替换。要更倾向于使用HashAggregate操作而不是Sort和Aggregate,请确保服务器配置参数的enable_groupagg被设置为ON。

当执行计划显示带有大量行的广播移动时,用户应该尝试消除广播移动。一种方法是使用服务配置参数gp_segments_for_planner来增加这种移动的代价评估,这样优化器会偏向其他可替代的方案。gp_segments_for_planner变量告诉查询规划器在其计算中使用多少主Segment,默认值是零量。增加主Segment的数量会增加移动的代价,因此会更加偏向重新分布移动而不是广播。例如,设置gp_segments_for_planner = 100000 会告诉规划器有100,000个segment。反过来,要影响规划器广播表而不是重新分布它,可以把gp_segments_for_planner设置为一个较低的值,例如2。

11.4  SeaSQL DWS分组扩展

SeaSQL DWS数据库对GROUP BY子句的聚集扩展可以让一些常见计算在数据库中执行的比在应用或者存储过程代码中更加高效:

·     GROUP BY ROLLUP(col1, col2, col3)

·     GROUP BY CUBE(col1, col2, col3)

·     GROUP BY GROUPING SETS((col1, col2), (col1, col3))

ROLLUP分组创建从最详细层次上滚到总计的聚集小计,后面跟着分组(或者表达式)列表。 ROLLUP接收分组列的一个有序列表,计算GROUP BY子句中指定的标准聚集值,然后根据该列表从右至左渐进地创建更高层的小计。最后创建总计。

CUBE分组创建给定分组(或者表达式)列表所有可能组合的小计。在多维分析术语中,CUBE产生一个数据立方体在指定维度可以被计算的所有小计。

用户可以用GROUPING SETS表达式选择性地指定想要创建的分组集。这允许在多个维度间进行精确的说明,而无需计算整个ROLLUP或者CUBE。

11.5  窗口函数

窗口函数是在结果集的划分上应用了聚集或者排名函数,例如,sum(population) over (partition by city)。窗口函数很强大,因为它们的所有工作都在数据库内完成,它们比通过从数据库中检索细节行并且预处理它们来产生类似结果的前端工具更有性能优势。

·     row_number()窗口函数为一个划分中的行产生行号,例如row_number() over (order by id)。

·     当查询计划表明一个表被多个操作扫描时,用户可以使用窗口函数来降低扫描次数。

·     一般情况下,可以通过使用窗口函数消除自连接。


12 高可用性

SeaSQL DWS数据库是支持高可用、容错性能的数据库服务。要保证达到要求的服务等级,每个Segment都必须有一个备用Segment以保证在它失效时能够及时顶上。

12.1  磁盘存储

源于SeaSQL DWS数据库的非共享MPP架构,每台Master和Segment主机都有它们自己独立的内存和磁盘存储空间,每个Master和Segment实例都有它们自己独立的数据目录。为了兼顾可靠性和高性能,推荐采用8到24块磁盘的硬件RAID5存储解决方案。当采用RAID5时,大量的磁盘会提升I/O吞吐量,因为条带会增加并行的磁盘I/O。有一个失效磁盘时,RAID控制器能够继续工作,因为它在每个磁盘上都保存了校验数据,这样它能够重构阵列中任何失效磁盘上的数据。如果配置了热备盘(或者配置了能够用新磁盘替代故障磁盘的操作器),控制器能够自动重建故障磁盘。

在RAID5模式下,故障磁盘上的每一个数据I/O都必须从剩余活动磁盘上重建出来,直到故障磁盘重建完成,因此会出现一段时间的性能下降。如果磁盘数据重建期间,SeaSQL DWS数据库Master和Segment配置了镜像实例,可以将任何受到影响的SeaSQL DWS数据库实例切换为它们的镜像以保证性能最优。

RAID磁盘阵列仍然可能会出现单点故障,例如整个RAID卷故障。在硬件级别上,可以通过RAID控制器提供的镜像功能或操作系统提供的镜像功能来防范磁盘阵列故障。

定期监控每台主机的可用磁盘空间是很重要的。可以通过查询gp_toolkit.gp_disk_free表来查看segment节点的磁盘可用空间。该视图会运行Linux命令df。在执行占用大量磁盘空间的操作(例如copy大表)前要确保检查可用磁盘空间。

最佳实践

·     使用带有8到24个磁盘的硬件RAID存储方案。

·     使用RAID5,这样磁盘阵列能容忍一个失效磁盘。

·     为了提升性能,建议打开RAID卡读写缓存(需要配置RAID卡备用电池),关闭物理磁盘的写缓存。

·     在磁盘阵列中配置一个热备,以允许在检测到磁盘失效时自动开始重建。

·     通过镜像RAID卷防止整个磁盘阵列的失效和重建期间的退化。

·     定期监控磁盘使用率并且在需要时增加额外的空间。

·     监控Segment是否倾斜,以确保数据被均匀地分布在所有Segment上。

12.2  Master镜像

SeaSQL DWS数据库的Master实例是客户端访问系统的唯一入口。Master实例存储全局系统目录,也就是存储有关数据库实例的元数据的一系列系统表,但是它不存储用户数据。

Master镜像使用两个进程使standby与Master同步,一个sender位于活动Master主机上,一个receiver位于standby主机上。随着客户端操作的变化数据被应用到Master系统目录上,活动Master会将预写日志(WAL)以流复制的方式应用到standby节点,以保证每一个应用到Master实例的事务也同时能够应用到了standby上。

镜像是一个热备。如果主Master失效,SeaSQL DWS会及时的自动切换到standby节点,并且对应的虚拟IP也会自动漂移到备用节点上,对于用户而言切换动作基本上无感知的,业务只会短暂的受影响。

12.3  Segment镜像

SeaSQL DWS数据库的每一个Segment实例都在Master实例的协调下存储和管理数据库数据的一部分。因此,镜像Segment是高可用方案的一个不可或缺的元素。

Segment镜像是主Segment的热备。SeaSQL DWS数据库会检测到Segment不可用时,自动激活其镜像。在正常操作期间,当主Segment实例活动时,数据以两种方式从主Segment复制到镜像Segment:

·     第一种,在事务被提交之前,事务提交日志从主Segment复制到镜像Segment。这会确保当镜像被激活时,主Segment上最后一个成功的事务所做的更改会出现在镜像上。当镜像被激活时,日志中的事务会被应用到镜像中的表上。

·     第二种,使用流复制方式将主Segment数据同步到镜像Segment中。

当活动的主Segment不能访问其镜像时,复制会停止。主Segment 会把没有被复制到镜像的更改保存在一个系统表中,等到镜像重新在线时这些更改会被复制到镜像。

Master会自动检测Segment故障并且激活镜像。故障时正在进行的事务会使用新的主Segment重新开始。根据镜像部署在主机上的方式,数据库系统可能会不平衡,直到原始的主Segment被恢复。例如,如果每台Segment主机有四个主Segment和四个镜像Segment,并且在一台主机上有一个镜像Segment被激活,那台主机将有五个活动的主Segment。查询直到最后一个Segment完成其工作才算结束,因此性能可能会退化,直到原始的主Segment被恢复使得系统恢复平衡。

当SeaSQL DWS数据库运行时,管理员通过运行gprecoverseg工具执行恢复。这个工具会定位故障的Segment、验证它们是否有效并且与当前活动的Segment对比事务状态以确定Segment离线期间发生的更改。gprecoverseg会与活动Segment同步数据并将该Segment重新拉回到在线状态。

在故障期间,有必要在Segment主机上保留足够的内存和CPU资源,以允许承担了主Segment的镜像实例的活动负载。

最佳实践

及时恢复失效的Segment,让系统回到最佳的平衡状态。

12.4  双集群

对于一些用例,可以通过维护两个存储同样数据的SeaSQL DWS数据库集群提供额外层次的冗余。是否实现双集群应该考虑到业务需求。

在双集群配置中为了保持数据同步,有两种推荐方法。

·     第一种方法被称作双ETL。ETL(抽取、转换和装载)是常见的将数据装载到数据仓库中的处理方式。通过双ETL提供了一个存有相同数据的备用集群。它还提供了在两个集群上查询数据的能力,并使得处理吞吐量翻倍。应用可以根据需要利用两个集群,还要确保ETL在两边都成功并且被验证。

·     第二种方法是双集群的备份和恢复。数据在主集群上被备份,然后备份被复制到第二个集群并且在其上恢复。备份和恢复机制比双ETL的延迟更高,但是需要开发的应用逻辑更少。对于按每天或者更低频率进行数据修改和ETL的场景,备份和恢复是理想的方案。

最佳实践

为了提供额外级别的冗余安全和额外的查询处理吞吐量,可以考虑双集群配置。

12.5  备份和恢复

推荐为SeaSQL DWS数据库进行备份,除非数据库中的数据可以很容易并且很干净地从源数据再生。备份可以防止操作错误、业务错误或者硬件错误。

gpbackup工具在Segment之间并行地做备份,因此随着集群的硬件尺寸增长备份的尺度也会放大。

备份策略必须考虑备份将被写到什么地方以及它们将被存放在哪里。备份可以放置在本地集群的磁盘上,但是它们不应永久存放在那里。如果数据库及其备份在同一种存储上,它们可能会同时丢失。备份还占据数据库存储或者操作所需的空间。在执行本地备份后,备份文件应该被拷贝到一个安全的、集群外的位置。

另一种策略是直接备份到一个NFS挂载存储上。如果集群中的每台主机都有一个NFS挂载,备份可以被直接写到NFS存储上。推荐使用一种可扩展的NFS方案以确保备份不会受到NFS设备的IO吞吐瓶颈限制。

最佳实践

·     定期备份SeaSQL DWS数据库,除非能很容易地从源数据恢复数据。

·     使用gpbackup备份想要备份的模式和表。

·     gpbackup在要备份的表上放置SHARED ACCESS锁。对少量表的备份在恢复时速度更快而且更方便,毕竟gprestore可以搜索整个数据库。

·     如果备份被保存在本地存储,当备份完成后需要将备份文件移动到一个安全且非集群存储的位置。备份文件和数据库文件存储放在一起很容易一起丢失。

12.6  检测故障的Master和Segment实例

即使系统已经检测到故障并为故障组件激活了它的备用节点,从系统故障中恢复回来仍然要求有系统管理员的干预。在任何一种情况下,故障组件都必须被替换或恢复,以保证系统处于完全冗余状态。在故障组件被恢复之前,当前活动组件会处于缺少备份状态,并且系统也并没有在最优的状态下运行。由于这些原因,及时执行恢复操作是非常必要的。持续的系统监控和自动故障告警会通过SNMP和email确保管理员注意到系统故障并且采取行动。

SeaSQL DWS数据库服务器的ftsprobe子进程负责故障检测。每隔一段时间,ftsprobe会连接到所有的Segment并且扫描所有Segment和数据库进程,这个时间间隔可以用gp_fts_probe_interval 配置参数设置。如果ftsprobe无法连接到一个Segment,它会在SeaSQL DWS数据库系统目录中把该Segment标记为down。在管理员运行恢复工具之前,该Segment都会保持down的状态。

最佳实践

通过Monitor可以查看SeaSQL DWS系统的总体状态。

不同款型规格的资料略有差异, 详细信息请向具体销售和400咨询。H3C保留在没有任何通知或提示的情况下对资料内容进行修改的权利!

新华三官网
联系我们