欢迎使用《Vertica 管理员指南》。本文档介绍如何设置和维护 Vertica Analytics Platform 数据库。
先决条件
本文档假设:
本文档描述了 Vertica 数据库管理员 (DBA) 执行的各项任务。这些任务只能使用安装 Vertica 时创建的专用数据库管理员帐户来执行。本文档集中的示例假设管理帐户的名称为 dbadmin。
若要执行某些群集配置和管理任务,DBA(管理帐户用户)必须能够提供这些主机的 root 密码。如果此要求与贵组织的安全策略冲突,则必须由您的 IT 工作人员来执行这些任务。
如果执行管理任务时使用的帐户与安装期间提供的帐户不同,Vertica 将发生文件所有权问题。
如果共享管理帐户密码,请确保任何时间只有一个用户运行 管理工具 。否则,自动配置传播无法正确运行。
管理工具要求调用者的 shell 为 /bin/bash
。其他 shell 将产生意外结果,且不受支持。
要使用 Vertica,必须先对其授权。Vertica 以一个或多个许可证文件的形式提供许可证,这些文件对许可证期限进行编码。
为防止引入使许可证失效的特殊字符,请勿在编辑器中打开许可证文件。通过这种方式打开文件可能会引入特殊字符,例如可能在编辑器内不可见的行结束符和文件终止符。无论是否可见,这些字符都会使许可证失效。
在 Windows 和 Linux 之间复制许可证密钥文件或将其复制到任何其他位置时,请注意不要以任何方式更改该文件。为了帮助防止应用程序尝试更改该文件,请将许可证文件封装在存档文件(例如 zip 或 .tar 文件)中。您应该备份一份许可证密钥文件。Micro Focus 建议您将备份保存在 /opt/vertica。
将许可证文件从一个位置复制到另一位置后,检查复制后的文件大小与您从 Vertica 收到的文件大小是否相同。
按照以下步骤获取许可证密钥文件:
使用您的通行证登录信息登录软件授权密钥站点。如果没有通行证帐户,请创建一个。
在“请求访问 (Request Access)”页面上,输入您的订单号并选择一个角色。
输入您的请求访问理由。
单击提交 (Submit)。
在请求获得批准后,您将收到一封确认电子邮件。在该站点上,单击授权 (Entitlements) 选项卡查看您的 Vertica 软件。
在“操作 (Action)”选项卡下,单击激活 (Activate)。您可以选择多种产品。
此时将打开“许可证激活 (License Activation)”页面。输入“目标名称 (Target Name)”。
选择您的 Vertica 版本和要激活的数量。
单击下一步 (Next)。
确认您的激活详细信息,然后单击“提交 (Submit)”。
此时将显示“激活结果 (Activation Results)”页面。按照新 Vertica 许可证安装或 Vertica 许可证更改中的说明完成安装或升级。
您的 Vertica 社区版下载包包含社区版许可证,允许三个节点和 1TB 的数据。Vertica 社区版许可证不会过期。
Vertica 的许可条款非常灵活。它可基于以下条件授权使用:
基于期限(在特定日期之前有效)。
基于大小(有效的目标存储量最多为指定的原始数据量)。
基于期限和基于大小。
无限制的持续时间和数据存储。
基于节点,CPU 和用户数无限制(一个节点是充当单一计算机系统的服务器,可以是物理服务器,也可以是虚拟服务器)。
即用即付模式,您只需按使用的小时数付费。此许可证可在 AWS Marketplace 上获得。有关详细信息,请参阅Amazon Web Services (AWS) 上 Vertica 的概述。
许可基础已编码到您的许可证密钥中。如果您不确定当前许可证,可以在 Vertica 内查看许可证信息。
Vertica 社区版 (CE) 是免费版本,允许客户创建数据库,但具有以下限制:
最多 3 个节点
最多 1 TB 数据
社区版许可证不能共置安装在 Hadoop 基础架构中,也不能用于查询以 Hadoop 格式存储的数据。
接受社区版 (CE) 许可证,即视为您同意收集一些匿名的、非识别性的使用数据。这些数据有助于 Vertica 了解客户如何使用产品并开发新功能。我们不会收集任何个人数据。有关收集内容的详细信息,请参阅社区版最终用户许可协议。
Vertica for SQL on Apache Hadoop 是一个具有自己许可证的单独产品。本文档涵盖这两个产品。有关可用功能和限制的详细信息,请参阅您的许可协议。
应用 Vertica 许可证密钥所遵循的步骤各不相同,具体视您应用的许可证类型和是否升级许可证而定。
按照以下步骤安装新 Vertica 许可证:
将您从软件授权密钥站点生成的许可证密钥文件复制到您的 管理主机。
确保将许可证密钥文件的文件权限设置为 400(读取权限)。
如果尚未安装 Vertica,请根据“安装 Vertica”中的说明进行安装。界面会提示您提供许可证密钥文件。
要安装社区版,请将默认路径留空并单击确定 (OK)。要应用评估版或高级版许可证,请输入下载到管理主机的许可证密钥文件的绝对路径,然后按确定 (OK)。第一次以 数据库超级用户身份登录并运行 管理工具时,界面将提示您接受最终用户许可协议 (EULA)。
选择查看 EULA (View EULA)。
退出 EULA,然后选择接受 EULA (Accept EULA) 以正式接受 EULA 并继续安装许可证,或者选择拒绝 EULA (Reject EULA) 以拒绝 EULA 并返回到“高级菜单 (Advanced Menu)”。
如果您的许可证即将到期,或者您要扩大许可的数据库数据大小,则必须续订或升级许可证。获得续订或升级许可证密钥文件后,可使用管理工具或管理控制台进行安装。
除非要增加数据库的容量,否则升级不需要新的许可证。您可以使用软件授权密钥为数据库增加容量,并且无需卸载并重新安装许可证即可增加容量。
将您从软件授权密钥站点生成的许可证密钥文件复制到您的 管理主机。
确保将许可证密钥文件的文件权限设置为 400(读取权限)。
如果数据库尚未运行,请启动数据库。
在管理工具中,选择“高级 (Advanced) > 升级许可证密钥 (Upgrade License Key)”,然后单击“确定 (OK)”。
输入新许可证密钥文件的绝对路径,然后单击“确定 (OK)”。界面将提示您接受最终用户许可协议 (EULA)。
选择“查看 EULA (View EULA)”。
退出 EULA,然后选择“接受 EULA (Accept EULA)”以正式接受 EULA 并继续安装许可证,或者选择“拒绝 EULA (Reject EULA)”以拒绝 EULA 并返回到“高级菜单 (Advanced Menu)”。
在管理控制台中您的数据库的“概览 (Overview)”页面中,单击“许可证 (License)”选项卡。此时将显示“许可证 (License)”页面。可在此页查看已安装的许可证。
单击“许可证 (License)”页面顶部的“安装新许可证 (Install New License)”。
从本地计算机浏览到许可证密钥的位置,然后上传该文件。
单击页面顶部的“应用 (Apply)”。管理控制台将提示您接受最终用户许可协议 (EULA)。
选中正式接受 EULA 并继续安装许可证的复选框,或者单击“取消 (Cancel)”退出。
如果要为数据库增加容量,则无需卸载并重新安装许可证。相反,您可以安装多个许可证来增加数据库的大小。此附加容量仅适用于具有相同格式的许可证,例如将高级版许可证容量添加到现有的高级版许可证类型中。当您增加容量时,许可证的大小将是两个许可证的总和;以前的许可证不会被覆盖。您不能使用两种不同的许可证格式添加容量,例如将 Hadoop 许可证容量添加到现有的高级版许可证。
您可以运行 AUDIT()
函数来验证许可证容量是否已增加。在审核功能自动运行期间,许可证中增加的容量将反映出来。如果您想立即看到增加容量的结果,请运行 AUDIT()
函数进行刷新。
可使用多个函数显示许可证期限和当前状态。
使用 DISPLAY_LICENSE SQL 函数显示许可证信息。此函数显示许可证的有效日期(或者 Perpetual
,如果许可证未到期)和任何原始数据限额。例如:
=> SELECT DISPLAY_LICENSE();
DISPLAY_LICENSE
---------------------------------------------------
Vertica Systems, Inc.
2007-08-03
Perpetual
500GB
(1 row)
也可以查询 LICENSES 系统表以查看安装的许可证的相关信息。该表显示许可证类型、许可证有效日期以及许可证强加的大小和节点限制。
或者,使用管理控制台中的 LICENSES 表。在数据库“概览 (Overview)”页面上,单击“许可证 (License)”选项卡以查看您安装的许可证的相关信息。
如果许可证包括原始数据大小限额,则 Vertica 会定期审核数据库大小,以确保其仍符合许可协议。如果许可证具有期限限制,Vertica 也会定期检查,以查看许可证是否已到期。您可以使用 GET_COMPLIANCE_STATUS 函数查看最新审核结果。
=> select GET_COMPLIANCE_STATUS();
GET_COMPLIANCE_STATUS
---------------------------------------------------------------------------------
Raw Data Size: 2.00GB +/- 0.003GB
License Size : 4.000GB
Utilization : 50%
Audit Time : 2011-03-09 09:54:09.538704+00
Compliance Status : The database is in compliance with respect to raw data size.
License End Date: 04/06/2011
Days Remaining: 28.59
(1 row)
要查看您的 ORC/Parquet 数据如何影响许可证合规性,请参阅查看 Hadoop 文件格式的许可证合规性。
许可证使用情况的相关信息位于“设置 (Settings)”页面上。请参阅监控数据库大小是否符合许可证要求。
您可以使用 EXTERNAL_TABLE_DETAILS 系统表来收集有关基于 Hadoop 文件格式的所有表的信息。此信息可以帮助您了解基于 ORC 和 Parquet 的数据使用了许可证数据限额中的多少数据量。
Vertica 在查询时计算此表中的值,因此,为避免出现性能问题,请将您的查询限制为按 table_schema、table_name 或 source_format 进行筛选。在谓词中只能使这三列,但可以使用所有常用的谓词运算符。
=> SELECT * FROM EXTERNAL_TABLE_DETAILS
WHERE source_format = 'PARQUET' OR source_format = 'ORC';
-[ RECORD 1 ]---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
schema_oid | 45035996273704978
table_schema | public
table_oid | 45035996273760390
table_name | ORC_demo
source_format | ORC
total_file_count | 5
total_file_size_bytes | 789
source_statement | COPY FROM 'ORC_demo/*' ORC
file_access_error |
-[ RECORD 2 ]---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
schema_oid | 45035196277204374
table_schema | public
table_oid | 45035996274460352
table_name | Parquet_demo
source_format | PARQUET
total_file_count | 3
total_file_size_bytes | 498
source_statement | COPY FROM 'Parquet_demo/*' PARQUET
file_access_error |
在计算外部表的大小时,Vertica 会计算在 COPY FROM 子句所指定的位置找到的所有数据。例如,如果您有一个包含 ORC 和分隔文件的目录,并且您使用“COPY FROM *”(而不是“COPY FROM *.orc”)定义外部表,则此表包括分隔文件的大小。(在查询此外部表时,可能也会遇到错误。)当您查询此表时,Vertica 不会验证您的表定义;它只是使用路径来查找要报告的文件。
还可以使用 AUDIT 函数来查找特定表或架构的大小。在 ORC 或 PARQUET 外部表上使用 AUDIT 函数时,将忽略容错度和置信水平参数。相反,AUDIT 总是返回磁盘上 ORC 或 Parquet 文件的大小。
=> select AUDIT('customers_orc');
AUDIT
-----------
619080883
(1 row)
您可以一直使用 Vertica 软件,直到列式数据达到许可证协议允许的最大原始数据大小。Vertica 会定期对列式数据大小运行审核,以确认您的数据库是否符合此协议。还可以使用两个函数运行自己的数据库大小审核:
AUDIT:估算数据库、架构或表的原始数据大小。
AUDIT_FLEX:估算数据库、架构或投影中的一个或多个 Flex 表的大小。
以下两个示例审核数据库和一个架构:
=> SELECT AUDIT('', 'database');
AUDIT
----------
76376696
(1 row)
=> SELECT AUDIT('online_sales', 'schema');
AUDIT
----------
35716504
(1 row)
AUDIT 和 AUDIT_FLEX 使用统计抽样来估算表中所存储数据(即数据库中存储的未压缩数据)的原始数据大小。对于大部分数据类型,Vertica 在评估原始数据大小时,会假定数据是以文本格式从数据库中导出来的,而不是以压缩数据形式。有关详细信息,请参阅评估数据类型占用空间。
通过使用统计抽样,审核功能会将它对数据库性能的影响降至最低。准确度和性能影响之间只能在一个很小的误差范围内进行权衡。数据库大小报告中包含误差范围,所以您可以对估算的准确性进行评估。
还会审核基于 ORC 和 Parquet 的外部表中的数据,无论它们是存储在本地 Vertica 群集的文件系统中,还是存储在远程 S3 或 Hadoop 群集中。AUDIT 始终使用底层数据文件的文件大小作为表中的数据量。例如,假定您有一个基于存储在 HDFS 中的 1GB ORC 文件的外部表,对该表进行审核后,报告该表的大小为 1GB。
出现在多个投影中的表数据只计数一次。审核也不包括以下数据:
临时表数据。
SET USING 列中的数据。
可通过外部表定义访问的非列式数据。ORC 和 Parquet 等列式格式的数据计入总数。
已删除但尚未清除的数据。
存储在系统和工作表中的数据,例如,监控表、 数据收集器表以及 Database Designer 表。
分隔符。
Vertica 按如下方式评估不同数据类型的占用空间:
使用 UTF-8 编码的字符串和二进制类型(CHAR、VARCHAR、BINARY、VARBINARY)按实际字节数进行计数。
数值数据类型按它们打印时的内容进行求值。每个位数计作一个字节,小数点、符号或科学记数法也是如此。例如,-123.456
按八个字节(六个数字加一个小数点和一个减号)计数。
日期/时间数据类型按它们转换为文本后的内容(包括任何连字符、空格和冒号)进行求值。例如,vsql 将时间戳值 2011-07-04 12:00:00
打印为 19 个字符或 19 个字节。
复杂类型的求值结果等于其各个组成部分的大小之和。数组计数为所有元素的总大小,ROW 计数为所有字段的总大小。
AUDIT 可以指定审核的容错度和置信度,默认情况下分别设置为 5% 和 99%。例如,可以通过将容错度和置信水平分别设置为 0% 和 100% 来获得高水平的审核准确度。与使用统计抽样估算原始数据大小不同,Vertica 将所有经过审核的数据转储为原始格式计算其大小。
以下示例以 25% 的容错度审核数据库:
=> SELECT AUDIT('', 25);
AUDIT
----------
75797126
(1 row)
以下示例以 25% 的容错度和 90% 的置信水平审核数据库:
=> SELECT AUDIT('',25,90);
AUDIT
----------
76402672
(1 row)
您的 Vertica 许可证可以包括数据存储限额。该限额可包含列式表数据或 Flex 表数据,也可以同时包含这两种类型的数据。AUDIT()
函数可估算列式表数据大小和任何 Flex 表实体化列。AUDIT_FLEX()
函数可估算 Flex 表或列式表中的 __raw__
列数据量。在许可证数据限制方面,__raw__
列中的数据以结构化数据大小的 1/10 来计算。通过监控列式表和 Flex 表的数据大小,您可以计划安排删除旧数据,以使您的数据库符合许可证要求,也可以考虑升级许可证以获得额外的数据存储。
__raw__
列数据。
Vertica 会定期对列式数据大小运行审核,以验证您的数据库是否符合许可证条款。您可以通过调用 GET_COMPLIANCE_STATUS 函数来查看最新审核的结果。
=> select GET_COMPLIANCE_STATUS();
GET_COMPLIANCE_STATUS
---------------------------------------------------------------------------------
Raw Data Size: 2.00GB +/- 0.003GB
License Size : 4.000GB
Utilization : 50%
Audit Time : 2011-03-09 09:54:09.538704+00
Compliance Status : The database is in compliance with respect to raw data size.
License End Date: 04/06/2011
Days Remaining: 28.59
(1 row)
通常,定期运行 GET_COMPLIANCE_STATUS 来监控数据库许可证状态足以确保您的数据库符合许可证要求。如果数据库开始接近其列式数据限额,可使用下面介绍的其他审核函数来确定不断增长的数据库方面以及最近删除项对数据库大小的影响。
可使用 AUDIT_LICENSE_SIZE 函数手动检查数据库中所有列式数据的许可证合规性。此函数执行的审核与 Vertica 定期自动执行的审核相同。AUDIT_LICENSE_SIZE 函数在后台检查运行,因此会立即返回结果。然后可使用 GET_COMPLIANCE_STATUS 查询这些结果。
__raw__
列中的数据。实体化列是已升级到实际列的虚拟列。您在创建 Flex 表时定义的列或使用 ALTER TABLE...ADD COLUMN
语句添加的列均为实际列。所有 __raw__
列都是实际列,但因其包含非结构化或半结构化数据,故将单独进行审核。
AUDIT_LICENSE_SIZE 的替代函数是使用 AUDIT 函数,向该函数传递一个空字符串即可审核整个数据库中的列式表大小。此函数以同步方式执行,当其估算出数据库大小后即会返回结果。
=> SELECT AUDIT('');
AUDIT
----------
76376696
(1 row)
数据库大小以字节为单位进行报告。此外,通过 AUDIT 函数,还可以使用其他参数来控制所估算数据库大小的准确性。有关完整的详细信息,请参阅与 AUDIT 函数相对应的条目。Vertica 不会将 AUDIT 函数结果视为正式审核,也不会根据这些结果采取任何许可证合规性操作。
可使用 AUDIT_FLEX 函数手动审核具有 __raw__
列的 Flex 表或列式表的数据使用情况。该函数会针对任何 __raw__
列计算存储在 ROS 容器中的已编码压缩数据。Flex 表中的实体化列由 AUDIT
函数来计算。AUDIT_FLEX 结果不包括临时 Flex 表的 __raw__
列中的数据。
如果审核确定列式表估算结果出人意料的大,请考虑所用存储最多的架构、表或分区。可使用 AUDIT 函数对架构、表或分区执行定向审核,方法是提供要查找其大小的实体的名称。例如,要在 VMart 示例数据库中查找 online_sales 架构的大小,请运行以下命令:
=> SELECT AUDIT('online_sales');
AUDIT
----------
35716504
(1 row)
也可以使用 AUDIT 函数的粒度实参来更改审核粒度,以便报告更大实体中每个对象(例如,架构中的每个表)的大小。请参阅 AUDIT 函数。
也可以通过管理控制台获取有关列式数据(对于列式表和 Flex 表中的实体化列)的数据存储信息。此信息可在数据库的“概览 (Overview)”页面中获得,该页面以网格视图形式显示了数据库的整体运行情况。
许可计的指针会根据已用量(以 MB 为单位)进行调整。
宽限期表示许可证的期限部分。
“审核 (Audit)”按钮将以图形表示方式返回与 AUDIT() 函数相同的信息。
“许可证 (License)”网格(“审核 (Audit)”按钮旁边)内的“详细信息 (Details)”链接提供了有关许可证使用情况的历史信息。此页面还显示了一个进度条,用于指示已用量趋近许可证限值的百分比。
Vertica 许可证的期限部分很容易管理 — 您已获得在特定日期之前使用 Vertica 的授权。如果许可证期限到期,Vertica 会向您发出警告,并在 管理工具和 vsql 中显示消息。例如:
=> CREATE TABLE T (A INT);
NOTICE 8723: Vertica license 432d8e57-5a13-4266-a60d-759275416eb2 is in its grace period; grace period expires in 28 days
HINT: Renew at https://softwaresupport.softwaregrp.com/
CREATE TABLE
请尽快登录 https://softwaresupport.softwaregrp.com/ 与 Vertica 取得联系以续订许可证,然后安装新许可证。宽限期到期后,Vertica 将停止处理 DML 查询,但允许执行 DDL 查询,并且会显示警告消息。如果许可证到期并且安装了一个或多个有效的替代许可证,Vertica 将使用替代许可证。
如果 Vertica 列式许可证包括原始数据大小限额,则 Vertica 会定期审核数据库大小,以确保其仍符合许可协议。有关此审核的详细信息,请参阅审核数据库大小。您还应监控数据库大小,以了解数据库将在何时接近许可的使用限额。通过监控数据库大小,您可以计划是升级许可证以允许数据库的内容继续增多,还是从数据库中删除数据,以便仍然符合许可证要求。有关详细信息,请参阅监控数据库大小是否符合许可证要求。
如果数据库大小接近许可的使用限额(高于许可证限制的 75%),您将在 管理工具、 vsql 和管理控制台中看到警告。您可以通过两种选择来消除这些警告:
将许可证升级至更大的数据大小限额。
从数据库中删除一些数据,以便维持在许可的原始数据大小限额之内。在 Vertica 下一次审核数据库大小并且审核结果显示数据库不再接近或高于许可的使用量之后,警告会消失。还可以手动运行数据库审核(有关详细信息,请参阅监控数据库大小是否符合许可证要求)。
如果在收到说明数据库大小正在接近许可的大小限额的警告之后,数据库继续增长,Vertica 会在宽限期结束后在系统的更多部分显示更多警告。使用 GET_COMPLIANCE_STATUS 函数检查您的许可证状态。
如果高级版数据库大小超出许可的数据限额,来自 ODBC 和 JDBC 客户端的所有成功的查询都将返回 SUCCESS_WITH_INFO 状态,而非普通的 SUCCESS 状态。随结果一起发送的消息包含一个有关数据库大小的警告。您的 ODBC 和 JDBC 客户端应当准备好处理这些消息,而不是假设凡是成功的请求都会返回 SUCCESS。
如果您的社区版数据库大小超过 1 TB 的限制,Vertica 将停止处理 DML 查询,但允许执行 DDL 查询,不过会显示警告消息。
要确保数据库符合要求,可以选择执行以下操作:
删除数据库表。您还可以考虑截断表或删除分区。请参阅 TRUNCATE TABLE 或 DROP_PARTITIONS。
升级到 Vertica 高级版(或评估许可证)。
可以使用 admintools
对数据库的许可证合规性进行审核,然后以 CSV 格式导出结果,具体做法如下:
admintools -t license_audit [--password=password] --database=database] [‑‑file=csv-file] [‑‑quiet]
其中:
database 必须是正在运行的数据库。如果数据库受密码保护,则还必须提供密码。
\--file csv-file
将输出定向到指定的文件。如果 csv-file 已经存在,工具则会返回一条错误消息。如果未指定此选项,输出则会定向到 stdout
。
--quiet
指定工具应在安静模式下运行;如果未指定,状态消息则发送至 stdout
。
运行 license_audit
工具相当于调用以下 SQL 语句:
select audit('');
select audit_flex('');
select * from dc_features_used;
select * from v_catalog.license_audits;
select * from v_catalog.user_audits;
审核结果包括以下信息:
已使用的 Vertica 功能的日志
估计的数据库大小
Vertica 许可证所允许的原始数据大小
数据库当前使用的授权限额的百分比
审核时间戳
以下截断示例显示 license_audit
生成的原始 CSV 输出:
FEATURES_USED
features_used,feature,date,sum
features_used,metafunction::get_compliance_status,2014-08-04,1
features_used,metafunction::bootstrap_license,2014-08-04,1
...
LICENSE_AUDITS
license_audits,database_size_bytes,license_size_bytes,usage_percent,audit_start_timestamp,audit_end_timestamp,confidence_level_percent,error_tolerance_percent,used_sampling,confidence_interval_lower_bound_bytes,confidence_interval_upper_bound_bytes,sample_count,cell_count,license_name
license_audits,808117909,536870912000,0.00150523690320551,2014-08-04 23:59:00.024874-04,2014-08-04 23:59:00.578419-04,99,5,t,785472097,830763721,10000,174754646,vertica
...
USER_AUDITS
user_audits,size_bytes,user_id,user_name,object_id,object_type,object_schema,object_name,audit_start_timestamp,audit_end_timestamp,confidence_level_percent,error_tolerance_percent,used_sampling,confidence_interval_lower_bound_bytes,confidence_interval_upper_bound_bytes,sample_count,cell_count
user_audits,812489249,45035996273704962,dbadmin,45035996273704974,DATABASE,,VMart,2014-10-14 11:50:13.230669-04,2014-10-14 11:50:14.069057-04,99,5,t,789022736,835955762,10000,174755178
AUDIT_SIZE_BYTES
audit_size_bytes,now,audit
audit_size_bytes,2014-10-14 11:52:14.015231-04,810584417
FLEX_SIZE_BYTES
flex_size_bytes,now,audit_flex
flex_size_bytes,2014-10-14 11:52:15.117036-04,11850
此部分介绍设置 Vertica 数据库所需的任务。我们假设您拥有有效许可证密钥文件,已安装 Vertica rpm 包,并且已按照所述运行安装脚本。
您使用以下工具完成配置过程:
按照此部分所述的顺序完成配置过程。
Vertica 强烈建议您先尝试创建和配置数据库。
您可以在开发过程中多次使用此通用配置过程,也可以在每次使用时对其进行修改以便符合不断变化的目标。您可以忽略准备实际数据文件和示例查询等步骤,并运行 Database Designer,而无需针对查询进行优化。例如,对于开发和测试,您可以创建、加载和查询数据库多次,并在最后一次时创建并加载生产数据库。
必须创建并指定目录,以便在其中存储编录和数据文件( 物理架构)。可以在安装或配置数据库时,或以后在数据库操作期间指定这些位置。编录和数据目录都必须归 数据库超级用户所有。
为数据库编录文件指定的目录(目录路径)将跨群集中的所有节点使用。例如,如果指定 /home/catalog 作为编录目录,Vertica 将在所有节点上使用该编录路径。编录目录应始终与任何数据文件目录相分离。
指定的数据路径也将跨群集中的所有节点使用。指定应该将数据存储在 /home/data 后,Vertica 将在所有数据库节点上使用此路径。
不要使用单个目录包含编录和数据文件。可以在不同的驱动器上存储编录和数据目录,这些目录可能位于主机的本地驱动器(建议用于编录目录)或共享存储位置(例如外部磁盘存储模块或 SAN)上。
在指定编录或数据路径之前,确保数据库的所有节点上都存在父目录。在 admintools 中创建数据库还会创建编录和数据目录,但每个节点上必须存在父目录。
安装期间无需指定磁盘存储位置。但可以通过在 install_vertica
脚本中使用 --data-dir
参数来执行该操作。请参阅在安装期间指定磁盘存储位置。
当您在 管理工具中调用创建数据库命令时,有个对话框允许您指定编录和数据位置。群集中的每个主机上都必须存在这些位置,并且这些位置必须归数据库管理员所有。
当单击确定 (OK) 时,Vertica 会自动创建以下子目录:
catalog-pathname/database-name/node-name_catalog/data-pathname/database-name/node-name_data/
例如,如果将
/home/dbadmin
的默认值(数据库管理员的主目录)用于 Stock Exchange 示例数据库,则将在群集中的每个节点上创建如下所示的编录和数据目录:
/home/dbadmin/Stock_Schema/stock_schema_node1_host01_catalog/home/dbadmin/Stock_Schema/stock_schema_node1_host01_data
编录和数据路径名称只能包含字母数字字符,不能有前置空格字符。如果不能遵守这些限制条件,将导致数据库创建失败。
如果其他数据库正在使用目录,Vertica 将拒绝覆盖目录。因此,如果您创建了一个用于评估的数据库,然后删除了该数据库,而您想要再使用该数据库名称,请确保您已彻底清理先前使用的磁盘存储位置。有关详细信息,请参阅管理存储位置。
可采用以下方法,使用 MC 界面指定在群集中存储数据库元数据的位置:
首次配置 MC 时
创建在 MC 上使用的新数据库时
创建初始存储位置之后,以后可以向数据库中添加其他存储位置。此操作不仅可以提供更多空间,还可以用于通过隔离具有不同 I/O 或访问模式的文件来控制磁盘使用情况并提高 I/O 性能。例如,考虑:
通过为 临时空间创建单独的存储位置,将执行引擎临时文件与数据文件隔离。
创建设置了标签的存储位置和存储策略,其中选定的数据库对象基于测量的性能统计信息或预测的访问模式而存储在不同存储位置。
如果使用共享 SAN 存储,请确保在磁盘空间或宽带的节点之间不存在资源争夺情况。
每个主机必须拥有其自己的编录和数据位置。主机不能共享编录或数据位置。
配置存储,以便每个节点都有足够的 I/O 带宽来单独访问存储。
您可以通过 管理控制台在 Vertica 群集上查看节点特定信息。有关详细信息,请参阅使用 MC 进行监控。
您应从防病毒扫描中排除 Vertica 编录和数据目录。某些防病毒产品已被识别为针对 Vertica 目录,有时会锁定或删除其中的文件。这将对 Vertica 性能和数据完整性产生不利影响。
已识别的防病毒产品包括:
ClamAV
SentinelOne
Sophos
Symantec
Twistlock
除了数据库中存储的实际数据外,Vertica 对若干数据重组操作均需磁盘空间,如 合并和在群集中管理节点。为获得最佳结果,Vertica 建议对于 K-安全=1 的数据库,每个节点的磁盘利用率不超过百分之六十 (60%) 以允许此类操作继续。
此外,某些查询执行运算符(如哈希联接和排序)若无法在内存 (RAM) 中完成,还会临时需要磁盘空间。您在查询、恢复、刷新投影等过程中可能会遇到此类运算符。所需的磁盘空间量(称为 临时空间)取决于查询性质、节点上的数据量以及系统上的并发用户数。默认情况下,数据磁盘上的任何未用磁盘空间均可用作临时空间。但是,Vertica 建议配置与数据磁盘空间分开的临时空间。
您可以在群集的任何节点上安装管理控制台,所以除了为数据库群集分配的磁盘空间外,它没有其他特殊的磁盘要求。
为 Vertica 数据库设计逻辑架构与为任何其他 SQL 数据库设计逻辑架构无任何区别。设计逻辑架构中更完整地描述了详细信息。
要创建逻辑架构,请准备一个 SQL 脚本(纯文本文件,扩展名通常为 .sql
)以执行下列操作:
创建其他架构(根据需要)。请参阅使用多个架构。
使用 CREATE TABLE 命令在数据库中创建表和列 约束。
使用 ALTER TABLE 命令定义必要的表约束。
使用 CREATE VIEW 命令定义表上的任何视图。
可使用下列项生成脚本:
架构设计器应用程序。
从现有数据库提取的架构。
文本编辑器。
作为模板的数据库 example-name_define_schema.sql
脚本之一。(请参阅
/opt/vertica/examples
中的示例数据库目录。)
在您的脚本文件中,请确保以下几点:
每个语句均以分号结尾。
您使用 Vertica 支持的 数据类型,如《SQL 参考手册》中所述。
创建数据库后,可通过执行架构脚本来对其进行测试,如创建逻辑架构中所述。如果遇到错误,请删除所有表,更正错误,然后再次运行脚本。
准备两个数据文件集:
测试数据文件。在完成部分数据加载后,使用测试文件来测试数据库。如果可以,使用部分实际数据文件来准备测试数据文件。
实际数据文件。测试并优化数据库之后,将您的数据文件用于初始数据加载。
可以命名每个数据文件,使其与逻辑架构中的相应表匹配。不用区分大小写。
可以使用扩展名 .tbl
或者希望使用的任何名称。例如,如果表的名称为 Stock_Dimension
,则相应数据文件的名称应为 stock_dimension.tbl
。当使用多个数据文件时,可在文件名称后附加 _nnn
(其中 nnn 是 001 至 999 之间的正整数)。例如,stock_dimension.tbl_001
、stock_dimension.tbl_002
以此类推。
准备 SQL 脚本以使用 vsql 上的 COPY 或通过 ODBC 将数据直接加载到物理存储中。
您需要加载下列项的脚本:
大型表
小型表
Vertica 建议您使用多个文件来加载大型表。要测试加载过程,请使用大小为 10GB 至 50GB 的文件。该大小具有以下优势:
可使用数据文件之一作为 Database Designer 的示例数据文件。
可以仅加载足够的数据来执行部分数据加载,然后再加载剩余数据。
如果单个加载失败并回退,您不会浪费过多时间。
测试加载过程之后,对于多太字节表,请以 250–500GB 的文件大小中断完全加载。
示例查询脚本用于测试架构和加载脚本中是否有错误。
包括您的用户希望针对数据库运行的查询的示例。如果没有执行任何真正的查询,仅编写可收集每个表的计数的简单 SQL。或者,您可以跳过此步骤。
有两种方法可以创建空数据库:
使用 管理控制台。有关详细信息,请参阅使用 MC 创建数据库。
使用 管理工具。
尽管可以创建多个数据库(例如,一个用于生产,一个用于测试),安装的每个 Vertica 分析数据库只能有一个活动数据库。
数据库名称必须遵循以下规则:
1-30 个字符
以字母开头
后跟字母(大写和小写)、数字和/或下划线的任意组合。
数据库名称区分大小写;但是,Vertica 强烈建议您不要使用仅大小写不同的名称创建数据库。例如,请勿创建名称分别为 mydatabase
和 MyDataBase
的两个数据库。
数据库密码可以包含字母、数字和下表中列出的特殊字符。密码不能包含非 ASCII Unicode 字符。
允许的密码长度介于 0-100 个字符之间。数据库超级用户可以使用 ALTER PROFILE 更改 Vertica 用户的最大密码长度。
您可以使用 配置文件 配置文件指定和控制密码定义。例如,配置文件可以定义密码的最大长度、重用次数、最小数字或所需位数以及其他详细信息。
下表列出了 Vertica 允许在数据库密码中使用的特殊 (ASCII) 字符。特殊字符可以出现在密码字符串的任何位置。例如,mypas$word
或 $mypassword
都是有效的。
$ /opt/vertica/bin/admintools
如果您正在使用远程终端应用程序,如 PuTTY 或 Cygwin bash shell,请参阅远程终端用户须知。
接受许可协议并指定许可证文件的位置。有关详细信息,请参阅 管理许可证。
仅当您第一次运行管理工具时,才需要执行此步骤
在“主菜单 (Main Menu)”上,单击配置菜单 (Configuration Menu),然后单击确定 (OK)。
在“配置菜单 (Configuration Menu)”上,单击创建数据库 (Create Database),然后单击确定 (OK)。
输入数据库名称和可选注释,然后单击确定 (OK)。有关命名准则和限制,请参阅创建数据库名称和密码。
为数据库设立超级用户密码。
要提供一个密码,请输入该密码,然后单击确定 (OK)。再次输入以确认该密码,然后单击确定 (OK)。
如果不想提供密码,请将其留空,然后单击确定 (OK)。如果不设置密码,Vertica 会提示您确认您确实不想为此数据库设立超级用户密码。单击是 (Yes) 创建没有密码的数据库,或者单击否 (No) 设立密码。
从安装 Vertica (
install_vertica -s
) 时指定的主机列表中选择要包括在数据库中的主机,然后单击确定 (OK)。
指定用于存储数据和 编录文件的目录,然后单击确定 (OK)。
编录和数据路径名称只能包含字母数字字符,不能有前置空格。如果不能遵守这些限制条件,将导致数据库创建失败。
例如:
编录路径名: /home/dbadmin
数据路径名: /home/dbadmin
查看当前数据库定义 (Current Database Definition) 屏幕,确认它表示您要创建的数据库,然后单击是 (Yes) 继续操作,或者单击否 (No) 修改数据库定义。
如果单击是 (Yes),Vertica 会创建您定义的数据库,然后显示一条消息,指示已成功创建数据库。
: 对于使用 3 个或更多节点创建的数据库,Vertica 会自动将
K-safety 设置为 1,从而确保在节点出现故障时数据库具有容错能力。有关详细信息,请参阅《管理员指南》和 MARK_DESIGN_KSAFE 中的故障恢复。
单击确定 (OK) 确认消息。
连接到数据库。
在管理工具的“主菜单 (Main Menu)”上,单击连接到数据库 (Connect to Database),再单击确定 (OK)。
有关详细信息,请参阅连接到数据库。
将显示 vsql 欢迎脚本:
Welcome to vsql, the Vertica Analytic Database interactive terminal.
Type: \h or \? for help with vsql commands
\g or terminate with semicolon to execute query
\q to quit
=>
运行逻辑架构脚本
在 vsql 中使用 \i meta-command 运行早前准备的 SQL 逻辑架构脚本。
与数据库断开连接
在 vsql 中使用 \q
meta-command 返回管理工具。
Vertica 建议,针对大型表,应首先执行部分数据加载并对数据库进行测试,然后再完成全部数据加载。此加载应加载具有代表性的数据量。
有关投影的详细信息,请参阅投影。
测试数据库以验证其是否正在按照预期效果运行。
检查查询是否存在语法错误并检查执行时间。
使用 vsql \timing meta-command 启用查询执行时间的显示(单位为毫秒)。
执行您先前准备的 SQL 示例查询脚本。
执行多个临时查询。
优化数据库由优化压缩和调优查询组成。(请参阅创建数据库设计。)
要优化数据库,请使用 Database Designer 创建和部署一个用于优化数据库的设计。请参阅使用 Database Designer 创建完整的设计。
运行 Database Designer 后,使用查询优化中介绍的技巧来提高某些查询类型的性能。
要完成加载:
监控系统资源使用情况。
当加载脚本运行时,继续运行 top
、free
和 df
实用程序并观察(如监控 Linux 资源使用情况中所述)。可以在群集中的任意节点或所有节点上执行此操作。确保系统未过度交换(查看 top
中的 kswapd
)或耗尽交换空间(查看是否有大量可用交换空间已使用)。
完成大型表加载。
运行剩余大型表加载脚本。
可通过检查查询执行时间来测试优化的设计:
使用 vsql \timing
元命令启用查询执行时间(以毫秒为单位)的显示。
执行 SQL 示例查询脚本,以测试架构和加载脚本中是否有错误。
执行多个临时查询
使用 \i meta-command 执行查询脚本;例如:
vmartdb=> \i vmart_query_03.sql customer_name | annual_income
------------------+---------------
James M. McNulty | 999979
Emily G. Vogel | 999998
(2 rows)
Time: First fetch (2 rows): 58.411 ms. All rows formatted: 58.448 ms
vmartdb=> \i vmart_query_06.sql
store_key | order_number | date_ordered
-----------+--------------+--------------
45 | 202416 | 2004-01-04
113 | 66017 | 2004-01-04
121 | 251417 | 2004-01-04
24 | 250295 | 2004-01-04
9 | 188567 | 2004-01-04
166 | 36008 | 2004-01-04
27 | 150241 | 2004-01-04
148 | 182207 | 2004-01-04
198 | 75716 | 2004-01-04
(9 rows)
Time: First fetch (9 rows): 25.342 ms. All rows formatted: 25.383 ms
数据库优化后,应高效地运行查询。如果发现要优化的查询,可以对设计进行逐步修改和更新。
区域设置指定用户的语言、所在国家/地区以及任何特殊的可变首选项(例如排序规则)。Vertica 会根据区域设置确定某些字符串函数的行为。此外,区域设置还确定了需要进行排序和比较的各种 SQL 命令(例如聚合 GROUP BY
和 ORDER BY
子句、联接和分析 ORDER BY
子句)的排序规则。
Vertica 数据库的默认区域设置为 en_US@collation=binary
(美国英语)。可以定义供数据库中的所有会话使用的新默认区域设置,也可以覆盖单个会话的区域设置。但是,无论会话排序规则如何,请始终采用默认 en_US@collation=binary
排序规则来对投影进行排序。特定区域设置的排序功能会在查询时应用。
如果您将区域设置设为 NULL,则 Vertica 会将区域设置设为 en_US_POSIX
。您可以通过发出 vsql 元命令 \locale
将区域设置设回默认区域设置和排序规则。例如:
=> set locale to '';
INFO 2567: Canonical locale: 'en_US_POSIX'
Standard collation: 'LEN'
English (United States, Computer)
SET
=> \locale en_US@collation=binary;
INFO 2567: Canonical locale: 'en_US'
Standard collation: 'LEN_KBINARY'
English (United States)
=> \locale
en_US@collation-binary;
可以通过 ODBC、JDBC 和 ADO.net 设置区域设置。
Vertica 将 ICU 库用于区域设置支持;您必须使用 ICU 区域设置语法指定区域设置。数据库会话使用的区域设置并非来源于操作系统(通过 LANG
变量),因此 Vertica 建议您为运行 vsql 的每个节点设置 LANG
,如下一个部分所述。
虽然 ICU 库可以指定排序规则、货币和日历首选项,但 Vertica 仅支持排序规则组件。与排序规则无关的任何关键字都将被拒绝。无论会话排序规则如何,始终使用 en_US@collation=binary
排序规则对投影进行排序。特定区域设置的排序功能会在查询时应用。
SET DATESTYLE TO
...
命令提供了日历的一些方面,但 Vertica 仅支持将美元作为货币。
此示例将会话区域设置设置为“泰语(Thai)”。
在操作系统级别,针对每个运行 vsql 的节点,按照以下方式将 LANG
变量设置为区域设置语言:
export LANG=th_TH.UTF-8
LANG=
未起作用,则表示可能未安装区域设置的操作系统支持。
针对每个 Vertica 会话(从 ODBC/JDBC 或 vsql 中),设置语言区域设置。
从 vsql 中:
\locale th_TH
从 ODBC/JDBC 中:
"SET LOCALE TO th_TH;"
在 PUTTY(或 ssh 终端)中,按照以下步骤更改设置:
settings > window > translation > UTF-8
单击应用 (Apply),然后单击保存 (Save)。
加载的所有数据必须采用 UTF-8 格式而非 ISO 格式,如分隔数据中所述。与 UTF-8 不兼容的字符集(例如 ISO 8859-1 (Latin1))不受支持,因此 SUBSTRING 等函数对于多字节字符而言无法正常工作。因此,区域设置的设置应无法正常工作。如果转换设置 ISO-8859-11:2001 (Latin/Thai) 正常工作,则表示未正确加载数据。要正确转换数据,请使用一个实用程序,例如 Linux
iconv
。
启动数据库之后,默认区域设置配置参数 DefaultSessionLocale
会设置初始区域设置。您可以覆盖各个会话的这个值。
要为数据库设置区域设置,请按照以下方式使用配置参数:
=> ALTER DATABASE DEFAULT SET DefaultSessionLocale = 'ICU-locale-identifier';
例如:
=> ALTER DATABASE DEFAULT SET DefaultSessionLocale = 'en_GB';
您可以通过两种方式覆盖当前会话的默认区域设置:
VSQL 命令
\locale
。例如:
=> \locale en_GBINFO:
INFO 2567: Canonical locale: 'en_GB'
Standard collation: 'LEN'
English (United Kingdom)
SQL 语句
SET LOCALE
。例如:
=> SET LOCALE TO en_GB;
INFO 2567: Canonical locale: 'en_GB'
Standard collation: 'LEN'
English (United Kingdom)
=> SET LOCALE TO LEN;
INFO 2567: Canonical locale: 'en'
Standard collation: 'LEN'
English
=> \locale LEN
INFO 2567: Canonical locale: 'en'
Standard collation: 'LEN'
English
Vertica 将数据库服务器的区域设置与客户端应用程序的区域设置区分开来:
服务器区域设置仅影响服务器端查询处理的排序行为。
客户端应用程序将验证区域设置是否适当,以正确显示字符。
以下部分描述了最佳实践,以确保可预测的结果。
服务器会话的区域设置应按照为数据库指定默认区域设置中所述进行设置。如果不同会话的区域设置不同,请的每个会话开始时,从客户端设置服务器的区域设置。
如果数据库没有默认的会话区域设置,请将会话的服务器区域设置设置为所需的区域设置。
运行 vsql 客户端的终端仿真器中的区域设置应该设置为等同于服务器端的会话区域设置(ICU 区域设置)。这样就能在服务器上正确排序数据,并在客户端上正确显示数据。
vsql 的所有输入数据应该为 UTF-8,而所有输出数据都以 UTF-8 进行编码。
Vertica 不支持非 UTF-8 编码和关联的区域设置值;
有关设置区域设置和编码的说明,请参阅终端仿真器文档。
ODBC 应用程序可以采用 ANSI 或 Unicode 模式。如果用户应用程序采用 Unicode,ODBC 使用的编码是 UCS-2。如果用户应用程序采用 ANSI,数据必须为单字节 ASCII,这与数据库服务器上使用的 UTF-8 兼容。向 Vertica 服务器传递数据时,ODBC 驱动程序会将 UCS-2 转换为 UTF-8,并会将 Vertica 服务器发来的数据从 UTF-8 转换为 UCS-2。
如果用户应用程序尚未采用 UCS-2,应用程序必须将输入数据转换为 UCS-2,否则会发生异常结果。例如:
对于传递到 ODBC API 的非 UCS-2 数据,当它被解释为 UCS-2 时,它可能会导致无效的 UCS-2 符号传递给 API,从而导致错误。
在备用编码中提供的符号可以是有效的 UCS-2 符号。如果发生这种情况,则会将不正确的数据插入数据库。
如果数据库没有默认会话区域设置,ODBC 应用程序应该使用 SQLSetConnectAttr
来设置所需的服务器会话区域设置(如果它不同于数据库范围设置)。这样可以在服务器上实现预期的排序规则以及字符串函数行为。
JDBC 和 ADO.NET 应用程序使用 UTF-16 字符集编码并负责将任何非 UTF-16 编码数据转换为 UTF-16。如果违反此编码,同样的警告也适用于 ODBC。
在传递到 Vertica 服务器并将 Vertica 服务器发送的数据从 UTF-8 转换为 UTF-16 时,JDBC 和 ADO.NET 驱动程序会将 UTF-16 数据转换为 UTF-8。
如果在数据库级别没有默认会话区域设置,JDBC 和 ADO.NET 应用程序应该通过执行 SET LOCALE TO 命令设置正确的服务器会话区域设置,以便在服务器上实现预计的排序规则以及字符串函数行为。有关详细信息,请参阅SET LOCALE。
默认情况下,Vertica 对所有会话使用 READ COMMITTED
隔离级别。您可以更改数据库或给定会话的默认隔离级别。
事务将保留其隔离级别直至其完成,即使在事务处理期间会话的隔离级别发生更改也是如此。Vertica 内部进程(例如 Tuple Mover 和 刷新操作)以及 DDL 操作始终以 SERIALIZABLE 隔离级别运行以确保一致性。
配置参数
TransactionIsolationLevel
将指定数据库隔离级别,并用作所有会话的默认值。使用
ALTER DATABASE
更改默认隔离级别。例如:
=> ALTER DATABASE DEFAULT SET TransactionIsolationLevel = 'SERIALIZABLE';
ALTER DATABASE
=> ALTER DATABASE DEFAULT SET TransactionIsolationLevel = 'READ COMMITTED';
ALTER DATABASE
数据库隔离级别的更改仅适用于将来的会话。现有会话及其事务会继续使用原始隔离级别。
使用
SHOW CURRENT
查看数据库隔离级别:
=> SHOW CURRENT TransactionIsolationLevel;
level | name | setting
----------+---------------------------+----------------
DATABASE | TransactionIsolationLevel | READ COMMITTED
(1 row)
SET SESSION CHARACTERISTICS AS TRANSACTION
更改特定会话的隔离级别。例如:
=> SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET
使用
SHOW
查看当前会话的隔离级别:
=> SHOW TRANSACTION_ISOLATION;
Vertica 支持一系列影响数据库多个方面行为的配置参数。通过在一个或多个级别使用适当的 ALTER 语句,可以设置这些参数。以下按优先级降序对其进行排列:
用户 (ALTER USER)
会话 (ALTER SESSION)
节点 (ALTER NODE)
数据库 (ALTER DATABASE)
您可以查询系统表 CONFIGURATION_PARAMETERS,获取所有用户可访问参数的当前设置。例如,以下查询获取分区参数的设置:它们的当前值和默认值、可以设置的级别,以及更改后是否需要重新启动数据库才能生效:
=> SELECT parameter_name, current_value, default_value, allowed_levels, change_requires_restart FROM configuration_parameters
WHERE parameter_name ILIKE '%partition%';
parameter_name | current_value | default_value | allowed_levels | change_requires_restart
------------------------------------+---------------+---------------+----------------+-------------------------
MaxPartitionCount | 1024 | 1024 | NODE, DATABASE | f
PartitionSortBufferSize | 67108864 | 67108864 | DATABASE | f
DisableAutopartition | 0 | 0 | SESSION, USER | f
PatternMatchingMaxPartitionMatches | 3932160 | 3932160 | NODE, DATABASE | f
PatternMatchingMaxPartition | 20971520 | 20971520 | NODE, DATABASE | f
ActivePartitionCount | 1 | 1 | NODE, DATABASE | f
(6 rows)
有关按类别分组的各个配置参数的详细信息,请参阅配置参数。
您可以使用适当的 ALTER 语句更改特定的配置参数。这些语句还允许您将配置参数重置为其默认值。例如,以下 ALTER 语句将数据库级别的 ActivePartitionCount 从 1 更改为 2,将会话级别的 DisablePartitionCount 从 0 更改为 1:
=> ALTER DATABASE DEFAULT SET ActivePartitionCount = 2;
ALTER DATABASE
=> ALTER SESSION SET DisableAutopartition = 1;
ALTER SESSION
=> SELECT parameter_name, current_value, default_value FROM configuration_parameters
WHERE parameter_name IN ('ActivePartitionCount', 'DisableAutopartition');
parameter_name | current_value | default_value
----------------------+---------------+---------------
ActivePartitionCount | 2 | 1
DisableAutopartition | 1 | 0
(2 rows)
您可以稍后将这些配置参数重置为默认值:
=> ALTER DATABASE DEFAULT CLEAR ActivePartitionCount;
ALTER DATABASE
=> ALTER SESSION CLEAR DisableAutopartition;
ALTER DATABASE
=> SELECT parameter_name, current_value, default_value FROM configuration_parameters
WHERE parameter_name IN ('ActivePartitionCount', 'DisableAutopartition');
parameter_name | current_value | default_value
----------------------+---------------+---------------
DisableAutopartition | 0 | 0
ActivePartitionCount | 1 | 1
(2 rows)
您可以通过两种方式来查看有效的配置参数值:
使用以下 SHOW
语句查看有效的配置参数:
SHOW CURRENT:返回有效配置参数值的设置。Vertica 按以下优先级升序检查所有级别的设置:
会话
节点
数据库
如果任何范围内均未设置任何值,则 SHOW CURRENT 将返回参数的默认值。
SHOW DATABASE:显示为数据库设置的配置参数值。
SHOW USER:显示为指定用户和所有用户设置的配置参数。
SHOW SESSION:显示为当前会话设置的配置参数值。
SHOW NODE:显示为节点设置的配置参数值。
如果配置参数需要重新启动才能生效,SHOW CURRENT
语句中的值可能与 SHOW
语句中的值有所不同。要查看哪些参数需要重新启动,请查询 CONFIGURATION_PARAMETERS 系统表。
您可以在多个系统表中查询配置参数。
SESSION_PARAMETERS 返回会话范围的参数。
CONFIGURATION_PARAMETERS 返回所有范围的参数:数据库、节点和会话。
USER_CONFIGURATION_PARAMETERS 提供对数据库用户有效的用户级别的配置参数信息。
为 Vertica 数据库设计逻辑架构与为任何其他 SQL 数据库设计逻辑架构相同。逻辑架构由架构、表、 视图以及 引用完整性约束等对 SQL 用户可见的对象组成。Vertica 支持任何您选择的关系架构设计。
如果只有一个数据库用户或者几个用户合作共享数据库,使用单个架构即可。但在许多情况下,需要使用更多架构来让用户及其应用程序能在单独的命名空间中创建和访问表。例如,使用更多架构将允许:
许多用户在不相互干扰的情况下访问数据库。
对各个架构进行配置,以便授予特定用户对该架构及其表的访问权限,同时限制其他用户。
使用第三方应用程序来创建在不同架构中名称相同的表,从而防止表冲突。
与其他 RDBMS 不同,Vertica 数据库中的架构不是绑定到一个用户的对象集合。
本部分提供有关何时以及如何使用多个架构分离数据库用户的示例。这些示例分为两类:使用多个专用架构;组合使用专用架构(即仅限单个用户的架构)和共享架构(即跨多个用户共享的架构)。
使用多个专用架构可以在涉及敏感信息时有效地将数据库用户彼此分离。通常,用户仅被授予一个架构及其内容的访问权限,从而提供架构级别的数据库安全性。数据库用户可以同时运行不同的应用程序、同一应用程序的多个副本,甚至同一应用程序的多个实例。这样一来,您便可以合并一个数据库上的应用程序,从而减少管理开销,同时更有效地利用资源。以下示例突出强调使用多个专用架构。
使用多个架构分离用户及其唯一的应用程序
在本示例中,两位数据库用户都为同一家公司工作。一位用户 (HRUser) 使用人力资源 (HR) 应用程序访问敏感的个人资料(如工资),而另一位用户 (MedUser) 通过医疗保健管理应用程序访问与公司医疗保健成本有关的信息。HRUser 不应能够访问公司医疗保健成本信息,而 MedUser 不应能够查看员工个人资料。
为了允许这些用户访问他们需要的数据,同时限制他们查看不应看到的数据,我们创建了两个具有适当用户访问权限的架构,如下所示:
HRSchema — 此架构归 HRUser 所有,且通过 HR 应用程序进行访问。
HRSchema — 此架构归 MedUser 所有,且通过医疗保健管理应用程序进行访问。
使用多个架构支持多租户
本示例与最后一个示例相类似,它也是通过将用户分离到不同的架构中,限制对敏感数据的访问。但在这种情况下,每位用户都使用同一应用程序的一个虚拟实例。
一个相关示例是零售市场分析公司,该公司向大型零售商提供数据和“软件即服务”(SaaS),帮助他们确定使用哪些促销方法能够最有效地推动客户销售额。
在本示例中,每位数据库用户相当于一位零售商,且每位用户只能访问自己的架构。零售市场分析公司需向每位零售客户提供同一应用程序的一个虚拟实例,且每个实例指向该用户的特定架构,以便在其中创建和更新表。这些架构中的表是由同一应用程序的实例创建的,所以使用相同名称,但由于它们处于单独的架构中,因此不会发生冲突。
此数据库中的架构示例可以是:
MartSchema — 归一家大型连锁百货商店 MartUser 所有的架构。
PharmSchema — 归一家大型连锁药店 PharmUser 所有的架构。
使用多个架构迁移至应用程序的新版本
使用多个架构是迁移至新软件应用程序版本的一种有效方法。在这种情况下,需创建一个新架构来支持软件的新版本,原有架构将保留到不再需要为止,以支持软件的原始版本。这称为“滚动应用程序升级”。
例如,公司可能使用 HR 应用程序存储员工资料。以下架构可用于软件的原始版本和更新版本:
HRSchema — 此架构归原始 HR 应用程序的架构用户 HRUser 所有。
V2HRSchema — 此架构归新版 HR 应用程序的架构用户 V2HRUser 所有。
在上述示例显示的情况中,数据库中的所有架构都是专用的,且用户之间不共享信息。但是,用户可能希望共享公共信息。例如,在零售案例中,MartUser 和 PharmUser 可能希望将其每个商店的特定产品销售额与整个行业内每个商店销售额的平均值相比较。由于此信息是行业平均值,不是特定于任何零售连锁店,因此可以将其放置在两位用户都被授予 USAGE 权限的架构中。
此数据库中的架构示例可能是:
MartSchema — 归一家大型连锁百货商店 MartUser 所有的架构。
PharmSchema — 归一家大型连锁药店 PharmUser 所有的架构。
IndustrySchema — 此架构归 DBUser(来自零售市场分析公司)所有,而 MartUser 和 PharmUser 对此架构具有 USAGE 权限。零售商不太可能获得除 USAGE(对于此架构)和 SELECT(对于其中的一个或多个表)以外的任何权限。
您可以根据需要为数据库创建任意数量的架构。例如,您可以为每个数据库用户创建一个架构。但是,架构与用户并不像在 Oracle 中一样是同义词。
默认情况下,只有超级用户可以创建架构或者授予某个用户创建架构的权限。(请参阅《SQL 参考手册》中的GRANT(数据库)。)
要创建架构,请使用 CREATE SCHEMA 语句,如《SQL 参考手册》中所述。
创建两个或多个架构后,每个 SQL 语句或函数必须识别与所引用的对象相关联的架构。您可以通过以下方式在多个架构中指定一个对象:
通过使用以点分隔的架构名称和对象名称来限定对象名称。例如,若要指定位于 Schema1
中的 MyTable
,请将名称限定为 Schema1.MyTable
。
如果引用的某个对象未加以限定,可使用包括所需架构的搜索路径。通过 设置搜索路径,Vertica 将自动搜索指定架构来查找对象。
每个用户会话都有一个架构搜索路径。Vertica 使用此搜索路径来查找未按架构名称限定的表和用户定义函数 (UDF)。会话搜索路径最初是从用户的配置文件中设置的。您可以随时通过调用
SET SEARCH_PATH
来更改会话的搜索路径。此搜索路径一直有效,直到处理下一个 SET SEARCH_PATH
语句或会话结束为止。
SHOW SEARCH_PATH
返回会话的当前搜索路径。例如:
=> SHOW SEARCH_PATH;
name | setting
-------------+---------------------------------------------------
search_path | "$user", public, v_catalog, v_monitor, v_internal
架构按优先级降序排列。第一个架构在搜索顺序中具有最高优先级。如果此架构存在,它也会定义为当前架构,供以非限定名称创建的表使用。您可以通过调用函数
CURRENT_SCHEMA
来识别当前架构:
=> SELECT CURRENT_SCHEMA;
current_schema
----------------
public
(1 row)
会话搜索路径最初是从用户的配置文件中设置的。如果用户配置文件中的搜索路径不是通过
CREATE USER
或
ALTER USER
设置,则该搜索路径将设置为数据库默认值:
=> CREATE USER agent007;
CREATE USER
=> \c - agent007
You are now connected as user "agent007".
=> SHOW SEARCH_PATH;
name | setting
-------------+---------------------------------------------------
search_path | "$user", public, v_catalog, v_monitor, v_internal
$user
解析为会话用户名(在此示例中为 agent007
)且具有最高优先级。如果架构 agent007
存在,Vertica 将开始在该架构中搜索不合格的表。此外,
CURRENT_SCHEMA
的调用将返回此架构。否则,Vertica 使用 public
作为当前架构并开始在其中进行搜索。
使用
ALTER USER
可修改现有用户的搜索路径。这些更改会覆盖搜索路径中的所有非系统架构,其中包括 $USER
。系统架构保持不变。对用户搜索路径的更改仅在用户启动新会话时生效;当前会话不受影响。
例如,以下语句将修改 agent007
的搜索路径,并授予对新搜索路径上的架构和表的访问权限:
=> ALTER USER agent007 SEARCH_PATH store, public;
ALTER USER
=> GRANT ALL ON SCHEMA store, public TO agent007;
GRANT PRIVILEGE
=> GRANT SELECT ON ALL TABLES IN SCHEMA store, public TO agent007;
GRANT PRIVILEGE
=> \c - agent007
You are now connected as user "agent007".
=> SHOW SEARCH_PATH;
name | setting
-------------+-------------------------------------------------
search_path | store, public, v_catalog, v_monitor, v_internal
(1 row)
要验证用户的搜索路径,请查询系统表
USERS
:
=> SELECT search_path FROM USERS WHERE user_name='agent007';
search_path
-------------------------------------------------
store, public, v_catalog, v_monitor, v_internal
(1 row)
要将用户的搜索路径恢复为数据库默认设置,请调用 ALTER USER
并将搜索路径设置为 DEFAULT
。例如:
=> ALTER USER agent007 SEARCH_PATH DEFAULT;
ALTER USER
=> SELECT search_path FROM USERS WHERE user_name='agent007';
search_path
---------------------------------------------------
"$user", public, v_catalog, v_monitor, v_internal
(1 row)
Vertica 仅在当前用户具有访问权限的现有架构中进行搜索。如果搜索路径中的架构不存在或用户缺乏对其的访问权限,Vertica 会默认将其排除搜索。例如,如果 agent007
缺乏架构 public
的 SELECT 权限,Vertica 会默认为跳过此架构。仅当 Vertica 在搜索路径的任何位置都找不到表时,它才会返回错误。
Vertica 最初会根据用户的配置文件来设置会话的搜索路径。您可以使用
SET SEARCH_PATH
更改当前会话的搜索路径。您可以通过两种方式使用 SET SEARCH_PATH
:
显式将会话搜索路径设置为一个或多个架构。例如:
=> \c - agent007
You are now connected as user "agent007".
dbadmin=> SHOW SEARCH_PATH;
name | setting
-------------+---------------------------------------------------
search_path | "$user", public, v_catalog, v_monitor, v_internal
(1 row)
=> SET SEARCH_PATH TO store, public;
SET
=> SHOW SEARCH_PATH;
name | setting
-------------+-------------------------------------------------
search_path | store, public, v_catalog, v_monitor, v_internal
(1 row)
将会话搜索路径设置为数据库默认值:
=> SET SEARCH_PATH TO DEFAULT;
SET
=> SHOW SEARCH_PATH;
name | setting
-------------+---------------------------------------------------
search_path | "$user", public, v_catalog, v_monitor, v_internal
(1 row)
SET SEARCH_PATH
覆盖搜索路径中的所有非系统架构,其中包括 $USER
。系统架构保持不变。
Vertica 支持跨多个架构引用表的 视图。例如,用户可能需要将员工薪资与行业平均值进行比较。在这种情况下,应用程序将查询两个架构:
平均薪资的共享架构 IndustrySchema
特定于公司的工资信息的专用架构 HRSchema
最佳实践: 在创建跨越多个架构的对象时,请使用限定的表名。如果架构内的查询路径或表结构在未来的某个日期发生变化,此命名约定可避免发生混淆。
在 Vertica 中,您可以分别通过
CREATE TABLE
和
CREATE TEMPORARY TABLE
创建永久表和临时表。
CREATE TABLE
在 Vertica
逻辑架构中创建一个表。例如:
CREATE TABLE vendor_dimension (
vendor_key INTEGER NOT NULL PRIMARY KEY,
vendor_name VARCHAR(64),
vendor_address VARCHAR(64),
vendor_city VARCHAR(64),
vendor_state CHAR(2),
vendor_region VARCHAR(32),
deal_size INTEGER,
last_deal_update DATE
);
有关详细信息,请参阅创建表。
CREATE TEMPORARY TABLE
创建数据仅存在于当前会话中的表。临时表数据容始终对其他会话不可见。
临时表可用于将复杂查询处理分为多步来进行。通常情况下,报告工具会容纳创建报告过程中产生的中间结果 — 例如,工具首先获取一个结果集,然后查询该结果集,等等。
CREATE TEMPORARY TABLE
可分别通过关键字 GLOBAL
和 LOCAL
在两个范围(全局和本地)创建表:
GLOBAL
(默认值):表定义对所有会话均可见。但是,表数据限定在会话范围内。
LOCAL
:表定义仅对在其中创建了它的会话可见。当会话结束时,Vertica 会自动删除表。
有关详细信息,请参阅创建临时表。
设计是用于优化查询性能的物理存储计划。Vertica 中的数据以物理方式存储在投影中。最初使用 INSERT、COPY(或 COPY LOCAL)将数据加载到某个表中时,Vertica 会为该表创建一个默认的 超投影。该超投影可确保所有数据均可用于查询。但是,这些超投影可能不会优化数据库性能,这导致查询性能较低、数据压缩较慢。
要提高性能,请为 Vertica 数据库创建一个设计以优化查询性能和数据压缩。您可以通过多种方式创建设计:
使用 Database Designer,这是一种建议用于设计以获得最佳性能的工具。
使用 Database Designer 创建初始设计,然后手动对其进行修改。
Database Designer 可帮助您将花费在手动数据库优化上的时间降至最低。也可以使用 Database Designer 按照要求以增量方式重新设计数据库,比如工作负载随时间的变化。
Database Designer 作为后台进程运行。如果您需要通宵运行一个大型设计,这非常有用。由于无需活动的 SSH 会话,因此设计和部署操作会在会话结束时继续无中断运行。
Vertica Database Designer 使用复杂的策略创建一个设计,该设计可以为临时查询和特定查询提供卓越的性能,同时高效地使用磁盘空间。
在设计过程中,Database Designer 会分析逻辑架构定义、示例数据和示例查询,然后以您自动或手动部署的 SQL 脚本的形式创建一个物理架构( 投影)。此脚本创建一个最小的超投影集来保证 K-safety。
大多数情况下,Database Designer 创建的投影可在物理约束范围内实现出色的查询性能,同时高效地使用磁盘空间。
当您运行 Database Designer 时,可使用几个常规选项:
创建全面或增量设计。
优化查询的执行、加载或平衡这两者。
需要 K-safety。
如果可行,建议使用未分段投影。
在创建设计前分析统计信息。
Database Designer 的设计基于您提供的以下信息:
您通常在正常数据库操作期间运行的设计查询。
包含示例数据的设计表。
Database Designer 生成以下输出:
一个设计脚本,该脚本以满足优化目标以及在整个群集中均匀分布数据的方式为设计创建投影。
一个部署脚本,该脚本为设计创建和刷新投影。为了获得全面的设计,部署脚本包含一些用于移除非优化投影的命令。部署脚本包括完整的设计脚本。
一个备份脚本,该脚本包含一些 SQL 语句,用于部署在部署前存在于系统上的设计。如果您需要还原到部署前的设计,此文件非常有用。
Database Designer 生成的设计:
不包括实时聚合投影或 Top-K 投影。必须手动创建这些投影。请参阅CREATE PROJECTION。
不要在 LONG VARBINARY 和 LONG VARCHAR 列上对投影进行排序、分段或分区。
运行 Database Designer 时,您可以选择在创建部署脚本后自动部署您的设计,或者在审查并测试了设计后手动部署该设计。Vertica 建议先在非生产服务器上测试设计,然后再将该设计部署到生产服务器。
Database Designer 生成的设计可包括以下建议:
以相同的顺序对 伙伴实例投影排序,这样可显著提高加载、恢复和站点节点性能。所有伙伴实例投影均具有相同的基本名,因此可将其标识为一个组。
对于全面设计,接受的查询数不受限制。
标识类似设计查询并为其分配一个签名。
对于具有相同签名的查询,Database Designer 会根据此签名具有的查询数为这些查询加权,然后再在创建设计时考虑加权的查询。
建议创建投影时在群集上均匀分布数据,以便将数据偏离降至最低。
考虑使用 UPDATE、DELETE 和 SELECT 语句,以生成质量更高的设计。
默认情况下,只有具有 DBADMIN 角色的用户才能运行 Database Designer。非 DBADMIN 用户只有在被授予必要的权限和 DBDUSER 角色之后,才能运行 Database Designer,如下所述。此外,您还可以允许用户在管理控制台上运行 Database Designer(请参阅允许用户在管理控制台上运行 Database Designer)。
使用 CREATE LOCATION 将临时文件夹添加到所有群集节点中:
=> CREATE LOCATION '/tmp/dbd' ALL NODES;
使用 GRANT DATABASE 授予所需用户在当前(默认)数据库上创建架构的 CREATE 权限:
=> GRANT CREATE ON DATABASE DEFAULT TO dbd-user;
使用 GRANT ROLE 将 DBDUSER 角色授予 dbd-user:
=> GRANT DBDUSER TO dbd-user;
在群集的所有节点上,使用 GRANT LOCATION 授予 dbd-user 临时文件夹的访问权限。
=> GRANT ALL ON LOCATION '/tmp/dbd' TO dbd-user;
分别使用 GRANT SCHEMA 和 GRANT TABLE 授予 dbd-user 对一个或多个数据库架构及其表的权限:
=> GRANT ALL ON SCHEMA this-schema[,...] TO dbd-user;
=> GRANT ALL ON ALL TABLES IN SCHEMA this-schema[,...] TO dbd-user;
通过以下方式之一启用 dbd-user 中的 DBDUSER 角色:
对于 dbd-user,使用 SET ROLE 启用 DBDUSER 角色:
=> SET ROLE DBDUSER;
对于 DBADMIN,使用 ALTER USER 在每次登录时自动为 dbd-user 启用 DBDUSER 角色:
=> ALTER USER dbd-user DEFAULT ROLE DBDUSER;
当您授予 DBDUSER 角色时,请确保将资源池与该用户相关联以在 Database Designer 运行期间管理资源。
多个用户可以同时运行 Database Designer,他们互相之间不会出现干扰,也不会耗尽群集的所有资源。当一个用户使用管理控制台或通过编程方式运行 Database Designer 时,其执行操作通常会限制在该用户的资源池内,但不太密集的任务可能会使用系统资源。
如上文所述,已授予 DBDUSER 角色和所需权限的用户也可以在管理控制台上运行 Database Designer:
以超级用户身份登录管理控制台。
单击 MC 设置 (MC Settings)。
单击用户管理 (User Management)。
指定 MC 用户
要创建 MC 用户,请单击 添加 (Add)。
要使用现有的 MC 用户,请选择该用户并单击 编辑 (Edit)。
单击数据库访问级别 (DB access level) 窗口旁边的添加 (Add)。
在添加权限 (Add Permissions) 窗口中:
从选择数据库 (Choose a database) 下拉列表中,选择要为其创建设计的数据库。
在 数据库用户名 (Database username) 字段中,输入您先前创建的 dbd-user 用户名。
在数据库密码字段中,输入数据库密码。
在限制访问 (Restrict access) 下拉列表中,选择此用户具有的 MC 用户级别。
单击确定 (OK) 以保存更改。
退出 MC 超级用户帐户。
现在,MC 用户已映射到 dbd-user。以 MC 用户身份登录,然后使用 Database Designer 为数据库创建优化设计。
对于 DBDUSER,以下约束适用:
设计必须将 K-safety 设置为等于系统 K-safety。如果设计因缺少足够的表伙伴实例投影而违反 K-safety,则设计不会完成。
只有部署设计完成之后,才可显式推进 Ancient History Mark (AHM)(例如,调用 MAKE_AHM_NOW)。
创建设计时,会自动获得处理该设计的权限。其他任务可能需要额外的权限:
对于设计表架构的 USAGE
对于设计表的 OWNER
对于查询文件所在存储位置的读取权限
文件中所有查询的 EXECUTE 权限
对于用户查询的 EXECUTE 权限
从用户查询结果中获取的每个设计查询的 EXECUTE 权限
对于设计脚本存储位置的 WRITE 权限
对于部署脚本存储位置的 WRITE 权限
当您运行 Database Designer 时,优化器会根据您指定的选项提出一组理想的投影建议。当您部署设计时,Database Designer 会根据这些投影创建设计。但是,由于空间或预算约束,Database Designer 可能无法创建所有建议的投影。此外,Database Designer 可能无法使用理想条件实施投影。
要获取有关投影的信息,请先启用 Database Designer 日志记录功能。启用后,Database Designer 会将有关所建议投影的信息存储在两个数据收集器表中。当 Database Designer 部署设计后,这些日志便会包含实际创建的所建议投影的相关信息。部署后,日志包含下列项的相关信息:
优化器提出的投影建议
Database Designer 在部署设计时实际创建的投影
Database Designer 创建但不具备优化器所标识的理想条件的投影
用于创建所有投影的 DDL
列优化
如果您未立即部署设计,请查看日志以确定是否需要进行更改。如果已经部署设计,则仍可以手动创建某些 Database Designer 未创建的投影。
要启用 Database Designer 日志记录功能,请参阅启用 Database Designer 的日志记录。
要查看记录的信息,请参阅查看 Database Designer 日志。
默认情况下,不会记录优化器提出并且 Database Designer 部署的投影的相关信息。
要启用 Database Designer 日志记录,请输入以下命令:
=> ALTER DATABASE DEFAULT SET DBDLogInternalDesignProcess = 1;
要禁用 Database Designer 日志记录,请输入以下命令:
=> ALTER DATABASE DEFAULT SET DBDLogInternalDesignProcess = 0;
您可以在两个数据收集器表中找到 Database Designer 考虑和部署的投影的相关数据:
DC_DESIGN_PROJECTION_CANDIDATES
DC_DESIGN_QUERY_PROJECTION_CANDIDATES
DC_DESIGN_PROJECTION_CANDIDATES 表包含有关优化器建议的所有投影的信息。该表还包括创建它们的 DDL。is_a_winner
字段指示此投影是否是实际部署的设计的一部分。要查看 DC_DESIGN_PROJECTION_CANDIDATES 表,请输入:
=> SELECT * FROM DC_DESIGN_PROJECTION_CANDIDATES;
DC_DESIGN_QUERY_PROJECTION_CANDIDATES 表列出所有设计查询的计划特征。
可能的特征包括:
FULLY DISTRIBUTED JOIN
MERGE JOIN
GROUPBY PIPE
FULLY DISTRIBUTED GROUPBY
RLE PREDICATE
VALUE INDEX PREDICATE
LATE MATERIALIZATION
对于所有设计查询,DC_DESIGN_QUERY_PROJECTION_CANDIDATES 表包括以下计划特征信息:
优化程序路径成本。
Database Designer 优势。
理想的计划特征及其描述,用于标识应如何优化引用的投影。
如果已部署设计,实际计划特征及其描述将包括在表中。此信息用于标识所引用投影的实际优化方式。
由于大多数投影都具有多个优化,因此每个投影通常具有多个行。要查看 DC_DESIGN_QUERY_PROJECTION_CANDIDATES 表,请输入:
=> SELECT * FROM DC_DESIGN_QUERY_PROJECTION_CANDIDATES;
要查看这些表中的示例数据,请参阅 Database Designer 日志示例数据。
在以下示例中,Database Designer 在为 VMart 示例数据库创建全面设计之后,创建了日志。输出显示 DC_DESIGN_PROJECTION_CANDIDATES 表中的两个记录。
第一个记录包含有关 customer_dimension_dbd_1_sort_$customer_gender$__$annual_income$ projection 的信息。此记录包含 Database Designer 用于创建投影的 CREATE PROJECTION 语句。is_a_winner
列为 t
,表示 Database Designer 在部署设计时创建了此投影。
第二个记录包含有关 product_dimension_dbd_2_sort_$product_version$__$product_key$ projection 的信息。对于此投影,is_a_winner
列为 f
。优化器建议 Database Designer 在设计过程中创建此投影。但是,Database Designer 在部署设计时,没有创建该投影。日志包含 CREATE PROJECTION 语句的 DDL。如果要手动添加投影,您可以使用该 DDL。有关详细信息,请参阅手动创建设计。
=> SELECT * FROM dc_design_projection_candidates;
-[ RECORD 1 ]--------+---------------------------------------------------------------
time | 2014-04-11 06:30:17.918764-07
node_name | v_vmart_node0001
session_id | localhost.localdoma-931:0x1b7
user_id | 45035996273704962
user_name | dbadmin
design_id | 45035996273705182
design_table_id | 45035996273720620
projection_id | 45035996273726626
iteration_number | 1
projection_name | customer_dimension_dbd_1_sort_$customer_gender$__$annual_income$
projection_statement | CREATE PROJECTION v_dbd_sarahtest_sarahtest."customer_dimension_dbd_1_
sort_$customer_gender$__$annual_income$"
(
customer_key ENCODING AUTO,
customer_type ENCODING AUTO,
customer_name ENCODING AUTO,
customer_gender ENCODING RLE,
title ENCODING AUTO,
household_id ENCODING AUTO,
customer_address ENCODING AUTO,
customer_city ENCODING AUTO,
customer_state ENCODING AUTO,
customer_region ENCODING AUTO,
marital_status ENCODING AUTO,
customer_age ENCODING AUTO,
number_of_children ENCODING AUTO,
annual_income ENCODING AUTO,
occupation ENCODING AUTO,
largest_bill_amount ENCODING AUTO,
store_membership_card ENCODING AUTO,
customer_since ENCODING AUTO,
deal_stage ENCODING AUTO,
deal_size ENCODING AUTO,
last_deal_update ENCODING AUTO
)
AS
SELECT customer_key,
customer_type,
customer_name,
customer_gender,
title,
household_id,
customer_address,
customer_city,
customer_state,
customer_region,
marital_status,
customer_age,
number_of_children,
annual_income,
occupation,
largest_bill_amount,
store_membership_card,
customer_since,
deal_stage,
deal_size,
last_deal_update
FROM public.customer_dimension
ORDER BY customer_gender,
annual_income
UNSEGMENTED ALL NODES;
is_a_winner | t
-[ RECORD 2 ]--------+-------------------------------------------------------------
time | 2014-04-11 06:30:17.961324-07
node_name | v_vmart_node0001
session_id | localhost.localdoma-931:0x1b7
user_id | 45035996273704962
user_name | dbadmin
design_id | 45035996273705182
design_table_id | 45035996273720624
projection_id | 45035996273726714
iteration_number | 1
projection_name | product_dimension_dbd_2_sort_$product_version$__$product_key$
projection_statement | CREATE PROJECTION v_dbd_sarahtest_sarahtest."product_dimension_dbd_2_
sort_$product_version$__$product_key$"
(
product_key ENCODING AUTO,
product_version ENCODING RLE,
product_description ENCODING AUTO,
sku_number ENCODING AUTO,
category_description ENCODING AUTO,
department_description ENCODING AUTO,
package_type_description ENCODING AUTO,
package_size ENCODING AUTO,
fat_content ENCODING AUTO,
diet_type ENCODING AUTO,
weight ENCODING AUTO,
weight_units_of_measure ENCODING AUTO,
shelf_width ENCODING AUTO,
shelf_height ENCODING AUTO,
shelf_depth ENCODING AUTO,
product_price ENCODING AUTO,
product_cost ENCODING AUTO,
lowest_competitor_price ENCODING AUTO,
highest_competitor_price ENCODING AUTO,
average_competitor_price ENCODING AUTO,
discontinued_flag ENCODING AUTO
)
AS
SELECT product_key,
product_version,
product_description,
sku_number,
category_description,
department_description,
package_type_description,
package_size,
fat_content,
diet_type,
weight,
weight_units_of_measure,
shelf_width,
shelf_height,
shelf_depth,
product_price,
product_cost,
lowest_competitor_price,
highest_competitor_price,
average_competitor_price,
discontinued_flag
FROM public.product_dimension
ORDER BY product_version,
product_key
UNSEGMENTED ALL NODES;
is_a_winner | f
.
.
.
以下示例显示了 DC_DESIGN_QUERY_PROJECTION_CANDIDATES 中两个记录的内容。这两个行应用于投影 ID 45035996273726626。
在第一个记录中,优化器建议 Database Designer 为 GROUPBY PIPE 算法优化 customer_gender
列。
在第二个记录中,优化器建议 Database Designer 为后续执行的实体化优化 public.customer_dimension 表。后续执行的实体化可以提高可能会溢出到磁盘的联接的性能。
=> SELECT * FROM dc_design_query_projection_candidates;
-[ RECORD 1 ]-----------------+------------------------------------------------------------
time | 2014-04-11 06:30:17.482377-07
node_name | v_vmart_node0001
session_id | localhost.localdoma-931:0x1b7
user_id | 45035996273704962
user_name | dbadmin
design_id | 45035996273705182
design_query_id | 3
iteration_number | 1
design_table_id | 45035996273720620
projection_id | 45035996273726626
ideal_plan_feature | GROUP BY PIPE
ideal_plan_feature_description | Group-by pipelined on column(s) customer_gender
dbd_benefits | 5
opt_path_cost | 211
-[ RECORD 2 ]-----------------+------------------------------------------------------------
time | 2014-04-11 06:30:17.48276-07
node_name | v_vmart_node0001
session_id | localhost.localdoma-931:0x1b7
user_id | 45035996273704962
user_name | dbadmin
design_id | 45035996273705182
design_query_id | 3
iteration_number | 1
design_table_id | 45035996273720620
projection_id | 45035996273726626
ideal_plan_feature | LATE MATERIALIZATION
ideal_plan_feature_description | Late materialization on table public.customer_dimension
dbd_benefits | 4
opt_path_cost | 669
.
.
.
您可以查看 Database Designer 为其创建的投影而实施的实际计划功能。为此,请查询 V_INTERNAL.DC_DESIGN_QUERY_PROJECTIONS 表:
=> select * from v_internal.dc_design_query_projections;
-[ RECORD 1 ]-------------------+-------------------------------------------------------------
time | 2014-04-11 06:31:41.19199-07
node_name | v_vmart_node0001
session_id | localhost.localdoma-931:0x1b7
user_id | 45035996273704962
user_name | dbadmin
design_id | 45035996273705182
design_query_id | 1
projection_id | 2
design_table_id | 45035996273720624
actual_plan_feature | RLE PREDICATE
actual_plan_feature_description | RLE on predicate column(s) department_description
dbd_benefits | 2
opt_path_cost | 141
-[ RECORD 2 ]-------------------+-------------------------------------------------------------
time | 2014-04-11 06:31:41.192292-07
node_name | v_vmart_node0001
session_id | localhost.localdoma-931:0x1b7
user_id | 45035996273704962
user_name | dbadmin
design_id | 45035996273705182
design_query_id | 1
projection_id | 2
design_table_id | 45035996273720624
actual_plan_feature | GROUP BY PIPE
actual_plan_feature_description | Group-by pipelined on column(s) fat_content
dbd_benefits | 5
opt_path_cost | 155
在运行 Database Designer 之前,您必须提供待创建设计的具体信息。
使用 Database Designer 创建的所有设计均必须具有符合标识符中所述约定的唯一名称,且长度不超过 32 个字符(如果您在管理工具或管理控制台中使用 Database Designer,则不超过 16 个字符)。
设计名称将合并到 Database Designer 生成的文件(例如其部署脚本)的名称中。这可以帮助您区分与不同设计关联的文件。
Database Designer 可以创建两种不同的设计类型:全面设计或增量设计。
全面设计将为指定架构中的所有表创建一个初始设计或替换设计。当您创建新的数据库时创建全面设计。
要帮助 Database Designer 创建一个有效的设计,请将具有代表性的数据加载到表中,然后再开始设计流程。当您将数据加载到表时,Vertica 会创建一个未经过优化的 超投影,这样 Database Designer 便拥有了一些有待优化的投影。如果某个表不包含任何数据,Database Designer 将无法优化该表。
或者,为 Database Designer 提供您计划使用的具有代表性的查询,以便 Database Designer 可以优化这些查询的设计。如果您未提供任何查询,Database Designer 会创建一个超投影常规优化方案,该方案可最大限度减少存储并且不包含任何查询特定的投影。
在全面设计期间,Database Designer 会创建一些用于执行以下操作的部署脚本:
创建投影以优化查询性能。
当 Database Designer 更改其决定保留的现有投影的编码时,创建替换伙伴实例投影。
创建和部署全面的数据库设计之后,数据库可能会随着时间以各种方式发生更改。您可以考虑定期使用 Database Designer 创建增量设计,以应对这些更改。涉及增量设计的更改可能会包括:
重要数据的添加或更新
您定期运行的新查询或修改后的查询
一个或多个查询的性能问题
架构更改
Database Designer 可以为以下三个目标之一优化设计:
一个完全优化的查询具有的优化率为 0.99。优化率是在 Database Designer 生成的设计中实现的查询好处与在理想计划中实现的查询好处的比率。优化比例在 designer.log
的 OptRatio 参数中设置。
Database Designer 需要一个或多个包含适量示例数据(大约 10 GB)的表,以创建最佳设计。具有大量数据的设计表将对 Database Designer 性能产生不利影响。数据太少的设计表则会导致 Database Designer 无法创建优化设计。如果设计表没有数据,则 Database Designer 将忽略它。
针对查询性能优化的数据库设计需要一组具有代表性的查询,或设计查询。对于增量设计,设计查询为必需项,全面设计则为可选项。您将在作为输入提供给 Database Designer 的 SQL 文件中列出设计查询。当您向设计中添加查询以及当查询再次构建设计时,Database Designer 会检查查询的有效性。如果查询无效,Database Designer 会将其忽略。
如果您使用管理控制台创建数据库设计,则可从输入文件或系统表 QUERY_REQUESTS 中提交查询。有关详细信息,请参阅手动创建设计。
设计查询的最大数量取决于设计类型:对于全面设计,最大查询次数 ≤200 次;对于增量设计,最大查询次数 ≤100 次。或者,您可以为设计查询分配权重,表示其相对重要性。Database Designer 使用这些权重在设计中确定查询的优先级。
创建全面设计时,Database Designer 会基于数据统计信息和查询来创建投影。它还会查看提交的设计表,进而决定是应该对投影进行分段(在群集节点之间分布),还是不分段(在所有群集节点上复制)。
默认情况下,Database Designer 仅建议分段投影。您可以启用 Database Designer 以建议未分段投影。在这种情况下,Database Designer 建议在部署到多节点群集时对大型表使用分段超投影,而对较小表建议使用未分段超投影。
Database Designer 使用以下算法来确定是否建议未分段投影。假设最大行计数等于包含最大行数的设计表中的行数,如果满足以下任何条件,则 Database Designer 建议使用未分段投影:
最大行计数
< 1,000,000,并且表中的行数 ≥ 最大行计数的 10%
最大行计数 ≥ 10,000,000,并且表中的行数 ≥ 最大行计数的 1%
表中的行数 ≤ 100,000
Database Designer 不会在以下对象上对投影进行分段:
单节点群集
LONG VARCHAR 和 LONG VARBINARY 列
有关详细信息,请参阅使用投影的高可用性。
默认情况下,Database Designer 在设计表添加到设计时分析其统计信息。准确的统计信息可帮助 Database Designer 优化压缩和查询性能。
分析统计信息需要花费一些时间和资源。如果您确定设计表统计信息为最新信息,则可指定跳过此步骤,避免因此而产生的开销。
有关详细信息,请参阅收集统计信息。
创建设计表并将数据加载到其中,然后指定希望运行 Database Designer 在创建物理架构时使用的参数之后,引导运行 Database Designer 创建构建设计所需的脚本。
当您构建数据库设计时,Vertica 会生成两个脚本:
部署脚本:design-name_deploy.sql — 包含一些 SQL 语句,用于为您正在部署的设计创建投影、部署设计以及删除未使用的投影。当部署脚本运行时,它会创建经过优化的设计。有关如何运行此脚本和部署设计的详细信息,请参阅部署设计。
设计脚本:design-name_design.sql — 包含 Database Designer 用来创建设计的 CREATE PROJECTION
语句。查看此脚本,确保您对设计感到满意。
设计脚本是部署脚本的一个子集。它作为部署脚本创建的投影的 DDL 备份。
使用管理控制台创建设计时:
在以下情况下必须重置设计:
您构建了一个设计,但未创建构建设计中描述的输出脚本。
您构建了一个设计,但 Database Designer 无法完成设计,因为未加载它期望的查询。
重置某个设计会丢弃上一次 Database Designer 构建的所有运行特定信息,但会保留其配置(设计类型、优化目标、K-safety 等)以及表和查询。
重置某个设计后,查看该设计以了解您需要做出哪些更改。例如,您可以修复错误,更改参数,或者检查或添加更多表或查询。然后,您可以重新构建该设计。
您只能在管理控制台中或者通过使用 DESIGNER_RESET_DESIGN 函数来重置设计。
运行 Database Designer 生成部署脚本后,Vertica 建议您先在非生产服务器上测试设计,然后再将其部署到生产服务器。
设计和部署过程均在后台运行。如果您需要通宵运行一个大型设计,这非常有用。由于无需活动的 SSH 会话,因此即使会话终止,设计/部署操作仍会继续无中断运行。
Database Designer 作为后台进程运行。多个用户可以同时运行 Database Designer,他们互相之间不会出现干扰,也不会用尽群集的所有资源。但是,如果多个用户同时在同一个表上部署设计,Database Designer 可能无法完成部署。为避免出现问题,请考虑以下操作:
安排可能冲突的 Database Designer 进程在晚间按顺序运行,以便无并发问题。
避免安排 Database Designer 同时在同一组表上运行。
可通两种方法部署设计:
Micro Focus 建议您在使用示例数据加载表之后立即运行 Database Designer 并部署优化投影,因为 Database Designer 提供针对当前数据库状态优化的投影。
如果您选择允许 Database Designer 在全面设计期间自动部署脚本且正在运行管理工具,Database Designer 会创建数据库当前设计的备份脚本。该脚本可帮助您重新创建可能已被新设计删除的投影的设计。备份脚本位于您在设计过程中指定的输出目录中。
如果您选择不让 Database Designer 自动运行部署脚本(例如,要维持预先存在的部署中的投影),则可以稍后手动运行部署脚本。请参阅手动部署设计。
要在运行 Database Designer 的同时部署设计,请执行下列操作之一:
在管理控制台中,选择设计并单击部署设计 (Deploy Design)。
在管理工具中,选择设计选项 (Design Options) 窗口中的部署设计 (Deploy Design)。
如果通过编程方式运行 Database Designer,请使用 DESIGNER_RUN_POPULATE_DESIGN_AND_DEPLOY 并将 deploy
参数设置为“true”。
部署完设计后,查询 DEPLOY_STATUS 系统表以查看部署所采取的步骤:
vmartdb=> SELECT * FROM V_MONITOR.DEPLOY_STATUS;
如果您选择不让 Database Designer 在设计时间部署您的设计,您可以使用部署脚本稍后部署该设计。
确保目标数据库包含与运行 Database Designer 的数据库相同的表和投影。该数据库还应包含示例数据。
要将投影部署到测试或生产环境,请按如下所述使用元命令
\i
在 vsql 中执行部署脚本,其中 design‑name 是数据库设计的名称:
=> \i design-name_deploy.sql
对于 K-safe 数据库,调用 Vertica 元函数
GET_PROJECTIONS
在新预测表上。检查输出,验证所有投影是否有足够的伙伴被识别为安全。
如果您为已包含数据的表创建投影,请调用
REFRESH
或
START_REFRESH
更新新投影。否则,这些投影不可用于查询处理。
调用
MAKE_AHM_NOW
,将
Ancient History Mark (AHM) 设置为最近的时期。
对于不再需要的投影,请调用
DROP PROJECTION
,否则将浪费磁盘空间并降低加载速度。
对于所有数据库投影,请调用
ANALYZE_STATISTICS
:
=> SELECT ANALYZE_STATISTICS ('');
此函数从用于存储投影的所有节点中收集和聚合数据样本及存储信息,然后将统计信息写入到编录中。
有三种方法可以使用 Database Designer 创建设计:
从管理控制台中打开数据库并选择窗口底部的设计 (Design) 页面。
有关使用管理控制台创建设计的详细信息,请参阅 在管理控制台中创建数据库设计
通过编程方式使用关于通过编程方式运行 Database Designer 中所述的步骤。要通过编程方式运行 Database Designer,您必须是 DBADMIN 或已被授予 DBDUSER 角色并已启用该角色。
从管理工具菜单中,选择配置 Menu > (Configuration Menu >) > 运行 Database Designer (Run Database Designer)。您必须是 DBADMIN 用户才能从管理工具 中运行 Database Designer。
有关使用管理工具创建设计的详细信息,请参阅 使用管理工具创建设计。
下表显示了 Database Designer 在每种工具中可以具有的功能:
要使用管理工具界面为数据库创建优化设计,您必须是 DBADMIN 用户。请遵循以下步骤:
以 dbadmin 用户身份登录,然后启动管理工具。
在主菜单中,启动要为其创建设计的数据库。该数据库必须正在运行,然后您才能为其创建设计。
在主菜单上,选择配置菜单 (Configuration Menu),然后单击确定 (OK)。
在“配置菜单 (Configuration Menu)”上,选择运行 Database Designer (Run Database Designer),然后单击确定 (OK)。
在选择要设计的数据库 (Select a database to design) 窗口中,输入您正在为其创建设计的数据库的名称,然后单击确定 (OK)。
在输入 Database Designer 输出的目录 (Enter the directory for Database Designer output) 窗口中,输入用于包含设计脚本、部署脚本、备份脚本和日志文件的目录的完整路径,然后单击确定 (OK)。
有关脚本的信息,请参阅构建设计。
在 Database Designer 窗口中,输入设计的名称,然后单击确定 (OK)。
在设计类型 (Design Type) 窗口中,选择要创建的设计类型,然后单击确定 (OK)。
有关详细信息,请参阅设计类型。
选择要添加到查询搜索路径的架构 (Select schema(s) to add to query search path) 窗口列出了所选数据库中的所有架构。选择包含您希望 Database Designer 在创建设计时考虑的代表性数据的架构,然后单击确定 (OK)。
有关选择要提交到 Database Designer 的架构和表的详细信息,请参阅设计含示例数据的表。
在优化目标 (Optimization Objectives) 窗口中,选择您想要的数据库优化目标:
最后一个窗口汇总了您做出的选择并为您提供了两个选项:
继续 (Proceed) 构建设计,并且如果您已指定立即部署设计,则部署设计。如果未指定部署,可以查看设计和部署脚本,然后手动部署设计,如手动部署设计中所述。
取消 (Cancel) 设计并根据需要返回以更改某些参数。
创建设计可能需要较长时间。要从管理工具窗口中取消正在运行的设计,请输入 Ctrl+C。
要为 VMart 示例数据库创建设计,请参阅入门中的使用 Database Designer 创建完整的设计。
Vertica 提供了一组元函数,支持通过编程方式访问 Database Designer 功能。通过编程方式运行 Database Designer 以执行以下任务:
优化您所拥有的表的性能。
无需超级用户或 DBADMIN 干预,即可创建或更新设计。
添加单独的查询和表,或者向设计中添加数据,然后重新运行 Database Designer 以根据此新信息更新设计。
自定义设计。
使用最近执行的查询设置数据库以定期自动运行 Database Designer。
为每个设计查询分配一个查询权重,用以指示该查询在创建设计时的重要性。为经常运行的查询分配更高的权重,以便 Database Designer 在创建设计时优先处理这些查询。
有关 Database Designer 函数的更多详细信息,请参阅Database Designer 函数类别。
Database Designer 函数通常按以下顺序执行以下操作:
有关详细信息,请参阅 以编程方式运行 Database Designer 的工作流程。有关所需权限的信息,请参阅 运行 Database Designer 函数的权限
DESIGNER_CREATE_DESIGN 指示 Database Designer 创建设计。
以下函数可让您指定设计属性:
DESIGNER_SET_DESIGN_TYPE:指定设计是全面设计,还是增量设计。
DESIGNER_DESIGN_PROJECTION_ENCODINGS:在指定投影中分析编码,并创建用于实施编码建议的脚本。
DESIGNER_SET_DESIGN_KSAFETY:为全面设计设置 K-safety 值。
DESIGNER_SET_OPTIMIZATION_OBJECTIVE:指定是否针对查询或加载性能优化设计。
DESIGNER_SET_PROPOSE_UNSEGMENTED_PROJECTIONS:允许设计中包含未分段的投影。
以下函数可用来向 Database Designer 设计中添加表和查询:
DESIGNER_ADD_DESIGN_TABLES:将指定表添加到设计中。
DESIGNER_ADD_DESIGN_QUERY、DESIGNER_ADD_DESIGN_QUERIES、DESIGNER_ADD_DESIGN_QUERIES_FROM_RESULTS:将查询添加到设计并对其加权。
以下函数将填充 Database Designer 工作区,并创建设计和部署脚本。您还可以分析统计信息,自动部署设计,以及在部署之后删除工作区:
DESIGNER_RUN_POPULATE_DESIGN_AND_DEPLOY:填充设计并创建设计和部署脚本。
DESIGNER_WAIT_FOR_DESIGN:等待当前正在运行的设计完成。
DESIGNER_RESET_DESIGN 会丢弃上一次 Database Designer 构建或部署的指定设计的所有运行特定信息,但会保留其配置。
下列函数将显示有关 Database Designer 所创建的投影和脚本的信息:
DESIGNER_OUTPUT_ALL_DESIGN_PROJECTIONS:发送到用于定义设计投影的标准输出 DDL 语句。
DESIGNER_OUTPUT_DEPLOYMENT_SCRIPT:将设计的部署脚本发送到标准输出。
以下函数将取消任何正在运行的 Database Designer 操作或者删除 Database Designer 设计及其所有内容:
DESIGNER_CANCEL_POPULATE_DESIGN:如果指定设计当前正在运行,取消对指定设计的填充和部署操作。
DESIGNER_DROP_DESIGN:移除与指定设计及其所有内容相关联的架构。
DESIGNER_DROP_ALL_DESIGNS:移除所有与当前用户关联的 Database Designer 相关的架构。
以下示例显示了通过以编程方式运行 Database Designer 来创建设计的步骤。
在运行此示例之前,您应具有 DBDUSER 角色,并且您应使用 SET ROLE DBDUSER 命令启用此角色:
在公共架构中创建一个表:
=> CREATE TABLE T(
x INT,
y INT,
z INT,
u INT,
v INT,
w INT PRIMARY KEY
);
将数据添加到表:
\! perl -e 'for ($i=0; $i<100000; ++$i) {printf("%d, %d, %d, %d, %d, %d\n", $i/10000, $i/100, $i/10, $i/2, $i, $i);}'
| vsql -c "COPY T FROM STDIN DELIMITER ',' DIRECT;"
在公共架构中创建第二个表:
=> CREATE TABLE T2(
x INT,
y INT,
z INT,
u INT,
v INT,
w INT PRIMARY KEY
);
将 T1
表中的数据复制到 T2
表,并提交更改:
=> INSERT /*+DIRECT*/ INTO T2 SELECT * FROM T;
=> COMMIT;
创建新设计:
=> SELECT DESIGNER_CREATE_DESIGN('my_design');
此命令将信息添加到 V_MONITOR 架构中的 DESIGNS 系统表。
将公共架构中的表添加到设计:
=> SELECT DESIGNER_ADD_DESIGN_TABLES('my_design', 'public.t');
=> SELECT DESIGNER_ADD_DESIGN_TABLES('my_design', 'public.t2');
这些命令将信息添加到 DESIGN_TABLES 系统表。
在 /tmp/examples
中或在具有 READ 和 WRITE 权限的其他目录中创建一个名为 queries.txt
的文件。将以下两个查询添加到此文件,然后保存它。Database Designer 使用以下查询来创建设计:
SELECT DISTINCT T2.u FROM T JOIN T2 ON T.z=T2.z-1 WHERE T2.u > 0;
SELECT DISTINCT w FROM T;
将查询文件添加到设计,并显示结果,即已接受查询、非设计查询和不可优化查询的数量:
=> SELECT DESIGNER_ADD_DESIGN_QUERIES
('my_design',
'/tmp/examples/queries.txt',
'true'
);
结果显示接受了两个查询:
Number of accepted queries =2
Number of queries referencing non-design tables =0
Number of unsupported queries =0
Number of illegal queries =0
DESIGNER_ADD_DESIGN_QUERIES 函数会填充 DESIGN_QUERIES 系统表。
将设计类型设置为 comprehensive。(这是默认值。)全面设计将为所有设计表创建一个初始或替换设计:
=> SELECT DESIGNER_SET_DESIGN_TYPE('my_design', 'comprehensive');
将优化目标设置为 query。此设置创建了一个主要用于提高查询性能的设计,它可能会推荐其他投影。这些投影会使数据库存储占用空间更大:
=> SELECT DESIGNER_SET_OPTIMIZATION_OBJECTIVE('my_design', 'query');
创建设计,然后将设计和部署脚本保存到 /tmp/examples
或具有 READ 和 WRITE 权限的其他目录。以下命令:
会分析统计信息
不会部署设计。
在部署之后,不会删除设计。
遇到错误时会停止。
=> SELECT DESIGNER_RUN_POPULATE_DESIGN_AND_DEPLOY
('my_design',
'/tmp/examples/my_design_projections.sql',
'/tmp/examples/my_design_deploy.sql',
'True',
'False',
'False',
'False'
);
此命令将信息添加到以下系统表:
检查已运行的 Database Designer 的状态,以查看 Database Designer 推荐了哪些投影。在 deployment_projection_name
列中:
rep
表示复制的投影
super
表示超投影
deployment_status
列为 pending
,因为设计尚未部署。
对于此示例,Database Designer 推荐了四种投影:
=> \x
Expanded display is on.
=> SELECT * FROM OUTPUT_DEPLOYMENT_STATUS;
-[ RECORD 1 ]--------------+-----------------------------
deployment_id | 45035996273795970
deployment_projection_id | 1
deployment_projection_name | T_DBD_1_rep_my_design
deployment_status | pending
error_message | N/A
-[ RECORD 2 ]--------------+-----------------------------
deployment_id | 45035996273795970
deployment_projection_id | 2
deployment_projection_name | T2_DBD_2_rep_my_design
deployment_status | pending
error_message | N/A
-[ RECORD 3 ]--------------+-----------------------------
deployment_id | 45035996273795970
deployment_projection_id | 3
deployment_projection_name | T_super
deployment_status | pending
error_message | N/A
-[ RECORD 4 ]--------------+-----------------------------
deployment_id | 45035996273795970
deployment_projection_id | 4
deployment_projection_name | T2_super
deployment_status | pending
error_message | N/A
查看 /tmp/examples/my_design_deploy.sql
脚本,以了解在运行部署脚本时如何创建投影。在此示例中,脚本也会向列分配编码方案 RLE 和 COMMONDELTA_COMP(如合适)。
从保存设计的目录中部署设计。
=> \i /tmp/examples/my_design_deploy.sql
现在,设计已部署,可以删除设计:
=> SELECT DESIGNER_DROP_DESIGN('my_design');
具有 DBDUSER 角色 的非 DBADMIN 用户可以运行 Database Designer 函数。使用户能够运行这些函数需要两个步骤:
DBADMIN 或超级用户授予用户 DBDUSER 角色:
=> GRANT DBDUSER TO username;
在 DBADMIN 撤销此角色之前,此角色一直存在。
在 DBDUSER 能够运行 Database Designer 函数之前,必须出现以下情况之一:
该用户启用 DBDUSER 角色:
=> SET ROLE DBDUSER;
超级用户将用户的默认角色设置为 DBDUSER:
=> ALTER USER username DEFAULT ROLE DBDUSER;
对于 DBDUSER,以下限制适用:
您可以将设计的 K-safety 设置为小于或等于系统 K-safety 的值。您无法更改系统 K-safety。
您不能显式更改 Ancient History Mark (AHM),即便在设计部署期间同样如此。
单个设计任务可能具有需要特定权限的依赖项:
当您授予用户 DBDUSER 角色时,请确保将资源池与该用户相关联以在 Database Designer 运行期间管理资源。这允许多个用户可以同时运行 Database Designer,他们互相之间不会出现干扰,也不会用尽群集的所有资源。
Vertica 强烈建议您使用由 Database Designer 生成的物理架构设计,因为该软件可提供 K-safety、卓越的查询性能以及存储空间的有效使用。如果任何查询未能按照预期高效运行,可考虑使用 Database Designer 增量设计流程来优化该查询的数据库设计。
如果 Database Designer 创建的投影仍然不能满足您的需求,您可以从头开始或者根据 Database Designer 创建的投影设计来编写自定义投影。
如果您不熟悉如何编写自定义投影,可以从修改 Database Designer 生成的现有设计开始入手。
要创建自定义设计或自定义现有设计:
规划新的设计或修改现有设计。请参阅规划您的设计。
创建或修改投影。有关详细信息,请参阅设计基础知识和CREATE PROJECTION。
将投影部署到一个测试环境。请参阅写入并部署自定义投影。
根据需要测试并修改投影。
敲定设计后,将投影部署到生产环境。
对于熟悉 SQL 的任何人来说,用于创建设计的语法是非常简单的。然而,对于任何成功的项目而言,成功的设计需要一些初始规划。在创建第一个设计前:
熟悉标准设计要求并将您的设计规划为包括这些要求。请参阅设计要求。
确定您需要将多少个投影包括在设计中。请参阅确定要使用的投影数量。
确定要用于列的压缩和编码类型。请参阅体系结构。
确定是否希望数据库具有 K-safe。Vertica 建议所有生产数据库都应至少将 K-safety 设置为 1 (K=1)。有效的 K-safe 值为 0、1 和 2。请参阅K-safety 设计。
物理架构设计是包含 CREATE PROJECTION 语句的脚本。这些语句确定包括在投影中的列及其优化方式。
如果您开始时使用 Database Designer,它将自动创建满足所有基础设计要求的设计。如果您打算手动创建或修改设计,请注意所有设计必须满足以下要求:
对于客户端应用程序所使用的数据库中的每个表,每个设计必须至少为其创建一个超投影。这些投影提供全面覆盖,用户可利用该覆盖范围根据需要执行临时查询。它们可以包含联接,而且通常被配置为通过排序顺序、压缩和编码最大限度提高性能。
查询特定的投影为可选项。如果您对超投影所提供的性能满意,则无需创建其他投影。但是,可通过优化特定查询工作负载来最大限度提高性能。
Vertica 建议所有生产数据库都应至少将 K-safety 设置为一 (K=1) 以支持高可用性和高恢复性。(K-safety 可以设置为 0、1 或 2。)请参阅使用投影的高可用性和 K-safety 设计。
如果节点数超过 20 但表为小型表,Vertica 建议您不要创建复制的投影。如果创建复制的投影,编录会变得非常大,而且性能可能会降低。相反,请考虑对这些投影分段。
在许多情况下,由一组超投影(及其伙伴实例)组成的设计可通过压缩和编码提供令人满意的性能。当已使用投影的排序顺序最大限度提高一个或多个查询谓语(WHERE 子句)的性能时,情况尤其如此。
但是,您可能希望添加其他查询特定投影以提高运行缓慢、经常使用或作为业务关键报告的一部分运行的查询的性能。您创建的其他投影(及其伙伴实例)的数量应由以下项目决定:
组织需求
群集中每个节点上的可用磁盘空间量
将数据加载到数据库中的可用时间量
随着针对特定查询而优化的投影数量的增加,这些查询的性能得到提高。但是,已用磁盘空间量和加载数据所需的时间量也会增加。因此,应创建和测试设计以确定您的数据库配置的最佳投影数量。平均而言,选择实施查询特定投影的组织可通过添加几个查询特定投影来实现最佳性能。
Vertica 建议所有生产数据库都应至少将 K-safety 设置为 1 (K=1)。生产数据库的有效 K-safety 值为 1 和 2。非生产数据库不必为 K-safe,可将该值设置为 0。
K-safe 数据库必须至少包含三个节点,如下表所示:
仅当物理架构设计满足某些冗余要求时,才能将 K-safety 设置为 1 或 2。请参阅K-safe 物理架构设计的要求。
要创建具有 K-safe 状态的设计,Vertica 建议您使用
Database Designer。使用 Database Designer 创建投影时,建议使用满足 K-safe 设计要求的投影定义并用 K-safety 级别加以标记。Database Designer 会创建一个脚本,该脚本使用
MARK_DESIGN_KSAFE
函数将物理架构的 K-safety 设置为 1。例如:
=> \i VMart_Schema_design_opt_1.sql
CREATE PROJECTION
CREATE PROJECTION
mark_design_ksafe
----------------------
Marked design 1-safe
(1 row)
默认情况下,当数据库的 K-safety 大于 0 时,Vertica 会创建 K-safe 超投影。
监控表可以通过编程方式访问,以启用外部操作,例如警报。通过查询
SYSTEM
表内 DESIGNED_FAULT_TOLERANCE
和 CURRENT_FAULT_TOLERANCE
列中的设置,可以监控 K-safety 级别。
当群集中的 K 个节点出现故障时,数据库将继续运行,但性能会受到影响。如果故障节点的数据无法从群集中另一个正常工作的节点中获取,那么后续节点故障可能会导致数据库关闭。
Database Designer 使用值为 1 的 K-safety 为至少包含三个节点的群集自动生成设计。(如果群集具有一个或两个节点,它将使用值为 0 的 K-safety 生成设计。)您可以修改为三节点(或更大)群集创建的设计,而且 K-safe 要求已设置完毕。
如果您创建自定义投影,物理架构设计必须满足以下要求才能在出现故障时成功恢复数据库:
分段投影必须在所有节点上均 分段。请参考分段设计和为 K-safety 安全设计分段投影。
复制的投影必须在所有节点上均得到复制。请参阅为 K‑Safety 设计未分段投影。
分段投影必须具有 K+1 个 伙伴实例投影 — 具有相同列数和分段条件的投影,其中对应的分段放置在不同节点上。
可使用
MARK_DESIGN_KSAFE
函数确定您的架构设计是否满足 K-safety 的要求。
如果您使用 Database Designer 生成一个您可以修改的全面设计并且您不希望设计具有 K-safe,请将 K-safety 级别设置为 0(零)。
如果您想要从头开始,请执行以下操作为一个不具有 K-safety (K=0) 的有效数据库建立最低投影要求:
投影必须符合数据库 K-safety 要求。通常,您必须为每个分段投影创建伙伴实例投影,其中伙伴实例投影的数量为 K+1。因此,如果系统 K-safety 设置为 1,则必须通过一个伙伴实例复制每个投影分段;如果 K-safety 设置为 2,则必须通过两个伙伴实例复制每个分段。
通过包括 SEGMENTED BY ... ALL NODES
,可以使用
CREATE PROJECTION
自动创建满足 K-safety 所需的伙伴实例投影数量。如果 CREATE PROJECTION
指定 K-safety (
KSAFE=n)
,Vertica 将使用该设置;如果语句省略 KSAFE
,Vertica 将使用系统 K-safety。
在以下示例中,CREATE PROJECTION
为表 ttt
创建分段投影 ttt_p1
。由于系统 K‑safety 设置为 1,因此 Vertica 需要每个分段投影拥有一个伙伴实例投影。由于“CREATE PROJECTION”语句省略“KSAFE”,因此 Vertica 使用系统 K‑safety 并创建两个伙伴实例投影:“ttt_p1_b0”和“ttt_p1_b1”:
=> SELECT mark_design_ksafe(1);
mark_design_ksafe
----------------------
Marked design 1-safe
(1 row)
=> CREATE TABLE ttt (a int, b int);
WARNING 6978: Table "ttt" will include privileges from schema "public"
CREATE TABLE
=> CREATE PROJECTION ttt_p1 as SELECT * FROM ttt SEGMENTED BY HASH(a) ALL NODES;
CREATE PROJECTION
=> SELECT projection_name from projections WHERE anchor_table_name='ttt';
projection_name
-----------------
ttt_p1_b0
ttt_p1_b1
(2 rows)
通过将后缀
_bn
附加到投影基本名(例如 ttt_p1_b0
),Vertica 自动为伙伴实例投影命名。
如果在单个节点上创建投影并且系统 K-safety 大于 0,则必须手动创建 K-safety 所需的伙伴实例数量。例如,可以在单个节点上为表 xxx
创建投影 xxx_p1
,如下所示:
=> CREATE TABLE xxx (a int, b int);
WARNING 6978: Table "xxx" will include privileges from schema "public"
CREATE TABLE
=> CREATE PROJECTION xxx_p1 AS SELECT * FROM xxx SEGMENTED BY HASH(a) NODES v_vmart_node0001;
CREATE PROJECTION
由于 K-safety 设置为 1,此投影的单个实例不是 K-safe。若尝试将数据插入到它的锚表 xxx
,则会返回如下错误:
=> INSERT INTO xxx VALUES (1, 2);
ERROR 3586: Insufficient projections to answer query
DETAIL: No projections that satisfy K-safety found for table xxx
HINT: Define buddy projections for table xxx
为了符合 K-safety,您必须为投影 xxx_p1
创建一个伙伴实例投影。例如:
=> CREATE PROJECTION xxx_p1_buddy AS SELECT * FROM xxx SEGMENTED BY HASH(a) NODES v_vmart_node0002;
CREATE PROJECTION
表 xxx
现在符合 K-safety 并接受 DML 语句(例如 INSERT
):
VMart=> INSERT INTO xxx VALUES (1, 2);
OUTPUT
--------
1
(1 row)
有关分段投影和伙伴实例的一般信息,请参阅分段投影。有关 K-safety 设计的信息,请参阅K-safety 设计和分段设计。
在许多情况下,维度表相对较小,因此您无需将它们分段。相应地,您应该设计一个 K-safe 数据库,以便可以复制其维度表的投影并且无需在所有群集节点上进行分段。您可以使用包括关键字 UNSEGMENTED ALL NODES
的
CREATE PROJECTION
语句创建这些投影。这些关键字用于指定在所有群集节点上创建投影的相同实例。
以下示例展示了如何为表 store.store_dimension
创建未分段投影:
=> CREATE PROJECTION store.store_dimension_proj (storekey, name, city, state)
AS SELECT store_key, store_name, store_city, store_state
FROM store.store_dimension
UNSEGMENTED ALL NODES;
CREATE PROJECTION
Vertica 使用相同的名称来标识未分段投影的所有实例 — 在此示例中为 store.store_dimension_proj
。关键字 ALL NODES
指定在所有节点上复制投影:
=> \dj store.store_dimension_proj
List of projections
Schema | Name | Owner | Node | Comment
--------+----------------------+---------+------------------+---------
store | store_dimension_proj | dbadmin | v_vmart_node0001 |
store | store_dimension_proj | dbadmin | v_vmart_node0002 |
store | store_dimension_proj | dbadmin | v_vmart_node0003 |
(3 rows)
有关投影名称约定的详细信息,请参阅投影命名。
可使用哈希分段对投影分段。哈希分段可基于内置的哈希函数对投影进行分段。该内置哈希函数可使多个节点中的数据实现正态分布,从而优化查询的执行。在投影中,要进行哈希的数据由一列或多列值组成,每一列都包含大量唯一值,并且值的分布偏移程度在可接受的范围内。满足标准的主键列非常适合进行哈希分段。
分段投影时,确定哪些列用于对投影分段。选择具有大量唯一数据值和在数据分布中可接受的偏离的一个或多个列。主键列是哈希分段的绝佳选择。这些列在查询中所用的所有表上必须唯一。
尽管您可以从头开始编写自定义投影,但 Vertica 建议您使用 Database Designer 创建设计来作为起点。这样可确保您获得满足基本要求的投影。
在编写自定义投影之前,请仔细查看规划您的设计中的主题。不遵循这些注意事项可能会生成无法正常工作的投影。
要手动修改或创建投影:
使用
CREATE PROJECTION
语句编写脚本以创建所需的投影。
使用元命令
\i
在 vsql 中运行脚本。
对于 K-safe 数据库,调用 Vertica 元函数
GET_PROJECTIONS
在新预测表上。检查输出,验证所有投影是否有足够的伙伴被识别为安全。
如果您为已包含数据的表创建投影,请调用
REFRESH
或
START_REFRESH
更新新投影。否则,这些投影不可用于查询处理。
调用
MAKE_AHM_NOW
,将
Ancient History Mark (AHM) 设置为最近的时期。
对于不再需要的投影,请调用
DROP PROJECTION
,否则将浪费磁盘空间并降低加载速度。
对于所有数据库投影,请调用
ANALYZE_STATISTICS
:
=> SELECT ANALYZE_STATISTICS ('');
此函数从用于存储投影的所有节点中收集和聚合数据样本及存储信息,然后将统计信息写入到编录中。
超投影需要满足以下要求:
它们必须包含表中的每个列。
对于 K-safe 设计,必须在数据库群集内的所有节点上复制超投影(适用于维度表),或者与伙伴投影进行配对并跨越所有节点进行分段(适用于很大的表和中等的表)。有关投影及其存储方式的概述,请参阅投影和使用投影的高可用性。有关设计细节,请参阅K-safety 设计。
要实现最大可用性,超投影需要最大限度降低存储要求,同时最大限度提升查询性能。为了实现这一目标,超投影中列的排序顺序以存储要求和常用查询为基础。
列排序顺序是将存储空间要求降至最低和最大限度提高查询性能的重要因素。
最小化存储不但可以节省物理资源,而且还可以减少磁盘 I/O,以显著提高性能。通过在排序顺序中优先考虑低基数列,可以最大程度地减少投影存储。这会减少 Vertica 检索查询结果时所存储和访问的行数。
确定投影排序列后,分析其数据并选择效率最高的编码方法。Vertica 优化器使用运行长度编码 (RLE) 为列提供首选项,因此请确保在适当的情况下使用它。运行长度编码将相同值的序列(运行)替换为包含值和出现次数的单个对。因此,它特别适合用于运行长度较大的低基数列。
可通过列排序顺序提高查询性能,方法如下:
排序顺序应尽可能地使用最低基数确定列的优先级。
请勿在 LONG VARBINARY 和 LONG VARCHAR 类型的列上排序投影。
选择投影的排序顺序时,Vertica 具有多条建议,可以帮助您实现最大查询性能,如以下示例中所示。
当处理低基数列中的谓词时,组合使用 RLE 和排序可最大程度减少存储要求,同时实现最大查询性能。
假设您具有一个包含以下值和编码类型的 students
表:
您可能具有类似如下所示的查询:
SELECT name FROM studentsWHERE gender = 'M' AND pass_fail = 'P' AND class = 'senior';
最快的数据访问方式是先处理非重复值数量最少的低基数列,然后再处理高基数列。对于对 gender
、class
、pass_fail
和 name
具有相同限制的查询,以下排序顺序可最大程度减少存储要求,同时实现最大查询性能。如下所示指定投影的 ORDER BY 子句:
ORDER BY students.gender, students.pass_fail, students.class, students.name
在本示例中,gender
列由两个 RLE 条目表示,pass_fail
列由四个条目表示,而 class
列由 16 个条目表示,而不考虑 students
表的基数。Vertica 可有效地查找一组满足所有谓词的行,从而大幅减少了在排序顺序早期出现的 RLE 编码列搜索工作。因此,如果您在局部谓词中使用低基数列(如上例所示),请尽早将这些列按照非重复基数的递增顺序(即按照每列中非重复值数量的递增顺序)放入投影排序顺序中。
如果先使用 student.class
排序此表,则可以提高仅对 student.class
列存在限制的查询的性能,并改善 student.class
列(包含非重复值数量最多的列)的压缩性能,但其他列也不会压缩。确定哪个投影更好取决于工作负载中的特定查询及其相对重要性。
随着列基数的增加,通过压缩节省的存储空间会降低;但是,随着在该列中存储值所需字节数的增加,通过压缩节省的存储空间也会增加。
为了充分发挥 RLE 编码的优势,请仅在列的平均运行长度大于 10(排序时)时使用该编码。例如,假设您具有包含以下列的表,各列按基数由低到高的顺序排列:
address.country, address.region, address.state, address.city, address.zipcode
zipcode
列可能在具有相同 zip 代码的行中没有 10 个排序条目,因此对该列执行运行长度编码很可能没有优势,而且可能会导致压缩性能变差。但在排序的运行长度中可能有超过 10 个国家/地区,因此对该国家/地区列应用 RLE 可以提高性能。
一般而言,在联接顺序中先放置用于局部谓词(如上例所示)的列可使谓词求值更加高效。另外,如果通过基数较高的列来唯一确定基数较低的列(如仅 city_id 唯一确定 state_id),则在排序顺序中先放置基数较低、通过函数确定的列总比先放置基数较高的列好。
例如,在以下排序顺序中,customer_info 表中的 Area_Code 列排在 Number 列之前。
ORDER BY = customer_info.Area_Code, customer_info.Number, customer_info.Address
在查询中,先放置 Area_Code
列,以便仅扫描 Number
列中以 978 开头的值。
=> SELECT AddressFROM customer_info WHERE Area_Code='978' AND Number='9780123457';
处理联接时,Vertica 优化器会从两种算法中进行选择:
合并联接 — 如果两个输入都按联接列预先排序,优化器会选择合并联接,因为这种算法更快而且使用的内存更少。
哈希联接 — 利用哈希联接算法,Vertica 会使用较小的(内部)联结表在内存中构建联接列的哈希表。哈希联接没有排序要求,但却会消耗更多内存,因为 Vertica 会构建一个包含内部表值的哈希表。如果投影未存储在联接列上,则优化器会选择一种哈希联接。
如果两个输入都预先排序,合并联接不必执行任何预处理,因而可以更快地执行联接。Vertica 使用的术语“排序-合并联接”是指在合并联接之前必须对至少一个输入排序的情况。只有当外部输入端已按联接列排序时,Vertica 才会排序内部输入端。
为了使 Vertica 查询优化器能够为特定联接选择使用有效的合并联接,请在联接的两端创建投影,并在相应投影中先放置联接列。如果两个表都非常大,内存中无法容纳任何一个表,那么这样做将非常重要。如果预期内存中可以同时容纳某个表要联接到的所有表,那么合并联接的优势比哈希联接要小很多,很可能并不值得为任何一个联接列创建投影。
如果您有一个重要查询,并且您会定期运行该查询,那么将该查询 WHERE 子句或 GROUP BY 子句中指定的列先放入排序顺序中可以为您节省时间。
如果该查询使用高基数列(如社会保险号),则将该列先放入投影的排序顺序中可能会牺牲存储空间,但您的最重要查询将得到优化。
如果有两个基数相等的列,则在排序顺序中先放置较大的列。例如,CHAR(20) 列占用 20 字节,但 INTEGER 列占用 8 字节。通过将 CHAR(20) 列放在 INTEGER 列前面,可以更好地压缩投影。
假设您有一个事实表,排序顺序中的前四列组成了另一个表的外键。为了获得最佳压缩效果,我们为事实表选择一个排序顺序,使其先显示外键,并以非重复基数的递增顺序排列。在设计事实表的投影时,还可应用其他因素,如按时间维度(若存在)分区。
在以下示例中,表 inventory
用于存储库存数据,product_key
和 warehouse_key
是 product_dimension
和 warehouse_dimension
表的外键:
=> CREATE TABLE inventory (
date_key INTEGER NOT NULL,
product_key INTEGER NOT NULL,
warehouse_key INTEGER NOT NULL,
...
);
=> ALTER TABLE inventory
ADD CONSTRAINT fk_inventory_warehouse FOREIGN KEY(warehouse_key)
REFERENCES warehouse_dimension(warehouse_key);
ALTER TABLE inventory
ADD CONSTRAINT fk_inventory_product FOREIGN KEY(product_key)
REFERENCES product_dimension(product_key);
库存表应当先按 warehouse_key 排序,然后再按 product_key 排序,因为 warehouse_key
列的基数很可能低于 product_key
的基数。
如果您测量和设置群集内的存储位置的性能,Vertica 将使用此信息确定在哪里基于列的排名来存储列。有关详细信息,请参阅设置存储性能。
Vertica 将投影排序顺序中包含的列存储在最快的可用存储位置。未包含在投影排序顺序中的列将存储在稍慢的磁盘中。每个投影的列按如下方式排序:
排序顺序中的列具有最高优先级(编号 >;1000)。
排序顺序中最后一列的排序编号为 1001。
排序顺序中倒数第二的列排序编号为 1002,以此类推,直到排序顺序中的第一列,其排序编号为 1000 + 排序列数。
剩余列的排序编号在 1000–1 之间,从 1000 开始,每列减一。
Vertica 随后将列从最高排号到最低排号存储在磁盘中。它将最高排号的列放在最快的磁盘上,将最低排号的列放在最慢的磁盘上。
通过手动覆盖这些列的默认排名,您可以修改将哪些列存储在快速磁盘上。要完成此操作,请设置列列表中的 ACCESSRANK
关键字。确保使用一个当前尚未用于另一个列的整数。例如,如果您要为某列提供最快速的访问排名,可使用一个明显高于 1000 + 排序列数量之和的数字。这样一来,您便可以随着时间的推移输入更多列,而不会跌入到您设置的访问排名中。
以下示例将列 store_key
的访问等级设置为 1500:
CREATE PROJECTION retail_sales_fact_p (
store_key ENCODING RLE ACCESSRANK 1500,
pos_transaction_number ENCODING RLE,
sales_dollar_amount,
cost_dollar_amount )
AS SELECT
store_key,
pos_transaction_number,
sales_dollar_amount,
cost_dollar_amount
FROM store.store_sales_fact
ORDER BY store_key
SEGMENTED BY HASH(pos_transaction_number) ALL NODES;
数据库用户应仅对执行其任务所需要的数据库资源具有访问权限。例如,大多数用户应该能够读取数据,但无法修改或插入新数据。少数用户通常需要用来执行更广泛数据库任务(例如,创建和修改架构、表和视图)的权限。极少数用户可以执行管理任务,例如重新平衡群集上的节点,或者启动或停止数据库。您还可以让某些用户将自己的权限扩展到其他用户。
客户端身份验证控制着数据库对象用户在数据库中可以访问和更改的内容。您可以使用 GRANT 语句为特定用户或角色指定访问权限。
每个 Vertica 数据库都有一个或多个用户。当用户连接至数据库时,他们必须使用超级用户在数据库中定义的有效凭证(用户名和密码)登录。
数据库用户拥有其在数据库中创建的表、过程和存储位置等对象。
在 Vertica 数据库中,共有三种类型的用户:
数据库管理员 (DBADMIN)
对象所有者
其他所有人 (PUBLIC)
安装时,新的 Vertica 数据库会自动包含具有 超级用户权限的用户。除非在安装期间显式命名,否则此用户被标识为 dbadmin
。此用户不能被删除,并且具有以下不可撤销的角色:
使用这些角色,dbadmin
用户可以执行所有数据库操作。此用户还可以创建其他具有管理权限的用户。
不要将 dbadmin
用户与 DBADMIN 角色的用户混淆。DBADMIN 角色是一组可以分配给一个或多个用户的权限。
Vertica 文档经常将 dbadmin
用户称为超级用户。此参考文档与 Linux 超级用户无关。
dbadmin
用户可以创建具有相同权限的其他用户:
创建用户:
=> CREATE USER DataBaseAdmin2; CREATE USER
向新用户 DataBaseAdmin2
授予相应角色:
=> GRANT dbduser, dbadmin, pseudosuperuser to DataBaseAdmin2;
GRANT ROLE
用户 DataBaseAdmin2
现在具有与授予给最初 dbadmin 用户相同的权限。
DataBaseAdmin2
使用 SET ROLE 启用已分配给您的角色:
=> \c - DataBaseAdmin2;
You are now connected to database "VMart" as user "DataBaseAdmin2". => SET ROLE dbadmin, dbduser, pseudosuperuser; SET ROLE
确认角色已启用:
=> SHOW ENABLED ROLES;
name | setting ------------------------------------------------- enabled roles | dbduser, dbadmin, pseudosuperuser
对象所有者是创建特定数据库对象并可以对该对象执行任何操作的用户。默认情况下,仅所有者(或 超级用户)可以对数据库对象执行操作。为了允许其他用户使用对象,所有者或超级用户必须使用其中一个 GRANT 语句向这些用户授予权限。
有关详细信息,请参阅数据库权限。
所有非 DBA(超级用户)或对象所有者都是 PUBLIC 用户。
新创建的用户默认没有 PUBLIC 架构的访问权。请确保对创建的所有用户运行 GRANT USAGE ON SCHEMA PUBLIC。
要创建数据库用户:
以超级用户身份从 vsql 连接至数据库。
发出
CREATE USER
语句及可选参数。
运行一系列 GRANT 语句,授予新用户权限。
要在 MC 上创建用户,请参阅在管理控制台中创建 MC 用户
默认情况下,新数据库用户有权在数据库中创建临时表。
新创建的用户默认没有 PUBLIC
架构的访问权。请确保对创建的所有用户运行 GRANT USAGE ON SCHEMA PUBLIC
可以使用
ALTER USER
语句更改用户相关信息,例如用户的密码。如果要将用户配置为不使用任何密码身份验证,可以在 CREATE USER
或 ALTER USER
语句中设置空密码 '',或在 CREATE USER
中忽略 IDENTIFIED BY
参数。
以下一系列命令将密码为“password”的用户 Fred 添加到数据库。第二个命令向 Fred 授予公共架构的 USAGE 权限:
=> CREATE USER Fred IDENTIFIED BY 'password';
=> GRANT USAGE ON SCHEMA PUBLIC to Fred;
使用双引号创建的用户名区分大小写。例如:
=> CREATE USER "FrEd1";
在上述示例中,登录名必须完全匹配。如果创建用户名时没有使用双引号(例如,FRED1
),则用户可以使用 FRED1
、FrEd1
、fred1
等登录。
ALTER USER 允许您为单个用户设置用户级别配置参数。这些设置会覆盖相同参数的数据库或会话级别设置。例如,以下 ALTER USER 语句将用户 Yvonne 和 Ahmed 的 DepotOperationsForQuery 设置为 FETCHES,从而覆盖默认设置 ALL:
=> SELECT user_name, parameter_name, current_value, default_value FROM user_configuration_parameters
WHERE user_name IN('Ahmed', 'Yvonne') AND parameter_name = 'DepotOperationsForQuery';
user_name | parameter_name | current_value | default_value
-----------+-------------------------+---------------+---------------
Ahmed | DepotOperationsForQuery | ALL | ALL
Yvonne | DepotOperationsForQuery | ALL | ALL
(2 rows)
=> ALTER USER Ahmed SET DepotOperationsForQuery='FETCHES';
ALTER USER
=> ALTER USER Yvonne SET DepotOperationsForQuery='FETCHES';
ALTER USER
若要标识用户级别配置参数,请查询系统表 CONFIGURATION_PARAMETERS 的 allowed_levels
列。例如,以下查询标识会影响存储库使用的用户级别参数:
n=> SELECT parameter_name, allowed_levels, default_value, current_level, current_value
FROM configuration_parameters WHERE allowed_levels ilike '%USER%' AND parameter_name ilike '%depot%';
parameter_name | allowed_levels | default_value | current_level | current_value
-------------------------+-------------------------+---------------+---------------+---------------
UseDepotForReads | SESSION, USER, DATABASE | 1 | DEFAULT | 1
DepotOperationsForQuery | SESSION, USER, DATABASE | ALL | DEFAULT | ALL
UseDepotForWrites | SESSION, USER, DATABASE | 1 | DEFAULT | 1
(3 rows)
可以用以下两种方式获取用户设置:
查询系统表 USER_CONFIGURATION_PARAMETERS:
=> SELECT * FROM user_configuration_parameters;
user_name | parameter_name | current_value | default_value
-----------+---------------------------+---------------+---------------
Ahmed | DepotOperationsForQuery | FETCHES | ALL
Yvonne | DepotOperationsForQuery | FETCHES | ALL
Yvonne | LoadSourceStatisticsLimit | 512 | 256
(3 rows)
使用 SHOW USER:
=> SHOW USER Yvonne PARAMETER ALL;
user | parameter | setting
--------+---------------------------+---------
Yvonne | DepotOperationsForQuery | FETCHES
Yvonne | LoadSourceStatisticsLimit | 512
(2 rows)
=> SHOW USER ALL PARAMETER ALL;
user | parameter | setting
--------+---------------------------+---------
Yvonne | DepotOperationsForQuery | FETCHES
Yvonne | LoadSourceStatisticsLimit | 512
Ahmed | DepotOperationsForQuery | FETCHES
(3 rows)
作为超级用户,您可以使用 ALTER USER...ACCOUNT LOCK 手动锁定数据库用户帐户,使用 ALTER USER...ACCOUNT UNLOCK 解锁数据库用户帐户。例如,下列命令将阻止用户 Fred 登录到数据库:
=> ALTER USER Fred ACCOUNT LOCK;
=> \c - Fred
FATAL 4974: The user account "Fred" is locked
HINT: Please contact the database administrator
以下示例解锁对 Fred 的用户帐户的访问权限:
=> ALTER USER Fred ACCOUNT UNLOCK;|
=> \c - Fred
You are now connected as user "Fred".
CREATE USER 可以指定锁定新帐户。与任何锁定的帐户一样,可以使用 ALTER USER...ACCOUNT UNLOCK 解锁它。
=> CREATE USER Bob ACCOUNT LOCK;
CREATE USER
用户的配置文件可以指定在登录尝试失败一定次数后锁定帐户。
作为超级用户,您可以在使用 CREATE USER 创建用户时或稍后使用 ALTER USER 设置任何用户的密码。非超级用户还可以使用 ALTER USER 更改自己的密码。有一个例外情况:使用 LDAPLink 服务添加到 Vertica 数据库的用户无法使用 ALTER USER 更改其密码。
如果您提供相关的 salt,您还可以为用户提供预先经过哈希处理的密码。salt 必须是十六进制字符串。此方法绕过密码复杂性要求。
要查看现有用户的密码哈希和 salt,请参阅 PASSWORDS 系统表。
更改用户的密码对其当前会话没有影响。
在以下示例中,使用密码“mypassword”创建用户“Bob”。
=> CREATE USER Bob IDENTIFIED BY 'mypassword';
CREATE USER
随后将密码更改为“Orca”。
=> ALTER USER Bob IDENTIFIED BY 'Orca' REPLACE 'mypassword';
ALTER USER
在以下示例中,使用预先经过哈希处理的密码和 salt 创建用户“Alice”。
=> CREATE USER Alice IDENTIFIED BY
'sha512e0299de83ecfaa0b6c9cbb1feabfbe0b3c82a1495875cd9ec1c4b09016f09b42c1'
SALT '465a4aec38a85d6ecea5a0ac8f2d36d8';
在管理控制台上,具有 ADMIN 或 IT 权限的用户可以重置用户的非 LDAP 密码:
登录管理控制台并导航至 MC 设置 (MC Settings) > 用户管理 (User management)。
单击选择要修改的用户,再单击编辑 (Edit)。
单击编辑密码 (Edit password),然后输入两次新密码。
单击确定 (OK),然后单击保存 (Save)。
角色是可以向一个或多个用户或其他角色授予的权限集。角色可帮助您授予和管理不同类别用户的权限集,而不是将这些权限单独授予每个用户。
例如,多个用户可能需要管理权限。您可以按如下方式向他们授予这些权限:
使用 CREATE ROLE创建管理员角色:
CREATE ROLE administrator;
使用一个或多个 GRANT 语句向此角色授予适当的权限。您可以在以后根据需要添加和移除权限。角色权限的更改会自动传播给拥有该角色的用户。
Vertica 具有以下预定义角色:
安装时,Vertica 会自动按如下方式授予和启用预定义角色:
将 DBADMIN、PSEUDOSUPERUSER 和 DBDUSER 角色不可撤销地授予 dbadmin 用户。这些角色始终会针对 dbadmin
启用,并且永远无法删除。
在创建时将 PUBLIC 授予 dbadmin
以及所有其他用户。此角色始终处于启用状态,并且无法删除或撤销。
安装后,dbadmin
用户和具有 PSEUDOSUPERUSER 角色的用户可以将一个或多个预定义角色授予任何用户或非预定义角色。例如,以下一组语句创建 userdba
角色并向它授予预定义角色 DBADMIN:
=> CREATE ROLE userdba;
CREATE ROLE
=> GRANT DBADMIN TO userdba WITH ADMIN OPTION;
GRANT ROLE
如果原始 授权(角色) 语句包含 WITH ADMIN OPTION,则被授予预定义角色的用户和角色可以将该角色授予其他用户。有一个例外:如果您向某个用户授予 PSEUDOSUPERUSER 角色并省略 WITH ADMIN OPTION,则被授予者可以将任何角色(包括所有预定义角色)授予其他用户。
例如,userdba
角色以前被授予 DBADMIN 角色。因为 GRANT 语句包含 WITH ADMIN OPTION,所以被分配 userdba
角色的用户可以将 DBADMIN 角色授予其他用户:
=> GRANT userdba TO fred;
GRANT ROLE
=> \c - fred
You are now connected as user "fred".
=> SET ROLE userdba;
SET
=> GRANT dbadmin TO alice;
GRANT ROLE
您可以向除 SYSMONITOR 以外的预定义角色授予对单个数据库对象(例如表或架构)的权限。例如:
=> CREATE SCHEMA s1;
CREATE SCHEMA
=> GRANT ALL ON SCHEMA s1 to PUBLIC;
GRANT PRIVILEGE
您可以向 PUBLIC 授予任何角色,包括预定义角色。例如:
=> CREATE ROLE r1;
CREATE ROLE
=> GRANT r1 TO PUBLIC;
GRANT ROLE
对于任何其他预定义角色,不能通过向其授予其他角色来修改它。尝试这样做会导致回退错误:
=> CREATE ROLE r2;
CREATE ROLE
=> GRANT r2 TO PSEUDOSUPERUSER;
ROLLBACK 2347: Cannot alter predefined role "pseudosuperuser"
DBADMIN
角色是在安装数据库时分配给 dbadmin
用户的预定义角色。安装后,dbadmin
用户和具有
PSEUDOSUPERUSER
角色的用户可以将任何角色授予任何用户或非预定义角色。
例如,超级用户 dbadmin
创建角色 fred
并向 fred
授予 DBADMIN
角色:
=> CREATE USER fred;
CREATE USER
=> GRANT DBADMIN TO fred WITH ADMIN OPTION;
GRANT ROLE
在用户 fred
启用其 DBADMIN role
后,他可以通过创建用户 alice
来行使他的 DBADMIN
权限。因为 GRANT
语句包含 WITH ADMIN OPTION
,所以 fred 也可以将 DBADMIN
角色授予用户 alice
:
=> \c - fred
You are now connected as user "fred".
=> SET ROLE dbadmin;
SET
CREATE USER alice;
CREATE USER
=> GRANT DBADMIN TO alice;
GRANT ROLE
下表列出了 DBADMIN 角色支持的权限:
创建用户和角色,并向他们授予角色和权限
创建和删除架构
查看所有系统表
查看和终止用户会话
访问任意用户创建的所有数据
PSEUDOSUPERUSER
角色是在安装数据库时自动分配给 dbadmin
用户的预定义角色。dbadmin
可以将此角色授予任何用户角色或非预定义角色。此后,PSEUDOSUPERUSER
用户可以将任何角色(包括预定义角色)授予其他用户。
具有 PSEUDOSUPERUSER
角色的用户有权履行管理权限(无法撤销)。角色权限包括:
绕过所有 GRANT/REVOKE 身份验证
创建架构和表
创建用户和角色,并向他们授予权限
修改用户帐户,例如,设置用户帐户的密码、锁定/解锁帐户。
创建或删除 UDF 库和函数,或任何外部过程
DBDUSER
角色是在安装数据库时分配给 dbadmin
用户的预定义角色。dbadmin
和任何 PSEUDOSUPERUSER
可以将此角色授予任何用户或非预定义角色。拥有此角色并启用它的用户可以从命令行调用 Database Designer 函数。
确保将资源池与 DBDUSER
角色相关联,以方便在运行 Database Designer 时进行资源管理。多个用户可以同时运行 Database Designer,他们互相之间不会出现干扰,也不会用尽群集的所有资源。当您以编程方式或使用管理工具运行 Database Designer 时,设计执行操作通常会限制在该用户的资源池内,但不太密集的任务可能会溢出到系统资源池。
除了作为 DBADMIN 用户维护 Vertica 以外,组织的数据库管理员可能还有许多责任。在本例中,DBADMIN 可能希望将一些 Vertica 管理任务委托给其他 Vertica 用户。
DBADMIN 可以将任务委托给 SYSMONITOR 角色,为其授予对系统表的访问权限,而不必授予完整的 DBADMIN 访问权限。
SYSMONITOR 角色提供以下权限。
查看所有标记为可监控的系统表。通过发出以下语句,您可以查看所有可监控表的列表:
=> select * from system_tables where is_monitorable='t';
如果在将 SYSMONITOR 授予用户或角色时包含 WITH ADMIN OPTION
,则该用户或角色可以将 SYSMONITOR 权限授予其他用户和角色。
若要为用户或角色授予 SYSMONITOR 角色,您必须是以下身份之一:
DBADMIN 用户
已分配有 SYSMONITOR 且具有 ADMIN OPTION 的用户
使用 GRANT(角色) SQL 语句为用户分配 SYSMONITOR 角色。以下示例将展示如何为 user1 授予 SYSMONITOR 角色,并通过使用 WITH ADMIN OPTION 参数包含管理权限。ADMIN OPTION 将为 SYSMONITOR 角色授予管理权限。
=> GRANT SYSMONITOR TO user1 WITH ADMIN OPTION
;
以下示例将展示如何为 user1 撤销 SYSMONITOR 角色的 ADMIN OPTION 权限。
=> REVOKE ADMIN OPTION for SYSMONITOR FROM user1
;
使用 CASCADE 为所有分配有 SYSMONITOR 角色的用户撤销 ADMIN OPTION 权限。
=> REVOKE ADMIN OPTION for SYSMONITOR FROM PUBLIC CASCADE
;
以下示例将展示如何:
创建用户
创建角色
为新角色授予 SYSMONITOR 权限
为用户授予角色
=> CREATE USER user1; => CREATE ROLE monitor; => GRANT SYSMONITOR to monitor; => GRANT monitor to user1;
以下示例将使用“授予 SYSMONITOR 角色”示例中创建的用户和角色来说明如何:
创建名为 personal_data 的表
以 user1 身份登录
为 user1 授予 monitor 角色。(您已在“授予 SYSMONITOR 角色”示例中为 monitor 授予了 SYSMONITOR 权限。)
以 user1 身份运行 SELECT 语句
操作结果取决于已授予 user1 的权限。
=> CREATE TABLE personal_data (SSN varchar (256));=> \c -user1;user1=> SET ROLE monitor;user1=> SELECT COUNT(*) FROM TABLES;COUNT ------- 1 (1 row)
由于您已分配了 SYSMONITOR 角色,因此 user1 可以看到 Tables 系统表中行的数量。在这个简单的示例中,数据库中只有一个表 (personal_data),因此 SELECT COUNT 返回一行。在实际情况中,SYSMONITOR 角色将看到数据库中的所有表。
使用下列命令检查分配有 SYSMONITOR 角色的用户是否可以访问系统表:
=> select table_name, is_monitorable from system_tables where table_name='<table_name>';
示例
以下示例将检查 SYSMONITOR 是否可以访问 current_session 系统表:
=> select table_name,is_monitorable from system_tables where table_name='current_session';table_name | is_monitorable -------------------------------- current_session | t
Is_monitorable 列中的 t 表示 SYSMONITOR 可以访问 current_session 系统表。
UDXDEVELOPER 角色是允许用户创建和替换用户所定义库的预定义角色。dbadmin
可以将此角色授予任何用户角色或非预定义角色。
具有 UDXDEVELOPER 角色的用户可以执行以下操作:
如果为库所有者或具有 DROP 权限:
CREATE OR REPLACE LIBRARY
要使用此角色的权限,您必须使用 SET ROLE 显式启用它。
具有 UDXDEVELOPER 角色的用户可以创建库,因此可以在数据库中安装任何 UDx 函数。UDx 函数以拥有数据库的 Linux 用户身份运行,因此可以访问 Vertica 有权访问的资源。
写得不好的函数会降低数据库性能。请仅将此角色授予您相信会负责任地使用 UDx 的用户。您可以通过在隔离模式下运行 UDx 并设置 FencedUDxMemoryLimitMB 配置参数来限制 UDx 可以使用的内存。
PUBLIC
角色是自动分配给所有新用户的预定义角色。它始终处于启用状态,并且无法删除或撤销。使用此角色授予所有数据库用户相同的最低权限集。
与所有其他角色一样,可以授予 PUBLIC
角色对单个对象和其他角色的权限。以下示例授予 PUBLIC
角色对表 publicdata
的 INSERT 和 SELECT 权限。这使所有用户都可以读取该表中的数据并插入新数据:
=> CREATE TABLE publicdata (a INT, b VARCHAR);
CREATE TABLE
=> GRANT INSERT, SELECT ON publicdata TO PUBLIC;
GRANT PRIVILEGE
=> CREATE PROJECTION publicdataproj AS (SELECT * FROM publicdata);
CREATE PROJECTION
=> \c - bob
You are now connected as user "bob".
=> INSERT INTO publicdata VALUES (10, 'Hello World');
OUTPUT
--------
1
(1 row)
以下示例向 PUBLIC
授予 employee
角色,以便所有数据库用户都具有 employee
权限:
=> GRANT employee TO public;
GRANT ROLE
PUBLIC
指定为被授予者的任何 GRANT
语句,子句 WITH ADMIN OPTION
都是无效的。
通过将角色授予其他角色,您可以构建角色层次结构,层次结构中较低的角色具有较窄的权限范围,而层次结构中较高的角色被授予角色及其权限的组合。当您按分层方式组织角色时,添加到较低级别角色的任何权限都会自动传播到它们上方的角色。
以下示例将创建两个角色,为其分配权限,然后将这两个角色分配给另一个角色。
创建名为 applog
的表:
=> CREATE TABLE applog (id int, sourceID VARCHAR(32), data TIMESTAMP, event VARCHAR(256));
创建 logreader
角色并授予它对表 applog
的只读权限:
=> CREATE ROLE logreader;
CREATE ROLE
=> GRANT SELECT ON applog TO logreader;
GRANT PRIVILEGE
创建 logwriter
角色并授予它对表 applog
的写入权限:
=> CREATE ROLE logwriter;
CREATE ROLE
=> GRANT INSERT, UPDATE ON applog to logwriter;
GRANT PRIVILEGE
创建 logadmin
角色并授予它对表 applog
的删除权限:
=> CREATE ROLE logadmin;
CREATE ROLE
=> GRANT DELETE ON applog to logadmin;
GRANT PRIVILEGE
将 logreader
和 logwriter
角色授予角色 logadmin
:
=> GRANT logreader, logwriter TO logadmin;
创建用户 bob
并向他授予 logadmin
角色:
=> CREATE USER bob;
CREATE USER
=> GRANT logadmin TO bob;
GRANT PRIVILEGE
修改用户 bob
的帐户,使他的 logadmin
角色在登录时自动启用:
=> ALTER USER bob DEFAULT ROLE logadmin;
ALTER USER
=> \c - bob
You are now connected as user "bob".
=> SHOW ENABLED_ROLES;
name | setting
---------------+----------
enabled roles | logadmin
(1 row)
只能为用户启用已向其显式授予的角色。在前面的示例中,对于 bob
,无法启用 logreader
或 logwriter
角色。它们只能通过启用 logadmin
来间接启用。
如果使用 WITH ADMIN OPTION
将一个或多个角色授予另一个角色,则被授予“较高”角色的用户将继承对下级角色的管理访问权限。
例如,您可以按如下方式,将以前授予的角色 logreader
和 logwriter
修改为 logadmin
:
=> GRANT logreader, logwriter TO logadmin WITH ADMIN OPTION;
NOTICE 4617: Role "logreader" was already granted to role "logadmin"
NOTICE 4617: Role "logwriter" was already granted to role "logadmin"
GRANT ROLE
现在,用户 bob
有权通过他的 logadmin
角色将其两个下级角色授予其他用户,在本例中是将角色 logreader
授予用户 Alice
:
=> \c - bob;
You are now connected as user "bob".
=> GRANT logreader TO Alice;
GRANT ROLE
=> \c - alice;
You are now connected as user "alice".
=> show available_roles;
name | setting
-----------------+-----------
available roles | logreader
(1 row)
因为在将 logadmin
角色授予 bob
时未包括 WITH ADMIN OPTION
,所以他不能将该角色授予 alice
:
=> \c - bob;
You are now connected as user "bob".
=> GRANT logadmin TO alice;
ROLLBACK 4925: The role "logadmin" cannot be granted to "alice"
作为具有
DBADMIN
或
PSEUDOSUPERUSER
角色的超级用户,您可以使用
CREATE ROLE
创建角色,使用
DROP ROLE
删除角色。
=> CREATE ROLE administrator;
CREATE ROLE
没有为新角色授予任何权限或角色。超级用户必须向新角色授予权限和访问权限。
如果您尝试删除授予用户或其他角色的角色,Vertica 将返回一则回退消息:
=> DROP ROLE administrator;
NOTICE: User Bob depends on Role administrator
ROLLBACK: DROP ROLE failed due to dependencies
DETAIL: Cannot drop Role administrator because other objects depend on it
HINT: Use DROP ROLE ... CASCADE to remove granted roles from the dependent users/roles
若要强制执行删除操作,请使用 CASCADE
限定 DROP ROL
E 语句:
=> DROP ROLE administrator CASCADE;
DROP ROLE
您可以使用 GRANT 语句为角色分配权限,就像您为用户分配权限一样。有关可以授予哪些权限的信息,请参阅数据库权限。
为角色授予权限会立即影响到活动的用户会话。为角色授予权限时,此权限便立即可供已启用该角色的所有用户使用。
以下示例将创建两个角色,并为这两个角色分配对同一个表的不同权限。
创建名为 applog
的表:
=> CREATE TABLE applog (id int, sourceID VARCHAR(32), data TIMESTAMP, event VARCHAR(256));
创建名为 logreader
和 logwriter
的角色:
=> CREATE ROLE logreader;
CREATE ROLE
=> CREATE ROLE logwriter;
CREATE ROLE
将 applog
的只读权限授予 logreader
,并将写入权限授予 logwriter
:
=> GRANT SELECT ON applog TO logreader;
GRANT PRIVILEGE
=> GRANT INSERT ON applog TO logwriter;
GRANT PRIVILEGE
使用 REVOKE 语句撤销角色的权限。撤销角色的权限会立即影响到处于活动状态的用户会话。当您撤销某个角色的权限时,通过该角色拥有此权限的用户将无法再使用此权限。
例如:
=> REVOKE INSERT ON applog FROM logwriter;
REVOKE PRIVILEGE
您可以使用 授权(角色) 将一个或多个角色分配给用户或其他角色:
GRANT role[,...] TO grantee[,...] [ WITH ADMIN OPTION ]
例如,您可以创建三个角色(appdata
、applogs
和 appadmin
)并为用户 bob
授予 appadmin
:
=> CREATE ROLE appdata;
CREATE ROLE
=> CREATE ROLE applogs;
CREATE ROLE
=> CREATE ROLE appadmin;
CREATE ROLE
=> GRANT appadmin TO bob;
GRANT ROLE
GRANT
可以将一个或多个角色分配给另一个角色。例如,以下 GRANT
语句将角色 appdata
和 applogs
授予角色 appadmin
:
=> GRANT appdata, applogs TO appadmin;
-- grant to other roles
GRANT ROLE
因为先前为用户 bob 分配了角色 appadmin
,所以他现在拥有向角色 appdata
和 applogs
授予的所有权限。
当您将一个角色授予另一个角色时,Vertica 会检查循环引用。在上一个示例中,已将角色 appdata
分配给 appadmin
角色。因此,随后将 appadmin
分配给 appdata
的尝试将失败,并返回以下警告:
=> GRANT appadmin TO appdata;
WARNING: Circular assignation of roles is not allowed
HINT: Cannot grant appadmin to appdata
GRANT ROLE
向用户授予角色后,必须启用该角色。您可以为当前会话启用角色:
=> SET ROLE appdata;
SET ROLE
还可以通过使用
ALTER USER...DEFAULT ROLE
修改用户的配置文件,在用户登录期间启用角色:
=> ALTER USER bob DEFAULT ROLE appdata;
ALTER USER
您可以通过使用 WITH ADMIN OPTION
选项限定 授权(角色) 语句,从而将对角色的管理访问权限委托给非超级用户。具有管理访问权限的用户可以管理其他用户对该角色的访问权限,包括向他们授予管理访问权限。在以下示例中,超级用户将具有管理权限的 appadmin
角色授予用户 bob
和 alice.
=> GRANT appadmin TO bob, alice WITH ADMIN OPTION;
GRANT ROLE
现在,两个用户都可以行使他们的管理权限来将 appadmin
角色授予其他用户或撤销该角色。例如,用户 bob
现在可以撤销用户 alice
的 appadmin
角色:
=> \connect - bob
You are now connected as user "bob".
=> REVOKE appadmin FROM alice;
REVOKE ROLE
以下示例创建名为 commenter
的角色并将该角色授予用户 bob
:
创建 comments
表:
=> CREATE TABLE comments (id INT, comment VARCHAR);
创建 commenter
角色:
=> CREATE ROLE commenter;
授予 commenter
对 comments
表的 INSERT 和 SELECT 权限:
=> GRANT INSERT, SELECT ON comments TO commenter;
向用户 bob
授予 commenter
角色。
=> GRANT commenter TO bob;
为了访问该角色及其关联权限,bob
为自己启用新授予的角色。
=> \c - bob
=> SET ROLE commenter;
因为 bob
对 comments
表具有 INSERT 和 SELECT 权限,所以他可以执行以下操作:
=> INSERT INTO comments VALUES (1, 'Hello World');
OUTPUT
--------
1
(1 row)
=> SELECT * FROM comments;
id | comment
----+-------------
1 | Hello World
(1 row)
=> COMMIT;
COMMIT
因为 bob
的角色缺少 DELETE 权限,所以以下语句会返回错误:
=> DELETE FROM comments WHERE id=1;
ERROR 4367: Permission denied for relation comments
REVOKE(角色)
可以从一个或多个被授权者(即用户或角色)撤销角色:
REVOKE [ ADMIN OPTION FOR ] role[,...] FROM grantee[,...] [ CASCADE ]
例如,以下语句从用户 bob
撤销 commenter
角色:
=> \c
You are now connected as user "dbadmin".
=> REVOKE commenter FROM bob;
REVOKE ROLE
您可以使用 ADMIN OPTION FOR
子句限定
REVOKE(角色)
。该子句从被授予者撤销将指定角色授予其他用户或角色的权限(由以前的 GRANT (Role)...WITH ADMIN OPTION
语句授予)。被授予者的当前角色不受影响。
以下示例撤销用户 Alice 授予和撤销 commenter
角色的权限:
=> \c
You are now connected as user "dbadmin".
=> REVOKE ADMIN OPTION FOR commenter FROM alice;
REVOKE ROLE
当您在会话中启用角色时,将获得分配给该角色的所有权限。您可以同时启用多个角色,从而获得这些角色的所有权限,以及已经直接授予您的任何权限。
默认情况下,对于用户,仅自动启用预定义角色。否则,在启动会话时,您必须使用 Vertica 函数
SET ROLE
显式启用分配的角色。
例如,dbadmin 创建 logreader
角色并将其分配给用户 alice
:
=> \c
You are now connected as user "dbadmin".
=> CREATE ROLE logreader;
CREATE ROLE
=> GRANT SELECT ON TABLE applog to logreader;
GRANT PRIVILEGE
=> GRANT logreader TO alice;
GRANT ROLE
用户 alice
必须启用新角色才能查看 applog
表:
=> \c - alice
You are now connected as user "alice".
=> SELECT * FROM applog;
ERROR: permission denied for relation applog
=> SET ROLE logreader;
SET
=> SELECT * FROM applog;
id | sourceID | data | event
----+----------+----------------------------+----------------------------------------------
1 | Loader | 2011-03-31 11:00:38.494226 | Error: Failed to open source file
2 | Reporter | 2011-03-31 11:00:38.494226 | Warning: Low disk space on volume /scratch-a
(2 rows)
您可以使用 SET ROLE ALL
启用对您的用户帐户可用的所有角色:
=> SET ROLE ALL;
SET
=> SHOW ENABLED_ROLES;
name | setting
---------------+------------------------------
enabled roles | logreader, logwriter
(1 row)
用户可以使用
SET ROLE NONE
禁用所有角色。此语句禁用当前会话的除预定义角色以外的所有其他角色:
=> SET ROLE NONE;
=> SHOW ENABLED_ROLES;
name | setting
---------------+---------
enabled roles |
(1 row)
默认情况下,会为新用户分配 PUBLIC 角色,在新会话开始时会自动启用此角色。通常,会创建其他角色并将它们分配给用户,但不会自动启用这些角色。相反,用户必须在每个新会话中使用
SET ROLE
显式启用为他们分配的角色。
您可以通过两种方式自动为用户启用角色:
在登录时为单个用户启用角色
在登录时为所有用户启用所有角色
为用户分配角色后,您可以通过使用
ALTER USER...DEFAULT ROLE
修改每个用户的配置文件来为该用户设置一个或多个默认角色。用户默认角色在用户会话开始时自动启用。如果用户通常依赖这些角色的权限来执行日常任务,您应该考虑为用户设置默认角色。
ALTER USER...DEFAULT ROLE
覆盖以前的默认角色设置。
以下示例显示了如何将 regional_manager
设置为用户 LilyCP
的默认角色:
=> \c
You are now connected as user "dbadmin".
=> GRANT regional_manager TO LilyCP;
GRANT ROLE
=> ALTER USER LilyCP DEFAULT ROLE regional_manager;
ALTER USER
=> \c - LilyCP
You are now connected as user "LilyCP".
=> SHOW ENABLED_ROLES;
name | setting
---------------+------------------
enabled roles | regional_manager
(1 row)
配置参数 EnableAllRolesOnLogin
指定是否在登录时为所有数据库用户启用所有角色。默认情况下,此参数设置为 0。如果设置为 1,Vertica 会在所有用户登录数据库时启用他们的角色。
您可以使用
ALTER USER...DEFAULT ROLE NONE
清除为用户分配的所有默认角色。例如:
=> ALTER USER fred DEFAULT ROLE NONE;
ALTER USER
=> SELECT user_name, default_roles, all_roles FROM users WHERE user_name = 'fred';
user_name | default_roles | all_roles
-----------+---------------+-----------
fred | | logreader
(1 row)
您可以通过三种方式获取有关角色的信息:
通过查询系统表 ROLES、USERS 和 GRANTS,分别获取有关角色、分配给角色的用户以及授予这些用户和角色的权限的全面信息。
HAS_ROLE
可获取该信息。
函数
HAS_ROLE
检查是否已将 Vertica 角色授予指定的用户或角色。非超级用户可以使用此函数检查自己的角色成员资格。超级用户可以使用它来确定其他用户和角色的角色分配。您还可以使用管理控制台检查角色分配。
在以下示例中,dbadmin
用户检查是否为用户 MikeL
分配了 admnistrator
角色:
=> \c
You are now connected as user "dbadmin".
=> SELECT HAS_ROLE('MikeL', 'administrator');
HAS_ROLE
----------
t
(1 row)
用户 MikeL
检查其是否具有 regional_manager
角色:
=> \c - MikeL
You are now connected as user "MikeL".
=> SELECT HAS_ROLE('regional_manager');
HAS_ROLE
----------
f
(1 row)
dbadmin 将 regional_manager
角色授予 administrator
角色。再次检查时,MikeL
会验证他现在具有 regional_manager
角色:
dbadmin=> \c
You are now connected as user "dbadmin".
dbadmin=> GRANT regional_manager to administrator;
GRANT ROLE
dbadmin=> \c - MikeL
You are now connected as user "MikeL".
dbadmin=> SELECT HAS_ROLE('regional_manager');
HAS_ROLE
----------
t
(1 row)
SHOW AVAILABLE ROLES
列出向您授予的所有角色:
=> SHOW AVAILABLE ROLES;
name | setting
-----------------+-----------------------------
available roles | logreader, logwriter
(1 row)
SHOW ENABLED ROLES
列出在会话中启用的角色:
=> SHOW ENABLED ROLES;
name | setting
---------------+----------
enabled roles | logreader
(1 row)
您可以单独或以联接方式查询表 ROLES、USERS 和 GRANTS,以获取有关用户角色、分配给这些角色的用户以及显式授予用户和通过角色隐式授予的权限的详细信息。
针对 ROLES 执行以下查询将返回用户可以访问的所有角色的名称,以及授予(分配)这些角色的角色。附加到角色的星号 (*) 表示用户可以将该角色授予其他用户:
=> SELECT * FROM roles;
name | assigned_roles
-----------------+----------------
public |
dbduser |
dbadmin | dbduser*
pseudosuperuser | dbadmin*
logreader |
logwriter |
logadmin | logreader, logwriter
(7 rows)
针对系统表 USERS 执行以下查询将返回所有具备 DBADMIN 角色的用户。附加到角色的星号 (*) 表示用户可以将该角色授予其他用户:
=> SELECT user_name, is_super_user, default_roles, all_roles FROM v_catalog.users WHERE all_roles ILIKE '%dbadmin%';
user_name | is_super_user | default_roles | all_roles
-----------+---------------+--------------------------------------+--------------------------------------
dbadmin | t | dbduser*, dbadmin*, pseudosuperuser* | dbduser*, dbadmin*, pseudosuperuser*
u1 | f | | dbadmin*
u2 | f | | dbadmin
(3 rows)
针对系统表 GRANTS 执行以下查询将返回向用户 Jane 或角色 R1 授予的权限。附加到权限的星号 (*) 表示用户可以将该权限授予其他用户:
=> SELECT grantor,privileges_description,object_name,object_type,grantee FROM grants WHERE grantee='Jane' OR grantee='R1';
grantor | privileges_description | object_name | object_type | grantee
--------+------------------------+-------------+--------------+-----------
dbadmin | USAGE | general | RESOURCEPOOL | Jane
dbadmin | | R1 | ROLE | Jane
dbadmin | USAGE* | s1 | SCHEMA | Jane
dbadmin | USAGE, CREATE* | s1 | SCHEMA | R1
(4 rows)
创建数据库对象(例如架构、表或视图)时,该对象的所有权将分配给创建它的用户。默认情况下,只有对象的所有者和具有超级用户权限的用户(如数据库管理员)才对新对象具有权限。只有这些用户(以及他们为其显式授权的其他用户)才能将对象权限授予其他用户
权限分别由 GRANT 和 REVOKE 语句授予和撤销。可以为给定对象授予的权限与对象类型相关。例如,表权限包括 SELECT、INSERT 和 UPDATE,而库和资源池权限仅包括 USAGE 权限。有关对象权限的汇总,请参阅数据库对象权限。
由于数据库对象的权限可能来自多个不同的来源(如显式授权、角色和继承),因此权限可能难以监控。使用 GET_PRIVILEGES_DESCRIPTION 元函数检查当前用户从指定数据库对象的所有源获得的有效权限。
所有用户都对他们拥有的对象拥有隐式权限。在创建对象时,它的所有者会自动被授予与对象类型相关的所有权限(请参阅数据库对象权限)。无论对象类型如何,以下权限都与所有权密不可分并且不能被撤销,即使是所有者也不能撤销:
将所有对象权限授予其他用户并撤销它们
ALTER(如适用)和 DROP
扩展以下权限:将对象权限授予其他用户和撤销该权限
对象所有者可以撤销他们自己的所有非隐式或普通权限。例如,在创建表时,它的所有者会自动被授予所有隐式和普通权限:
如果用户 Joan
创建表 t1
,她可以撤销自己的普通权限 UPDATE 和 INSERT,这实际上会将该表设为只读:
=> \c - Joan
You are now connected as user "Joan".
=> CREATE TABLE t1 (a int);
CREATE TABLE
=> INSERT INTO t1 VALUES (1);
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> REVOKE UPDATE, INSERT ON TABLE t1 FROM Joan;
REVOKE PRIVILEGE
=> INSERT INTO t1 VALUES (3);
ERROR 4367: Permission denied for relation t1
=> SELECT * FROM t1;
a
---
1
(1 row)
Joan 随后可以还原自己的 UPDATE 和 INSERT 权限:
=> GRANT UPDATE, INSERT on TABLE t1 TO Joan;
GRANT PRIVILEGE
dbadmin=> INSERT INTO t1 VALUES (3);
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
dbadmin=> SELECT * FROM t1;
a
---
1
3
(2 rows)
您可以在三个级别管理权限继承:
数据库
架构
表和视图
默认情况下,继承权限在数据库级别启用,在架构级别禁用。如果在两个级别都启用了权限继承,新表和视图在创建时会自动继承这些权限。您还可以排除特定表和视图的继承。
默认情况下,通过配置参数 disableinheritedprivileges
在数据库级别启用继承权限。要启用继承的权限:
=> ALTER DATABASE [database name] SET disableinheritedprivileges = 0;
要禁用继承的权限:
=> ALTER DATABASE [database name] SET disableinheritedprivileges = 1;
使用 ALTER SCHEMA 启用继承权限DEFAULT INCLUDE PRIVILEGES 仅影响新创建的表和视图。
此设置不会影响已存在的表和视图。
默认情况下,在架构级别禁用继承的权限。如果为数据库启用了继承的权限,您可以使用 CREATE SCHEMA 和 ALTER SCHEMA 允许数据库的表和视图继承架构权限。除非显式排除,否则在架构上授予的权限将自动由其中的所有新表和视图继承。
有关哪些表和视图从哪些架构继承权限的信息,请参阅 INHERITING_OBJECTS。
有关每个表或视图继承哪些权限的信息,请参阅 INHERITED_PRIVILEGES。
如果针对数据库禁用继承的权限,则对其架构启用继承不起作用。尝试启用可能会返回以下消息:
Inherited privileges are globally disabled; schema parameter is set but has no effect.
启用架构权限继承对现有的表和视图不起作用。您必须使用 ALTER TABLE 和 ALTER VIEW 针对现有的表和视图显式设置架构继承。您还可以分别使用 CREATE TABLE/ALTER TABLE 和 CREATE VIEW/ALTER VIEW 显式排除表和视图继承架构权限。
您可以在架构创建期间使用以下语句启用架构权限继承:
=> CREATE SCHEMA s1 DEFAULT INCLUDE PRIVILEGES;
如果架构已经存在,您可以使用 ALTER SCHEMA 让所有新创建的表和视图继承架构的权限。在此语句之前在架构上创建的表和视图不受影响:
=> ALTER SCHEMA s1 DEFAULT INCLUDE PRIVILEGES;
在架构上启用继承的权限后,可以使用 GRANT(架构) 将架构权限授予用户和角色:
=> GRANT USAGE, CREATE, SELECT, INSERT ON SCHEMA S1 TO PUBLIC;
GRANT PRIVILEGE
使用 ALTER SCHEMA 启用继承权限DEFAULT INCLUDE PRIVILEGES 仅影响新创建的表和视图。
此设置不会影响已存在的表和视图。
如果为数据库和架构启用了继承的权限,则授予架构的权限将自动授予其中的所有新表和视图。您还可以显式排除表和视图继承架构权限。
有关哪些表和视图从哪些架构继承权限的信息,请参阅 INHERITING_OBJECTS。
有关每个表或视图继承哪些权限的信息,请参阅 INHERITED_PRIVILEGES。
CREATE TABLE/ALTER TABLE 和 CREATE VIEW/ALTER VIEW 可以允许表和视图从其父架构继承权限。例如,以下语句在架构 s1 上启用继承,因此新表 s1.t1 和视图 s1.myview 会自动继承在该架构上设置的权限(如果适用):
=> CREATE SCHEMA s1 DEFAULT INCLUDE PRIVILEGES;
CREATE SCHEMA
=> GRANT USAGE, CREATE, SELECT, INSERT ON SCHEMA S1 TO PUBLIC;
GRANT PRIVILEGE
=> CREATE TABLE s1.t1 ( ID int, f_name varchar(16), l_name(24));
WARNING 6978: Table "t1" will include privileges from schema "s1"
CREATE TABLE
=> CREATE VIEW s1.myview AS SELECT ID, l_name FROM s1.t1
WARNING 6978: View "myview" will include privileges from schema "s1"
CREATE VIEW
如果架构已经存在,您可以使用 ALTER SCHEMA 让所有新创建的表和视图继承架构的权限。但是,在此语句之前在架构上创建的表和视图不受影响:
=> CREATE SCHEMA s2;
CREATE SCHEMA
=> CREATE TABLE s2.t22 ( a int );
CREATE TABLE
...
=> ALTER SCHEMA S2 DEFAULT INCLUDE PRIVILEGES;
ALTER SCHEMA
在此例中,在架构 s2 已经包含表 s2.t22 之后,在该架构上启用继承的权限。要在此表和其他现有表和视图上设置继承,必须使用 ALTER TABLE 和 ALTER VIEW 对它们显式设置架构继承:
=> ALTER TABLE s2.t22 INCLUDE SCHEMA PRIVILEGES;
您可以使用 CREATE TABLE/ALTER TABLE 和 CREATE VIEW/ALTER VIEW 禁止表和视图继承架构权限。
以下示例显示了如何创建不继承架构权限的表:
=> CREATE TABLE s1.t1 ( x int) EXCLUDE SCHEMA PRIVILEGES;
您可以修改现有表,使其不继承架构权限:
=> ALTER TABLE s1.t1 EXCLUDE SCHEMA PRIVILEGES;
以下步骤显示了用户 Joe
如何针对给定架构启用权限继承,以便其他用户可以访问该架构中的表。
Joe
创建架构 schema1
,并在其中创建表 table1
:
=>\c - Joe
You are now connected as user Joe
=> CREATE SCHEMA schema1;
CRDEATE SCHEMA
=> CREATE TABLE schema1.table1 (id int);
CREATE TABLE
Joe
将对 schema1
的 USAGE 和 CREATE 权限授予 Myra
:
=> GRANT USAGE, CREATE ON SCHEMA schema1 to Myra;
GRANT PRIVILEGE
Myra
查询 schema1.table1
,但查询失败:
=>\c - Myra
You are now connected as user Myra
=> SELECT * FROM schema1.table1;
ERROR 4367: Permission denied for relation table1
Joe
授予 Myra
对 schema1
的 SELECT ON SCHEMA
权限:
=>\c - Joe
You are now connected as user Joe
=> GRANT SELECT ON SCHEMA schema1 to Myra;
GRANT PRIVILEGE
Joe
使用 ALTER TABLE
包含 table1
的 SCHEMA 权限:
=> ALTER TABLE schema1.table1 INCLUDE SCHEMA PRIVILEGES;
ALTER TABLE
Myra
的查询现在成功:
=>\c - Myra
You are now connected as user Myra
=> SELECT * FROM schema1.table1;
id
---
(0 rows)
Joe
修改 schema1
以包含权限,以便在 schema1
中创建的所有表都继承架构权限:
=>\c - Joe
You are now connected as user Joe
=> ALTER SCHEMA schema1 DEFAULT INCLUDE PRIVILEGES;
ALTER SCHEMA
=> CREATE TABLE schema1.table2 (id int);
CREATE TABLE
启用继承的权限后,Myra
可以查询 table2
,而 Joe
不必显式授予对表的权限:
=>\c - Myra
You are now connected as user Myra
=> SELECT * FROM schema1.table2;
id
---
(0 rows)
为了向所有用户设置最低级别的权限,Vertica 具有特殊的 PUBLIC 角色,会向每个用户自动授予此角色。此角色会自动启用,但数据库管理员或 超级用户也可以使用 GRANT 语句单独向用户授予更高的权限。
管理控制台 (MC) 的权限通过角色进行管理,这些角色确定了用户对 MC 和 MC 管理的 Vertica 数据库(通过 MC 界面)的访问权限。MC 权限不会更改或覆盖 Vertica 权限或角色。有关详细信息,请参阅用户、角色和权限。
本主题列出了 Vertica 中的数据库对象所需的权限。
除非另有说明,否则 超级用户可以执行下表中显示的所有操作。对象所有者始终可以对自己的对象执行操作。
某些操作(如设置其他用户的默认资源池或选择视图)取决于其他用户的有效权限。如果其他用户通过角色获得这些先决权限,则他必须具有默认角色,操作才能成功。
有关更改用户默认角色的详细信息,请参阅自动启用角色。
PUBLIC 架构存在于任何新创建的 Vertica 数据库中。必须授予新创建的用户对此架构的访问权限:
=> GRANT USAGE ON SCHEMA public TO user;
数据库超级用户还必须为新用户显式授予 CREATE 权限,并为他们授予各个对象权限,以便新用户可以在 PUBLIC 架构中创建或查找对象。
下表使用以下缩写:
UDF = 分级
UDT = 转换
UDAnF= 分析
UDAF = 聚合
您可以使用一个或一组 vsql \d 元命令和SQL 系统表来查看您有权查看的对象。
使用 \dn 查看架构名称和所有者。
使用 \dn 查看数据库中的所有表,以及系统表 V_CATALOG.TABLES
使用 \dn 查看显示架构、投影名称、所有者和节点的投影,以及系统表 V_CATALOG.PROJECTIONS
对于 Vertica 数据库中大多数的用户可见对象(如表和模型),可以显式授予权限。对于某些对象(如投影),权限是从其他对象隐式派生的。
下表概述了可为 Vertica 数据库对象显式授予的权限:
超级用户可以无限制地访问所有非加密数据库元数据。对于非超级用户,对特定对象的元数据的访问权限取决于他们对这些对象的权限:
用来存储表数据的投影没有与其直接关联的所有者或权限。相反,创建、访问或更改投影的权限派生自针对该投影的锚表和相应架构设置的权限。
除非拥有所有权,否则超级用户仅对密钥、证书和 TLS 配置具有隐式 DROP 权限。这让超级用户能够在他们各自的系统表(CRYPTOGRAPHIC_KEYS、CERTIFICATES 和 TLS_CONFIGURATIONS)中查看这些对象是否存在并删除它们,但不允许他们查看密钥或证书文本。
有关授予额外权限的详细信息,请参阅 GRANT(密钥)和 GRANT(TLS 配置)。
Vertica 支持通过 GRANT 和 REVOKE 语句(例如,GRANT(架构)和 REVOKE(架构)、GRANT(表)和 REVOKE(表) 等等)来控制用户对数据库对象的访问。通常,超级用户会在创建数据库后不久创建用户和角色,然后使用 GRANT 语句为所创建的用户和角色分配权限。
在适用的情况下,GRANT 语句需要对象架构的 USAGE 权限。以下用户可以授予和撤销权限:
超级用户:所有数据库对象(包括数据库本身)的所有权限
非超级用户:他们拥有的对象的所有权限
包括 WITH GRANT OPTION 的权限的被授予者:对该对象的相同权限
在以下示例中,dbadmin(具有超级用户权限)创建用户 Carol
。随后的 GRANT 语句授予 Carol
架构和表权限:
对架构的 CREATE 和 USAGE 权限 PUBLIC
对表 public.applog
的 SELECT、INSERT 和 UPDATE 权限。此 GRANT 语句还包括 WITH GRANT OPTION
。这使得 Carol
可以将此表上的相同权限授予其他用户 — 在本例中,将 SELECT 权限授予用户 Tom
:
=> CREATE USER Carol;
CREATE USER
=> GRANT CREATE, USAGE ON SCHEMA PUBLIC to Carol;
GRANT PRIVILEGE
=> GRANT SELECT, INSERT, UPDATE ON TABLE public.applog TO Carol WITH GRANT OPTION;
GRANT PRIVILEGE
=> GRANT SELECT ON TABLE public.applog TO Tom;
GRANT PRIVILEGE
Vertica 超级用户是在安装时自动创建的数据库用户(默认情况下,名为 dbadmin
)。Vertica 超级用户对数据库用户、权限和角色拥有完整且不可撤销的授权。
超级用户可以更改任何用户和角色的权限,并覆盖具有 PSEUDOSUPERUSER 角色的用户所授予的任何权限。他们还可以授予和撤销用户拥有的任何对象的权限,并重新分配对象所有权。
对于大多数编录对象,超级用户拥有所有可能的权限。但是,对于密钥、证书和 TLS 配置,超级用户在默认情况下只能获得 DROP 权限,并且必须由其所有者授予其他权限。有关详细信息,请参阅 GRANT(密钥)和 GRANT(TLS 配置)。
超级用户可以看到所存在的全部密钥、证书和 TLS 配置,但是,除非向他们授予 USAGE 权限,否则他们看不到密钥或证书的文本。
通常情况下,架构所有者为创建该架构的用户。默认情况下,架构所有者有权在架构内创建对象。所有者还可以更改架构:重新分配所有权、重命名以及启用或禁用架构权限继承。
架构所有权不一定授予所有者对该架构中对象的访问权限。对象访问权限取决于向他们授予的权限。
所有其他用户和角色必须由所有者或超级用户显式授予对架构的访问权限。
数据库以及其中的每个对象都具有所有者。对象所有者通常是创建该对象的人员,但是超级用户可以更改对象(如表和序列)的所有权。
对象所有者必须具有合适的架构权限才能访问、更改、重命名、移动或删除其拥有的任何对象,而不需要任何额外的权限。
对象所有者还可以:
将自己所拥有的对象的权限授予其他用户
WITH GRANT OPTION 子句指定用户可以向其他用户授予权限。例如,如果用户 Bob 创建一个表,则 Bob 可以将该表的权限授予用户 Ted、Alice 等等。
将权限授予 **角色**
被授予角色的用户会获得权限。
如授予和撤销权限中所述,特定用户可使用带有或不带有可选 WITH GRANT OPTION 的 GRANT 语句授予权限,WITH GRANT OPTION 子句允许用户向其他用户授予相同的权限。
超级用户可向其他用户授予对所有对象类型的权限。
超级用户或对象所有者可向 角色授予权限。然后,被授予角色的用户会获得相应权限。
对象所有者可使用可选的 WITH GRANT OPTION 子句向其他用户授予对于对象的权限。
用户需要具有对架构的 USAGE 权限以及对于对象的相应权限。
当用户授予一个显式的权限列表时,例如 GRANT INSERT, DELETE, REFERENCES ON applog TO Bob
:
只有成功授予了所有角色,GRANT 语句才会成功执行。如果任一授予操作失败,则整个语句将回退。
如果用户没有所列权限的授予选项,Vertica 将返回 ERROR。
当用户授予 ALL 权限(例如 GRANT ALL ON applog TO Bob
)时,语句始终会成功执行。Vertica 会授予授予者具有 WITH GRANT OPTION 的所有权限,而略过没有可选 WITH GRANT OPTION 的权限。
例如,如果用户 Bob 删除了具有可选授予选项的对于表 applog 的权限,则仅会向 Bob 授予 DELETE 权限,语句会成功执行:
=> GRANT DELETE ON applog TO Bob WITH GRANT OPTION;GRANT PRIVILEGE
有关详细信息,请参阅 GRANT 语句。
下面的非超级用户可以撤销对象的权限:
对象所有者
对象权限的授予者
用户还必须具有对对象架构的 USAGE 权限。
例如,针对系统表 V_CATALOG.GRANTS
执行以下查询将显示用户 u1
、u2
和 u3
对架构 s1
和表 s1.t1
具有以下权限:
=> SELECT object_type, object_name, grantee, grantor, privileges_description FROM v_catalog.grants
WHERE object_name IN ('s1', 't1') AND grantee IN ('u1', 'u2', 'u3');
object_type | object_name | grantee | grantor | privileges_description
-------------+-------------+---------+---------+---------------------------
SCHEMA | s1 | u1 | dbadmin | USAGE, CREATE
SCHEMA | s1 | u2 | dbadmin | USAGE, CREATE
SCHEMA | s1 | u3 | dbadmin | USAGE
TABLE | t1 | u1 | dbadmin | INSERT*, SELECT*, UPDATE*
TABLE | t1 | u2 | u1 | INSERT*, SELECT*, UPDATE*
TABLE | t1 | u3 | u2 | SELECT*
(6 rows)
privileges_description
下权限上的星号 (*) 表示被授予者可以将这些权限授予其他用户。
在以下语句中,u2
撤销它在 s1.t1
上授予 u3
的 SELECT 权限。u3
随后尝试查询此表时返回错误:
=> \c - u2
You are now connected as user "u2".
=> REVOKE SELECT ON s1.t1 FROM u3;
REVOKE PRIVILEGE
=> \c - u3
You are now connected as user "u2".
=> SELECT * FROM s1.t1;
ERROR 4367: Permission denied for relation t1
如果您撤销用户对某个对象的权限,则该用户不能再将这些相同权限授予其他用户。如果该用户先前已将撤销的权限授予其他用户,则 REVOKE
语句还必须包含 CASCADE
选项才能撤销这些其他用户的权限;否则,它会返回错误。
例如,用户 u2
可以授予 SELECT、INSERT 和 UPDATE 权限,并将这些权限授予用户 u4
:
=> \c - u2
You are now connected as user "u2".
=> GRANT SELECT, INSERT, UPDATE on TABLE s1.t1 to u4;
GRANT PRIVILEGE
如果您通过查询 V_CATALOG.GRANTS
来查看是否拥有表 s1.t1
的权限,它会返回以下结果集:
=> \ c
You are now connected as user "dbadmin".
=> SELECT object_type, object_name, grantee, grantor, privileges_description FROM v_catalog.grants
WHERE object_name IN ('t1') ORDER BY grantee;
object_type | object_name | grantee | grantor | privileges_description
-------------+-------------+---------+---------+------------------------------------------------------------
TABLE | t1 | dbadmin | dbadmin | INSERT*, SELECT*, UPDATE*, DELETE*, REFERENCES*, TRUNCATE*
TABLE | t1 | u1 | dbadmin | INSERT*, SELECT*, UPDATE*
TABLE | t1 | u2 | u1 | INSERT*, SELECT*, UPDATE*
TABLE | t1 | u4 | u2 | INSERT, SELECT, UPDATE
(3 rows)
现在,如果用户 u1
想要撤销用户 u2
的 UPDATE 权限,则撤销操作必须级联到用户 u4
(该用户也拥有 u2
授予的 UPDATE 权限);否则,REVOKE
语句返回错误:
=> \c - u1
=> REVOKE update ON TABLE s1.t1 FROM u2;
ROLLBACK 3052: Dependent privileges exist
HINT: Use CASCADE to revoke them too
=> REVOKE update ON TABLE s1.t1 FROM u2 CASCADE;
REVOKE PRIVILEGE
=> \c
You are now connected as user "dbadmin".
=> SELECT object_type, object_name, grantee, grantor, privileges_description FROM v_catalog.grants
WHERE object_name IN ('t1') ORDER BY grantee;
object_type | object_name | grantee | grantor | privileges_description
-------------+-------------+---------+---------+------------------------------------------------------------
TABLE | t1 | dbadmin | dbadmin | INSERT*, SELECT*, UPDATE*, DELETE*, REFERENCES*, TRUNCATE*
TABLE | t1 | u1 | dbadmin | INSERT*, SELECT*, UPDATE*
TABLE | t1 | u2 | u1 | INSERT*, SELECT*
TABLE | t1 | u4 | u2 | INSERT, SELECT
(4 rows)
您还可以撤销用户的授予者权限而不撤销这些权限。例如,用户 u1
可以阻止用户 u2
将 INSERT 权限授予其他用户,但允许用户 u2
保留该权限:
=> \c - u1
You are now connected as user "u1".
=> REVOKE GRANT OPTION FOR INSERT ON TABLE s1.t1 FROM U2 CASCADE;
REVOKE PRIVILEGE
u2
先前授予用户 u4
对表 s1.t1
的 INSERT 权限。当您撤销 u2
授予此权限的能力时,必须从其任何被授予者(在本例中为用户 u4
)中移除该权限。
您可以通过查询 V_CATALOG.GRANTS
表来查看是否拥有 s1.t1
表的权限,从而确认撤销操作的结果:
=> \c
You are now connected as user "dbadmin".
=> SELECT object_type, object_name, grantee, grantor, privileges_description FROM v_catalog.grants
WHERE object_name IN ('t1') ORDER BY grantee;
object_type | object_name | grantee | grantor | privileges_description
-------------+-------------+---------+---------+------------------------------------------------------------
TABLE | t1 | dbadmin | dbadmin | INSERT*, SELECT*, UPDATE*, DELETE*, REFERENCES*, TRUNCATE*
TABLE | t1 | u1 | dbadmin | INSERT*, SELECT*, UPDATE*
TABLE | t1 | u2 | u1 | INSERT, SELECT*
TABLE | t1 | u4 | u2 | SELECT
(4 rows)
查询结果显示:
用户 u2
保留对表的 INSERT 权限,但不能再将 INSERT 权限授予其他用户(没有星号)。
撤销操作向下级联到被授予者 u4
,该用户现在失去 INSERT 权限。
撤销对于对象的权限可能会在整个组织内产生级联影响。如果为用户撤销了授予选项,则此用户授予其他用户的权限也会被撤销。
如果权限是由多个授予者授予用户或角色的,则要为被授予者完全撤销此权限,每个最初授予者都必须撤销该权限。唯一例外是超级用户可以撤销对象所有者授予的权限,反之亦然。
在以下示例中,对表 t1 的 SELECT 权限是通过一个从超级用户到 User3 的用户链授予的。
超级用户为 User1 授予对架构 s1 的 CREATE 权限:
=> \c - dbadmin
You are now connected as user "dbadmin".
=> CREATE USER User1;
CREATE USER
=> CREATE USER User2;
CREATE USER
=> CREATE USER User3;
CREATE USER
=> CREATE SCHEMA s1;
CREATE SCHEMA
=> GRANT USAGE on SCHEMA s1 TO User1, User2, User3;
GRANT PRIVILEGE
=> CREATE ROLE reviewer;
CREATE ROLE
=> GRANT CREATE ON SCHEMA s1 TO User1;
GRANT PRIVILEGE
User1 在架构 s1 中创建新表 t1,然后为 User2 授予对 s1.t1 的 SELECT WITH GRANT OPTION 权限:
=> \c - User1
You are now connected as user "User1".
=> CREATE TABLE s1.t1(id int, sourceID VARCHAR(8));
CREATE TABLE
=> GRANT SELECT on s1.t1 to User2 WITH GRANT OPTION;
GRANT PRIVILEGE
User2 为 User3 授予对 s1.t1 的 SELECT WITH GRANT OPTION 权限。
=> \c - User2
You are now connected as user "User2".
=> GRANT SELECT on s1.t1 to User3 WITH GRANT OPTION;
GRANT PRIVILEGE
User3 为查看者角色授予对 s1.t1 的 SELECT 权限:
=> \c - User3
You are now connected as user "User3".
=> GRANT SELECT on s1.t1 to reviewer;
GRANT PRIVILEGE
用户不能撤销处于链上游的权限。例如,User2 没有为 User1 授予权限,因此当 User1 运行以下 REVOKE 命令时,Vertica 会回退命令:
=> \c - User2
You are now connected as user "User2".
=> REVOKE CREATE ON SCHEMA s1 FROM User1;
ROLLBACK 0: "CREATE" privilege(s) for schema "s1" could not be revoked from "User1"
用户可以间接地为通过级联链接受权限的用户撤销权限,如上例所示。这里,用户可以使用 CASCADE 选项为链“下游”的所有用户撤销权限。超级用户或 User1 可以使用 CASCADE 选项为所有用户撤销对表 s1.t1 的 SELECT 权限。例如,超级用户或 User1 可以执行以下语句为链中的所有用户和角色撤销 SELECT 权限:
=> \c - User1
You are now connected as user "User1".
=> REVOKE SELECT ON s1.t1 FROM User2 CASCADE;
REVOKE PRIVILEGE
当超级用户或 User1 执行上述语句时,会为 User2、User3 和查看者角色撤销对表 s1.t1 的 SELECT 权限。同时也会为 User2 和 User3 撤销 GRANT 权限,超级用户可以通过查询 V_CATALOG.GRANTS 系统表进行验证。
=> SELECT * FROM grants WHERE object_name = 's1' AND grantee ILIKE 'User%';
grantor | privileges_description | object_schema | object_name | grantee
---------+------------------------+---------------+-------------+---------
dbadmin | USAGE | | s1 | User1
dbadmin | USAGE | | s1 | User2
dbadmin | USAGE | | s1 | User3
(3 rows)
超级用户或对象所有者可以使用其中一个 ALTER 语句修改权限,如更改序列所有者或表所有者。重新分配给新所有者时,不会将授权从原始所有者转移到新所有者;由原始所有者所执行的授权会被删除。
您可以通过查询以下系统表来查看有关权限、授予者、被授予者和对象的信息:
附加到权限后面的星号 (*) 表示用户可以将该权限授予其他用户。
您还可以使用 GET_PRIVILEGES_DESCRIPTION 元函数查看指定数据库对象的有效权限。
若要查看显式授予的对象权限,请查询 GRANTS 表。
以下查询返回 myschema 架构的显式权限。
=> SELECT grantee, privileges_description FROM grants WHERE object_name='myschema';
grantee | privileges_description
---------+------------------------
Bob | USAGE, CREATE
Alice | CREATE
(2 rows)
若要查看哪些表和视图从哪些架构继承权限,请查询 INHERITING_OBJECTS 表。
以下查询返回从其父架构(客户)继承权限的表和视图。
=> SELECT * FROM inheriting_objects WHERE object_schema='customers';
object_id | schema_id | object_schema | object_name | object_type
-------------------+-------------------+---------------+---------------+-------------
45035996273980908 | 45035996273980902 | customers | cust_info | table
45035996273980984 | 45035996273980902 | customers | shipping_info | table
45035996273980980 | 45035996273980902 | customers | cust_set | view
(3 rows)
若要查看表和视图继承的特定权限及其关联的授予语句的信息,请查询 INHERITED_PRIVILEGES 表。
以下查询返回表和视图从其父架构(客户)继承的权限。
=> SELECT object_schema,object_name,object_type,privileges_description,principal,grantor FROM inherited_privileges WHERE object_schema='customers';
object_schema | object_name | object_type | privileges_description | principal | grantor
---------------+---------------+-------------+---------------------------------------------------------------------------+-----------+---------
customers | cust_info | Table | INSERT*, SELECT*, UPDATE*, DELETE*, ALTER*, REFERENCES*, DROP*, TRUNCATE* | dbadmin | dbadmin
customers | shipping_info | Table | INSERT*, SELECT*, UPDATE*, DELETE*, ALTER*, REFERENCES*, DROP*, TRUNCATE* | dbadmin | dbadmin
customers | cust_set | View | SELECT*, ALTER*, DROP* | dbadmin | dbadmin
customers | cust_info | Table | SELECT | Val | dbadmin
customers | shipping_info | Table | SELECT | Val | dbadmin
customers | cust_set | View | SELECT | Val | dbadmin
customers | cust_info | Table | INSERT | Pooja | dbadmin
customers | shipping_info | Table | INSERT | Pooja | dbadmin
(8 rows)
若要查看当前用户对指定数据库对象的有效权限,请使用 GET_PRIVILEGES_DESCRIPTION 元函数。
在以下示例中,用户 Glenn 设置了 REPORTER 角色,并希望检查他对架构 s1
和表 s1.articles
的有效权限。
表 s1.articles
从其架构 (s1
) 继承权限。
REPORTER 角色具有以下权限:
对架构的 SELECT 权限 s1
对表的 INSERT WITH GRANT OPTION 权限 s1.articles
用户 Glenn 具有以下权限:
对架构 s1
的 UPDATE 和 USAGE 权限。
对表 s1.articles
的 DELETE 权限。
GET_PRIVILEGES_DESCRIPTION 返回 Glenn 对架构 s1
的以下有效权限:
=> SELECT GET_PRIVILEGES_DESCRIPTION('schema', 's1');
GET_PRIVILEGES_DESCRIPTION
--------------------------------
SELECT, UPDATE, USAGE
(1 row)
GET_PRIVILEGES_DESCRIPTION 返回 Glenn 对表 s1.articles
的以下有效权限:
=> SELECT GET_PRIVILEGES_DESCRIPTION('table', 's1.articles');
GET_PRIVILEGES_DESCRIPTION
--------------------------------
INSERT*, SELECT, UPDATE, DELETE
(1 row)
CREATE ACCESS POLICY 可用于在表上创建访问策略,以指定某些用户和角色可以从这些表中查询多少数据。访问策略通常会阻止这些用户查看表中特定列和行的数据。您可以将访问策略应用于表列和行。如果表的行和列上都有访问策略,Vertica 首先筛选行访问策略,然后筛选列访问策略。
可为任何表类型(列式、外部或 Flex 表)创建大部分访问策略。不能在 Flex 表上创建列访问策略。也可以对任何列类型(包括联接)上创建访问策略。
CREATE ACCESS POLICY 可以在各个表列上创建访问策略,每列一个策略。每个列访问策略都允许您为不同的用户和角色指定对该列数据的不同访问级别。列访问表达式还可以指定如何为用户和角色呈现列数据。
以下示例在 client_dimension
表中的 customer_address
列上创建访问策略。此访问策略为具有 administrator
角色的非超级用户提供对该列中所有数据的完全访问权限,但会屏蔽所有其他用户的客户地址数据:
=> CREATE ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address
-> CASE
-> WHEN ENABLED_ROLE('administrator') THEN customer_address
-> ELSE '**************'
-> END ENABLE;
CREATE ACCESS POLICY
Vertica 使用此策略来确定它授予用户 MaxineT 和 MikeL 的访问权限,对于这两个用户分别分配了 employee
和 administrator
角色。当这些用户查询 customer_dimension
表时,Vertica 会应用列访问策略表达式,如下所示:
=> \c - MaxineT;
You are now connected as user "MaxineT".
=> SET ROLE employee;
SET
=> SELECT customer_type, customer_name, customer_gender, customer_address, customer_city FROM customer_dimension;
customer_type | customer_name | customer_gender | customer_address | customer_city
---------------+-------------------------+-----------------+------------------+------------------
Individual | Craig S. Robinson | Male | ************** | Fayetteville
Individual | Mark M. Kramer | Male | ************** | Joliet
Individual | Barbara S. Farmer | Female | ************** | Alexandria
Individual | Julie S. McNulty | Female | ************** | Grand Prairie
...
=> \c - MikeL
You are now connected as user "MikeL".
=> SET ROLE administrator;
SET
=> SELECT customer_type, customer_name, customer_gender, customer_address, customer_city FROM customer_dimension;
customer_type | customer_name | customer_gender | customer_address | customer_city
---------------+-------------------------+-----------------+------------------+------------------
Individual | Craig S. Robinson | Male | 138 Alden Ave | Fayetteville
Individual | Mark M. Kramer | Male | 311 Green St | Joliet
Individual | Barbara S. Farmer | Female | 256 Cherry St | Alexandria
Individual | Julie S. McNulty | Female | 459 Essex St | Grand Prairie
...
访问策略具有以下限制:
一列只能有一个访问策略。
不能为除原生数组以外的复杂类型列设置列访问策略。
不能为 Flex 表中的实体化列设置列访问策略。可以为 __raw__
列设置访问策略,但此举将限制对整个表的访问。
行访问策略对临时表和具有聚合投影的表无效。
访问策略表达式不能包含:
子查询
聚合函数
分析函数
用户定义的转换函数 (UDTF)
如果查询优化器无法使用计算值替换仅涉及常量的确定性表达式,则会阻止所有 DML 操作,例如 INSERT。
CREATE ACCESS POLICY 可以为给定的表创建单行访问策略。此策略允许您为不同的用户和角色指定对表行数据的不同级别的访问权限。当用户启动查询时,Vertica 会对所有表行上访问策略的 WHERE 表达式求值。查询仅返回当前用户或角色的表达式求值结果为 true 的行。
例如,您可能希望为四个角色指定对表 store.store_store_sales
的不同访问级别:
employee
:具有此角色的用户只能访问 employee_key
列中将他们标识为员工的销售记录。以下查询显示有多少销售记录(在 store.store_sales_fact
中)与每个用户(在 public.emp_dimension
中)相关联:
=> SELECT COUNT(sf.employee_key) AS 'Total Sales', sf.employee_key, ed.user_name FROM store.store_sales_fact sf
JOIN emp_dimension ed ON sf.employee_key=ed.employee_key
WHERE ed.job_title='Sales Associate' GROUP BY sf.employee_key, ed.user_name ORDER BY sf.employee_key
Total Sales | employee_key | user_name
-------------+--------------+-------------
533 | 111 | LucasLC
442 | 124 | JohnSN
487 | 127 | SamNS
477 | 132 | MeghanMD
545 | 140 | HaroldON
...
563 | 1991 | MidoriMG
367 | 1993 | ThomZM
(318 rows)
regional_manager
:具有此角色 (public.emp_dimension
) 的用户只能访问他们管理的销售区域 (store.store_dimension
) 的销售记录:
=> SELECT distinct sd.store_region, ed.user_name, ed.employee_key, ed.job_title FROM store.store_dimension sd
JOIN emp_dimension ed ON sd.store_region=ed.employee_region WHERE ed.job_title = 'Regional Manager';
store_region | user_name | employee_key | job_title
--------------+-----------+--------------+------------------
West | JamesGD | 1070 | Regional Manager
South | SharonDM | 1710 | Regional Manager
East | BenOV | 593 | Regional Manager
MidWest | LilyCP | 611 | Regional Manager
NorthWest | CarlaTG | 1058 | Regional Manager
SouthWest | MarcusNK | 150 | Regional Manager
(6 rows)
dbadmin
和 administrator
:具有这些角色的用户可以无限制地访问所有表数据。
给定这些用户以及与他们关联的数据,您可以在 store.store_store_sales
上创建如下所示的行访问策略:
CREATE ACCESS POLICY ON store.store_sales_fact FOR ROWS WHERE
(ENABLED_ROLE('employee')) AND (store.store_sales_fact.employee_key IN
(SELECT employee_key FROM public.emp_dimension WHERE user_name=CURRENT_USER()))
OR
(ENABLED_ROLE('regional_manager')) AND (store.store_sales_fact.store_key IN
(SELECT sd.store_key FROM store.store_dimension sd
JOIN emp_dimension ed ON sd.store_region=ed.employee_region WHERE ed.user_name = CURRENT_USER()))
OR ENABLED_ROLE('dbadmin')
OR ENABLED_ROLE ('administrator')
ENABLE;
以下示例指示具有指定角色的用户可以使用的不同访问级别:
dbadmin
可以访问 store.store_sales_fact
中的所有行:
=> \c
You are now connected as user "dbadmin".
=> SELECT count(*) FROM store.store_sales_fact;
count
---------
5000000
(1 row)
用户 LilyCP
具有 regional_manager
角色,因此她可以访问她管理的中西部地区的所有销售数据:
=> \c - LilyCP;
You are now connected as user "LilyCP".
=> SET ROLE regional_manager;
SET
=> SELECT count(*) FROM store.store_sales_fact;
count
--------
782272
(1 row)
用户 SamRJ
具有 employee
角色,因此他只能访问与其关联的销售数据:
=> \c - SamRJ;
You are now connected as user "SamRJ".
=> SET ROLE employee;
SET
=> SELECT count(*) FROM store.store_sales_fact;
count
-------
417
(1 row)
以下限制适用于行访问策略:
一个表只能有一个行访问策略。
下表中的行访问策略无效:
具有聚合投影的表
临时表
系统表
视图
您不能在具有行访问策略的表上创建定向查询。
默认情况下,Vertica 遵守以下规则:用户只能编辑他们可以看到的内容。也就是说,您必须能够看到表中所有行和列的原始值(存储在表中)以及最初为它们定义的数据类型,才能执行对表数据进行修改的操作。例如,如果某一列被定义为 VARCHAR(9),并且该列的访问策略指定同一列为 VARCHAR(10),则使用该访问策略的用户将无法执行以下操作:
INSERT
UPDATE
DELETE
MERGE
COPY
您可以通过在新或现有访问策略中指定 GRANT TRUSTED 来覆盖此行为。在评估用户能否执行上述操作时,此选项强制访问策略完全遵循显式 GRANT 语句。
您可以使用 ACCESS_POLICY 系统表查看现有访问策略。
在启用行访问策略的表上,只有当行访问策略中的条件求值为 TRUE 时,才能执行 DML 操作。例如:
t1 如下所示:
A | B
---+---
1 | 1
2 | 2
3 | 3
在 t1 上创建以下行访问策略:
=> CREATE ACCESS POLICY ON t1 for ROWS
WHERE enabled_role('manager')
OR
A<2
ENABLE;
启用此策略后,对于要执行 DML 操作的用户,存在以下行为:
具有管理员角色的用户可以在表中的所有行上执行 DML,因为策略中的 WHERE 子句评估为 TRUE。
具有非管理员角色的用户只能执行 SELECT 以返回列 A 中值小于 2 的数据。如果访问策略必须读取表中的数据以符合某个条件,则不允许执行 DML 操作。
在启用列访问策略的表上,如果可以查看最初为整个列定义的类型,则可以执行 DML 操作。
假设使用以下数据类型和值创建表 t1:
=> CREATE TABLE t1 (A int, B int);
=> INSERT INTO t1 VALUES (1,2);
=> SELECT * FROM t1;
A | B
---+---
1 | 2
(1 row)
假设创建了以下访问策略,该策略在执行时将列 A 的数据类型从 INT 强制为 VARCHAR(20)。
=> CREATE ACCESS POLICY on t1 FOR column A A::VARCHAR(20) ENABLE;
Column "A" is of type int but expression in Access Policy is of type varchar(20). It will be coerced at execution time
在这种情况下,u1 可以查看 A 列的全部内容,但由于活动访问策略未指定 A 列的原始数据类型,因此 u1 无法对 A 列执行 DML 操作。
=> \c - u1
You are now connected as user "u1".
=> SELECT A FROM t1;
A
---
1
(1 row)
=> INSERT INTO t1 VALUES (3);
ERROR 6538: Unable to INSERT: "Access denied due to active access policy on table "t1" for column "A""
在访问策略中指定 GRANT TRUSTED 会覆盖默认行为(“用户只能编辑他们可以看到的内容”),并指示访问策略在评估用户能否执行 DML 操作时完全遵循显式 GRANT 语句。
如果数据存储形式与语义上的“真”形式不匹配,GRANT TRUSTED 很有用。
例如,当与 Voltage SecureData 集成时,一个常见的用例是使用 VoltageSecureProtect 存储加密数据,其中解密留给访问策略中用来调用 VoltageSecureAccess 的 case 表达式。在这种情况下,虽然解密形式被直观地理解为数据的“真”形式,但它仍然以其加密形式存储在表中;可以查看解密数据的用户将看不到存储的数据,因此无法执行 DML 操作。您可以使用 GRANT TRUSTED 来覆盖此行为,并允许用户在拥有授权的情况下执行这些操作。
在以下示例中,customer_info 表包含客户名字和姓氏以及 SSN 的列。SSN 很敏感,应该控制对它的访问,因此在插入表时使用 VoltageSecureProtect 对其进行加密:
=> CREATE TABLE customer_info(first_name VARCHAR, last_name VARCHAR, ssn VARCHAR);
=> INSERT INTO customer_info SELECT 'Alice', 'Smith', VoltageSecureProtect('998-42-4910' USING PARAMETERS format='ssn');
=> INSERT INTO customer_info SELECT 'Robert', 'Eve', VoltageSecureProtect('899-28-1303' USING PARAMETERS format='ssn');
=> SELECT * FROM customer_info;
first_name | last_name | ssn
------------+-----------+-------------
Alice | Smith | 967-63-8030
Robert | Eve | 486-41-3371
(2 rows)
在此系统中,角色“trusted_ssn”标识特权用户,Vertica 将为这些特权用户使用 VoltageSecureAccess 对“ssn”列的值进行解密。为了允许这些特权用户执行他们有权执行的 DML 操作,您可以使用以下访问策略:
=> CREATE ACCESS POLICY ON customer_info FOR COLUMN ssn
CASE WHEN enabled_role('trusted_ssn') THEN VoltageSecureAccess(ssn USING PARAMETERS format='ssn')
ELSE ssn END
GRANT TRUSTED
ENABLE;
同样请注意,GRANT TRUSTED 允许表上所有具有 GRANT 的用户执行指定的操作,包括没有“trusted_ssn”角色的用户。
访问策略会影响 Vertica Database Designer 生成的投影设计,以及优化器为查询执行创建的计划。
Database Designer 为给定表创建投影时,将考虑适用于当前用户的访问策略。Database Designer 为表生成的投影集已针对该用户的访问权限以及具有类似访问权限的其他用户进行优化。但这些投影对于具有不同访问权限的用户可能不太理想。这些差异可能会对 Vertica 如何高效处理第二组用户的查询有一些影响。当您评估表的投影设计时,请选择一种为所有授权用户优化访问的设计。
Vertica 优化器通过在其查询计划中重写用户查询来强制执行访问策略,这会影响查询性能。例如,clients 表具有行和列访问策略,均已启用。当用户查询此表时,查询优化器会生成一个用来重写查询的计划,因此它包含两个策略:
=> SELECT * FROM clients;
查询优化器生成一个查询计划,该计划按如下方式重写查询:
SELECT * FROM (
SELECT custID, password, CASE WHEN enabled_role('manager') THEN SSN ELSE substr(SSN, 8, 4) END AS SSN FROM clients
WHERE enabled_role('broker') AND
clients.clientID IN (SELECT brokers.clientID FROM brokers WHERE broker_name = CURRENT_USER())
) clients;
默认情况下,您只能管理您拥有的表的访问策略。您可以选择使用 AccessPolicyManagementSuperuserOnly 参数(默认值为 false)仅允许超级用户管理访问策略:
=> ALTER DATABASE DEFAULT SET PARAMETER AccessPolicyManagementSuperuserOnly = 1;
ALTER DATABASE
您可以通过多种方式查看和管理表的访问策略:
您可以通过两种方式查看访问策略:
查询系统表 ACCESS_POLICY。例如,以下查询返回表 public.customer_dimension
上的所有访问策略:
=> \x
=> SELECT policy_type, is_policy_enabled, table_name, column_name, expression FROM access_policy WHERE table_name = 'public.customer_dimension';
-[ RECORD 1 ]-----+----------------------------------------------------------------------------------------
policy_type | Column Policy
is_policy_enabled | Enabled
table_name | public.customer_dimension
column_name | customer_address
expression | CASE WHEN enabled_role('administrator') THEN customer_address ELSE '**************' END
使用 EXPORT_TABLES、EXPORT_OBJECTS 或 EXPORT_CATALOG 从数据库编录导出表 DDL。例如:
=> SELECT export_tables('','customer_dimension');
export_tables
-----------------------------------------------------------------------------
CREATE TABLE public.customer_dimension
(
customer_key int NOT NULL,
customer_type varchar(16),
customer_name varchar(256),
customer_gender varchar(8),
...
CONSTRAINT C_PRIMARY PRIMARY KEY (customer_key) DISABLED
);
CREATE ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address CASE WHEN enabled_role('administrator') THEN customer_address ELSE '**************' END ENABLE;
ALTER ACCESS POLICY 可以修改现有访问策略的表达式。例如,您可以通过扩展对 dbadmin 角色的访问权限来修改前面示例中的访问策略:
=> ALTER ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address
CASE WHEN enabled_role('dbadmin') THEN customer_address
WHEN enabled_role('administrator') THEN customer_address
ELSE '**************' END ENABLE;
ALTER ACCESS POLICY
查询系统表 ACCESS_POLICY 会确认此更改:
=> SELECT policy_type, is_policy_enabled, table_name, column_name, expression FROM access_policy
WHERE table_name = 'public.customer_dimension' AND column_name='customer_address';
-[ RECORD 1 ]-----+-------------------------------------------------------------------------------------------------------------------------------------------
policy_type | Column Policy
is_policy_enabled | Enabled
table_name | public.customer_dimension
column_name | customer_address
expression | CASE WHEN enabled_role('dbadmin') THEN customer_address WHEN enabled_role('administrator') THEN customer_address ELSE '**************' END
表的所有者可以启用和禁用表中的行和列访问策略。
启用和禁用表中的访问策略。
ALTER ACCESS POLICY ON [schema.]table FOR ROWS { ENABLE | DISABLE }
以下示例先禁用后重新启用表 customer_dimension
上的行访问策略:
=> ALTER ACCESS POLICY ON customer_dimension FOR ROWS DISABLE;
ALTER ACCESS POLICY
=> ALTER ACCESS POLICY ON customer_dimension FOR ROWS ENABLE;
ALTER ACCESS POLICY
按如下方式启用和禁用表列的访问策略:
ALTER ACCESS POLICY ON [schema.]table FOR COLUMN column { ENABLE | DISABLE }
以下示例先禁用后重新启用表 customer_dimension.customer_address
上的同一个列访问策略:
=> ALTER ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address DISABLE;
ALTER ACCESS POLICY
=> ALTER ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address ENABLE;
ALTER ACCESS POLICY
按如下方式将访问策略从一个表复制到另一个表。非超级用户必须同时拥有源表和目标表的所有权:
ALTER ACCESS POLICY ON [schema.]table { FOR COLUMN column | FOR ROWS } COPY TO TABLE table
当您使用以下函数(而非 CREATE TABLE AS SELECT 或 CREATE TABLE LIKE)创建表的副本或移动其内容时,会将原始表的访问策略复制到新/目标表:
若要将访问策略复制到另一个表,请使用 ALTER ACCESS POLICY。
例如,您可以按如下方式复制行访问策略:
=> ALTER ACCESS POLICY ON public.emp_dimension FOR ROWS COPY TO TABLE public.regional_managers_dimension;
以下语句将列 employee_key
上的访问策略从表 public.emp_dimension
复制到 store.store_sales_fact
:
=> ALTER ACCESS POLICY ON public.emp_dimension FOR COLUMN employee_key COPY TO TABLE store.store_sales_fact;
Vertica 管理工具可用于轻松地执行管理任务。使用管理工具可以执行大部分 Vertica 数据库管理任务。
使用 管理主机上的 数据库超级用户帐户运行管理工具(如果可能)。确保没有其他管理工具进程正在运行。
如果管理主机没有响应,请在群集中的其他节点上运行管理工具。该节点将永久接管管理主机的角色。
管理工具(即“admintools”)支持各种命令来管理数据库。
要运行 admintools,必须为 dbadmin
用户启用 SSH 和本地连接。
/opt/vertica/bin/admintools [--debug ][
{ -h | --help }
| { -a | --help_all}
| { -t | --tool } name_of_tool [options]
]
未限定的 admintools
命令会显示“主菜单 (Main Menu)”对话框。
如果您不熟悉此类界面,请阅读使用管理工具界面。
dbadmin
用户
首次以 数据库超级用户身份登录并运行管理工具时,会显示用户界面。
在最终用户许可协议 (EULA) 窗口中,键入 accept
以继续。
此时会显示一个窗口,请求填入从 Vertica 网站下载的许可证密钥文件的位置。默认路径是
/tmp/vlicense.dat
。
键入许可证密钥的绝对路径(例如,
Vertica 管理工具是使用对话框实现的,对话框是一个在终端(字符单元)窗口中工作的图形用户界面。该界面响应某些终端窗口(尤其是本地 Linux 窗口)中的鼠标单击,但您可能会发现它仅响应击键。因此,此部分介绍如何仅采用击键来使用管理工具。
在所有对话框中,当您准备好运行命令、选择文件或取消对话框时,请按 Enter 键。此部分的命令描述并非明示按 Enter 键。
在窗体对话框(也称作对话框)中,使用 Tab 键在确定 (OK)、取消 (Cancel)、帮助 (Help) 和窗体字段区域之间切换。光标在窗体字段区域中时,使用向上和向下箭头键选择一个字段(高亮显示),然后输入信息。在所有字段中输入完信息后,按 Enter 键。
联机帮助以文本对话框形式提供。如果您在查看帮助时遇到问题,请参阅远程终端用户须知。
图形界面的外观取决于终端窗口使用的颜色和字体设置。本文档中的屏幕截图是使用在 Windows 平台上运行的 PuTTy 终端应用程序中的默认颜色和字体设置完成的。
如果您使用的是 PuTTY,则可以使管理工具看起来像本文档中的屏幕截图:
在 PuTTY 窗口中,右键单击标题区域并选择“更改设置 (Change Settings)”。
创建或更改保存的会话。
在“类别 (Category)”对话框中,单击“窗口 (Window)”>“外观 (Appearance)”。
在“字体 (Font)”设置中,单击“更改...(Change...)”按钮。
选择“字体 (Font)”:Courier New:“常规大小 (Regular Size)”:10
单击“应用 (Apply)”。
对用于运行管理工具的每个现有会话重复这些步骤。
也可以将转换改为支持 UTF-8:
在 PuTTY 窗口中,右键单击标题区域并选择“更改设置 (Change Settings)”。
创建或更改保存的会话。
在“类别 (Category)”对话框中,单击“窗口 (Window)”>“转换 (Translation)”。
在“假设接收的数据应使用的字符集 (Received data assumed to be in which character set)”下拉菜单中,选择“UTF-8”。
单击“应用 (Apply)”。
关于使用管理工具的帮助 (Help on Using the Administration Tools) 命令将显示有关使用管理工具的帮助屏幕。
管理工具中的大多数联机帮助都是上下文相关的。例如,如果使用向上/向下箭头选择一个命令,请按 Tab 键移动到“帮助 (Help)”按钮,然后按 Return 键,此时将看到选定的命令相关的帮助。
使用向上/向下箭头键选择需要查看其帮助的命令。
使用“制表符 (Tab)”键将光标移动到“帮助 (Help)”按钮。
按 Enter(返回)。
使用向上和向下箭头键选择要查看其帮助的字段。
使用“制表符 (Tab)”键将光标移动到“帮助 (Help)”按钮。
按 Enter(返回)。
某些帮助文件太长,无法在单个屏幕中显示。使用向上和向下箭头键在文本中滚动。
如果进行以下更改,则故障节点的特定于管理工具的元数据将与其他群集节点不同步:
修改重新启动策略
添加一个或多个节点
删除一个或多个节点。
将节点还原到数据库群集时,可以借助管理工具使用最新的管理工具元数据更新该节点:
可以使用管理工具执行大多数数据库管理任务,但是也可以选择使用更直观和动态的 管理控制台。
下表对两个界面中可用的功能进行了比较。继续使用管理工具和命令行执行管理控制台尚不支持的操作。
管理控制台提供管理工具所提供的部分但不是全部功能。MC 还提供在管理工具中不可用的功能。
此工具显示数据库中节点的当前状态。
在“主菜单 (Main Menu)”上,选择查看数据库群集状态 (View Database Cluster State),然后单击确定 (OK)。
正在运行的数据库的正常状态为“全部开启 (ALL UP)”。已停止的数据库的正常状态为“全部关闭 (ALL DOWN)”。
如果某些主机处于“开启 (UP)”状态,而某些主机处于“关闭 (DOWN)”状态,请使用管理工具中的在主机上重新启动 Vertica (Restart Vertica on Host) 重新启动已关闭的特定主机,或者可以按照启动和停止数据库中所述启动数据库(除非您有已知的节点故障,并希望以该状态继续运行)。
显示为“正在初始化 (INITIALIZING)”或“正在恢复 (RECOVERING)”的节点指示正在进行故障恢复。
处于其他状态(例如 NEEDS_CATCHUP)的节点为过渡节点,除非永久存在,否则可以忽略。
此工具使用 vsql 连接到正在运行的 数据库。当登录到具有访问权限的任何用户帐户时,可以从数据库中的任何节点使用管理工具连接到数据库。无法从不是数据库节点的主机使用管理工具进行连接。要从其他主机进行连接,请按照从命令行进行连接中所述运行 vsql。
在“主菜单 (Main Menu)”上,单击连接到数据库 (Connect to Database),再单击确定 (OK)。
提供数据库密码(如果要求):
Password:
在使用 CREATE USER 命令创建新用户时,可以配置密码,也可以将其留空。如果在创建用户时配置了密码,则无法绕开密码。可以使用 ALTER USER 命令更改用户的密码。
管理工具连接到数据库,并将控制权移交给 vsql。
Welcome to vsql, the Vertica Analytic Database interactive terminal.
Type: \h or \? for help with vsql commands
\g or terminate with semicolon to execute query
\q to quit
=>
有关详细信息,请参阅使用 vsql。
此工具在正在运行的数据库中的一个或多个主机上重新启动 Vertica 进程。如果 Vertica 进程在主机上停止或被终止,请使用此工具。
要查看群集中节点的当前状态,请在“主菜单 (Main Menu)”上选择查看数据库群集状态 (View Database Cluster State)。
单击确定 (OK) 返回到“主菜单 (Main Menu)”。
如果一个或多个节点关闭,请选择在主机上重新启动 Vertica (Restart Vertica on Host),然后单击确定 (OK)。
选择包含要重新启动的主机的数据库,然后单击确定 (OK)。
选择要重新启动的一个或多个主机,然后单击确定 (OK)。
输入数据库密码。
再次选择查看数据库群集状态 (View Database Cluster State) 以验证是否所有节点都已启动。
“配置菜单 (Configuration Menu)”可用于执行以下任务:
使用以下过程通过 admintools 创建企业模式或 Eon 模式数据库。要在管理控制台中使用浏览器内向导创建数据库,请参阅使用 MC 创建数据库。
在“配置菜单 (Configuration Menu)”上,单击“创建数据库 (Create Database)”。单击 OK。
选择“企业模式 (Enterprise Mode)”作为数据库模式。
输入数据库名称和可选注释。单击 OK。
输入密码。有关规则,请参阅创建数据库名称和密码。
如果未输入密码,系统会提示您进行确认:“是 (Yes)”表示输入超级用户密码,“否 (No)”表示创建没有超级用户密码的数据库。
如果输入了密码,请再次输入密码。
选择要包括在数据库中的主机。此列表中的主机是安装时 (
install_vertica -
s) 指定的主机。
指定用于存储编录和数据文件的目录。
检查当前数据库定义是否正确。单击“是 (Yes)”以继续。
将显示一条消息,指示您已成功创建数据库。单击 OK。
在“配置菜单 (Configuration Menu)”上,单击“创建数据库 (Create Database)”。单击 OK。
选择“Eon 模式 (Eon Mode)”作为数据库模式。
输入数据库名称和可选注释。单击 OK。
输入密码。有关规则,请参阅创建数据库名称和密码。
仅限 AWS: 如果未输入密码,系统会提示您进行确认:“是 (Yes)”表示输入超级用户密码,“否 (No)”表示创建没有超级用户密码的数据库。
如果输入了密码,请再次输入密码。
选择要包括在数据库中的主机。此列表中的主机是安装时 (
install_vertica -s
) 指定的主机。
指定用于存储编录和存储库的目录、存储库大小、公共存储位置以及分片数量。
存储库大小: 使用后跟 %、K、G 或 T 的整数。默认值为存储相应存储库的文件系统的总磁盘空间的 60%。
公共存储: 在您的实例所在的同一区域中使用现有的 Amazon S3 存储桶。指定新的子文件夹名称,Vertica 将在现有的 S3 存储桶中动态创建该名称。例如,s3://existingbucket/newstorage1。您可以在现有子文件夹中创建新的子文件夹,但如果未指定任何新的子文件夹名称,数据库创建将回退。
分片数量: 使用整数。默认值等于节点数。为了获得最佳性能,分片数量不应大于节点数量的 2 倍。当节点数量大于分片数量(使用 ETS)时,仪表板查询的吞吐量会提高。当分片数量超过节点数量时,您可以在将来扩展群集以提高长分析查询的性能。
检查当前数据库定义是否正确。单击“是 (Yes)”以继续。
将显示一条消息,指示您已成功创建数据库。单击 OK。
此工具将删除现有的 数据库。只有 数据库超级用户可以删除数据库。
在配置菜单 (Configuration Menu) 上,单击删除数据库 (Drop Database),再单击确定 (OK)。
选择要删除的数据库,然后单击确定 (OK)。
单击是 (Yes) 以确定要删除数据库。
键入是 (yes),然后单击确定 (OK),再次确认您确实要删除数据库。
将显示一条消息,指示您已成功删除数据库。单击确定 (OK)。
当 Vertica 删除数据库时,它还会自动删除引用该数据库的节点定义。以下情况除外:
另一个数据库使用节点定义。如果另一个数据库引用了其中任何节点定义,则不会删除任何节点定义。
节点定义是为主机定义的唯一节点。(Vertica 使用节点定义来查找可用于创建数据库的主机,因此移除为主机定义的唯一节点可能会导致该主机对新数据库不可用。)
此工具显示现有 数据库的特征。
在配置菜单 (Configuration Menu) 上,选择查看数据库 (View Database),然后单击确定 (OK)。
选择要查看的数据库。
Vertica 会显示有关数据库的以下信息:
数据库的名称。
数据库日志文件的名称和位置。
数据库群集中的主机。
重新启动策略设置的值。
注意: 此设置确定 K-Safe 数据库中的节点在数据库重新启动时是否重新启动。请参阅设置重新启动策略。
数据库端口。
编录目录的名称和位置。
通过重新启动策略,可以确定 K-Safe 数据库中的节点在数据库重新启动时是否自动重新启动。由于整个数据库处于关闭状态时此功能不会自动重新启动节点,因此对非 K-Safe 的数据库无用。
要设置数据库的重新启动策略:
打开管理工具。
在“主菜单 (Main Menu)”上,选择配置菜单 (Configuration Menu),然后单击确定 (OK)。
在“配置菜单 (Configuration Menu)”中,选择设置重新启动策略 (Set Restart Policy),然后单击确定 (OK)。
选择要设置重新启动策略的数据库,然后单击确定 (OK)。
为数据库选择以下策略之一:
从不 (Never) — 节点从不自动重新启动。
K-Safe — 如果数据库群集仍处于开启状态,则节点将自动重新启动。这是默认设置。
始终 (Always) — 单节点数据库上的节点将自动重新启动。
单击确定 (OK)。
按照下述过程进行操作可防止 Vertica 将磁盘缺失或挂载错误的情况错误地诊断为数据损坏,进而避免发生耗时的全节点恢复。
如果服务器因硬件问题(例如磁盘错误或控制器故障)而发生故障,请在修复硬件时:
将计算机重启至运行级别 1,这是仅适用于控制台的 root 模式。
运行级别 1 可禁止网络连接并阻止 Vertica 尝试重新连接到群集。
在运行级别 1 中,验证硬件是否已得到修复,控制器是否处于联机状态,以及是否可以继续执行任何 RAID 恢复操作。
只有在确认硬件一致之后才能重启至运行级别 3 或更高级别。
此时将激活网络,同时 Vertica 会重新加入群集并自动恢复任何缺失的数据。请注意,在单节点数据库中,如果与投影关联的任何文件被删除或损坏,Vertica 将删除与该投影关联的所有文件,这可能会导致数据丢失。
运行 管理工具。
$ /opt/vertica/bin/adminTools
在管理工具主菜单 (Main Menu) 上,单击配置菜单 (Configuration Menu),再单击确定 (OK)。
在配置菜单 (Configuration Menu) 上,单击安装外部过程 (Install External Procedure),再单击确定 (OK)。
选择要在其上安装外部过程的数据库。
选择要安装的文件或手动键入完整文件路径,然后单击确定 (OK)。
如果不是超级用户,则会提示您输入密码,然后单击确定 (OK)。
管理工具会自动在数据库中的每个节点上创建
database-name/procedures
目录,并在这些目录中为您安装外部过程。
单击对话框中的确定 (OK),这表示安装已成功完成。
“高级菜单 (Advanced Menu)”选项可用于执行以下任务:
Vertica 提供了将整个数据库回退至特定 时期的功能,主要用于帮助更正数据加载或其他意外中断期间的人为错误。例如,假设您已执行批量加载,且群集在特定 COPY 命令期间无法运行。您可能要放弃所有时期并回退到提交上一个 COPY 命令的点,并再次运行未结束的命令。您可以通过检查日志文件确定该点(请参阅监控日志文件)。
在“高级 (Advanced)”菜单上,选择将数据库回退至上一个完好的时期 (Roll Back Database to Last Good Epoch)。
选择要回退的数据库。该数据库必须已停止。
接受建议的重新启动时期或指定其他时期。
确认要放弃指定时期之后的更改。
数据库成功重新启动。
此命令尝试在单个节点上正常关闭 Vertica 进程。
在“高级菜单 (Advanced Menu)”上,选择在主机上停止 Vertica (Stop Vertica on Host),然后单击确定 (OK)。
选择要停止的主机。
确认要停止这些主机。
如果命令成功,查看数据库群集状态将显示选定的主机状态为“关闭 (DOWN)”。
如果该命令无法停止任何选定节点,请继续执行在主机上终止 Vertica 进程。
此命令会向节点上的 Vertica 进程发送终止信号。
在“高级菜单 (Advanced Menu)”上,选择在主机上终止 Vertica 进程 (Kill Vertica Process on Host),然后单击确定 (OK)。
选择要在其上终止 Vertica 进程的主机。
确认要停止进程。
如果命令成功,查看数据库群集状态 (View Database Cluster State) 将显示选定的主机状态为“关闭 (DOWN)”。
以下步骤适用于获得许可的 Vertica 用户。完成这些步骤会将许可证密钥文件复制到数据库中。有关详细信息,请参阅管理许可证。
在“高级菜单 (Advanced Menu)”上,选择升级许可证密钥 (Upgrade License Key)。单击确定 (OK)。
选择要升级许可证密钥的数据库。
输入下载的许可证密钥文件的绝对路径名(例如
/tmp/vlicense.dat
)。单击确定 (OK)。
当您看到一条指示升级成功的消息时,单击“确定 (OK)”。
群集管理可用于在数据库群集中添加、替换或删除主机。这些过程通常是添加、删除或替换数据库节点这一较大过程的一部分。
要使用群集管理:
在主菜单 (Main Menu) 上,选择“高级菜单 (Advanced Menu)”,然后单击确定 (OK)。
在“高级菜单 (Advanced Menu)”上,选择群集管理 (Cluster Management),然后单击确定 (OK)。
选择以下选项之一,然后单击确定 (OK)。
使用管理工具的帮助 (Help Using the Administration Tools) 命令会显示有关使用管理工具的帮助屏幕。
管理工具中的大多数联机帮助都是上下文相关的。例如,如果使用向上/向下箭头选择一个命令,请按 Tab 键移动到“帮助 (Help)”按钮,然后按 Return,此时将看到与选定命令相关的帮助。
管理工具配置数据(元数据)包含数据库启动所需的信息,例如数据库群集中每个参与主机的主机名/IP 地址。
为了便于在管理工具中解析主机名,在命令行和安装实用程序中,Vertica 将强制通过管理工具提供的所有主机名使用 IP 地址:
安装期间
Vertica 会立即将通过命令行选项 --hosts
、-add-hosts
或 --remove-hosts
提供的任何主机名转换为其等效 IP 地址。
如果在安装期间提供解析为多个 IP 地址的主机名(例如在多宿主系统中),安装程序将提示您选择一个 IP 地址。
Vertica 保留您提供的名称只是为了将其用于消息和提示;在内部,这些主机名会被存储为 IP 地址。
在管理工具中
所有主机都使用 IP 形式以便直接比较(例如 db = database = database.example.com)。
在命令行中
Vertica 会将任何主机名值转换为用于在配置元数据中查找主机的 IP 地址。如果主机具有已解析的多个 IP 地址,Vertica 将测试每个 IP 地址以确认其是否在元数据中,并选择第一个匹配项。没有匹配项表示主机不是数据库群集的一部分。
元数据的可移植性更高,因为当您安装或升级数据库时,Vertica 不需要群集中的主机名完全相同。
admintools 在连接到数据库并对数据库执行操作时的行为可能因配置而异。特别是,admintools 会考虑它与其他节点的连接、这些节点的状态以及 dbadmin 使用的身份验证方法。
admintools 会在群集主机之间使用无密码 SSH 连接进行大多数操作,这是在安装期间使用 install_vertica 脚本配置或确认的
在大多数情况下,当向数据库发出命令时,admintools 倾向于使用其与目标主机的 SSH 连接,并使用 localhost 客户端连接到 Vertica 数据库
传入 IP 地址决定了所使用的身份验证方法。也就是说,客户端连接的行为可能与本地连接的行为不同,默认情况下本地连接可能是受信任的
dbadmin 应该具有本地信任或基于密码的身份验证方法
当决定使用哪个主机进行多步操作时,admintools 首选 localhost,然后重新连接到已知良好的节点
管理工具允许对 K-Safe 数据库执行某些操作,即使某些节点没有响应也是如此。
数据库必须已使用 MARK_DESIGN_KSAFE 函数标记为 K-Safe。
管理工具中的以下管理功能在某些节点没有响应时可以操作。
查看数据库群集状态
连接到数据库
启动数据库(包括手动恢复)
停止数据库
替换节点(假设已关闭的节点为被替换的节点)
查看数据库参数
升级许可证密钥
管理工具中的以下管理功能要求所有节点都已启动才能正常运行:
创建数据库
运行 Database Designer
删除数据库
设置重新启动策略
将数据库回退至 上一个完好的时期
可以从命令行或 shell 脚本调用大部分管理工具。
/opt/vertica/bin/admintools {
{ -h | --help }
| { -a | --help_all}
| { [--debug ] { -t | --tool } toolname [ tool-args ] }
}
/opt/vertica/bin
添加到您的搜索路径中。
若要返回所有可用工具的列表,请在命令提示符中输入 admintools -h
。
若要显示特定工具及其选项或命令的帮助,请用 --help
或 -h
限定指定工具的名称,如下例所示:
$ admintools -t connect_db --help
Usage: connect_db [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to connect
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
若要在单个帮助文本中列出所有可用工具及其命令和选项,请输入 admintools -a
。
Usage:
adminTools [-t | --tool] toolName [options]
Valid tools are:
command_host
connect_db
create_db
db_add_node
db_add_subcluster
db_remove_node
db_remove_subcluster
db_replace_node
db_status
distribute_config_files
drop_db
host_to_node
install_package
install_procedure
kill_host
kill_node
license_audit
list_allnodes
list_db
list_host
list_node
list_packages
logrotate
node_map
re_ip
rebalance_data
restart_db
restart_node
restart_subcluster
return_epoch
revive_db
set_restart_policy
set_ssl_params
show_active_db
start_db
stop_db
stop_host
stop_node
stop_subcluster
uninstall_package
upgrade_license_key
view_cluster
-------------------------------------------------------------------------
Usage: command_host [options]
Options:
-h, --help show this help message and exit
-c CMD, --command=CMD
Command to run
-F, --force Provide the force cleanup flag. Only applies to start,
restart, condrestart. For other options it is ignored.
-------------------------------------------------------------------------
Usage: connect_db [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to connect
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
-------------------------------------------------------------------------
Usage: create_db [options]
Options:
-h, --help show this help message and exit
-D DATA, --data_path=DATA
Path of data directory[optional] if not using compat21
-c CATALOG, --catalog_path=CATALOG
Path of catalog directory[optional] if not using
compat21
--compat21 (deprecated) Use Vertica 2.1 method using node names
instead of hostnames
-d DB, --database=DB Name of database to be created
-l LICENSEFILE, --license=LICENSEFILE
Database license [optional]
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes [optional]
-P POLICY, --policy=POLICY
Database restart policy [optional]
-s HOSTS, --hosts=HOSTS
comma-separated list of hosts to participate in
database
--shard-count=SHARD_COUNT
[Eon only] Number of shards in the database
--communal-storage-location=COMMUNAL_STORAGE_LOCATION
[Eon only] Location of communal storage
-x COMMUNAL_STORAGE_PARAMS, --communal-storage-params=COMMUNAL_STORAGE_PARAMS
[Eon only] Location of communal storage parameter file
--depot-path=DEPOT_PATH
[Eon only] Path to depot directory
--depot-size=DEPOT_SIZE
[Eon only] Size of depot
--force-cleanup-on-failure
Force removal of existing directories on failure of
command
--force-removal-at-creation
Force removal of existing directories before creating
the database
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-------------------------------------------------------------------------
Usage: db_add_node [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of the database
-s HOSTS, --hosts=HOSTS
Comma separated list of hosts to add to database
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
-a AHOSTS, --add=AHOSTS
Comma separated list of hosts to add to database
-c SCNAME, --subcluster=SCNAME
Name of subcluster for the new node
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
--compat21 (deprecated) Use Vertica 2.1 method using node names
instead of hostnames
-------------------------------------------------------------------------
Usage: db_add_subcluster [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to be modified
-s HOSTS, --hosts=HOSTS
Comma separated list of hosts to add to the subcluster
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
-c SCNAME, --subcluster=SCNAME
Name of the new subcluster for the new node
--is-primary Create primary subcluster
--is-secondary Create secondary subcluster
--control-set-size=CONTROLSETSIZE
Set the number of nodes that will run spread within
the subcluster
--like=CLONESUBCLUSTER
Name of an existing subcluster from which to clone
properties for the new subcluster
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-------------------------------------------------------------------------
Usage: db_remove_node [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to be modified
-s HOSTS, --hosts=HOSTS
Name of the host to remove from the db
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
--compat21 (deprecated) Use Vertica 2.1 method using node names
instead of hostnames
--skip-directory-cleanup
Caution: this option will force you to do a manual
cleanup. This option skips directory deletion during
remove node. This is best used in a cloud environment
where the hosts being removed will be subsequently
discarded.
-------------------------------------------------------------------------
Usage: db_remove_subcluster [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to be modified
-c SCNAME, --subcluster=SCNAME
Name of subcluster to be removed
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
--skip-directory-cleanup
Caution: this option will force you to do a manual
cleanup. This option skips directory deletion during
remove subcluster. This is best used in a cloud
environment where the hosts being removed will be
subsequently discarded.
-------------------------------------------------------------------------
Usage: db_replace_node [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of the database
-o ORIGINAL, --original=ORIGINAL
Name of host you wish to replace
-n NEWHOST, --new=NEWHOST
Name of the replacement host
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-------------------------------------------------------------------------
Usage: db_status [options]
Options:
-h, --help show this help message and exit
-s STATUS, --status=STATUS
Database status UP,DOWN or ALL(list running dbs -
UP,list down dbs - DOWN list all dbs - ALL
-------------------------------------------------------------------------
Usage: distribute_config_files
Sends admintools.conf from local host to all other hosts in the cluster
Options:
-h, --help show this help message and exit
-------------------------------------------------------------------------
Usage: drop_db [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Database to be dropped
-------------------------------------------------------------------------
Usage: host_to_node [options]
Options:
-h, --help show this help message and exit
-s HOST, --host=HOST comma separated list of hostnames which is to be
converted into its corresponding nodenames
-d DB, --database=DB show only node/host mapping for this database.
-------------------------------------------------------------------------
Usage: admintools -t install_package --package PACKAGE -d DB -p PASSWORD
Examples:
admintools -t install_package -d mydb -p 'mypasswd' --package default
# (above) install all default packages that aren't currently installed
admintools -t install_package -d mydb -p 'mypasswd' --package default --force-reinstall
# (above) upgrade (re-install) all default packages to the current version
admintools -t install_package -d mydb -p 'mypasswd' --package hcat
# (above) install package hcat
See also: admintools -t list_packages
Options:
-h, --help show this help message and exit
-d DBNAME, --dbname=DBNAME
database name
-p PASSWORD, --password=PASSWORD
database admin password
-P PACKAGE, --package=PACKAGE
specify package or 'all' or 'default'
--force-reinstall Force a package to be re-installed even if it is
already installed.
-------------------------------------------------------------------------
Usage: install_procedure [options]
Options:
-h, --help show this help message and exit
-d DBNAME, --database=DBNAME
Name of database for installed procedure
-f PROCPATH, --file=PROCPATH
Path of procedure file to install
-p OWNERPASSWORD, --password=OWNERPASSWORD
Password of procedure file owner
-------------------------------------------------------------------------
Usage: kill_host [options]
Options:
-h, --help show this help message and exit
-s HOSTS, --hosts=HOSTS
comma-separated list of hosts on which the vertica
process is to be killed using a SIGKILL signal
--compat21 (deprecated) Use Vertica 2.1 method using node names
instead of hostnames
-------------------------------------------------------------------------
Usage: kill_node [options]
Options:
-h, --help show this help message and exit
-s HOSTS, --hosts=HOSTS
comma-separated list of hosts on which the vertica
process is to be killed using a SIGKILL signal
--compat21 (deprecated) Use Vertica 2.1 method using node names
instead of hostnames
-------------------------------------------------------------------------
Usage: license_audit --dbname DB_NAME [OPTIONS]
Runs audit and collects audit results.
Options:
-h, --help show this help message and exit
-d DATABASE, --database=DATABASE
Name of the database to retrieve audit results
-p PASSWORD, --password=PASSWORD
Password for database admin
-q, --quiet Do not print status messages.
-f FILE, --file=FILE Output results to FILE.
-------------------------------------------------------------------------
Usage: list_allnodes [options]
Options:
-h, --help show this help message and exit
-------------------------------------------------------------------------
Usage: list_db [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to be listed
-------------------------------------------------------------------------
Usage: list_host [options]
Options:
-h, --help show this help message and exit
-------------------------------------------------------------------------
Usage: list_node [options]
Options:
-h, --help show this help message and exit
-n NODENAME, --node=NODENAME
Name of the node to be listed
-------------------------------------------------------------------------
Usage: admintools -t list_packages [OPTIONS]
Examples:
admintools -t list_packages # lists all available packages
admintools -t list_packages --package all # lists all available packages
admintools -t list_packages --package default # list all packages installed by default
admintools -t list_packages -d mydb --password 'mypasswd' # list the status of all packages in mydb
Options:
-h, --help show this help message and exit
-d DBNAME, --dbname=DBNAME
database name
-p PASSWORD, --password=PASSWORD
database admin password
-P PACKAGE, --package=PACKAGE
specify package or 'all' or 'default'
-------------------------------------------------------------------------
Usage: logrotateconfig [options]
Options:
-h, --help show this help message and exit
-d DBNAME, --dbname=DBNAME
database name
-r ROTATION, --rotation=ROTATION
set how often the log is rotated.[
daily|weekly|monthly ]
-s MAXLOGSZ, --maxsize=MAXLOGSZ
set maximum log size before rotation is forced.
-k KEEP, --keep=KEEP set # of old logs to keep
-------------------------------------------------------------------------
Usage: node_map [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB List only data for this database.
-------------------------------------------------------------------------
Usage: re_ip [options]
Replaces the IP addresses of hosts and databases in a cluster, or changes the
control messaging mode/addresses of a database.
Options:
-h, --help show this help message and exit
-f MAPFILE, --file=MAPFILE
A text file with IP mapping information. If the -O
option is not used, the command replaces the IP
addresses of the hosts in the cluster and all
databases for those hosts. In this case, the format of
each line in MAPFILE is: [oldIPaddress newIPaddress]
or [oldIPaddress newIPaddress, newControlAddress,
newControlBroadcast]. If the former,
'newControlAddress' and 'newControlBroadcast' would
set to default values. Usage: $ admintools -t re_ip -f
<mapfile>
-O, --db-only Updates the control messaging addresses of a database.
Also used for error recovery (when Re-IP encounters
some certain errors, a mapfile is auto-generated).
Format of each line in MAPFILE: [NodeName
AssociatedNodeIPaddress, newControlAddress,
newControlBrodcast]. 'NodeName' and
'AssociatedNodeIPaddress' must be consistent with
admintools.conf. Usage: $ admintools -t re_ip -f
<mapfile> -O -d <db_name>
-i, --noprompts System does not prompt for the validation of the new
settings before performing the Re-IP. Prompting is on
by default.
-T, --point-to-point Sets the control messaging mode of a database to
point-to-point. Usage: $ admintools -t re_ip -d
<db_name> -T
-U, --broadcast Sets the control messaging mode of a database to
broadcast. Usage: $ admintools -t re_ip -d <db_name>
-U
-d DB, --database=DB Name of a database. Required with the following
options: -O, -T, -U.
-------------------------------------------------------------------------
Usage: rebalance_data [options]
Options:
-h, --help show this help message and exit
-d DBNAME, --dbname=DBNAME
database name
-k KSAFETY, --ksafety=KSAFETY
specify the new k value to use
-p PASSWORD, --password=PASSWORD
--script Don't re-balance the data, just provide a script for
later use.
-------------------------------------------------------------------------
Usage: restart_db [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to be restarted
-e EPOCH, --epoch=EPOCH
Epoch at which the database is to be restarted. If
'last' is given as argument the db is restarted from
the last good epoch.
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
-k, --allow-fallback-keygen
Generate spread encryption key from Vertica. Use under
support guidance only.
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-------------------------------------------------------------------------
Usage: restart_node [options]
Options:
-h, --help show this help message and exit
-s HOSTS, --hosts=HOSTS
comma-separated list of hosts to be restarted
-d DB, --database=DB Name of database whose node is to be restarted
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
--new-host-ips=NEWHOSTS
comma-separated list of new IPs for the hosts to be
restarted
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-F, --force force the node to start and auto recover if necessary
--compat21 (deprecated) Use Vertica 2.1 method using node names
instead of hostnames
--waitfordown-timeout=WAITTIME
Seconds to wait until nodes to be restarted are down
-------------------------------------------------------------------------
Usage: restart_subcluster [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database whose subcluster is to be restarted
-c SCNAME, --subcluster=SCNAME
Name of subcluster to be restarted
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
-s NEWHOSTS, --hosts=NEWHOSTS
Comma separated list of new hosts to rebind to the
nodes
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-F, --force Force the nodes in the subcluster to start and auto
recover if necessary
-------------------------------------------------------------------------
Usage: return_epoch [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database
-p PASSWORD, --password=PASSWORD
Database password in single quotes
-------------------------------------------------------------------------
Usage: revive_db [options]
Options:
-h, --help show this help message and exit
-s HOSTS, --hosts=HOSTS
comma-separated list of hosts to participate in
database
-n NODEHOST, --node-and-host=NODEHOST
pair of nodename-hostname values delimited by "|" eg:
"v_testdb_node0001|10.0.0.1"Note: Each node-host pair
has to be specified as a new argument
--communal-storage-location=COMMUNAL_STORAGE_LOCATION
Location of communal storage
-x COMMUNAL_STORAGE_PARAMS, --communal-storage-params=COMMUNAL_STORAGE_PARAMS
Location of communal storage parameter file
-d DBNAME, --database=DBNAME
Name of database to be revived
--force Force cleanup of existing catalog directory
--display-only Describe the database on communal storage, and exit
--strict-validation Print warnings instead of raising errors while
validating cluster_config.json
-------------------------------------------------------------------------
Usage: set_restart_policy [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database for which to set policy
-p POLICY, --policy=POLICY
Restart policy: ('never', 'ksafe', 'always')
-------------------------------------------------------------------------
Usage: set_ssl_params [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database whose parameters will be set
-k KEYFILE, --ssl-key-file=KEYFILE
Path to SSL private key file
-c CERTFILE, --ssl-cert-file=CERTFILE
Path to SSL certificate file
-a CAFILE, --ssl-ca-file=CAFILE
Path to SSL CA file
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
-------------------------------------------------------------------------
Usage: show_active_db [options]
Options:
-h, --help show this help message and exit
-------------------------------------------------------------------------
Usage: start_db [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to be started
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-F, --force force the database to start at an epoch before data
consistency problems were detected.
-U, --unsafe Start database unsafely, skipping recovery. Use under
support guidance only.
-k, --allow-fallback-keygen
Generate spread encryption key from Vertica. Use under
support guidance only.
-s HOSTS, --hosts=HOSTS
comma-separated list of hosts to be started
--fast Attempt fast startup on un-encrypted eon db. Fast
startup will use startup information from
cluster_config.json
-------------------------------------------------------------------------
Usage: stop_db [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database to be stopped
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
-F, --force Force the databases to shutdown, even if users are
connected.
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-------------------------------------------------------------------------
Usage: stop_host [options]
Options:
-h, --help show this help message and exit
-s HOSTS, --hosts=HOSTS
comma-separated list of hosts on which the vertica
process is to be killed using a SIGTERM signal
--compat21 (deprecated) Use Vertica 2.1 method using node names
instead of hostnames
-------------------------------------------------------------------------
Usage: stop_node [options]
Options:
-h, --help show this help message and exit
-s HOSTS, --hosts=HOSTS
comma-separated list of hosts on which the vertica
process is to be killed using a SIGTERM signal
--compat21 (deprecated) Use Vertica 2.1 method using node names
instead of hostnames
-------------------------------------------------------------------------
Usage: stop_subcluster [options]
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database whose subcluster is to be stopped
-c SCNAME, --subcluster=SCNAME
Name of subcluster to be stopped
-p DBPASSWORD, --password=DBPASSWORD
Database password in single quotes
--timeout=NONINTERACTIVE_TIMEOUT
set a timeout (in seconds) to wait for actions to
complete ('never') will wait forever (implicitly sets
-i)
-i, --noprompts do not stop and wait for user input(default false).
Setting this implies a timeout of 20 min.
-------------------------------------------------------------------------
Usage: uninstall_package [options]
Options:
-h, --help show this help message and exit
-d DBNAME, --dbname=DBNAME
database name
-p PASSWORD, --password=PASSWORD
database admin password
-P PACKAGE, --package=PACKAGE
specify package or 'all' or 'default'
-------------------------------------------------------------------------
Usage: upgrade_license_key --database mydb --license my_license.key
upgrade_license_key --install --license my_license.key
Updates the vertica license.
Without '--install', updates the license used by the database and
the admintools license cache.
With '--install', updates the license cache in admintools that
is used for creating new databases.
Options:
-h, --help show this help message and exit
-d DB, --database=DB Name of database. Cannot be used with --install.
-l LICENSE, --license=LICENSE
Required - path to the license.
-i, --install When option is included, command will only update the
admintools license cache. Cannot be used with
--database.
-p PASSWORD, --password=PASSWORD
Database password.
-------------------------------------------------------------------------
Usage: view_cluster [options]
Options:
-h, --help show this help message and exit
-x, --xpand show the full cluster state, node by node
-d DB, --database=DB filter the output for a single database
本主题阐述如何启动和停止 Vertica 数据库以及如何使用数据库索引工具。
可通过以下方式之一启动数据库:
可使用 Vertica 管理工具 (Administration Tools) 启动数据库:
打开管理工具 (Administration Tools) 并选择查看数据库群集状态 (View Database Cluster State),以确保所有节点都处于关闭状态且没有其他数据库正在运行。
打开管理工具。有关访问管理工具的信息,请参阅使用管理工具。
在主菜单 (Main Menu) 上,选择启动数据库 (Start Database),再选择确定 (OK)。
选择要启动的数据库,再单击确定 (OK)。
输入数据库密码,再单击确定 (OK)。
当提示数据库已成功启动时,单击确定 (OK)。
检查日志文件,以确保未发生启动问题。
可使用命令行工具start_db
启动数据库:
$ /opt/vertica/bin/admintools ‑t start_db ‑d db‑name
[‑p password]
[‑s host1[,...] | ‑‑hosts=host1[,...]]
[‑‑timeout seconds]
[‑i | ‑‑noprompts]
[‑‑fast]
[‑F | ‑‑force]
以下示例使用 start_db
启动单节点数据库:
$ /opt/vertica/bin/admintools -t start_db -d VMart
Info:
no password specified, using none
Node Status: v_vmart_node0001: (DOWN)
Node Status: v_vmart_node0001: (DOWN)
Node Status: v_vmart_node0001: (DOWN)
Node Status: v_vmart_node0001: (DOWN)
Node Status: v_vmart_node0001: (DOWN)
Node Status: v_vmart_node0001: (DOWN)
Node Status: v_vmart_node0001: (DOWN)
Node Status: v_vmart_node0001: (DOWN)
Node Status: v_vmart_node0001: (UP)
Database VMart started successfully
在启动 Eon 模式数据库时,您可以启动所有主节点或一部分主节点。在这两种情况下,将主节点列表传递给 start_db
以从 ‑s
选项开始。
需要满足以下要求:
start_db
工具无法启动已停止的主机,例如基于云的虚拟机。您必须手动启动主机或使用 MC 启动群集。以下示例启动六节点 Eon 模式数据库中的三个主节点:
$ admintools -t start_db -d verticadb -p 'password' \
-s 10.11.12.10,10.11.12.20,10.11.12.30
Starting nodes:
v_verticadb_node0001 (10.11.12.10)
v_verticadb_node0002 (10.11.12.20)
v_verticadb_node0003 (10.11.12.30)
Starting Vertica on all nodes. Please wait, databases with a large catalog may take a while to initialize.
Node Status: v_verticadb_node0001: (DOWN) v_verticadb_node0002: (DOWN) v_verticadb_node0003: (DOWN)
Node Status: v_verticadb_node0001: (DOWN) v_verticadb_node0002: (DOWN) v_verticadb_node0003: (DOWN)
Node Status: v_verticadb_node0001: (DOWN) v_verticadb_node0002: (DOWN) v_verticadb_node0003: (DOWN)
Node Status: v_verticadb_node0001: (DOWN) v_verticadb_node0002: (DOWN) v_verticadb_node0003: (DOWN)
Node Status: v_verticadb_node0001: (DOWN) v_verticadb_node0002: (DOWN) v_verticadb_node0003: (DOWN)
Node Status: v_verticadb_node0001: (DOWN) v_verticadb_node0002: (DOWN) v_verticadb_node0003: (DOWN)
Node Status: v_verticadb_node0001: (UP) v_verticadb_node0002: (UP) v_verticadb_node0003: (UP)
Syncing catalog on verticadb with 2000 attempts.
Database verticadb: Startup Succeeded. All Nodes are UP
数据库启动后, 辅助子群集将关闭。您可以根据需要选择启动它们。请参阅启动子群集。
启动包含部分主节点的数据库
作为最佳实践,Vertica 建议您始终启动包含所有主节点的 Eon 模式数据库。有时,并非针对每个主节点都能启动主机。在这种情况下,您可能需要启动包含部分数据库主节点的数据库。
如果 start_db
指定数据库主节点的子集,则适用以下要求:
如果无法满足其中一个或两个条件,则 start_db
返回错误。在下面的示例中,start_db
指定了包含九个主节点的数据库中的三个主节点。该命令返回一个错误,它无法启动少于五个主节点的数据库:
$ admintools -t start_db -d verticadb -p 'password' \
-s 10.11.12.10,10.11.12.20,10.11.12.30
Starting nodes:
v_verticadb_node0001 (10.11.12.10)
v_verticadb_node0002 (10.11.12.20)
v_verticadb_node0003 (10.11.12.30)
Error: Quorum not satisfied for verticadb.
3 < minimum 5 of 9 primary nodes.
Attempted to start the following nodes:
Primary
v_verticadb_node0001 (10.11.12.10)
v_verticadb_node0003 (10.11.12.30)
v_verticadb_node0002 (10.11.12.20)
Secondary
hint: you may want to start all primary nodes in the database
Database start up failed. Cluster partitioned.
如果您尝试启动包含部分主节点的数据库并且群集无法启动,Vertica 进程可能会继续在某些主机上运行。如果是这样,后续尝试启动数据库时将返回如下错误:
Error: the vertica process for the database is running on the following hosts:
10.11.12.10
10.11.12.20
10.11.12.30
This may be because the process has not completed previous shutdown activities. Please wait and retry again.
Database start up failed. Processes still running.
Database verticadb did not start successfully: Processes still running.. Hint: you may need to start all primary nodes.
在启动数据库之前,必须在错误消息中列出的主机上,使用 admintools 菜单或 admintools 命令行的 stop_host
工具停止 Vertica 服务器进程:
$ admintools -t stop_host -s 10.11.12.10,10.11.12.20,10.11.12.30
在很多情况下,必须停止数据库,例如,在升级或执行各种维护任务之前。可以通过以下方法之一停止正在运行的数据库:
如果已连接任何用户或者 Database Designer 正在构建或部署数据库设计,则无法停止正在运行的数据库。
提示
要使用 admintools 停止正在运行的数据库:
验证是否所有群集节点都已启动。如果任何节点已关闭,请确定并重新启动它们。
关闭所有用户会话:
通过查询
SESSIONS
系统表确定所有具有活动会话的用户。通知用户即将关闭,并请求他们关闭其会话。
通过将配置参数 MaxClientSessions 暂时重置为 0 来阻止用户启动新会话:
=> ALTER DATABASE DEFAULT SET MaxClientSessions = 0;
使用 Vertica 函数
CLOSE_SESSION
和
CLOSE_ALL_SESSIONS
关闭所有剩余用户会话。
SHUTDOWN
强制关闭数据库并阻止新会话。
打开 Vertica 管理工具。
从主菜单中:
选择“停止数据库 (Stop Database)”
单击确定 (OK)
选择要停止的数据库,然后单击确定 (OK)。
输入密码(如果系统询问),然后单击确定 (OK)。
当系统提示数据库关闭完成时,单击确定 (OK)。
可以使用命令行工具 stop_db
停止数据库:
$ /opt/vertica/bin/admintools -t stop_db -d db-name [-p password] [-F | --force]
如果省略 -F
(或 --force
)选项,则该命令将检查有无活动会话。如果用户已连接到数据库,则该命令将中止并显示一条错误消息,还将列出所有活动会话。例如:
$ /opt/vertica/bin/admintools -t stop_db -d VMart
Info: no password specified, using none
Active session details
| Session id | Host Ip | Connected User |
| ------- -- | ---- -- | --------- ---- |
| v_vmart_node0001-91901:0x162 | 10.20.100.247 | ryan |
Database VMart not stopped successfully for the following reason:
Unexpected output from shutdown: Shutdown: aborting shutdown
NOTICE: Cannot shut down while users are connected
使用选项 -F
(或 --force
)将覆盖用户连接并强制关闭。
可以使用 SHUTDOWN 函数停止数据库。默认情况下,如果连接了任何用户,则关闭将失败。如果要强制关闭而不考虑任何活动用户连接,则可以运行以下命令:
=> SELECT SHUTDOWN('true');
SHUTDOWN
-------------------------
Shutdown: sync complete
(1 row)
在 Eon 模式数据库中,可以使用 SHUTDOWN_SUBCLUSTER 和 SHUTDOWN_WITH_DRAIN 函数停止子群集。SHUTDOWN_SUBCLUSTER 会立即关闭子群集,而 SHUTDOWN_WITH_DRAIN 会执行正常关闭,在关闭子群集之前从子群集中排空客户端连接。有关详细信息,请参阅启动和停止子群集。
以下示例演示了如何使用 SHUTDOWN_WITH_DRAIN 关闭 Eon 模式数据库中的所有子群集:
=> SELECT SHUTDOWN_WITH_DRAIN('', 0);
NOTICE 0: Begin shutdown of subcluster (default_subcluster, analytics)
SHUTDOWN_WITH_DRAIN
-----------------------------------------------------------------------
Shutdown message sent to subcluster (default_subcluster, analytics)
(1 row)
作为超级用户,您可以对 Vertica 数据库运行索引工具以执行两个任务:
对现有数据存储上的每个块运行循环冗余检查 (CRC),以检查 ROS 数据块的数据完整性。
检查 ROS 容器中的排序顺序是否正确。
如果数据库出现故障,请从 Linux 命令行调用索引工具。如果数据库已启动,请使用 Vertica 元函数
RUN_INDEX_TOOL
从 VSQL 调用:
如果从命令行调用,索引工具仅在当前节点上运行。但是,您可以同时对多个节点运行索引工具。
索引工具将操作相关的摘要信息写入至标准输出;结果的详细信息记录在两个位置之一,具体取决于调用该工具的环境:
有关评估输出中是否可能有错误的信息,请参阅:
可以通过将操作范围缩小到一个或多个投影并指定用于执行函数的线程数来优化元函数性能。有关详细信息,请参阅
RUN_INDEX_TOOL
。
在每次提取数据磁盘以进行查询处理时,Vertica 都会评估每个 ROS 数据块中的 CRC 值。如果在提取数据时出现 CRC 错误,则会将以下信息写入 vertica.log
文件:
CRC Check Failure Details:File Name:
File Offset:
Compressed size in file:
Memory Address of Read Buffer:
Pointer to Compressed Data:
Memory Contents:
事件管理器也会收到关于 CRC 错误的通知,因此您可以使用 SNMP 陷阱来捕获 CRC 错误:
"CRC mismatch detected on file <file_path>. File may be corrupted. Please check hardware and drivers."
如果您从 vsql、ODBC 或 JDBC 运行查询,则查询会返回 FileColumnReader ERROR
。此消息指出特定块的 CRC 与给定记录不匹配,如下所示:
hint: Data file may be corrupt. Ensure that all hardware (disk and memory) is working properly.
Possible solutions are to delete the file <pathname> while the node is down, and then allow the node
to recover, or truncate the table data.code: ERRCODE_DATA_CORRUPTED
如果 ROS 数据未按投影的顺序正确排序,则依赖于已排序数据的查询结果就会不正确。如果您怀疑有或已经发现不正确的查询结果,则可以使用索引工具来检查 ROS 排序顺序。索引工具会对每个 ROS 行进行评估,以确定其排序是否正确。如果检查发现某行的顺序不正确,则它会向日志文件中写入一条错误消息以及未排序行的行号和内容。
打开 indextool.log
文件。例如:
$ cd VMart/v_check_node0001_catalog
查看包含 OID 编号和字符串 Sort Order Violation
的错误消息。例如:
<INFO> ...on oid 45035996273723545: Sort Order Violation:
通过对 indextool.log
运行 grep
,查找有关排序顺序违规字符串的详细信息。例如,以下命令将返回每个字符串前面的行 (-B1
) 以及后面的四行 (-A4
) :
[15:07:55][vertica-s1]: grep -B1 -A4 'Sort Order Violation:' /my_host/databases/check/v_check_node0001_catalog/indextool.log
2012-06-14 14:07:13.686 unknown:0x7fe1da7a1950 [EE] <INFO> An error occurred when running index tool thread on oid 45035996273723537:
Sort Order Violation:
Row Position: 624
Column Index: 0
Last Row: 2576000
This Row: 2575000
--
2012-06-14 14:07:13.687 unknown:0x7fe1dafa2950 [EE] <INFO> An error occurred when running index tool thread on oid 45035996273723545:
Sort Order Violation:
Row Position: 3
Column Index: 0
Last Row: 4
This Row: 2
--
通过查询
STORAGE_CONTAINERS
系统表,查找发生排序顺序违规的投影。使用等于 indextool.log
中所列 OID 值的 storage_oid
。例如:
=> SELECT * FROM storage_containers WHERE storage_oid = 45035996273723545;
可以在 Vertica 中创建两种类型的原生表(ROS 格式):列式表和 Flex 表。可以将这两种类型的表创建为永久表或临时表。还可以创建查询一组特定表列的视图。
此部分中描述的表将其数据存储在 Vertica 数据库中,并由 Vertica 数据库进行管理。Vertica 还支持在数据库中定义并在外部存储数据的外部表。有关外部表的详细信息,请参阅使用外部数据。
使用 CREATE TABLE 语句在 Vertica 逻辑架构中创建原生表。可以直接指定列,如以下示例所示,也可以使用 LIKE 或 AS 子句从另一个表派生表定义。可以指定约束、分区、分段和其他因素。有关详细信息和限制,请参阅参考页面。
以下示例显示了基本表定义:
=> CREATE TABLE orders(
orderkey INT,
custkey INT,
prodkey ARRAY[VARCHAR(10)],
orderprices ARRAY[DECIMAL(12,2)],
orderdate DATE
);
与传统数据库将数据存储在表中不同,Vertica 会将表数据以物理方式存储在 投影(即表列的集合)中。投影以优化查询执行的格式存储数据。与实体化视图类似,它们会将结果集存储在磁盘上,而不是每次在查询中使用时都进行计算。
为了对 Vertica 表进行查询或执行任何操作,该表必须具有一个或多个与之关联的 投影。有关详细信息,请参阅投影。
可以使用 INFER_TABLE_DDL 函数检查 Parquet、ORC、JSON 或 Avro 数据,并为表定义生成一个起点。此函数返回 CREATE TABLE 语句,它可能需要进一步编辑。对于函数无法推断数据类型的列,该函数会将类型标记为未知并发出警告。对于 VARCHAR 和 VARBINARY 列,可能需要调整长度。请始终查看函数返回的语句,但特别是对于具有很多列的表,使用该函数可以节省时间和精力。
Parquet、ORC 和 Avro 文件包含架构信息,但 JSON 文件并不包含。对于 JSON,该函数检查原始数据以生成一个或多个候选表定义。有关 JSON 示例,请参阅函数参考页面。
在以下示例中,该函数从 Parquet 输入推断出完整的表定义,但 VARCHAR 列使用默认大小,可能需要进行调整:
=> SELECT INFER_TABLE_DDL('/data/people/*.parquet'
USING PARAMETERS format = 'parquet', table_name = 'employees');
WARNING 9311: This generated statement contains one or more varchar/varbinary columns which default to length 80
INFER_TABLE_DDL
-------------------------------------------------------------------------
create table "employees"(
"employeeID" int,
"personal" Row(
"name" varchar,
"address" Row(
"street" varchar,
"city" varchar,
"zipcode" int
),
"taxID" int
),
"department" varchar
);
(1 row)
对于 Parquet 文件,可以使用 GET_METADATA 函数检查文件并报告元数据,包括有关列的信息。
CREATE TEMPORARY TABLE
创建数据仅存在于当前会话中的表。临时表数据容始终对其他会话不可见。
默认情况下,所有临时表数据都为事务范围数据,也就是说,当 COMMIT
语句结束当前事务后,这些数据将被丢弃。如果 CREATE TEMPORARY TABLE
包括参数 ON COMMIT PRESERVE ROWS
,表数据会保留下来,直至当前会话结束。
临时表可用于将复杂查询处理分为多步来进行。通常情况下,报告工具会容纳创建报告过程中产生的中间结果 — 例如,工具首先获取一个结果集,然后查询该结果集,等等。
创建临时表后,Vertica 会自动为该表生成默认 投影。有关详细信息,请参阅自动投影。
CREATE TEMPORARY TABLE
可分别通过关键字 GLOBAL
和 LOCAL
在两个范围(全局和本地)创建表:
您可以指定临时表数据为事务范围还是会话范围数据:
[ON COMMIT DELETE ROWS](#on)
(默认):Vertica 会在每个事务结束时自动移除所有表数据。
[ON COMMIT PRESERVE ROWS](#on2)
:Vertica 会在当前会话中跨事务保留表数据。Vertica 会在会话结束时自动截断表。
如果使用 ON COMMIT PRESERVE ROWS
创建临时表,则在该表含有数据时无法为其添加投影。您必须先使用
TRUNCATE TABLE
移除该表中的所有数据。
您可以为使用 ON COMMIT DELETE ROWS
创建的临时表(无论其中是否填充了数据)创建投影。但是,CREATE PROJECTION
会结束您可能已添加数据的任何事务,因此投影始终为空。
ON COMMIT DELETE ROWS
默认情况下,Vertica 会在当前事务结束时从临时表(全局或本地)中移除所有数据。
例如:
=> CREATE TEMPORARY TABLE tempDelete (a int, b int);
CREATE TABLE
=> INSERT INTO tempDelete VALUES(1,2);
OUTPUT
--------
1
(1 row)
=> SELECT * FROM tempDelete;
a | b
---+---
1 | 2
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM tempDelete;
a | b
---+---
(0 rows)
如果需要,您可以在同一个事务中多次使用
DELETE
,以便反复刷新表数据。
ON COMMIT PRESERVE ROWS
您可以通过为临时表定义 ON COMMIT PRESERVE ROWS
关键字,指定临时表在当前会话中跨事务保留数据。Vertica 仅在当前会话结束时自动移除表中的所有数据。
例如:
=> CREATE TEMPORARY TABLE tempPreserve (a int, b int) ON COMMIT PRESERVE ROWS;
CREATE TABLE
=> INSERT INTO tempPreserve VALUES (1,2);
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM tempPreserve;
a | b
---+---
1 | 2
(1 row)
=> INSERT INTO tempPreserve VALUES (3,4);
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM tempPreserve;
a | b
---+---
1 | 2
3 | 4
(2 rows)
以下 Eon 模式限制适用于临时表:
CREATE TEMPORARY TABLE
语句将 k‑num 设置为大于 0,Vertica 会返回警告。
如果对当前会话的订阅发生更改,则该会话中的临时表将变得不可访问。会话订阅发生更改的原因包括:
某个节点离开了参与节点的列表。
新节点出现在了参与节点的列表中。
为一个或多个分片更改了活动节点。
通过显式调用
DO_TM_TASK('mergeout')
或使用
ALTER TABLE...ALTER COLUMN
更改列数据类型,在同一会话中由用户触发的合并操作。
可以使用带有 LIKE
子句的
CREATE TABLE
从现有表创建一个表:
CREATE TABLE [schema.]table-name LIKE [schema.]existing-table
[ {INCLUDING | EXCLUDING} PROJECTIONS ]
[ {INCLUDE | EXCLUDE} [SCHEMA] PRIVILEGES ]
使用 LIKE
创建表会复制源表定义以及与其关联的任何存储策略。它不会复制列上的表数据或表达式。
CREATE TABLE...LIKE
会复制所有表约束,但以下情况除外:
外键约束
从序列获取值的任何列,包括 IDENTITY
和 AUTO_INCREMENT
列。Vertica 会将列值复制到新表中,但会移除原始约束。例如,以下表定义对列 ID
设置 IDENTITY
约束:
CREATE TABLE public.Premium_Customer
(
ID IDENTITY ,
lname varchar(25),
fname varchar(25),
store_membership_card int
);
以下 CREATE TABLE...LIKE
语句将此表复制为 All_Customers
。Vertica 会从 All_Customers.ID
中移除 IDENTITY
约束,从而将其更改为具有 NOT NULL
约束的整数列:
=> CREATE TABLE All_Customers like Premium_Customer;
CREATE TABLE
=> select export_tables('','All_Customers');
export_tables
---------------------------------------------------
CREATE TABLE public.All_Customers
(
ID int NOT NULL,
lname varchar(25),
fname varchar(25),
store_membership_card int
);
(1 row)
可以使用 INCLUDING PROJECTIONS
或 EXCLUDING PROJECTIONS
限定 LIKE
子句,指定是否从源表中复制投影:
EXCLUDING PROJECTIONS
(默认值):不从源表复制投影。
INCLUDING PROJECTIONS
:从源表复制当前投影。Vertica 会根据 Vertica 命名约定命名新投影,以避免与现有对象发生名称冲突。
可以为新表指定架构权限的默认继承:
EXCLUDE [SCHEMA] PRIVILEGES
(默认)禁用从架构继承权限
INCLUDE [SCHEMA] PRIVILEGES
为表授予向其架构授予的相同权限
有关详细信息,请参阅设置表和视图的权限继承。
对于源表存在以下限制:
不能具有过期投影。
不能为临时表。
创建表 states
:
=> CREATE TABLE states (
state char(2) NOT NULL, bird varchar(20), tree varchar (20), tax float, stateDate char (20))
PARTITION BY state;
向表中填充数据:
INSERT INTO states VALUES ('MA', 'chickadee', 'american_elm', 5.675, '07-04-1620');
INSERT INTO states VALUES ('VT', 'Hermit_Thrasher', 'Sugar_Maple', 6.0, '07-04-1610');
INSERT INTO states VALUES ('NH', 'Purple_Finch', 'White_Birch', 0, '07-04-1615');
INSERT INTO states VALUES ('ME', 'Black_Cap_Chickadee', 'Pine_Tree', 5, '07-04-1615');
INSERT INTO states VALUES ('CT', 'American_Robin', 'White_Oak', 6.35, '07-04-1618');
INSERT INTO states VALUES ('RI', 'Rhode_Island_Red', 'Red_Maple', 5, '07-04-1619');
查看表内容:
=> SELECT * FROM states;
state | bird | tree | tax | stateDate
-------+---------------------+--------------+-------+----------------------
VT | Hermit_Thrasher | Sugar_Maple | 6 | 07-04-1610
CT | American_Robin | White_Oak | 6.35 | 07-04-1618
RI | Rhode_Island_Red | Red_Maple | 5 | 07-04-1619
MA | chickadee | american_elm | 5.675 | 07-04-1620
NH | Purple_Finch | White_Birch | 0 | 07-04-1615
ME | Black_Cap_Chickadee | Pine_Tree | 5 | 07-04-1615
(6 rows
创建示例投影并刷新:
=> CREATE PROJECTION states_p AS SELECT state FROM states;
=> SELECT START_REFRESH();
创建一个类似于 states
表的表并包括其投影:
=> CREATE TABLE newstates LIKE states INCLUDING PROJECTIONS;
查看这两个表的投影。Vertica 已将投影从 states
复制到 newstates
:
=> \dj
List of projections
Schema | Name | Owner | Node | Comment
-------------------------------+-------------------------------------------+---------+------------------+---------
public | newstates_b0 | dbadmin | |
public | newstates_b1 | dbadmin | |
public | newstates_p_b0 | dbadmin | |
public | newstates_p_b1 | dbadmin | |
public | states_b0 | dbadmin | |
public | states_b1 | dbadmin | |
public | states_p_b0 | dbadmin | |
public | states_p_b1 | dbadmin | |
查看表 newstates
,它显示从 states
复制的列:
=> SELECT * FROM newstates;
state | bird | tree | tax | stateDate
-------+------+------+-----+-----------
(0 rows)
使用 CREATE TABLE...LIKE
语句时,系统也会复制与表关联的存储策略对象。添加到新表的数据会使用与源表相同的标记存储位置,除非您更改了存储策略。有关详细信息,请参阅处理存储位置。
CREATE TABLE 可以指定 AS 子句以从查询创建表,如下所示:
CREATE [TEMPORARY] TABLE [schema.]table-name
[ ( column-name-list ) ]
[ {INCLUDE | EXCLUDE} [SCHEMA] PRIVILEGES ]
AS [ /*+ LABEL */ ] [ AT epoch ] query [ ENCODED BY column‑ref‑list ]
Vertica 会从查询结果创建一个表,并向其中加载结果集。例如:
=> CREATE TABLE cust_basic_profile AS SELECT
customer_key, customer_gender, customer_age, marital_status, annual_income, occupation
FROM customer_dimension WHERE customer_age>18 AND customer_gender !='';
CREATE TABLE
=> SELECT customer_age, annual_income, occupation FROM cust_basic_profile
WHERE customer_age > 23 ORDER BY customer_age;
customer_age | annual_income | occupation
--------------+---------------+--------------------
24 | 469210 | Hairdresser
24 | 140833 | Butler
24 | 558867 | Lumberjack
24 | 529117 | Mechanic
24 | 322062 | Acrobat
24 | 213734 | Writer
...
可以使用以下一个或两个选项来限定 AS 子句:
LABEL 提示,标识用于分析和调试的语句
AT epoch 子句,用于指定查询返回历史数据
可以在 AS 子句中的两个位置嵌入 LABEL 提示:
紧跟在关键字 AS 之后:
CREATE TABLE myTable AS /*+LABEL myLabel*/...
在 SELECT 语句中:
CREATE TABLE myTable AS SELECT /*+LABEL myLabel*/
如果 AS 子句在两个位置均包含 LABEL 提示,则第一个标签具有优先权。
可以使用 AT epoch 子句限定 CREATE TABLE AS 查询,以指定查询返回历史数据,其中 epoch 是以下值之一:
EPOCH LATEST:返回数据直到当前时期(但不包括当前时期)。结果集包括来自最新提交的 DML 事务的数据。
EPOCH integer:返回数据直到 integer 指定的时期(包括该指定时期)。
TIME 'timestamp':从 timestamp 指定的时期返回数据。
有关 Vertica 如何使用时期的详细信息,请参阅时期。
有关详细信息,请参阅历史查询。
如果查询返回零宽度列,Vertica 会自动将其转换为 VARCHAR(80) 列。例如:
=> CREATE TABLE example AS SELECT '' AS X;
CREATE TABLE
=> SELECT EXPORT_TABLES ('', 'example');
EXPORT_TABLES
----------------------------------------------------------
CREATE TEMPORARY TABLE public.example
(
X varchar(80)
);
如果从查询创建临时表,则必须指定 ON COMMIT PRESERVE ROWS 才能向该表中加载结果集。否则,Vertica 会创建一个空表。
如果查询输出含有除简单列以外的表达式,例如常量或函数,则必须为该表达式指定别名,或在列名列表中列出所有列。
不能将 CREATE TABLE AS SELECT 与返回复杂类型值的 SELECT 一起使用。但是,可以使用 CREATE TABLE LIKE。
许多安全系统包含的记录必须能够证明不会受到更改的影响。行和块校验和等保护策略会产生高开销。此外,对于数据库管理员或其他具有足够权限的用户进行的未经授权的更改(无论是有意还是无意),这些方法并非万无一失。
不可变表只能插入,无论用户权限如何,都无法修改其中的现有数据。禁止更新行值和删除行。还禁止对表元数据进行某些更改(例如,重命名表列),以防止试图规避这些限制。从外部源获取数据的扁平表或外部表不能设置为不可变。
可以使用 ALTER TABLE 将现有表定义为不可变表:
ALTER TABLE table SET IMMUTABLE ROWS;
一旦设置,表不变性将无法恢复,并立即应用于所有现有的表数据,以及此后加载的所有数据。为了修改不可变表的数据,必须将数据复制到新表,例如,使用 COPY、CREATE TABLE...AS 或 COPY_TABLE。
当对表执行 ALTER TABLE...SET IMMUTABLE ROWS 时,Vertica 会在系统表 TABLES 中为该表设置两列。这两列一起显示该表何时变为不可变表:
禁止对不可变表执行以下操作:
ALTER TABLE...ADD COLUMN,其中新列使用 SET USING 子句进行定义。
当目标表不可变时,不允许使用以下分区管理函数:
通常,可以对不影响现有行数据的不可变表执行任何 DML 操作,例如,使用 COPY 或 INSERT 添加行。将数据添加到不可变表后,将无法更改。
其他允许的操作通常分为两类:
对表的 DDL 的更改对其数据没有影响:
ALTER TABLE...SET SCHEMA:更改表架构。
ALTER TABLE...OWNER TO:转移表所有权。
ALTER TABLE...ALTER COLUMN...SET DATATYPE:更改列的数据类型。仅当列数据类型更改对列的存储数据没有影响时,才允许进行列数据类型更改。
对多个表行或整个表的块操作:
默认情况下,架构和表仅受可用磁盘空间和许可证容量的限制。可以为架构或单个表设置磁盘配额,例如为了支持多租户。设置、修改或移除磁盘配额需要超级用户权限。
增加存储大小的大多数用户操作都会强制实施磁盘配额。在某些操作(例如恢复)期间,表可能会暂时超出其配额。如果将配额降低到当前使用量以下,虽然不会丢失任何数据,但无法添加更多数据。请将配额视为建议,而不是硬性限制。
架构配额(如果已设置)必须大于其中的最大表配额。
磁盘配额是由整数和度量单位(K、M、G 或 T)组成的字符串,例如“15G”或“1T”。不要在数字和单位之间使用空格。不支持其他度量单位。
要在创建时设置配额,请为 CREATE SCHEMA 或 CREATE TABLE 使用 DISK_QUOTA 选项:
=> CREATE SCHEMA internal DISK_QUOTA '10T';
CREATE SCHEMA
=> CREATE TABLE internal.sales (...) DISK_QUOTA '5T';
CREATE TABLE
=> CREATE TABLE internal.leads (...) DISK_QUOTA '12T';
ROLLBACK 0: Table can not have a greater disk quota than its Schema
要修改、添加或移除现有架构或表的配额,请使用 ALTER SCHEMA 或 ALTER TABLE:
=> ALTER SCHEMA internal DISK_QUOTA '20T';
ALTER SCHEMA
=> ALTER TABLE internal.sales DISK_QUOTA SET NULL;
ALTER TABLE
可以设置低于当前使用量的配额。ALTER 操作成功后,架构或表暂时超出配额,此时将无法执行增加数据使用量的操作。
在 Eon 模式下,磁盘使用量是用于架构或表的所有分片使用的所有空间的总和。此值仅针对主订阅计算。
在企业模式下,磁盘使用量是所有节点上用于架构或表的所有存储容器使用的空间总和。该总和不包括伙伴实例投影,但包括所有其他投影。
磁盘使用量根据压缩大小进行计算。
配额(如果存在)影响大多数 DML 和 ILM 操作,包括:
添加或刷新列(请参阅 ALTER COLUMN)
表的子集还原(如果它会超出架构配额)
以下示例显示了超出表配额导致故障:
=> CREATE TABLE stats(score int) DISK_QUOTA '1k';
CREATE TABLE
=> COPY stats FROM STDIN;
1
2
3
4
5
\.
ERROR 0: Disk Quota Exceeded for the Table object public.stats
HINT: Delete data and PURGE or increase disk quota at the table level
DELETE 不会释放空间,因为已删除的数据仍保留在存储容器中。由删除操作添加的删除向量不计入配额,因此删除是与配额无关的操作。清除数据时回收已删除数据的磁盘空间;请参阅移除表数据。
一些不常见的操作(例如 ADD COLUMN、RESTORE 和 SWAP PARTITION)可能会在事务期间创建新的存储容器。这些操作在完成时会清理多余的位置,但在操作进行过程中,表或架构可能会超出其配额。如果在这些操作期间收到磁盘配额错误,则可以暂时增加配额,执行该操作,然后重置配额。
配额不影响恢复、重新平衡或 Tuple Mover 操作。
DISK_QUOTA_USAGES 系统表显示具有配额的表和架构的当前磁盘使用量。此表不报告没有配额的对象。
您可以使用此表监视使用量,并做出调整配额的决策:
=> SELECT * FROM DISK_QUOTA_USAGES;
object_oid | object_name | is_schema | total_disk_usage_in_bytes | disk_quota_in_bytes
-------------------+-------------+-----------+---------------------+---------------------
45035996273705100 | s | t | 307 | 10240
45035996273705104 | public.t | f | 614 | 1024
45035996273705108 | s.t | f | 307 | 2048
(3 rows)
在定义表之后,可以使用
ALTER TABLE
修改现有表列。可以对列执行以下操作:
使用 ALTER TABLE
重命名列,如下所示:
ALTER TABLE [schema.]table-name RENAME [ COLUMN ] column-name TO new-column-name
以下示例将 Retail.Product_Dimension
表中的列从 Product_description
重命名为 Item_description
:
=> ALTER TABLE Retail.Product_Dimension
RENAME COLUMN Product_description TO Item_description;
如果重命名视图引用的列,该列不会显示在视图的结果集中,即使视图使用通配符 (*) 来表示表中的所有列。重新创建视图以合并列的新名称。
通常,可以使用 ALTER TABLE 更改列的数据类型(如果这样做不需要重新组织存储)。修改列的数据类型后,加载的数据符合新定义。
以下几个部分介绍与更改列的数据类型相关的要求和限制。
Vertica 支持转换以下数据类型:
Vertica 不允许对需要重新组织存储的类型进行数据类型转换:
Boolean
日期/时间
近似数字类型
BINARY 到 VARBINARY,反之亦然
如果列属于以下情况之一,也不能更改列的数据类型:
主键
外键
包含在该表的任何投影的 SEGMENTED BY 子句中。
复杂类型列。有一个例外:在外部表中,可以将基元列类型更改为复杂类型。
可以绕过其中一些限制。有关详细信息,请参阅使用列数据转换。
可以扩展同一类数据类型中的列。这样做对于在列中存储较大的项目很有用。Vertica 在执行转换之前会验证数据。
通常,还可以减小数据类型类中的列宽。如果原始声明的长度比您需要的要长,这对于回收存储很有用,特别是对于字符串。仅当满足以下条件时,才能减小列宽:
现有列数据不大于新宽度。
数据库群集中的所有节点都已启动。
否则,Vertica 会返回错误并且转换失败。例如,如果尝试将列从 varchar(25)
转换为 varchar(10)
,只要所有列数据不超过 10 个字符,Vertica 就允许转换。
在以下示例中,列 y
和 z
最初定义为 VARCHAR 数据类型,并分别加载值 12345
和 654321
。将列 z
的宽度减小到 5 的尝试失败,因为它包含六个字符的数据。将列 y
的宽度减小到 5 的尝试成功,因为它的内容符合新的宽度:
=> CREATE TABLE t (x int, y VARCHAR, z VARCHAR);
CREATE TABLE
=> CREATE PROJECTION t_p1 AS SELECT * FROM t SEGMENTED BY hash(x) ALL NODES;
CREATE PROJECTION
=> INSERT INTO t values(1,'12345','654321');
OUTPUT
--------
1
(1 row)
=> SELECT * FROM t;
x | y | z
---+-------+--------
1 | 12345 | 654321
(1 row)
=> ALTER TABLE t ALTER COLUMN z SET DATA TYPE char(5);
ROLLBACK 2378: Cannot convert column "z" to type "char(5)"
HINT: Verify that the data in the column conforms to the new type
=> ALTER TABLE t ALTER COLUMN y SET DATA TYPE char(5);
ALTER TABLE
如果列是集合数据类型,则可以使用 ALTER TABLE 更改其边界或最大二进制大小。这些属性是在表创建时设置的,随后可以更改。
您可以使集合有界,设置其最大元素数,如下面的示例所示。
=> ALTER TABLE test.t1 ALTER COLUMN arr SET DATA TYPE array[int,10];
ALTER TABLE
=> \d test.t1
List of Fields by Tables
Schema | Table | Column | Type | Size | Default | Not Null | Primary Key | Foreign Key
--------+-------+--------+-----------------+------+---------+----------+-------------+-------------
test | t1 | arr | array[int8, 10] | 80 | | f | f |
(1 row)
或者,可以设置整个集合的二进制大小,而不是设置边界。二进制大小是显式设置的,也可以通过 DefaultArrayBinarySize 配置参数设置。下面的示例从默认值创建一个数组列,更改默认值,然后使用 ALTER TABLE 将其更改为新的默认值。
=> SELECT get_config_parameter('DefaultArrayBinarySize');
get_config_parameter
----------------------
100
(1 row)
=> CREATE TABLE test.t1 (arr array[int]);
CREATE TABLE
=> \d test.t1
List of Fields by Tables
Schema | Table | Column | Type | Size | Default | Not Null | Primary Key | Foreign Key
--------+-------+--------+-----------------+------+---------+----------+-------------+-------------
test | t1 | arr | array[int8](96) | 96 | | f | f |
(1 row)
=> ALTER DATABASE DEFAULT SET DefaultArrayBinarySize=200;
ALTER DATABASE
=> ALTER TABLE test.t1 ALTER COLUMN arr SET DATA TYPE array[int];
ALTER TABLE
=> \d test.t1
List of Fields by Tables
Schema | Table | Column | Type | Size | Default | Not Null | Primary Key | Foreign Key
--------+-------+--------+-----------------+------+---------+----------+-------------+-------------
test | t1 | arr | array[int8](200)| 200 | | f | f |
(1 row)
或者,可以显式设置二进制大小,而不是使用默认值。
=> ALTER TABLE test.t1 ALTER COLUMN arr SET DATA TYPE array[int](300);
如果 Vertica 保留任何超过新宽度的历史数据,则无法减小列的宽度。要减小列宽,首先从表中移除该数据:
将 AHM 推进到比需要从表中移除的历史数据更近的时期。
使用函数
PURGE_TABLE
清除表中 AHM 之前的所有历史数据。
例如,在前面的示例中,您可以更新 t.z
列中的数据,如下所示:
=> UPDATE t SET z = '54321';
OUTPUT
--------
1
(1 row)
=> SELECT * FROM t;
x | y | z
---+-------+-------
1 | 12345 | 54321
(1 row)
尽管现在 z 列中没有数据超过 5 个字符,但 Vertica 保留了其早期数据的历史记录,因此尝试将列宽减小到 5 会返回错误:
=> ALTER TABLE t ALTER COLUMN z SET DATA TYPE char(5);
ROLLBACK 2378: Cannot convert column "z" to type "char(5)"
HINT: Verify that the data in the column conforms to the new type
您可以通过清除表的历史数据来减小列宽,如下所示:
=> SELECT MAKE_AHM_NOW();
MAKE_AHM_NOW
-------------------------------
AHM set (New AHM Epoch: 6350)
(1 row)
=> SELECT PURGE_TABLE('t');
PURGE_TABLE
----------------------------------------------------------------------------------------------------------------------
Task: purge operation
(Table: public.t) (Projection: public.t_p1_b0)
(Table: public.t) (Projection: public.t_p1_b1)
(1 row)
=> ALTER TABLE t ALTER COLUMN z SET DATA TYPE char(5);
ALTER TABLE
Vertica 通过禁止对表列进行某些数据转换来符合 SQL 标准。但是,当从非 SQL 数据库转换数据时,有时需要绕过此限制。以下示例描述了一种这样的解决方法,使用下表:
=> CREATE TABLE sales(id INT, price VARCHAR) UNSEGMENTED ALL NODES;
CREATE TABLE
=> INSERT INTO sales VALUES (1, '$50.00');
OUTPUT
--------
1
(1 row)
=> INSERT INTO sales VALUES (2, '$100.00');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM SALES;
id | price
----+---------
1 | $50.00
2 | $100.00
(2 rows)
要将 price
列的现有数据类型从 VARCHAR 转换为 NUMERIC,请完成以下步骤:
添加一个新列供临时使用。为该列分配 NUMERIC 数据类型,并从现有 price 列中派生其默认值。
将列 temp_price
添加到表 sales
。可以暂时使用新列,将其数据类型设置为所需的数据类型 (NUMERIC),并从 price
列中派生其默认值。将新列的默认值转换为 NUMERIC 数据类型并查询该表:
=> ALTER TABLE sales ADD COLUMN temp_price NUMERIC(10,2) DEFAULT
SUBSTR(sales.price, 2)::NUMERIC;
ALTER TABLE
=> SELECT * FROM SALES;
id | price | temp_price
----+---------+------------
1 | $50.00 | 50.00
2 | $100.00 | 100.00
(2 rows)
使用 ALTER TABLE 从新列 temp_price
中删除默认表达式。Vertica 会保留存储在此列中的值:
=> ALTER TABLE sales ALTER COLUMN temp_price DROP DEFAULT;
ALTER TABLE
删除无关的 price
列。在这样做之前,必须先推进
AHM 以清除会阻止删除操作的历史数据:
推进 AHM:
=> SELECT MAKE_AHM_NOW();
MAKE_AHM_NOW
-------------------------------
AHM set (New AHM Epoch: 6354)
(1 row)
删除原始 price 列:
=> ALTER TABLE sales DROP COLUMN price CASCADE;
ALTER COLUMN
现在可以将 temp_price
列重命名为 price
:
使用 ALTER TABLE
重命名列:
=> ALTER TABLE sales RENAME COLUMN temp_price to price;
再次查询 sales 表:
=> SELECT * FROM sales;
id | price
----+--------
1 | 50.00
2 | 100.00
(2 rows)
您可以定义一个列,以便 Vertica 通过以下子句之一从表达式自动设置其值:
DEFAULT
SET USING
DEFAULT USING
DEFAULT 选项将列值设置为指定值。它具有以下语法:
DEFAULT default-expression
当您执行以下操作时会设置 Default 值:
将新行加载到表中,例如,使用 INSERT 或 COPY。Vertica 使用默认值填充新行中的 DEFAULT 列。现有行中的值(包括具有 DEFAULT 表达式的列)保持不变。
对表执行 UPDATE,并将 DEFAULT 列的值设置为 DEFAULT
:
=> UPDATE table-name SET column-name=DEFAULT;
将具有 DEFAULT 表达式的列添加到现有表。将新列添加到表中时,Vertica 会使用其默认值填充新列。
DEFAULT 表达式无法使用 ALTER TABLE...ADD COLUMN 指定易变函数。要指定易变函数,请使用 CREATE TABLE 或 ALTER TABLE...ALTER COLUMN 语句。
当对列调用 REFRESH_COLUMNS 函数时,SET USING 选项将该列值设置为表达式。此选项具有以下语法:
SET USING using-expression
这种方法对于大型非标准化(扁平)表很有用,其中多个列通过查询其他表来获取它们的值。
SET USING 具有以下限制:
不允许使用易变函数。
表达式不能指定序列。
Vertica 限制了复制表数据的多个元函数的使用:COPY_TABLE、COPY_PARTITIONS_TO_TABLE、MOVE_PARTITIONS_TO_TABLE 和 SWAP_PARTITIONS_BETWEEN_TABLES:
如果源表和目标表都具有 SET USING 列,则仅当每个源 SET USING 列具有相应的目标 SET USING 列时,才允许执行该操作。
如果只有源表具有 SET USING 列,则不允许使用 SWAP_PARTITIONS_BETWEEN_TABLES。
如果只有目标表具有 SET USING 列,则不允许执行该操作。
DEFAULT USING 选项对列设置 DEFAULT 和 SET USING 约束,相当于对同一列分别使用 DEFAULT 和 SET USING 执行相同的表达式。它具有以下语法:
DEFAULT USING expression
例如,以下列定义实际上是相同的:
=> ALTER TABLE public.orderFact ADD COLUMN cust_name varchar(20)
DEFAULT USING (SELECT name FROM public.custDim WHERE (custDim.cid = orderFact.cid));
=> ALTER TABLE public.orderFact ADD COLUMN cust_name varchar(20)
DEFAULT (SELECT name FROM public.custDim WHERE (custDim.cid = orderFact.cid))
SET USING (SELECT name FROM public.custDim WHERE (custDim.cid = orderFact.cid));
DEFAULT USING 支持与 SET USING 相同的表达式,并受到相同的限制。
通常,DEFAULT 和 SET USING 支持相同的表达式。这些包括:
以下限制适用于 DEFAULT 和 SET USING 表达式:
返回值数据类型必须与列数据类型匹配,或强制转换为列数据类型。
表达式必须返回一个符合列边界的值。例如,定义为 VARCHAR(1)
的列不能设置为默认字符串 abc
。
在临时表中,DEFAULT 和 SET USING 不支持子查询。如果尝试创建一个临时表,其中 DEFAULT 或 SET USING 使用子查询表达式,Vertica 会返回错误。
一个列的 SET USING 表达式不能在同一个表中指定另一个也使用 SET USING 设置其值的列。同样,一个列的 DEFAULT 表达式不能在同一个表中指定另一个也使用 DEFAULT 设置其值的列,或者其值自动设置为序列的列。但是,一个列的 SET USING 表达式可以指定另一个使用 DEFAULT 设置其值的列。
NULL
,因为它仅在最初将 SET USING 列设置为 NULL
的加载操作上设置。
DEFAULT 和 SET USING 表达式仅支持一个 SELECT 语句;尝试在该表达式中包含多个 SELECT 语句会返回错误。例如,给定表 t1
:
=> SELECT * FROM t1;
a | b
---+---------
1 | hello
2 | world
(2 rows)
尝试使用以下 DEFAULT 表达式创建表 t2
会返回错误:
=> CREATE TABLE t2 (aa int, bb varchar(30) DEFAULT (SELECT 'I said ')||(SELECT b FROM t1 where t1.a = t2.aa));
ERROR 9745: Expressions with multiple SELECT statements cannot be used in 'set using' query definitions
如果 SET USING 或 DEFAULT 查询表达式联接两个同名的列,则列名称必须包含它们的表名称。否则,Vertica 假定两个列都引用维度表,并且谓词的计算结果始终为 true。
例如,表 orderFact 和 custDim 都包含 cid 列。扁平表 orderFact 使用 SET USING 查询表达式定义列 cust_name。由于查询谓词引用两个表中的 cid 列,因此列名称是完全限定的:
=> CREATE TABLE public.orderFact
(
...
cid int REFERENCES public.custDim(cid),
cust_name varchar(20) SET USING (
SELECT name FROM public.custDim WHERE (custDIM.cid = orderFact.cid)),
...
)
创建表 t
,其中包含两个列 date
和 state
,并插入一行数据:
=> CREATE TABLE t (date DATE, state VARCHAR(2));
CREATE TABLE
=> INSERT INTO t VALUES (CURRENT_DATE, 'MA');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMMIT
SELECT * FROM t;
date | state
------------+-------
2017-12-28 | MA
(1 row)
使用 ALTER TABLE 添加第三个列,它从列 date
中提取整数月份值:
=> ALTER TABLE t ADD COLUMN month INTEGER DEFAULT date_part('month', date);
ALTER TABLE
当查询表 t
时,Vertica 会在列 date
中返回月份数:
=> SELECT * FROM t;
date | state | month
------------+-------+-------
2017-12-28 | MA | 12
(1 row)
通过从 date
中减去 30 天来更新表 t
:
=> UPDATE t SET date = date-30;
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM t;
date | state | month
------------+-------+-------
2017-11-28 | MA | 12
(1 row)
month
中的值保持不变。
从列 date
刷新 month
中的默认值:
=> UPDATE t SET month=DEFAULT;
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> SELECT * FROM t;
date | state | month
------------+-------+-------
2017-11-28 | MA | 11
(1 row)
本示例显示了添加了两个整数值的用户定义的标量函数。该函数名为 add2ints
,采用两个实参。
开发和部署函数,如标量函数 (UDSF) 中所述。
创建示例表 t1
,其中包含两个整数列:
=> CREATE TABLE t1 ( x int, y int );
CREATE TABLE
在 t1 中插入一些值:
=> insert into t1 values (1,2);
OUTPUT
--------
1
(1 row)
=> insert into t1 values (3,4);
OUTPUT
--------
1
(1 row)
使用 ALTER TABLE 向 t1
中添加列,其默认列值派生自 UDSF add2ints
:
alter table t1 add column z int default add2ints(x,y);
ALTER TABLE
列出新列:
select z from t1;
z
----
3
7
(2 rows)
定义表 t1
和 t2
。列 t2.b
被定义为通过其 SET USING 子句中的查询从列 t1.b
获取其数据:
=> CREATE TABLE t1 (a INT PRIMARY KEY ENABLED, b INT);
CREATE TABLE
=> CREATE TABLE t2 (a INT, alpha VARCHAR(10),
b INT SET USING (SELECT t1.b FROM t1 WHERE t1.a=t2.a))
ORDER BY a SEGMENTED BY HASH(a) ALL NODES;
CREATE TABLE
表 t2
的定义包括排除 SET USING 列 b
的 SEGMENTED BY 和 ORDER BY 子句。如果省略这些子句,Vertica 会为此表创建一个自动投影,在其 SEGMENTED BY 和 ORDER BY 子句中指定列 b
。在任何投影的分段或排序顺序中包含 SET USING 列会阻止 REFRESH_COLUMNS 函数填充此列。相反,它返回一个错误。
有关此限制和其他限制的详细信息,请参阅 REFRESH_COLUMNS。
使用数据填充表:
=> INSERT INTO t1 VALUES(1,11),(2,22),(3,33),(4,44);
=> INSERT INTO t2 VALUES (1,'aa'),(2,'bb');
=> COMMIT;
COMMIT
查看表 t2
中的数据:SET USING 列 b
中的列为空,等待调用 Vertica 函数 REFRESH_COLUMNS:
=> SELECT * FROM t2;
a | alpha | b
---+-------+---
1 | aa |
2 | bb |
(2 rows)
通过调用 REFRESH_COLUMNS 函数刷新表 t2
中的列数据:
=> SELECT REFRESH_COLUMNS ('t2','b', 'REBUILD');
REFRESH_COLUMNS
---------------------------
refresh_columns completed
(1 row)
在此示例中,使用可选实参 REBUILD 调用 REFRESH_COLUMNS。此实参指定替换 SET USING 列 b
中的所有数据。对任何新的 SET USING 列使用 REBUILD 调用 REFRESH_COLUMNS 通常是一种很好的做法。有关详细信息,请参阅REFRESH_COLUMNS。
查看刷新后的列 b
中的数据,其数据是从表 t1
中获取的,如列的 SET USING 查询中指定的那样:
=> SELECT * FROM t2 ORDER BY a;
a | alpha | b
---+-------+----
1 | aa | 11
2 | bb | 22
(2 rows)
DEFAULT 和 SET USING 表达式支持可以从其他表中获取值的子查询,并将这些子查询与当前表中的值结合使用来计算列值。以下示例将列 gmt_delivery_time
添加到事实表 customer_orders
中。该列指定一个 DEFAULT 表达式来设置新列中的值,如下所示:
调用元函数 NEW_TIME,它执行以下任务:
使用 customer_orders
中的客户密钥查询 customers
维度表以获得客户时区。
使用查询到的时区数据将本地交付时间转换为 GMT。
使用转换后的值填充 gmt_delivery_time
列。
=> CREATE TABLE public.customers(
customer_key int,
customer_name varchar(64),
customer_address varchar(64),
customer_tz varchar(5),
...);
=> CREATE TABLE public.customer_orders(
customer_key int,
order_number int,
product_key int,
product_version int,
quantity_ordered int,
store_key int,
date_ordered date,
date_shipped date,
expected_delivery_date date,
local_delivery_time timestamptz,
...);
=> ALTER TABLE customer_orders ADD COLUMN gmt_delivery_time timestamp
DEFAULT NEW_TIME(customer_orders.local_delivery_time,
(SELECT c.customer_tz FROM customers c WHERE (c.customer_key = customer_orders.customer_key)),
'GMT');
可以使用
ALTER TABLE
修改表的定义,以响应不断变化的数据库架构要求。更改表定义通常比在临时表中暂存数据更有效,它消耗的资源和存储空间更少。
对于列级别更改,请参阅管理表列。
有关更改和重新组织表分区的详细信息,请参阅对现有表数据进行分区。
可以使用 ALTER TABLE..ADD COLUMN 将列添加到持久表:
ALTER TABLE
...
ADD COLUMN [IF NOT EXISTS] column datatype
[column‑constraint]
[ENCODING encoding‑type]
[PROJECTIONS (projections‑list) | ALL PROJECTIONS ]
当使用 ADD COLUMN 更改表时,Vertica 会对该表采用 O 锁,直到操作完成。该锁可防止 DELETE、UPDATE、INSERT 和 COPY 语句访问表。该锁还会阻止在 SERIALIZABLE 隔离级别发出的 SELECT 语句,直到操作完成。
您可以在节点下线时添加列。
向表添加列时,Vertica 会自动将该列添加到该表的 超投影。ADD..COLUMN 子句还可以使用以下选项之一指定将列添加到一个或多个非超投影:
PROJECTIONS (projections-list
):将新列添加到该表的一个或多个投影,以投影基本名称的逗号分隔列表形式指定。Vertica 会将该列添加到每个投影的所有伙伴实例。投影列表不能包含具有预聚合数据的投影,例如实时聚合投影;否则,Vertica 会回退 ALTER TABLE 语句。
ALL PROJECTIONS
会将列添加到该表的所有投影,不包括具有预聚合数据的投影。
例如,store_orders
表有两个投影:超投影 store_orders_super
和用户创建的投影 store_orders_p
。以下 ALTER TABLE..ADD COLUMN 语句将列 expected_ship_date
添加到 store_orders
表。由于该语句省略了 PROJECTIONS
选项,因此 Vertica 仅将该列添加到表的超投影:
=> ALTER TABLE public.store_orders ADD COLUMN expected_ship_date date;
ALTER TABLE
=> SELECT projection_column_name, projection_name FROM projection_columns WHERE table_name ILIKE 'store_orders'
ORDER BY projection_name , projection_column_name;
projection_column_name | projection_name
------------------------+--------------------
order_date | store_orders_p_b0
order_no | store_orders_p_b0
ship_date | store_orders_p_b0
order_date | store_orders_p_b1
order_no | store_orders_p_b1
ship_date | store_orders_p_b1
expected_ship_date | store_orders_super
order_date | store_orders_super
order_no | store_orders_super
ship_date | store_orders_super
shipper | store_orders_super
(11 rows)
以下 ALTER TABLE...ADD COLUMN 语句包括 PROJECTIONS 选项。这指定在添加操作中包含投影 store_orders_p
。Vertica 将新列添加到此投影和表的超投影:
=> ALTER TABLE public.store_orders ADD COLUMN delivery_date date PROJECTIONS (store_orders_p);
=> SELECT projection_column_name, projection_name FROM projection_columns WHERE table_name ILIKE 'store_orders'
ORDER BY projection_name, projection_column_name;
projection_column_name | projection_name
------------------------+--------------------
delivery_date | store_orders_p_b0
order_date | store_orders_p_b0
order_no | store_orders_p_b0
ship_date | store_orders_p_b0
delivery_date | store_orders_p_b1
order_date | store_orders_p_b1
order_no | store_orders_p_b1
ship_date | store_orders_p_b1
delivery_date | store_orders_super
expected_ship_date | store_orders_super
order_date | store_orders_super
order_no | store_orders_super
ship_date | store_orders_super
shipper | store_orders_super
(14 rows)
将新列添加到具有关联视图的表时,系统不会更新视图的结果集,即便该视图使用了通配符 (*) 来表示所有表列。要整合新列,您必须重新创建视图。
ALTER TABLE...DROP COLUMN 会删除指定的表列以及与已删除的列对应的 ROS 容器:
ALTER TABLE [schema.]table DROP [ COLUMN ] [IF EXISTS] column [CASCADE | RESTRICT]
删除操作完成后,将恢复自当前时期起备份的数据,但不包含列。从当前时期之前的备份恢复的数据将重新添加表列。由于删除操作会从表中物理清除对象存储和编录定义(表历史记录),AT EPOCH(历史)查询对于已删除的列不返回任何内容。
更改的表保留其对象 ID。
不能删除或更改主键列或参与表分区子句的列。
不能删除任何投影排序顺序中的第一列,或参与投影分段表达式的列。
在企业模式下,所有节点必须都处于启动状态。此限制不适用于 Eon 模式。
不能删除与访问策略关联的列。尝试删除可能会导致以下错误:ERROR 6482: Failed to parse Access Policies for table "t1"
如果要删除的表列具有依赖项,则必须使用 CASCADE 选项来限定 DROP COLUMN 子句。例如,目标列可能会按投影排序顺序进行指定。在这种情况和其他情况下,DROP COLUMN...CASCADE 将通过重组编录定义或删除投影来处理依赖项。在所有情况下,CASCADE 将执行删除列所需的最低水平的重组。
使用 CASCADE 删除具有以下依赖项的列:
您可能要删除被另一列作为其默认值引用的表列。例如,下表定义为具有两个列 a
和 b
,其中 b
将从列 a
获取其默认值。
=> CREATE TABLE x (a int) UNSEGMENTED ALL NODES;
CREATE TABLE
=> ALTER TABLE x ADD COLUMN b int DEFAULT a;
ALTER TABLE
这种情况下,删除列 a
需要执行以下过程:
通过 ALTER COLUMN..DROP DEFAULT 移除默认依赖项:
=> ALTER TABLE x ALTER COLUMN b DROP DEFAULT;
如果以下条件中的一条或两条为 true,则为目标表创建替换超投影:
目标列为表的第一个排序顺序列。如果表没有显式排序顺序,则默认表排序顺序将第一个表列指定为第一个排序顺序列。在这种情况下,新的超投影必须指定排除目标列的排序顺序。
如果将表分段,则在分段表达式中指定目标列。在这种情况下,新的超投影必须指定排除目标列的分段表达式。
假定上一个示例中表 x
具有默认排序顺序 (a,b)。由于列 a
为该表的第一个排序顺序列,因此必须创建对列 b
进行排序的替换超投影:
=> CREATE PROJECTION x_p1 as select * FROM x ORDER BY b UNSEGMENTED ALL NODES;
运行
START_REFRESH
:
=> SELECT START_REFRESH();
START_REFRESH
----------------------------------------
Starting refresh background process.
(1 row)
运行 MAKE_AHM_NOW:
=> SELECT MAKE_AHM_NOW();
MAKE_AHM_NOW
-------------------------------
AHM set (New AHM Epoch: 1231)
(1 row)
删除列:
=> ALTER TABLE x DROP COLUMN a CASCADE;
Vertica 将实施 CASCADE 指令,如下所示:
删除表 x
的原始超投影 (x_super
)。
通过删除列 a
更新替换超投影 x_p1
。
下面的一系列命令成功删除了 BYTEA 数据类型列:
=> CREATE TABLE t (x BYTEA(65000), y BYTEA, z BYTEA(1));
CREATE TABLE
=> ALTER TABLE t DROP COLUMN y;
ALTER TABLE
=> SELECT y FROM t;
ERROR 2624: Column "y" does not exist
=> ALTER TABLE t DROP COLUMN x RESTRICT;
ALTER TABLE
=> SELECT x FROM t;
ERROR 2624: Column "x" does not exist
=> SELECT * FROM t;
z
---
(0 rows)
=> DROP TABLE t CASCADE;
DROP TABLE
下面的一系列命令尝试删除 FLOAT(8) 列,但失败,因为没有足够的投影来维持 K-safety。
=> CREATE TABLE t (x FLOAT(8),y FLOAT(08));
CREATE TABLE
=> ALTER TABLE t DROP COLUMN y RESTRICT;
ALTER TABLE
=> SELECT y FROM t;
ERROR 2624: Column "y" does not exist
=> ALTER TABLE t DROP x CASCADE;
ROLLBACK 2409: Cannot drop any more columns in t
=> DROP TABLE t CASCADE;
ALTER TABLE...ALTER CONSTRAINT
可以启用或禁用主键、唯一和检查约束的强制实施。必须使用关键字 ENABLED
或 DISABLED
来限定此子句:
ENABLED
强制实施指定的约束。
DISABLED
禁用指定约束的强制实施。
例如:
ALTER TABLE public.new_sales ALTER CONSTRAINT C_PRIMARY ENABLED;
有关详细信息,请参阅约束强制执行。
ALTER TABLE...RENAME TO
重命名一个或多个表。重命名的表保留其原始 OID。
可以通过提供两个逗号分隔的列表来重命名多个表。Vertica 根据两个列表中的顺序映射这些名称。只有第一个列表可以使用架构限定表名。例如:
=> ALTER TABLE S1.T1, S1.T2 RENAME TO U1, U2;
RENAME TO
参数将以原子方式应用:重命名所有表,或不重命名任何表。例如,如果要重命名的表数量与新名称数量不匹配,则所有表都不会被重命名。
可以使用 ALTER TABLE...RENAME TO
在同一架构内交换表,而无需实际移动数据。不能跨架构交换表。
以下示例通过中间表 temp
交换表 T1
和 T2
中的数据:
t1
到 temp
t2
到 t1
temp
到 t2
=> DROP TABLE IF EXISTS temp, t1, t2;
DROP TABLE
=> CREATE TABLE t1 (original_name varchar(24));
CREATE TABLE
=> CREATE TABLE t2 (original_name varchar(24));
CREATE TABLE
=> INSERT INTO t1 VALUES ('original name t1');
OUTPUT
--------
1
(1 row)
=> INSERT INTO t2 VALUES ('original name t2');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> ALTER TABLE t1, t2, temp RENAME TO temp, t1, t2;
ALTER TABLE
=> SELECT * FROM t1, t2;
original_name | original_name
------------------+------------------
original name t2 | original name t1
(1 row)
ALTER TABLE...SET SCHEMA
可以将表从一个架构移动到另一个架构。Vertica 会自动将锚定到源表的所有投影移动到目标架构。它还会将所有 IDENTITY
和 AUTO_INCREMENT
列移动到目标架构。
在架构之间移动表需要对当前架构具有 USAGE
权限,并对目标架构具有 CREATE
权限。一次只能在架构之间移动一个表。不能在架构之间移动临时表。
如果新架构中已经存在要移动的同名表或任何投影,该语句将回退并且不会移动该表或任何投影。要解决名称冲突:
重命名要移动的任何冲突表或投影。
再次运行
ALTER TABLE...SET SCHEMA
。
以下示例将表 T1
从架构 S1
移至架构 S2
。锚定到表 T1
的所有投影都会自动移至架构 S2
:
=> ALTER TABLE S1.T1 SET SCHEMA S2;
作为超级用户或表所有者,可以使用
ALTER TABLE...OWNER TO
重新分配表所有权,如下所示:
ALTER TABLE [schema.]table-name OWNER TO owner-name
将表从一个架构移动到另一个架构时,更改表所有权很有用。当表所有者离职或更换工作职责时,所有权重新分配也很有用。由于可以更改表所有者,因此表无需完全重写,进而可以避免生产率下降。
更改表所有权会自动导致以下更改:
由原始所有者对表进行的授权将被删除,对该表的所有现有权限都将从前一个所有者那里撤销。表所有权的更改对架构权限没有影响。
从属 IDENTITY/AUTO-INCREMENT
序列的所有权随表一起转移。但是,对于使用
CREATE SEQUENCE
创建的指定序列,所有权不会更改。要转移这些序列的所有权,请使用
ALTER SEQUENCE
。
新的表所有权会传播到其投影。
在以下示例中,用户 Bob 连接到数据库,查找表,然后将表 t33
的所有权从自己转移到用户 Alice。
=> \c - Bob
You are now connected as user "Bob".
=> \d
Schema | Name | Kind | Owner | Comment
--------+--------+-------+---------+---------
public | applog | table | dbadmin |
public | t33 | table | Bob |
(2 rows)
=> ALTER TABLE t33 OWNER TO Alice;
ALTER TABLE
当 Bob 再次查找数据库表时,他不再看到表 t33
:
=> \d List of tables
List of tables
Schema | Name | Kind | Owner | Comment
--------+--------+-------+---------+---------
public | applog | table | dbadmin |
(1 row)
当用户 Alice 连接到数据库并查找表时,她将看到她是表 t33
的所有者。
=> \c - Alice
You are now connected as user "Alice".
=> \d
List of tables
Schema | Name | Kind | Owner | Comment
--------+------+-------+-------+---------
public | t33 | table | Alice |
(2 rows)
Alice 或超级用户可以将表所有权转移回 Bob。在以下情况下,超级用户执行转移操作。
=> \c - dbadmin
You are now connected as user "dbadmin".
=> ALTER TABLE t33 OWNER TO Bob;
ALTER TABLE
=> \d
List of tables
Schema | Name | Kind | Owner | Comment
--------+----------+-------+---------+---------
public | applog | table | dbadmin |
public | comments | table | dbadmin |
public | t33 | table | Bob |
s1 | t1 | table | User1 |
(4 rows)
也可以查询系统表
V_CATALOG.TABLES
以查看表和所有者信息。注意,所有权更改不会更改表 ID。
在下面的一系列命令中,超级用户将表所有权更改回 Alice,并查询系统表 TABLES
。
=> ALTER TABLE t33 OWNER TO Alice;
ALTER TABLE
=> SELECT table_schema_id, table_schema, table_id, table_name, owner_id, owner_name FROM tables;
table_schema_id | table_schema | table_id | table_name | owner_id | owner_name
-------------------+--------------+-------------------+------------+-------------------+------------
45035996273704968 | public | 45035996273713634 | applog | 45035996273704962 | dbadmin
45035996273704968 | public | 45035996273724496 | comments | 45035996273704962 | dbadmin
45035996273730528 | s1 | 45035996273730548 | t1 | 45035996273730516 | User1
45035996273704968 | public | 45035996273795846 | t33 | 45035996273724576 | Alice
(5 rows)
现在,超级用户将表所有权更改回 Bob,并再次查询 TABLES
表。除了 owner_name
行从 Alice 更改为 Bob,没有其他改变。
=> ALTER TABLE t33 OWNER TO Bob;
ALTER TABLE
=> SELECT table_schema_id, table_schema, table_id, table_name, owner_id, owner_name FROM tables;
table_schema_id | table_schema | table_id | table_name | owner_id | owner_name
-------------------+--------------+-------------------+------------+-------------------+------------
45035996273704968 | public | 45035996273713634 | applog | 45035996273704962 | dbadmin
45035996273704968 | public | 45035996273724496 | comments | 45035996273704962 | dbadmin
45035996273730528 | s1 | 45035996273730548 | t1 | 45035996273730516 | User1
45035996273704968 | public | 45035996273793876 | foo | 45035996273724576 | Alice
45035996273704968 | public | 45035996273795846 | t33 | 45035996273714428 | Bob
(5 rows)
序列可用于将列的默认值设置为连续整数值。序列保证了唯一性,并避免了约束强制执行问题和开销。序列对于主键列特别有用。
虽然序列对象值保证唯一,但不能保证它们连续,因此您可能会将返回的值解释为缺失。例如,两个节点可以用不同的速率递增序列。具有较大处理负载的节点将递增序列,但在具有较小处理负载的节点上递增的值不连续。
Vertica 支持以下序列类型:
命名序列是以升序或降序顺序生成唯一编号的数据库对象。命名序列是通过
CREATE SEQUENCE
语句独立定义的,并且独立于引用它们的表进行管理。一个表可以将一个或多个列的默认值设置为命名序列。
AUTO_INCREMENT/IDENTITY 列序列:列约束 AUTO_INCREMENT
和 IDENTITY
是同义词,用于指定在添加新行时递增或递减列的值。此序列类型与表相关,不会独立保留。一个表只能包含一个 AUTO_INCREMENT
或 IDENTITY
列。
下表列出了两种序列类型之间的差异:
命名序列是由
CREATE SEQUENCE
定义的序列。虽然可以将表列的值设置为命名序列,但与 AUTO_INCREMENT 和 IDENTITY 序列不同,命名序列独立于表而存在。
当应用程序要求表或表达式中使用唯一标识符时,最常使用命名序列。在命名序列返回一个值后,它永远不会在同一会话中再次返回相同的值。
使用
CREATE SEQUENCE
创建命名序列。该语句只需提供序列名称即可;所有其他参数均为可选参数。要创建序列,用户必须对包含该序列的架构具有 CREATE 权限。
以下示例将创建一个起始值为 100 的升序命名序列 my_seq
:
=> CREATE SEQUENCE my_seq START 100;
CREATE SEQUENCE
在创建命名序列对象时,还可以通过设置其 INCREMENT
参数指定其递增量或递减量值。如果省略此参数(如上一示例中所示),则默认值设置为 1。
可以通过对序列调用函数
NEXTVAL
来递增或递减序列(直接在序列本身上递增或递减,或通过向引用该序列的表中添加新行来间接地递增或递减)。对新序列第一次调用时,NEXTVAL
将该序列初始化为其起始值。Vertica 还会为序列创建缓存。随后对序列调用 NEXTVAL
会递增其值。
下面对 NEXTVAL
的调用会将新 my_seq
序列初始化为 100:
=> SELECT NEXTVAL('my_seq');
nextval
---------
100
(1 row)
可以通过对序列调用
CURRVAL
来获取该序列的当前值。例如:
=> SELECT CURRVAL('my_seq');
CURRVAL
---------
100
(1 row)
CURRVAL
在以下情况下将返回错误:如果对尚未由 NEXTVAL
初始化的新序列或对尚未在新会话中访问的现有序列调用它。例如:
=> CREATE SEQUENCE seq2;
CREATE SEQUENCE
=> SELECT currval('seq2');
ERROR 4700: Sequence seq2 has not been accessed in the session
表可以将任何列的默认值设置为命名序列。表创建者必须具有以下权限:SELECT(对于序列)、USAGE(对于其架构)。
在以下示例中,列 id
从命名序列 my_seq
获取其默认值:
=> CREATE TABLE customer(id INTEGER DEFAULT my_seq.NEXTVAL,
lname VARCHAR(25),
fname VARCHAR(25),
membership_card INTEGER
);
对于插入到表 customer
中的每一行,该序列调用 NEXTVAL
函数来设置 id
列的值。例如:
=> INSERT INTO customer VALUES (default, 'Carr', 'Mary', 87432);
=> INSERT INTO customer VALUES (default, 'Diem', 'Nga', 87433);
=> COMMIT;
对于每一行,插入操作都会对序列 my_seq
调用 NEXTVAL
,这会将该序列递增至 101 和 102,并将 id
列设置为这些值:
=> SELECT * FROM customer;
id | lname | fname | membership_card
-----+-------+-------+-----------------
101 | Carr | Mary | 87432
102 | Diem | Nga | 87433
(1 row)
当创建命名序列时,其 CACHE
参数决定了每个节点在会话期间所保留的序列值的数量。默认缓存值为 250K,因此每个节点在每个会话期间为每个序列保留 250,000 个值。此默认缓存大小为大规模插入或复制操作提供了一种有效的方式。
如果将序列缓存设置为较低的数字,则节点可能会更频繁地请求一组新的缓存值。提供新缓存时,Vertica 必须锁定编录。在 Vertica 释放锁之前,其他数据库活动(例如表插入)会被阻止,这将对整体性能产生不利影响。
当一个新会话启动时,节点缓存最初为空。默认情况下,启动程序节点会为群集中的所有节点请求和保留缓存。可以通过将配置参数 ClusterSequenceCacheMode
设置为 0 来更改此默认值,以便每个节点都请求其自己的缓存。
有关 Vertica 如何请求缓存并在群集中的所有节点之间分发缓存的信息,请参阅序列缓存。
Vertica 在所有节点之间分发会话。群集节点第一次对序列调用 NEXTVAL 函数以递增(或递减)其值时,该节点会请求其自己的序列值缓存。然后,该节点会为当前会话维护该缓存。当其他节点调用 NEXTVAL 时,它们也会创建和维护其自己的序列值缓存。
在会话期间,节点会以不同的频率独立调用 NEXTVAL。每个节点都使用其自己的缓存来填充序列。所有序列值都保证唯一,但是可能与另一个节点上执行的 NEXTVAL 语句出现乱序。因此,序列值通常不连续。
在所有情况下,每行仅递增序列一次。因此,如果多个列引用了同一序列,则 NEXTVAL 会将该行中的所有列设置为相同的值。这适用于联接表的行。
Vertica 按如下方式计算序列的当前值:
在每个语句结束时,会话中使用的所有序列的状态都会返回到启动程序节点。
启动程序节点计算每个序列在所有节点上的所有状态下的最大
CURRVAL
。
此最大值将用作后续语句中的 CURRVAL
,直到调用另一个 NEXTVAL。
在下列情况下,缓存中的序列值可能会丢失:
如果在调用 NEXTVAL 后某条语句失败了(因此耗用了缓存中的一个序列值),则值就会丢失。
如果连接断开了(如删除了会话),则缓存中尚未通过 NEXTVAL 返回的所有剩余值都会丢失。
一个或多个节点尚未用完其当前缓存分配时,启动程序节点便为每个节点分发新的缓存块。有关此场景的信息,请参阅序列缓存。
可以使用 ALTER SEQUENCE...RESTART 恢复丢失的序列值,这会在下一个会话中将序列重置为指定的值。
ALTER SEQUENCE
可以通过两种方式更改命名序列:
重置控制序列行为的参数,例如其起始值或最小值和最大值的范围。这些更改仅在您启动新数据库会话时生效。
重置序列名称、架构或所有权。这些更改会立即生效。
ALTER SEQUENCE
语句不能同时进行这两种类型的更改。
ALTER SEQUENCE
可以通过以下参数更改一个或多个序列属性:
这些更改仅在您启动新数据库会话时生效。例如,如果创建一个从 10 开始并按 1(默认值)递增的命名序列 my_sequence
,则序列每次调用 NEXTVAL
都会按 1 递增其值:
=> CREATE SEQUENCE my_sequence START 10;
=> SELECT NEXTVAL('my_sequence');
nextval
---------
10
(1 row)
=> SELECT NEXTVAL('my_sequence');
nextval
---------
11
(1 row)
以下 ALTER SEQUENCE
语句指定重新启动序列时从 50 开始:
=>ALTER SEQUENCE my_sequence RESTART WITH 50;
但是,此更改在当前会话中不起作用。对 NEXTVAL
的下一次调用会将序列递增至 12:
=> SELECT NEXTVAL('my_sequence');
NEXTVAL
---------
12
(1 row)
只有在启动新的数据库会话后,该序列才会在 50 处重新启动:
=> \q
$ vsql
Welcome to vsql, the Vertica Analytic Database interactive terminal.
=> SELECT NEXTVAL('my_sequence');
NEXTVAL
---------
50
(1 row)
可以使用 ALTER SEQUENCE
对命名序列进行以下更改:
重命名它。
将其移动到另一个架构。
重新分配所有权。
这些更改中的每个更改都需要使用单独的 ALTER SEQUENCE
语句。这些更改会立即生效。
例如,以下语句将序列从 my_seq
重命名为 serial
:
=> ALTER SEQUENCE s1.my_seq RENAME TO s1.serial;
以下语句将序列 s1.serial
移动到架构 s2
中:
=> ALTER SEQUENCE s1.my_seq SET SCHEMA TO s2;
以下语句将 s2.serial
的所有权重新分配给另一个用户:
=> ALTER SEQUENCE s2.serial OWNER TO bertie;
使用
DROP SEQUENCE
移除命名序列。例如:
=> DROP SEQUENCE my_sequence;
如果满足以下条件之一,则不能删除序列:
其他对象依赖该序列。 DROP SEQUENCE
不支持级联操作。
列的 DEFAULT
表达式引用该序列。在删除该序列之前,必须移除对它的所有列引用。
AUTO_INCREMENT 和 IDENTITY 约束是将列与序列相关联的同义词。当添加新行时,此序列会自动递增列值。
在表中定义 AUTO_INCREMENT 或 IDENTITY 列,如下所示:
CREATE TABLE table-name...
(column-name
{AUTO_INCREMENT | IDENTITY}
( [ cache-size | start, increment [, cache-size ] ] )
AUTO_INCREMENT/IDENTITY 序列由定义它们的表拥有,并且不存在于该表之外。与命名序列不同,您不能使用 ALTER SEQUENCE 管理 AUTO_INCREMENT/IDENTITY 序列。例如,您不能独立于其表更改 AUTO_INCREMENT/IDENTITY 序列的架构。如果将表移动到另一个架构,序列会自动随之移动。
可以通过调用 LAST_INSERT_ID 函数获取为 AUTO_INCREMENT/IDENTITY 序列生成的最后一个值。
以下限制适用于 AUTO_INCREMENT/IDENTITY 列:
一个表只能包含一个 AUTO_INCREMENT/IDENTITY 列。
即使未提交尝试向表中插入值的事务,AUTO_INCREMENT/IDENTITY 值也绝对不会回退。
不能更改 AUTO_INCREMENT/IDENTITY 列的值。
具有 AUTO_INCREMENT/IDENTITY 列的表还可以包含一个或多个设置为命名序列的列。
以下示例显示了如何使用 IDENTITY 列约束创建一个包含 ID 列的表。ID 列的初始值为 1。每次插入一行时,该列都将以 1 为增量递增。
创建名为 Premium_Customer
的表:
=> CREATE TABLE Premium_Customer(
ID IDENTITY(1,1),
lname VARCHAR(25),
fname VARCHAR(25),
store_membership_card INTEGER
);
=> INSERT INTO Premium_Customer (lname, fname, store_membership_card )
VALUES ('Gupta', 'Saleem', 475987);
IDENTITY 列的种子值为 1,它指定加载到表中的第一行的值,增量为 1,它指定添加到上一行的 IDENTITY 值的值。
确认添加的行并查看 ID 值:
=> SELECT * FROM Premium_Customer;
ID | lname | fname | store_membership_card
----+-------+--------+-----------------------
1 | Gupta | Saleem | 475987
(1 row)
增加一个行:
=> INSERT INTO Premium_Customer (lname, fname, store_membership_card)
VALUES ('Lee', 'Chen', 598742);
调用 Vertica 函数 LAST_INSERT_ID。该函数将返回值 2,因为您之前插入了一个新客户 (Chen Lee),并且每次插入一行时,此值都会递增:
=> SELECT LAST_INSERT_ID();
last_insert_id
----------------
2
(1 row)
查看 Premium_Customer
表中的所有 ID 值:
=> SELECT * FROM Premium_Customer;
ID | lname | fname | store_membership_card
----+-------+--------+-----------------------
1 | Gupta | Saleem | 475987
2 | Lee | Chen | 598742
(2 rows)
接下来的三个示例说明使用 IDENTITY 实参的三种有效方法。这些示例对 AUTO_INCREMENT 实参也有效。
第一个示例使用缓存 100,以及起始值 (1) 和增量值 (1) 的默认值:
=> CREATE TABLE t1(x IDENTITY(100), y INT);
第二个示例将起始值和增量值指定为 1,并将默认缓存值指定为 250,000:
=> CREATE TABLE t2(y IDENTITY(1,1), x INT);
第三个示例将起始值和增量值指定为 1,并将缓存值指定为 100:
=> CREATE TABLE t3(z IDENTITY(1,1,100), zx INT);
缓存对于所有序列类型都是类似的:命名序列、标识序列和自动递增序列。为了在群集中的节点之间为给定序列分配缓存,Vertica 使用以下过程。
默认情况下,当会话开始时,群集启动程序节点会为其自身和群集中的其他节点请求缓存。
启动程序节点会在分发执行计划时将缓存一并分发给其他节点。
由于启动程序节点会为所有节点请求缓存,因此只有启动程序会在请求缓存时锁定全局编录。
这种方法最适合处理大型 INSERT-SELECT 和 COPY 操作。下图显示了启动程序如何在三节点群集中为命名序列请求和分发缓存,其中将该序列的缓存设置为 250 K:
各节点在不同的时间用尽缓存。当执行相同的查询时,节点会根据需要单独请求其他缓存。
对于同一个会话中的新查询,如果启动程序使用其所有缓存执行上一个查询执行,则该启动程序可能具有空缓存。在这种情况下,启动程序会为所有节点请求缓存。
可以通过将配置参数 ClusterSequenceCacheMode
设置为 0(禁用)来更改节点获得序列缓存的方式。将此参数设置为 0 时,群集中的所有节点都会请求各自的缓存和编录锁。但是,一开始执行大型 INSERT-SELECT 和 COPY 操作时,如果所有节点的缓存为空,每个节点会同时请求缓存。这些多个请求会导致全局编录上同时出现多个锁定,从而对性能产生不利影响。因此,ClusterSequenceCacheMode
应保持设置为其默认值 1(启用)。
以下示例比较了 ClusterSequenceCacheMode
的不同设置如何影响 Vertica 管理序列缓存的方式。该示例假定一个三节点群集,每个节点 250 K 缓存(默认值),序列 ID 值递增量为 1。
MERGE
语句可以根据与源数据集联接的结果对目标表执行更新和插入操作。联接只能将源行与一个目标行相匹配;否则,Vertica 会返回错误。
MERGE
采用以下语法:
MERGE INTO target‑table USING source‑dataset ON join-condition
matching‑clause[ matching‑clause ]
合并操作至少包含三个组成部分:
要对其执行更新和插入操作的目标表。 MERGE
对目标表采用 X(互斥)锁,直到合并操作完成。
联接到另一个数据集,即以下数据集之一:表、视图或子查询结果集。
一个或两个匹配子句:
WHEN MATCHED THEN UPDATE SET
和
WHEN NOT MATCHED THEN INSERT
。
在此示例中,合并操作涉及两个表:
visits_daily
记录每日餐厅流量,并随着每次顾客到访而更新。此表中的数据每 24 小时刷新一次。
visits_history
存储顾客到访各个餐厅的历史记录(无限期累计)。
每天晚上,您都会将 visits_daily
的每日到访计数合并到 visits_history
。合并操作通过两种方式修改目标表:
更新现有顾客数据。
为首次到访的顾客插入新数据行。
一个 MERGE
语句将这两项操作作为单个 (upsert) 事务来执行。
源表和目标表 visits_daily
和 visits_history
定义如下:
CREATE TABLE public.visits_daily
(
customer_id int,
location_name varchar(20),
visit_time time(0) DEFAULT (now())::timetz(6)
);
CREATE TABLE public.visits_history
(
customer_id int,
location_name varchar(20),
visit_count int
);
表 visits_history
包含三个顾客行,他们分别到访了 Etoile 和 LaRosa 两家餐厅:
=> SELECT * FROM visits_history ORDER BY customer_id, location_name;
customer_id | location_name | visit_count
-------------+---------------+-------------
1001 | Etoile | 2
1002 | La Rosa | 4
1004 | Etoile | 1
(3 rows)
到营业结束时,表 visits_daily
包含三行餐厅到访数据:
=> SELECT * FROM visits_daily ORDER BY customer_id, location_name;
customer_id | location_name | visit_time
-------------+---------------+------------
1001 | Etoile | 18:19:29
1003 | Lux Cafe | 08:07:00
1004 | La Rosa | 11:49:20
(3 rows)
以下 MERGE
语句将 visits_daily
数据合并到 visits_history
中:
对于匹配的顾客,MERGE
会更新出现计数。
对于不匹配的顾客,MERGE
会插入新行。
=> MERGE INTO visits_history h USING visits_daily d
ON (h.customer_id=d.customer_id AND h.location_name=d.location_name)
WHEN MATCHED THEN UPDATE SET visit_count = h.visit_count + 1
WHEN NOT MATCHED THEN INSERT (customer_id, location_name, visit_count)
VALUES (d.customer_id, d.location_name, 1);
OUTPUT
--------
3
(1 row)
MERGE
返回已更新和插入的行数。在本例中,返回值指定三个更新和插入项:
顾客 1001
第三次到访 Etoile
新顾客 1003
首次到访新餐厅 Lux Cafe
客户 1004
首次到访 La Rosa
如果现在查询表 visits_history
,结果集会显示合并(更新和插入)的数据。更新行和新行高亮显示:
MERGE
操作将目标表联接到以下数据源之一:
另一个表
视图
子查询结果集
可以将一个表中的数据合并到另一个表中,如下所示:
MERGE INTO target‑table USING { source‑table | source‑view } join-condition
matching‑clause[ matching‑clause ]
如果指定视图,则 Vertica 会将视图名称扩展到其封装的查询,并将结果集用作合并源数据。
例如,VMart 表 public.product_dimension
包含当前和停产的产品。可以将所有停产的产品移动到单独的表 public.product_dimension_discontinued
中,如下所示:
=> CREATE TABLE public.product_dimension_discontinued (
product_key int,
product_version int,
sku_number char(32),
category_description char(32),
product_description varchar(128));
=> MERGE INTO product_dimension_discontinued tgt
USING product_dimension src ON tgt.product_key = src.product_key
AND tgt.product_version = src.product_version
WHEN NOT MATCHED AND src.discontinued_flag='1' THEN INSERT VALUES
(src.product_key,
src.product_version,
src.sku_number,
src.category_description,
src.product_description);
OUTPUT
--------
1186
(1 row)
源表 product_dimension
使用 product_key
和 product_version
两列来标识唯一产品。MERGE
语句在这些列上联接源表和目标表,以便返回不匹配行的单个实例。WHEN NOT MATCHED
子句包含一个筛选器 (src.discontinued_flag='1'
),它将结果集缩减为仅包含停产的产品。剩余的行将插入到目标表 product_dimension_discontinued
。
可以将子查询返回的结果集合并到表中,如下所示:
MERGE INTO target‑table USING (subquery) sq-alias join-condition
matching‑clause[ matching‑clause ]
例如,VMart 表 public.product_dimension
定义如下(DDL 截断):
CREATE TABLE public.product_dimension
(
product_key int NOT NULL,
product_version int NOT NULL,
product_description varchar(128),
sku_number char(32),
...
)
ALTER TABLE public.product_dimension
ADD CONSTRAINT C_PRIMARY PRIMARY KEY (product_key, product_version) DISABLED;
product_key
和 product_version
列构成表的主键。可以修改此表,使其包含连接这两列的值的单个列。此列可用于唯一标识每个产品,同时还保留 product_key
和 product_version
的原始值。
可以使用查询另外两列的 MERGE
语句填充新列:
=> ALTER TABLE public.product_dimension ADD COLUMN product_ID numeric(8,2);
ALTER TABLE
=> MERGE INTO product_dimension tgt
USING (SELECT (product_key||'.0'||product_version)::numeric(8,2) AS pid, sku_number
FROM product_dimension) src
ON tgt.product_key||'.0'||product_version::numeric=src.pid
WHEN MATCHED THEN UPDATE SET product_ID = src.pid;
OUTPUT
--------
60000
(1 row)
以下查询验证新列值是否对应于 product_key
和 product_version
中的值:
=> SELECT product_ID, product_key, product_version, product_description
FROM product_dimension
WHERE category_description = 'Medical'
AND product_description ILIKE '%diabetes%'
AND discontinued_flag = 1 ORDER BY product_ID;
product_ID | product_key | product_version | product_description
------------+-------------+-----------------+-----------------------------------------
5836.02 | 5836 | 2 | Brand #17487 diabetes blood testing kit
14320.02 | 14320 | 2 | Brand #43046 diabetes blood testing kit
18881.01 | 18881 | 1 | Brand #56743 diabetes blood testing kit
(3 rows)
MERGE
支持以下匹配子句的一个实例:
[WHEN MATCHED THEN UPDATE SET](#WHEN_MATCHED)
[WHEN NOT MATCHED THEN INSERT](#WHEN_NOT_MATCHED)
每个匹配子句都可以指定一个附加筛选器,如更新和插入筛选器中所述。
WHEN MATCHED THEN UPDATE SET
通常使用源表中的数据更新联接到源表的所有目标表行:
WHEN MATCHED [ AND update-filter ] THEN UPDATE
SET { target‑column = expression }[,...]
Vertica 只能对源表的联接列中的唯一值执行联接。如果源表的联接列包含多个匹配值,MERGE
语句将返回运行时错误。
WHEN NOT MATCHED THEN INSERT
WHEN NOT MATCHED THEN INSERT
会为从联接中排除的每个源表行向目标表中插入一个新行:
WHEN NOT MATCHED [ AND insert-filter ] THEN INSERT
[ ( column‑list ) ] VALUES ( values‑list )
column‑list 是目标表中一个或多个目标列的逗号分隔列表,按任意顺序列出。 MERGE
按相同的顺序将 column‑list 列映射到 values‑list 值,并且每个列-值对都必须兼容。如果省略 column‑list,Vertica 会根据表定义中的列顺序将 values‑list 值映射到列。
例如,给定以下源表和目标表定义:
CREATE TABLE t1 (a int, b int, c int);
CREATE TABLE t2 (x int, y int, z int);
以下 WHEN NOT MATCHED
子句在新插入的行中隐式设置目标表列 a
、b
和 c
的值:
MERGE INTO t1 USING t2 ON t1.a=t2.x
WHEN NOT MATCHED THEN INSERT VALUES (t2.x, t2.y, t2.z);
相反,以下 WHEN NOT MATCHED
子句从合并操作中排除列 t1.b
和 t2.y
。WHEN NOT MATCHED
子句显式将目标表和源表中的两组列进行配对: t1.a
对 t2.x
以及 t1.c
对 t2.z
。Vertica 将排除的列 t1.b
设置为 Null:
MERGE INTO t1 USING t2 ON t1.a=t2.x
WHEN NOT MATCHED THEN INSERT (a, c) VALUES (t2.x, t2.z);
MERGE
语句中的每个 WHEN MATCHED
和 WHEN NOT MATCHED
子句都可以选择分别指定更新筛选器和插入筛选器:
WHEN MATCHED AND update-filter THEN UPDATE ...
WHEN NOT MATCHED AND insert-filter THEN INSERT ...
Vertica 还支持用于指定更新和插入筛选器的 Oracle 语法:
WHEN MATCHED THEN UPDATE SET column-updates WHERE update-filter
WHEN NOT MATCHED THEN INSERT column-values WHERE insert-filter
每个筛选器都可以指定多个条件。Vertica 按如下方式处理筛选器:
更新筛选器应用于目标表中由 MERGE
联接返回的匹配行集。对于更新筛选器求值结果为 true 的每一行,Vertica 都会更新指定的列。
插入筛选器应用于从 MERGE
联接中排除的源表行集。对于插入筛选器求值结果为 true 的每一行,Vertica 都会向目标表中添加一个具有指定值的新行。
例如,给定表 t11
和 t22
中的以下数据:
=> SELECT * from t11 ORDER BY pk;
pk | col1 | col2 | SKIP_ME_FLAG
----+------+------+--------------
1 | 2 | 3 | t
2 | 3 | 4 | t
3 | 4 | 5 | f
4 | | 6 | f
5 | 6 | 7 | t
6 | | 8 | f
7 | 8 | | t
(7 rows)
=> SELECT * FROM t22 ORDER BY pk;
pk | col1 | col2
----+------+------
1 | 2 | 4
2 | 4 | 8
3 | 6 |
4 | 8 | 16
(4 rows)
可以使用以下 MERGE
语句将表 t11
中的数据合并到表 t22
中,其中包括更新和插入筛选器:
=> MERGE INTO t22 USING t11 ON ( t11.pk=t22.pk )
WHEN MATCHED
AND t11.SKIP_ME_FLAG=FALSE AND (
COALESCE (t22.col1<>t11.col1, (t22.col1 is null)<>(t11.col1 is null))
)
THEN UPDATE SET col1=t11.col1, col2=t11.col2
WHEN NOT MATCHED
AND t11.SKIP_ME_FLAG=FALSE
THEN INSERT (pk, col1, col2) VALUES (t11.pk, t11.col1, t11.col2);
OUTPUT
--------
3
(1 row)
=> SELECT * FROM t22 ORDER BY pk;
pk | col1 | col2
----+------+------
1 | 2 | 4
2 | 4 | 8
3 | 4 | 5
4 | | 6
6 | | 8
(5 rows)
Vertica 按如下方式使用更新和插入筛选器:
根据更新筛选器条件对所有匹配的行进行求值。Vertica 会更新以下两个条件的求值结果均为 true 的每一行:
源列 t11.SKIP_ME_FLAG
设置为 false。
COALESCE
函数的求值结果为 true。
根据插入筛选器,对源表中所有不匹配的行进行求值。对于列 t11.SKIP_ME_FLAG
设置为 false 的每一行,Vertica 都会在目标表中插入一个新行。
可以通过以下几种方式提高 MERGE
性能:
使用小于目标表的源表。
Vertica 查询优化器会自动选择最佳投影来实施合并操作。良好投影设计策略提供的投影可帮助查询优化器避免额外的排序和数据传输操作,并提高 MERGE
性能。
例如,以下 MERGE
语句片段分别在 tgt.a
和 src.b
列上联接源表和目标表 tgt
和 src
:
=> MERGE INTO tgt USING src ON tgt.a = src.b ...
如果 tgt
和 src
表的投影采用以下投影设计之一(其中输入通过投影 ORDER BY
子句进行预排序),则 Vertica 可以使用局部合并联接:
如果满足以下条件,Vertica 即准备了一个经过优化的查询计划:
MERGE
语句同时包含两个匹配子句
WHEN MATCHED THEN UPDATE SET
和
WHEN NOT MATCHED THEN INSERT
。如果 MERGE
语句仅包含一个匹配子句,那么它使用的是未经优化的查询计划。
MERGE
语句不包括更新和插入筛选器。
目标表联接列具有唯一键或主键约束。此要求不适用于源表联接列。
两个匹配子句指定目标表中的所有列。
两个匹配子句指定相同的源值。
有关评估
EXPLAIN
生成的查询计划的详细信息,请参阅 MERGE 路径。
后面的示例使用一个简单的架构来说明 Vertica 在哪些条件下会为 MERGE
准备优化查询计划,在哪些条件下不会准备该计划:
CREATE TABLE target(a INT PRIMARY KEY, b INT, c INT) ORDER BY b,a;
CREATE TABLE source(a INT, b INT, c INT) ORDER BY b,a;
INSERT INTO target VALUES(1,2,3);
INSERT INTO target VALUES(2,4,7);
INSERT INTO source VALUES(3,4,5);
INSERT INTO source VALUES(4,6,9);
COMMIT;
经过优化的 MERGE 语句
Vertica 可以为以下 MERGE
语句准备一个经过优化的查询计划,原因是:
目标表的联接列 t.a
具有主键约束。
目标表 (a,b,c)
中的所有列都包含在 UPDATE
和 INSERT
子句中。
UPDATE
和 INSERT
子句指定相同的源值:s.a
、s.b
和 s.c
。
MERGE INTO target t USING source s ON t.a = s.a
WHEN MATCHED THEN UPDATE SET a=s.a, b=s.b, c=s.c
WHEN NOT MATCHED THEN INSERT(a,b,c) VALUES(s.a, s.b, s.c);
OUTPUT
--------
2
(1 row)
输出值 2 表示成功,同时也说明了源中更新/插入到目标的行数。
未优化 MERGE 语句
在下一个示例中,MERGE
语句在未经优化的情况下运行,因为 UPDATE/INSERT
子句中的源值不相同。具体来说,UPDATE
子句包括列 s.a
和 s.c
的常数,而 INSERT
子句不包括:
MERGE INTO target t USING source s ON t.a = s.a
WHEN MATCHED THEN UPDATE SET a=s.a + 1, b=s.b, c=s.c - 1
WHEN NOT MATCHED THEN INSERT(a,b,c) VALUES(s.a, s.b, s.c);
为了使前面的 MERGE
语句符合优化条件,请重写该语句,以使 UPDATE
和 INSERT
子句中的源值相同:
MERGE INTO target t USING source s ON t.a = s.a
WHEN MATCHED THEN UPDATE SET a=s.a + 1, b=s.b, c=s.c -1
WHEN NOT MATCHED THEN INSERT(a,b,c) VALUES(s.a + 1, s.b, s.c - 1);
以下限制适用于使用
MERGE
更新和插入表数据。
如果在目标表中启用了主键、唯一键或检查约束以自动强制实施,Vertica 会在您加载新数据时强制实施这些约束。如果发生违规,Vertica 会回滚操作并返回错误。
合并操作中不能指定以下列,否则将返回错误:
Identity/auto-increment 列,或默认值设置为命名序列的列。
Flex 表中的 Vmap 列,例如 __raw__
。
复杂类型的列(ARRAY、SET、ROW)。
Vertica 提供了从表中移除数据的多种方法:
下表汇总了各种数据移除操作之间的差异。
下表可以帮助您确定最适合移除表数据的操作:
Vertica 已针对查询密集型工作负载进行了优化,因此,DELETE 和 UPDATE 查询可能无法达到与其他查询相同的性能水平。DELETE 和 UPDATE 操作必须更新所有投影,因此这些操作只能与最慢的投影一样快。
要提高 DELETE 和 UPDATE 查询的性能,请考虑以下问题:
如果投影包含查询谓词所需的所有列,则投影已针对 DELETE 和 UPDATE 操作进行了优化。通常,对经过优化的投影执行 DML 操作时,速度明显快于未经优化的投影。
例如,假设有以下表和投影:
=> CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER);
=> CREATE PROJECTION p1 (a, b, c) AS SELECT * FROM t ORDER BY a;
=> CREATE PROJECTION p2 (a, c) AS SELECT a, c FROM t ORDER BY c, a;
在以下查询中,p1
和 p2
都符合 DELETE 和 UPDATE 优化条件,因为列 a
可用:
=> DELETE from t WHERE a = 1;
在以下示例中,只有投影 p1
符合 DELETE 和 UPDATE 优化条件,因为 b 列在 p2
中不可用:
=> DELETE from t WHERE b = 1;
为了符合 DELETE 优化条件,在 DELETE 或 UPDATE 语句的 WHERE 子句中引用的所有目标表列都必须位于投影定义中。
例如,以下简单架构包含两个表和三个投影:
=> CREATE TABLE tb1 (a INT, b INT, c INT, d INT);
=> CREATE TABLE tb2 (g INT, h INT, i INT, j INT);
第一个投影引用 tb1
中的所有列并按列 a
进行排序:
=> CREATE PROJECTION tb1_p AS SELECT a, b, c, d FROM tb1 ORDER BY a;
伙伴实例投影引用 tb1
中的列 a
并按此列进行排序:
=> CREATE PROJECTION tb1_p_2 AS SELECT a FROM tb1 ORDER BY a;
以下投影引用 tb2
中的所有列并按列 i
进行排序:
=> CREATE PROJECTION tb2_p AS SELECT g, h, i, j FROM tb2 ORDER BY i;
考虑以下 DML 语句,其 WHERE
子句引用 tb1.a
。由于 tb1
的两个投影都包含列 a
,因此二者都符合经过优化的 DELETE 的条件:
=> DELETE FROM tb1 WHERE tb1.a IN (SELECT tb2.i FROM tb2);
在以下条件下,不支持经过优化的 DELETE 操作:
子查询引用目标表时存在复制的投影。例如不支持以下语法:
=> DELETE FROM tb1 WHERE tb1.a IN (SELECT e FROM tb2, tb2 WHERE tb2.e = tb1.e);
子查询不返回多个行。例如不支持以下语法:
=> DELETE FROM tb1 WHERE tb1.a = (SELECT k from tb2);
设计投影,使得经常使用的 DELETE 或 UPDATE 谓词列按照大型 DELETE 和 UPDATE 操作的所有投影的排序顺序显示。
例如,假设您对投影执行的大多数 DELETE 查询如下所示:
=> DELETE from t where time_key < '1-1-2007'
要优化 DELETE 操作,请使得 time_key
出现在所有投影的 ORDER BY 子句中。此架构设计可以提高 DELETE 操作的性能。
此外,将排序列添加到排序顺序中,以便每个排序键值组合都唯一标识一行或一小组行。有关详细信息,请参阅选择排序顺序:最佳实践。要分析投影是否存在排序顺序问题,请使用 EVALUATE_DELETE_PERFORMANCE 函数。
在 Vertica 中,删除操作不会从物理存储中移除行。 DELETE 会将行标记为已删除,UPDATE 也是如此,后者会合并删除和插入操作。在这两种情况下,Vertica 都会将丢弃的行保留为历史数据,在清除这些历史数据之前,仍然可通过历史查询访问这些数据。
保留历史数据的成本有双部分:
向已删除的行和删除标记分配磁盘空间。
典型(非历史)查询必须读取并跳过已删除的数据,这可能会影响性能。
清除操作将从物理存储中永久移除历史数据,并释放磁盘空间供重复使用。只有 Ancient History Mark (AHM) 之前的历史数据才符合清除条件。
可以通过两种方式清除数据:
在这两种情况下,Vertica 都会清除直到并包括 AHM 时期的所有历史数据,然后重置 AHM。有关 Vertica 如何使用时期的详细信息,请参阅时期。
清除数据的首选方法是建立一个策略来确定哪个已删除的数据符合清除条件。当 Tuple Mover 执行 合并操作时,会自动清除符合条件的数据。
Vertica 提供了两种方法用于确定已删除的数据何时符合清除条件:
指定保存删除数据的时间
指定保存的 时期数
指定保存删除数据的时间是确定可以清除哪些已删除数据的首选方法。默认情况下,仅当节点处于关闭状态时,Vertica 才会保存历史数据。
要更改保存已删除数据的指定时间,请使用 HistoryRetentionTime
配置参数:
=> ALTER DATABASE DEFAULT SET HistoryRetentionTime = {seconds | -1};
在上述语法中:
Seconds 是保存已删除数据的时长(单位为秒)。
-1 表示您不想使用 HistoryRetentionTime
配置参数确定哪些已删除数据符合清除条件。如果您更想使用其他方法 (HistoryRetentionEpochs
) 来确定可以清除哪些已删除数据,可使用此设置。
以下示例将历史时期保留级别设置为 240 秒:
=> ALTER DATABASE DEFAULT SET HistoryRetentionTime = 240;
除非您有理由限制时期数,否则 Vertica 建议您指定保存删除数据的时间。
要通过 HistoryRetentionEpochs
配置参数指定保存的历史时期数:
关闭 HistoryRetentionTime
配置参数:
=> ALTER DATABASE DEFAULT SET HistoryRetentionTime = -1;
通过 HistoryRetentionEpochs
配置参数设置历史时期保留级别:
=> ALTER DATABASE DEFAULT SET HistoryRetentionEpochs = {num_epochs | -1};
num_epochs 是要保存的历史时期数。
-1 表示您不想使用 HistoryRetentionEpochs
配置参数从时期映射中截取历史时期。默认情况下,HistoryRetentionEpochs
设置为 -1。
以下示例将要保存的历史时期数设置为 40:
=> ALTER DATABASE DEFAULT SET HistoryRetentionEpochs = 40;
将立即在数据库群集内的所有节点上实施修改。您无需重新启动数据库。
HistoryRetentionTime
和 HistoryRetentionEpochs
,则优先使用 HistoryRetentionTime
。
有关更多详细信息,请参阅时期管理参数。有关 Vertica 如何使用时期的信息,请参阅时期。
如果要保留所有历史数据,可按以下方式将历史时期保留参数的值设置为 -1:
=> ALTER DABABASE mydb SET HistoryRetentionTime = -1;
=> ALTER DATABASE DEFAULT SET HistoryRetentionEpochs = -1;
可以按如下方式手动清除已删除的数据:
设置清除已删除数据的截止日期。首先,调用以下函数之一以验证当前的 Ancient History Mark ( AHM):
GET_AHM_TIME
返回 AHM 的 TIMESTAMP 值。
GET_AHM_EPOCH
返回 AHM 所在的时期编号。
使用以下函数之一将 AHM 设置为所需的截止日期:
SET_AHM_TIME
将 AHM 设置为包含
启动程序节点上指定的 TIMESTAMP 值的
时期。
SET_AHM_EPOCH
将 AHM 设置为指定的时期。
MAKE_AHM_NOW
将 AHM 设置为最大允许值。这让您可以清除所有已删除的数据。
如果调用 SET_AHM_TIME
,请记住指定的时间戳将映射到默认具有三分钟粒度的时期。因此,如果将 AHM 时间指定为 2008-01-01 00:00:00.00
,则 Vertica 可能会清除 2008 年前三分钟的数据,或保留 2007 年最后三分钟的数据。
使用以下函数之一从所需的投影中清除已删除的数据:
PURGE
清除物理架构中的所有投影。
PURGE_TABLE
清除锚定到指定表的所有投影。
PURGE_PROJECTION
清除指定的投影。
PURGE_PARTITION
清除指定的分区。
Tuple Mover 执行
合并操作以清除数据。Vertica 会定期调用 Tuple Mover 以执行合并操作,如 Tuple Mover 参数所配置的那样。可以通过调用函数
DO_TM_TASK
来手动调用 Tuple Mover。
有关 Vertica 如何使用时期的详细信息,请参阅时期。
TRUNCATE TABLE 移除与目标表及其投影相关联的所有存储。Vertica 会保留表和投影定义。如果截断后的表包含过时投影,则当 TRUNCATE TABLE 返回时,这些投影将被清除并标记为最新。
TRUNCATE TABLE 在语句执行后提交整个事务,即使未能截断表也是如此。不能回退 TRUNCATE TABLE 语句。
使用 TRUNCATE TABLE 进行测试。可以使用它来移除表中的所有数据并向其中加载新数据,而无需重新创建表及其投影。
TRUNCATE TABLE 会对表采用 O(所有者)锁,直到截断过程完成。随后释放保存点。
如果操作无法在目标表上获取 O lock,Vertica 将尝试关闭该表上运行的任何内部 tuple mover 会话。如果成功,则可以继续操作。在用户会话中运行的显式 Tuple Mover 操作不会关闭。如果显式 Tuple Mover 操作在表上运行,则该操作仅在 Tuple Mover 操作完成后继续。
不能截断外部表。
=> INSERT INTO sample_table (a) VALUES (3);
=> SELECT * FROM sample_table;
a
---
3
(1 row)
=> TRUNCATE TABLE sample_table;
TRUNCATE TABLE
=> SELECT * FROM sample_table;
a
---
(0 rows)
可以通过重新构建表来大规模回收磁盘空间,如下所示:
创建一个与要重新构建的表具有相同(或相似)定义的表。
创建新表的投影。
使用
INSERT...SELECT
将目标表中的数据复制到新表中。
删除旧表及其投影。
使用旧表的名称,通过
ALTER TABLE...RENAME
重命名新表。
DROP TABLE
从数据库编录中删除表。如果有任何投影与该表相关联,则 DROP TABLE
会返回一条错误消息,除非它还包含 CASCADE
选项。有一个例外:该表仅有一个自动生成的超投影(自动投影)与之关联。
在以下示例中,DROP TABLE
尝试移除有多个投影与之关联的表。由于它省略了 CASCADE
选项,因此 Vertica 返回错误:
=> DROP TABLE d1;
NOTICE: Constraint - depends on Table d1
NOTICE: Projection d1p1 depends on Table d1
NOTICE: Projection d1p2 depends on Table d1
NOTICE: Projection d1p3 depends on Table d1
NOTICE: Projection f1d1p1 depends on Table d1
NOTICE: Projection f1d1p2 depends on Table d1
NOTICE: Projection f1d1p3 depends on Table d1
ERROR: DROP failed due to dependencies: Cannot drop Table d1 because other objects depend on it
HINT: Use DROP ... CASCADE to drop the dependent objects too.
=> DROP TABLE d1 CASCADE;
DROP TABLE
=> CREATE TABLE mytable (a INT, b VARCHAR(256));
CREATE TABLE
=> DROP TABLE IF EXISTS mytable;
DROP TABLE
=> DROP TABLE IF EXISTS mytable; -- Doesn't exist
NOTICE: Nothing was dropped
DROP TABLE
下一次尝试包含 CASCADE
选项,因此成功:
=> DROP TABLE d1 CASCADE;
DROP TABLE
=> CREATE TABLE mytable (a INT, b VARCHAR(256));
CREATE TABLE
=> DROP TABLE IF EXISTS mytable;
DROP TABLE
=> DROP TABLE IF EXISTS mytable; -- Doesn't exist
NOTICE: Nothing was dropped
DROP TABLE
在以下示例中,DROP TABLE
包含选项 IF EXISTS
。此选项指定当要删除的一个或多个表不存在时不报告错误。此子句在 SQL 脚本(例如在尝试重新创建表之前确保它已被删除)中十分有用。
=> DROP TABLE IF EXISTS mytable;
DROP TABLE
=> DROP TABLE IF EXISTS mytable; -- Table doesn't exist
NOTICE: Nothing was dropped
DROP TABLE
如果视图引用的表被删除,然后替换为同名的其他表,则该视图会继续正常运行,并使用新表的内容。新表必须具有相同的列定义。
Vertica 提供了多种设置来控制客户端连接:
限制用户可以同时打开的客户端连接数。
限制客户端连接在自动断开之前可以处于空闲状态的时间。
使用连接负载均衡将为客户端连接提供服务所产生的开销分摊到各个节点。
使用 TCP keepalive 检测无响应的客户端。
排空子群集以拒绝与该子群集建立的任何新客户端连接。有关详细信息,请参阅排空客户端连接。
与给定节点建立的客户端连接总数不能超过 MaxClientSessions
中设置的限值。
对客户端的 MAXCONNECTIONS
属性进行更改不会对当前会话产生影响;这些更改将仅应用于新会话。例如,如果将用户的连接模式从 DATABASE
更改为 NODE
,则当前节点连接不会受到影响。此更改仅应用于 在调用节点上保留的新会话。
当 Vertica 关闭某个客户端连接时,将取消该客户端正在进行的操作(如有)。
Vertica 会使用 TCP keepalive 来检测无响应的客户端并确定应关闭连接的时间。
以下参数控制数据库的 TCP keepalive 策略。数据库的参数会覆盖内核级别的等效参数(tcp_keepalive_time
、tcp_keepalive_intvl
、tcp_keepalive_probes
):
KeepAliveIdleTime:发送第一个 TCP keepalive 探测器以确保客户端仍处于连接状态之前的时间(以秒为单位,默认值为 7200 秒)。
KeepAliveProbeInterval:keepalive 探测器之间的时间间隔(以秒为单位,默认值为 75 秒)。
KeepAliveProbeCount:将客户端连接视为断开并关闭之前,必须由客户端取消确认的连续 keepalive 探测器次数(默认值为 9)。
要查看当前会话的有效 TCP keepalive 设置,请使用 SHOW CURRENT:
=> SHOW CURRENT KeepAliveIdleTime, KeepAliveProbeInterval, KeepAliveProbeCount;
level | name | setting
---------+------------------------+---------
DEFAULT | KeepAliveIdleTime | 7200
DEFAULT | KeepAliveProbeInterval | 75
DEFAULT | KeepAliveProbeCount | 9
(3 rows)
以下是示例 TCP keepalive 策略:
10 分钟后,将第一个 keepalive 探测器发送到客户端。
每隔 30 秒发送一次连续的 keepalive 探测器。
如果客户端无法响应 10 个 keepalive 探测器,则将连接视为断开并关闭。
要将此策略设为客户端连接的默认策略:
=> ALTER DATABASE DEFAULT SET KeepAliveIdleTime = 600;
=> ALTER DATABASE DEFAULT SET KeepAliveProbeInterval = 30;
=> ALTER DATABASE DEFAULT SET KeepAliveProbeCount = 10;
同样,要将此策略用于当前会话,可以使用 ALTER SESSION。这会覆盖默认/数据库级别策略:
=> ALTER SESSION SET KeepAliveIdleTime = 600;
=> ALTER SESSION SET KeepAliveProbeInterval = 30;
=> ALTER SESSION SET KeepAliveProbeCount = 10;
如果客户端继续响应 TCP keepalive 探测器,但未运行任何查询,则将客户端的会话视为空闲。空闲会话最终会超时。可以在三个级别设置允许会话空闲的最长时间,按优先级降序排列:
以 dbadmin 身份为各个用户设置
IDLESESSIONTIMEOUT
属性。此属性会覆盖所有其他会话超时设置。
用户可以使用
SET SESSION IDLESESSIONTIMEOUT
来限制当前会话的空闲时间。非超级用户只能将会话空闲时间设置为不大于自己设置的 IDLESESSIONTIMEOUT
值。如果没有为用户显式设置会话空闲时间,则该用户的会话空闲时间将从节点或数据库设置继承。
以 dbadmin 身份对数据库或各个节点设置配置参数
DEFAULTIDLESESSIONTIMEOUT
。您可以使用配置参数
DEFAULTIDLESESSIONTIMEOUT
来限制默认数据库群集或各个节点。此参数会为所有非超级用户设置默认超时值。
所有设置都应用于持续处于空闲状态的会话,即未在运行查询的会话。如果客户端在查询执行期间很慢或没有响应,则该时间不会应用于超时。例如,流式批量插入所需的时间不会计入超时。从开始等待来自某会话的任何类型的消息那一刻起,服务器会将该会话标识为空闲。
要手动关闭用户会话,请使用
CLOSE_USER_SESSIONS
:
=> SELECT CLOSE_USER_SESSIONS ('Joe');
close_user_sessions
------------------------------------------------------------------------------
Close all sessions for user Joe sent. Check v_monitor.sessions for progress.
(1 row)
用户执行某个查询后,由于某种原因,该查询需要很长时间才能完成(例如,由于服务器流量或查询比较复杂)。在这种情况下,用户可能会认为该查询失败并打开另一个会话来运行相同查询。现在,两个会话会使用额外的连接运行相同的查询。
为了防止出现这种情况,您可以通过修改其 MAXCONNECTIONS
用户属性来限制各个用户可以运行的会话数。这样有助于最大程度地降低运行冗余查询的概率。此外,还有助于防止用户使用数据库所设置的所有可用连接。例如,针对用户 SuzyQ
的以下设置将限制该用户在任何时候运行的数据库会话不得超过两个:
=> CREATE USER SuzyQ MAXCONNECTIONS 2 ON DATABASE;
当用户多次连接到服务器时,可限制设置客户端连接所能防止出现的另一个问题。用户连接过多会耗尽数据库配置参数
MaxClientSessions
所设置的允许连接数。
当群集发生以下变化时,客户端连接限值的行为可能会发生变化:
添加或移除节点。
节点发生故障或恢复。
连接请求之间的节点可用性发生变化对连接限值的影响较小。
就遵守连接限值而言,当节点在连接请求之间发生故障或恢复时,不会产生重大影响。无需执行特别操作来处理此问题。但是,如果节点发生故障,其活动会话将退出且群集中的其他节点也会删除自己的会话。这将释放连接。查询可能会挂起,在这种情况下,会话被阻止合乎情理且符合预期。
您可以管理用户可以向服务器打开的活动会话数,以及这些会话的持续时间。这样有助于防止过度使用可用资源,并可以提高总体吞吐量。
您可以在两个级别定义连接限值:
为各个用户设置 MAXCONNECTIONS 属性。此属性指定用户可以在各个节点上或跨数据库群集同时打开的会话数。例如,以下 ALTER USER 语句允许用户 Joe 最多打开 10 个并发会话:
=> ALTER USER Joe MAXCONNECTIONS 10 ON DATABASE;
对数据库或各个节点设置配置参数 MaxClientSessions。此参数指定可以在数据库群集中的节点上运行的最大客户端会话数,默认设置为 50。系统会始终为 dbadmin 用户额外保留五个会话。这使他们能够在客户端会话总数等于 MaxClientSessions 时登录。
与给定节点建立的客户端连接总数不能超过 MaxClientSessions 中设置的限值。
对客户端 MAXCONNECTIONS 属性进行更改不会对当前会话产生影响;这些更改将仅应用于新会话。例如,如果将用户的连接模式从 DATABASE 更改为 NODE,则当前节点连接不会受到影响。此更改仅应用于 在调用节点上保留的新会话。
空闲会话最终会超时。可以在三个级别设置允许会话空闲的最长时间,按优先级降序排列:
以 dbadmin 身份为各个用户设置 IDLESESSIONTIMEOUT 属性。此属性会覆盖所有其他会话超时设置。
用户可以使用 SET SESSION IDLESESSIONTIMEOUT 来限制当前会话的空闲时间。非超级用户只能将会话空闲时间设置为不大于自己设置的 IDLESESSIONTIMEOUT 值。如果没有为用户显式设置会话空闲时间,则该用户的会话空闲时间将从节点或数据库设置继承。
以 dbadmin 身份对数据库或各个节点设置配置参数 DEFAULTIDLESESSIONTIMEOUT。您可以使用配置参数 DEFAULTIDLESESSIONTIMEOUT 来限制默认数据库群集或各个节点。此参数会为所有非超级用户设置默认超时值。
所有设置都应用于持续处于空闲状态的会话,即未在运行查询的会话。如果客户端在查询执行期间很慢或没有响应,则该时间不会应用于超时。例如,流式批量插入所需的时间不会计入超时。从开始等待来自某会话的任何类型的消息那一刻起,服务器会将该会话标识为空闲。
如有必要,您可以使用 CLOSE_USER_SESSIONS 手动关闭用户会话:
=> SELECT CLOSE_USER_SESSIONS ('Joe');
close_user_sessions
------------------------------------------------------------------------------
Close all sessions for user Joe sent. Check v_monitor.sessions for progress.
(1 row)
用户执行某个查询后,由于某种原因,该查询需要很长时间才能完成(例如,由于服务器流量或查询比较复杂)。在这种情况下,用户可能会认为该查询失败并打开另一个会话来运行相同查询。现在,两个会话会使用额外的连接运行相同的查询。
为了防止出现这种情况,您可以通过修改其 MAXCONNECTIONS 用户属性来限制各个用户可以运行的会话数。这样有助于最大程度地降低运行冗余查询的概率。此外,还有助于防止用户使用数据库所设置的所有可用连接。例如,针对用户 SuzyQ 的以下设置将限制该用户在任何时候运行的数据库会话不得超过两个:
=> CREATE USER SuzyQ MAXCONNECTIONS 2 ON DATABASE;
当用户多次连接到服务器时,可限制设置客户端连接所能防止出现的另一个问题。用户连接过多会耗尽数据库配置参数 MaxClientSessions 所设置的允许连接数。
当群集发生以下变化时,客户端连接限值的行为可能会发生变化:
添加或移除节点。
节点发生故障或恢复。
连接请求之间的节点可用性发生变化对连接限值的影响较小。
就遵守连接限值而言,当节点在连接请求之间发生故障或恢复时,不会产生重大影响。无需执行特别操作来处理此问题。但是,如果节点发生故障,其活动会话将退出且群集中的其他节点也会删除自己的会话。这将释放连接。查询可能会挂起,在这种情况下,会话被阻止合乎情理且符合预期。
仅限 Eon 模式
排空子群集中的客户端连接会将子群集中的所有节点标记为正在排空,从而为关闭子群集做好准备。来自现有用户会话的工作继续排空节点,但节点拒绝新的客户端连接,并且会从负载均衡操作中排除。如果客户端尝试连接到正在排空的节点,它们会收到指示节点处于排空状态的错误。负载均衡操作不包括正在排空的节点,因此只有在负载均衡策略中的所有节点都在排空时,选择加入连接负载均衡的客户端才会收到连接错误。您无需更改任何连接负载均衡配置即可使用此功能。dbadmin 仍然可以连接到正在排空的节点。
要在关闭子群集之前排空客户端连接,可以使用 SHUTDOWN_WITH_DRAIN 函数。此函数执行正常关闭以将子群集标记为正在排空,直到现有连接完成其工作并关闭或达到用户指定的超时时间。当满足这些条件之一时,此函数继续关闭子群集。Vertica 提供了多个可用于独立执行 SHUTDOWN_WITH_DRAIN 过程中每个步骤的元函数。您可以使用 START_DRAIN_SUBCLUSTER 函数将子群集标记为正在排空,然后在其连接关闭后使用 SHUTDOWN_SUBCLUSTER 函数关闭子群集。
您可以使用 CANCEL_DRAIN_SUBCLUSTER 函数将子群集中的所有节点标记为未在排空。一旦节点既处于 UP 状态又未在排空,该节点就会接受新的客户端连接。如果正在排空的子群集中的所有节点均已关闭,则其节点的排空状态会自动重置为未在排空。
您可以通过查询 DRAINING_STATUS 系统表来监控每个节点的排空状态以及客户端连接信息,例如每个节点上的活动用户会话数。
以下示例排空名为 analytics 的子群集,然后取消排空该子群集。
要将 analytics 子群集标记为正在排空,请使用负超时值调用 SHUTDOWN_WITH_DRAIN:
=> SELECT SHUTDOWN_WITH_DRAIN('analytics', -1);
NOTICE 0: Draining has started on subcluster (analytics)
您可以通过查询 DRAINING_STATUS 系统表来确认子群集正在排空:
=> SELECT node_name, subcluster_name, is_draining FROM draining_status ORDER BY 1;
node_name | subcluster_name | is_draining
-------------------+--------------------+--------------
verticadb_node0001 | default_subcluster | f
verticadb_node0002 | default_subcluster | f
verticadb_node0003 | default_subcluster | f
verticadb_node0004 | analytics | t
verticadb_node0005 | analytics | t
verticadb_node0006 | analytics | t
如果客户端尝试直接连接到正在排空的子群集中的节点,它们会收到以下错误消息:
$ /opt/vertica/bin/vsql -h noeIP --password password verticadb analyst
vsql: FATAL 10611: New session rejected because subcluster to which this node belongs is draining connections
要取消 analytics
子群集的正常关闭,可以键入 Ctrl+C:
=> SELECT SHUTDOWN_WITH_DRAIN('analytics', -1);
NOTICE 0: Draining has started on subcluster (analytics)
^CCancel request sent
ERROR 0: Cancel received after draining started and before shutdown issued. Nodes will not be shut down. The subclusters are still in the draining state.
HINT: Run cancel_drain_subcluster('') to restore all nodes to the 'not_draining' state
如上提示中所述,可以运行 CANCEL_DRAIN_SUBCLUSTER 将子群集中正在排空的节点的状态重置为未在排空:
=> SELECT CANCEL_DRAIN_SUBCLUSTER('analytics');
CANCEL_DRAIN_SUBCLUSTER
--------------------------------------------------------
Targeted subcluster: 'analytics'
Action: CANCEL DRAIN
(1 row)
要确认子群集不再排空,可以再次查询 DRAINING_STATUS 系统表:
=> SELECT node_name, subcluster_name, is_draining FROM draining_status ORDER BY 1;
node_name | subcluster_name | is_draining
-------------------+--------------------+-------
verticadb_node0001 | default_subcluster | f
verticadb_node0002 | default_subcluster | f
verticadb_node0003 | default_subcluster | f
verticadb_node0004 | analytics | f
verticadb_node0005 | analytics | f
verticadb_node0006 | analytics | f
(6 rows)
在 Vertica 群集中,主机的每个客户端连接都会产生一小部分的内存和处理器时间开销。如果多个客户端连接到单个主机,此开销可能会影响数据库性能。可通过指定某些客户端连接到群集中的特定主机来分配客户端连接的开销。但是,这种手动平衡会随着环境中添加新的客户端和主机而变得十分困难。
连接负载均衡可让主机将客户端连接重定向到其他主机,因此有助于在群集中自动分配客户端连接所产生的开销。通过重定向连接,客户端连接所产生的开销会在群集内进行分配,而无需手动为各个客户端分配特定主机。客户端可以连接到一小部分主机,它们会自然而然地重定向到群集中的其他主机。负载均衡不会将连接重定向到即将排空的主机。有关详细信息,请参阅排空客户端连接。
本机连接负载均衡是 Vertica Analytic Database 服务器和客户端库以及 vsql 的一项内置功能。服务器和客户端均需启用负载均衡才能正常工作。启用连接负载均衡后,数据库群集中的主机可以将尝试连接它的客户端重定向到群集中当前处于活动状态的其他主机。此重定向基于负载均衡策略,仅执行一次,因此客户端不会从一个主机跳跃到其他主机。
由于本机连接负载均衡并入到了 Vertica 客户端库中,因此,连接到 Vertica 的任何客户端应用程序只需设置连接参数即可透明地利用此项功能。
如何选择实施连接负载均衡功能取决于您的网络环境。由于本机连接负载均衡功能更易于实施,因此您应选择使用此项功能,除非您的网络配置要求通过防火墙将客户端与 Vertica 数据库中的主机分隔开。
有关本机连接负载均衡的详细信息,请参阅关于本机连接负载均衡。
本机连接负载均衡是内置在 Vertica 服务器和客户端库中的一种功能,它可以帮助分摊客户端连接在数据库中主机上产生的 CPU 和内存开销。它可以防止客户端连接在群集中的主机之间分布不均匀。
本机连接负载均衡有两种类型:
群集范围的平衡 — 此方法是传统的连接负载均衡方法。它是 Vertica 9.2 版之前的唯一负载均衡类型。使用此方法,可以在整个群集中应用单个负载均衡策略。与群集的所有连接都以相同的方式处理。
负载均衡策略 — 此方法允许您根据客户端连接源设置不同的负载均衡策略。例如,您可以制定一个策略,将本地网络外部的连接重定向到群集中的一组节点,并将本地网络内部的连接重定向到另一组节点。
经典连接负载均衡功能为与数据库的所有客户端连接应用单个策略。数据库和客户端都必须启用负载均衡选项才能对连接进行负载均衡。如果客户端和服务器都启用负载均衡,则在客户端尝试与 Vertica 建立连接时,会发生以下过程:
客户端连接数据库群集中的主机时,连接参数指示它正在请求负载均衡连接。
主机根据当前负载均衡方案,从群集中当前正在运行的主机列表中选择一个主机。在所有方案下,主机都可以选择自身。
主机告诉客户端它选择了哪个主机来处理客户端连接。
如果主机选择数据库中其他主机来处理客户端连接,客户端将断开与初始主机的连接。否则,客户端将跳到步骤 6。
客户端与将负责处理其连接的主机建立连接。客户端设置第二个连接请求,使第二个主机不会将连接解释为负载均衡请求。
客户端连接继续正常运行(如果连接已启用 SSL,则协商加密,并继续对用户进行身份验证)。
此过程对客户端应用程序是透明的。客户端驱动程序会自动断开与初始主机的连接,然后重新连接到为负载均衡选择的主机。
在混合 IPv4 和 IPv6 环境中,平衡仅对配置了本机连接负载均衡的地址系列起作用。例如,如果您使用 IPv4 地址配置了负载均衡,则 IPv6 客户端无法使用负载均衡,不过 IPv6 客户端仍然可以连接,只是不进行负载均衡而已。
本机负载均衡器为客户端返回要使用的 IP 地址。该地址必须是客户端可以到达的地址。如果您的节点位于专用网络上,则本机负载均衡要求您通过以下两种方式之一发布公共地址:
负载均衡方案控制主机如何选择哪个主机来处理客户端连接。以下是三种可用方案:
NONE
(默认值):禁用本机连接负载均衡。
ROUNDROBIN
:从群集中处于启动状态的主机循环列表中选择下一个主机。例如,在包含三节点的群集中,依次迭代节点 1、节点 2 和节点 3,然后返回到节点 1。群集中的每个主机都在循环链表中维护自己的指向下一个主机的指针,而不存在一个群集范围内的状态。
RANDOM
:从群集中所有处于启动的主机中随机选择一个主机。
您可以使用 SET_LOAD_BALANCE_POLICY 函数设置本机连接负载均衡方案。有关说明,请参阅启用和禁用本机连接负载均衡。
本机连接负载均衡与 ADO.NET 驱动程序的连接池结合使用。客户端建立的到初始主机的连接以及到负载均衡主机的最终连接会使用池化连接(如果它们可用)。
如果客户端连接将 JDBC 和 ODBC 驱动程序与第三方连接池解决方案结合使用,初始连接不会被池化,因为它不是一个完整的客户端连接。最终连接会被池化,因为它是一个标准的客户端连接。
客户端库包含故障转移功能,如果连接属性中指定的主机无法到达,则通过此功能,可以连接备份主机。使用本机连接负载均衡时,故障转移功能只能用于数据库的初始连接。如果客户端重定向到的主机没有对客户端的连接请求进行响应,客户端不会尝试连接备份主机,而是向用户返回连接错误。
客户端仅重定向到已知正常运行的主机。因此,只有当目标主机在客户端被重定向到它的那一刻发生故障时,才会发生这种连接失败。有关详细信息,请参阅 ADO.NET 连接故障转移、JDBC 连接故障转移和连接故障转移。
只有数据库 超级用户才能启用或禁用群集范围的经典连接负载均衡。要启用或禁用负载均衡,请使用 SET_LOAD_BALANCE_POLICY 函数设置负载均衡策略。将负载均衡策略设置为“NONE”以外的任何其他值可在服务器中启用负载均衡。以下示例通过将负载均衡策略设置为 ROUNDROBIN 来启用本机连接负载均衡。
=> SELECT SET_LOAD_BALANCE_POLICY('ROUNDROBIN');
SET_LOAD_BALANCE_POLICY
--------------------------------------------------------------------------------
Successfully changed the client initiator load balancing policy to: roundrobin
(1 row)
要禁用本机连接负载均衡,请使用 SET_LOAD_BALANCE_POLICY 将此策略设置为“NONE”:
=> SELECT SET_LOAD_BALANCE_POLICY('NONE');
SET_LOAD_BALANCE_POLICY
--------------------------------------------------------------------------
Successfully changed the client initiator load balancing policy to: none
(1 row)
export_address
列的值。之后,客户端使用 export_address
进行连接。node_address
指定要用于在节点间分配通信的地址。安装数据库时,export_address
和 node_address
被设置为相同的值。如果将 Vertica 安装到了专用地址上,必须将每个节点的 export_address 设置为公共地址。
默认情况下,客户端连接不会进行负载均衡,即使服务器启用了连接负载均衡也是如此。客户端必须通过设置连接参数来指明其愿意对自己的连接请求进行负载均衡。 有关在客户端启用负载均衡的信息,请参阅 ADO.NET 中的负载均衡、JDBC 中的负载均衡和负载均衡。对于 vsql,使用 -C
命令行选项来启用负载均衡。
当负载均衡策略为 ROUNDROBIN 时,Vertica 群集中的每个主机都保持其自己的状态,即它选择哪个主机来处理下一个客户端连接。可使用 RESET_LOAD_BALANCE_POLICY 函数将此状态重置为其初始值(通常为具有最低节点 ID 的主机):
=> SELECT RESET_LOAD_BALANCE_POLICY();
RESET_LOAD_BALANCE_POLICY
-------------------------------------------------------------------------
Successfully reset stateful client load balance policies: "roundrobin".
(1 row)
查询 V_CATALOG.DATABASES 的 LOAD_BALANCE_POLICY 列可确定服务器上本机连接负载均衡的状态:
=> SELECT LOAD_BALANCE_POLICY FROM V_CATALOG.DATABASES;
LOAD_BALANCE_POLICY
---------------------
roundrobin
(1 row)
客户端可以通过查询 V_MONITOR.CURRENT_SESSION 表的 NODE_NAME 列来确定它已连接到哪个节点:
=> SELECT NODE_NAME FROM V_MONITOR.CURRENT_SESSION;
NODE_NAME
------------------
v_vmart_node0002
(1 row)
连接负载均衡策略基于连接的来源重定向连接,从而帮助分摊服务客户端连接的负载。这些策略还可以通过在节点之间分摊连接来帮助防止节点达到其客户端连接限制并拒绝新连接。有关客户端连接限制的详细信息,请参阅限制客户端连接的数量和长度。
负载均衡策略包括:
网络地址,用于标识节点上的特定 IP 地址/端口号组合。
一个或多个路由规则,用来将一系列客户端 IP 地址映射到连接负载均衡组。
当客户端连接到启用负载均衡的数据库中的节点时,该节点会根据客户端的 IP 地址评估所有路由规则,以确定是否有任何匹配项。如果多个规则与该 IP 地址匹配,则节点应用最有针对性的规则(影响最少 IP 地址的规则)。
如果节点找到匹配规则,它会使用该规则来确定用于处理客户端连接的潜在节点池。在评估潜在目标节点时,它始终确保节点当前处于启动状态。然后,最初联系的节点根据组的分布方案选择组中的节点之一。分布方案可以是随机选择一个节点,也可以是按“循环”轮换顺序选择一个节点。例如,在一个三节点群集中,循环顺序是节点 1、节点 2、节点 3,然后再回到节点 1。
在对规则进行处理之后,如果节点确定客户端的连接应当由另一个节点处理,该节点会通知客户端它选择了哪个节点。客户端断开与初始节点的连接,并连接到所选节点以继续连接过程(在连接启用 TLS/SSL 的情况下进行协商加密,或者通过身份验证)。
如果初始节点根据路由规则选择自己,它会通知客户端继续执行连接过程的下一个步骤。
如果没有路由规则与传入 IP 地址匹配,该节点会检查 Vertica 和客户端是否均启用了经典连接负载均衡。如果是,它将根据经典负载均衡策略处理连接。有关详细信息,请参阅经典连接负载均衡。
最后,如果数据库在 Eon 模式下运行,节点会尝试应用默认的内部负载均衡规则。请参阅下面的默认子群集内部负载均衡策略。
如果没有路由规则与传入 IP 地址和经典负载均衡匹配,并且默认子群集内部负载均衡规则不适用,则节点将自行处理连接。如果节点无法遵循负载均衡规则,它也会自行处理连接。例如,如果负载均衡组中作为规则目标的所有节点都已关闭,则最初联系的节点会自行处理客户端连接。在这种情况下,节点不会尝试应用任何其他具有较少限制且适用于传入连接的负载均衡规则。它仅尝试应用单个负载均衡规则。
使用负载均衡策略,可以:
确保来自内部网络内外的连接被定向到客户端的有效 IP 地址。例如,假设您的 Vertica 节点有两个 IP 地址:一个用于外部网络,另一个用于内部网络。这些网络相互排斥。您无法从公共网络访问专用网络,也无法从专用网络访问公共网络。负载均衡规则需要为客户端提供它们实际可以访问的 IP 地址。
允许访问 NAT 路由器后面的多个 Vertica 群集节点。NAT 路由器可通过单个 IP 地址从外部网络访问。NAT 路由器专用网络内的系统可以使用不同的端口号在这个 IP 地址上访问。您可以创建一个负载均衡策略,将客户端连接重定向到 NAT 的 IP 地址,但使用不同的端口号。
指定多组节点来为源自一个 IP 地址范围的客户端连接提供服务。例如,如果为您的 ETL 系统设置了一个 IP 地址范围,您可以仅允许 ETL 系统的客户端连接到任意一组 Vertica 节点、一个子群集或一个容错组。这种技术可用于隔离服务客户端连接到几个节点所需的开销。当您在 Eon 模式数据库中使用子群集来隔离工作负载时,这种技术很有用(有关详细信息,请参阅子群集)。
连接负载均衡策略适用于 IPv4 和 IPv6。就负载均衡策略而言,这两个地址系列代表不同的网络。如果您希望负载均衡策略既能够处理 IPv4 地址又能够处理 IPv6 地址,则必须为每个协议创建单独的网络地址、负载均衡组和规则集。当客户端建立与群集中某个节点的连接时,它使用的寻址协议将确定 Vertica 在决定是否以及如何平衡连接时参考哪组规则。
在 Eon 模式下运行的数据库具有默认连接负载均衡策略,该策略有助于在子群集中的节点之间分摊客户端连接的处理负载。如果客户端在连接到节点的同时选择加入连接负载均衡,节点将检查有无适用于客户端 IP 地址的负载均衡策略。如果它没有找到任何适用的负载均衡规则,并且没有启用经典负载均衡,则节点将回退到默认的内部负载均衡规则。此规则在与最初联系的节点相同的子群集中的节点之间分布连接。
与其他连接负载均衡策略一样,子群集中的节点必须定义一个网络地址,以便它们有资格处理客户端连接。如果子群集中不存在具有网络地址的节点,则该节点不应用默认的子群集内部负载均衡规则,并且不会对连接进行负载均衡。
当您主要对每个子群集内的负载均衡连接感兴趣时,此默认规则很方便。您只需为子群集中的节点创建网络地址,而无需创建负载均衡组或规则。选择加入负载均衡的客户端随后会在子群集中的节点之间自动平衡。
如果您的节点有多个网络地址,默认的子群集内部负载均衡规则会选择最先创建的地址作为负载均衡规则的目标。例如,假设您在一个节点上为专用 IP 地址 192.168.1.10 创建一个网络地址,然后为公共 IP 地址 233.252.0.1 的节点创建另一个网络地址。默认子群集内部连接负载均衡规则始终选择 192.168.1.10 作为规则的目标。
如果您希望默认内部负载均衡规则选择不同的网络地址作为其目标,请删除节点上的其他网络地址,然后重新创建它们。删除并重新创建其他地址会使您希望规则选择的地址成为最旧的地址。例如,假设您希望规则使用在专用地址 (192.168.1.10) 之后创建的公共地址 (233.252.0.1)。在这种情况下,您可以删除 192.168.1.10 的地址,然后重新创建它。之后,在默认情况下,该规则使用旧的公共地址 233.252.0.1。
如果您打算为子群集中的节点创建多个网络地址,请先创建要用于默认子群集内部负载均衡的网络地址。例如,假设您想使用默认子群集内部负载均衡规则来对大多数客户端连接进行负载均衡。但是,您还想创建一个连接负载均衡策略来管理源自一组 ETL 系统的连接。在这种情况下,首先创建要用于默认内部负载均衡规则的网络地址,然后为 ETL 系统创建网络地址。
经典负载均衡功能与负载均衡策略功能有以下几个区别:
在经典连接负载均衡中,您只需在客户端和服务器上启用负载均衡选项,并启用负载均衡。实施负载均衡策略的步骤更多:必须创建地址、组和规则,然后在客户端上启用负载均衡。
经典连接负载均衡仅支持使用群集范围的单个策略来重定向连接。使用连接负载均衡策略,可以根据连接的来源选择哪些节点处理客户端连接。这使您可以更灵活地处理复杂的情况。例如,通过基于 NAT 的路由器路由连接,或者让可通过多个 IP 地址访问的节点位于不同的网络上。
在经典连接负载均衡中,群集中的每个节点只能通过一个 IP 地址访问。此地址在 NODES 系统表的 EXPORT_ADDRESS 列中设置。使用连接负载均衡策略,可以为与节点关联的每个 IP 地址创建一个网络地址。然后,创建用于重定向到这些地址的规则。
您必须遵循以下三个步骤来创建负载均衡策略:
为要参与连接负载均衡策略的每个节点创建一个或多个网络地址。
创建一个或多个负载均衡组作为路由规则的目标。负载均衡组可以将特定网络地址的集合作为目标。或者,可以从容错组或子群集创建一个组。您可以使用 IP 地址筛选器将负载均衡组的成员限制为容错组或子群集的子集。
创建一个或多个路由规则。
尽管并不绝对必需,但最好始终测试负载均衡策略以确保它按预期方式工作。
完成这些步骤后,Vertica 会将负载均衡策略应用于选择加入连接负载均衡的客户端连接。 有关在客户端启用负载均衡的信息,请参阅 ADO.NET 中的负载均衡、JDBC 中的负载均衡和负载均衡。对于 vsql,使用 -C
命令行选项来启用负载均衡。
这些步骤在此部分的其他主题中有说明。
网络地址为节点上的 IP 地址和端口号分配名称。在定义负载均衡组时使用这些地址。一个节点可以有多个与之关联的网络地址。例如,假设一个节点有一个 IP 地址只能从本地网络外部访问,而另一个 IP 地址只能从网络内部访问。在这种情况下,您可以使用外部 IP 地址定义一个网络地址,并使用内部地址定义另一个网络地址。然后,您可以创建两个不同的负载均衡策略,一个用于外部客户端,另一个用于内部客户端。
您可以使用 CREATE NETWORK ADDRESS 语句创建网络地址。此语句采用:
要分配给网络地址的名称
节点的名称
要与网络地址关联的节点的 IP 地址
节点用于接受客户端连接的端口号(可选)
以下示例演示了如何创建三个网络地址,每个地址对应于三节点数据库中的每个节点。
=> SELECT node_name,node_address,node_address_family FROM v_catalog.nodes;
node_name | node_address | node_address_family
------------------+--------------+----------------------
v_vmart_node0001 | 10.20.110.21 | ipv4
v_vmart_node0002 | 10.20.110.22 | ipv4
v_vmart_node0003 | 10.20.110.23 | ipv4
(4 rows)
=> CREATE NETWORK ADDRESS node01 ON v_vmart_node0001 WITH '10.20.110.21';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node02 ON v_vmart_node0002 WITH '10.20.110.22';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node03 on v_vmart_node0003 WITH '10.20.110.23';
CREATE NETWORK ADDRESS
为 IPv6 地址创建网络地址的方式相同:
=> CREATE NETWORK ADDRESS node1_ipv6 ON v_vmart_node0001 WITH '2001:0DB8:7D5F:7433::';
CREATE NETWORK ADDRESS
Vertica 不会对您在 CREATE NETWORK ADDRESS 语句中提供的 IP 地址执行任何测试。您必须测试您提供给此语句的 IP 地址,确认它们对应于正确的节点。
Vertica 不限制您提供的地址,因为它通常不知道可通过哪些网络地址访问节点。例如,您的节点可从外部网络,通过一个未配置 Vertica 使用的 IP 地址进行访问。或者,您的节点可能同时具有 IPv4 和 IPv6 地址,Vertica 只知道其中一个地址。
例如,假设上例中的 v_vmart_node0003 不能 通过 IP 地址 192.168.1.5 not 访问。您仍然可以使用该地址为该节点创建网络地址:
=> CREATE NETWORK ADDRESS node04 ON v_vmart_node0003 WITH '192.168.1.5';
CREATE NETWORK ADDRESS
如果您创建的网络组和路由规则以该地址为目标,客户端连接将连接到错误的节点,或者由于连接到不属于 Vertica 群集的主机而失败。
默认情况下,CREATE NETWORK ADDRESS 语句假设节点客户端连接的端口号是默认值 5433。有时,您可能有一个节点在不同的端口上侦听客户端连接。您可以使用 PORT 关键字为网络地址提供备用端口号。
例如,假设您的节点位于 NAT 路由器后面。在这种情况下,您可以让您的节点侦听不同的端口号,以便 NAT 路由器可以将连接路由到这些端口号。在为这些节点创建网络地址时,您需要提供 NAT 路由器的 IP 地址,以及节点正在侦听的端口号。例如:
=> CREATE NETWORK ADDRESS node1_nat ON v_vmart_node0001 WITH '192.168.10.10' PORT 5433;
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node2_nat ON v_vmart_node0002 with '192.168.10.10' PORT 5434;
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node3_nat ON v_vmart_node0003 with '192.168.10.10' PORT 5435;
CREATE NETWORK ADDRESS
为节点创建网络地址后,您可以创建网络地址的集合,以便您可以使用路由规则将它们确定为目标。这些网络地址的集合称为负载均衡组。您可以通过两种方法选择要包括在负载均衡组中的地址:
网络地址列表
一个或多个容错组或子群集的名称,加上采用 CIDR 格式的 IP 地址范围。根据地址范围可以选择 Vertica 将容错组或子群集中的哪些网络地址添加到负载均衡组中。只有属于所提供的 IP 地址范围的网络地址才会添加到负载均衡组。此筛选器可用于让负载均衡组基于构成容错组或子群集的部分节点。
您可以使用 CREATE LOAD BALANCE GROUP 语句创建负载均衡组。当您的组基于地址列表时,此语句采用组名和地址列表。以下示例演示了为四个节点创建地址,然后基于这些节点创建两个组。
=> CREATE NETWORK ADDRESS addr01 ON v_vmart_node0001 WITH '10.20.110.21';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS addr02 ON v_vmart_node0002 WITH '10.20.110.22';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS addr03 on v_vmart_node0003 WITH '10.20.110.23';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS addr04 on v_vmart_node0004 WITH '10.20.110.24';
CREATE NETWORK ADDRESS
=> CREATE LOAD BALANCE GROUP group_1 WITH ADDRESS addr01, addr02;
CREATE LOAD BALANCE GROUP
=> CREATE LOAD BALANCE GROUP group_2 WITH ADDRESS addr03, addr04;
CREATE LOAD BALANCE GROUP
=> SELECT * FROM LOAD_BALANCE_GROUPS;
name | policy | filter | type | object_name
------------+------------+-----------------+-----------------------+-------------
group_1 | ROUNDROBIN | | Network Address Group | addr01
group_1 | ROUNDROBIN | | Network Address Group | addr02
group_2 | ROUNDROBIN | | Network Address Group | addr03
group_2 | ROUNDROBIN | | Network Address Group | addr04
(4 rows)
一个网络地址可以是任意多个负载均衡组的一部分。但是,每个组的每个节点只能有一个网络地址。您不能将两个属于同一节点的网络地址添加到同一负载均衡组中。
要从一个或多个容错组创建负载均衡组,请提供:
负载均衡组的名称
一个或多个容错组的名称
CIDR 格式的 IP 地址筛选器,根据容错组的 IP 地址筛选要添加到负载均衡组的容错组。Vertica 会排除容错组中不属于此范围的任何网络地址。如果您希望将容错组中的所有节点都添加到负载均衡组中,请指定筛选器 0.0.0.0/0。
以下示例从容错组创建两个负载均衡组。第一个通过对所有 IP 地址使用 CIDR 表示法,包括容错组中的所有网络地址。第二个使用 IP 地址筛选器,将容错组限制在容错组中的三个节点(共四个)。
=> CREATE FAULT GROUP fault_1;
CREATE FAULT GROUP
=> ALTER FAULT GROUP fault_1 ADD NODE v_vmart_node0001;
ALTER FAULT GROUP
=> ALTER FAULT GROUP fault_1 ADD NODE v_vmart_node0002;
ALTER FAULT GROUP
=> ALTER FAULT GROUP fault_1 ADD NODE v_vmart_node0003;
ALTER FAULT GROUP
=> ALTER FAULT GROUP fault_1 ADD NODE v_vmart_node0004;
ALTER FAULT GROUP
=> SELECT node_name,node_address,node_address_family,export_address
FROM v_catalog.nodes;
node_name | node_address | node_address_family | export_address
------------------+--------------+---------------------+----------------
v_vmart_node0001 | 10.20.110.21 | ipv4 | 10.20.110.21
v_vmart_node0002 | 10.20.110.22 | ipv4 | 10.20.110.22
v_vmart_node0003 | 10.20.110.23 | ipv4 | 10.20.110.23
v_vmart_node0004 | 10.20.110.24 | ipv4 | 10.20.110.24
(4 rows)
=> CREATE LOAD BALANCE GROUP group_all WITH FAULT GROUP fault_1 FILTER
'0.0.0.0/0';
CREATE LOAD BALANCE GROUP
=> CREATE LOAD BALANCE GROUP group_some WITH FAULT GROUP fault_1 FILTER
'10.20.110.21/30';
CREATE LOAD BALANCE GROUP
=> SELECT * FROM LOAD_BALANCE_GROUPS;
name | policy | filter | type | object_name
----------------+------------+-----------------+-----------------------+-------------
group_all | ROUNDROBIN | 0.0.0.0/0 | Fault Group | fault_1
group_some | ROUNDROBIN | 10.20.110.21/30 | Fault Group | fault_1
(2 rows)
您还可以为 CREATE LOAD BALANCE GROUP 语句提供多个容错组:
=> CREATE LOAD BALANCE GROUP group_2_faults WITH FAULT GROUP
fault_2, fault_3 FILTER '0.0.0.0/0';
CREATE LOAD BALANCE GROUP
从子群集创建负载均衡组类似于从容错组创建负载均衡组。只需在 CREATE LOAD BALANCE GROUP 语句中使用 WITH SUBCLUSTER 而不是 WITH FAULT GROUP 即可。
=> SELECT node_name,node_address,node_address_family,subcluster_name
FROM v_catalog.nodes;
node_name | node_address | node_address_family | subcluster_name
----------------------+--------------+---------------------+--------------------
v_verticadb_node0001 | 10.11.12.10 | ipv4 | load_subcluster
v_verticadb_node0002 | 10.11.12.20 | ipv4 | load_subcluster
v_verticadb_node0003 | 10.11.12.30 | ipv4 | load_subcluster
v_verticadb_node0004 | 10.11.12.40 | ipv4 | analytics_subcluster
v_verticadb_node0005 | 10.11.12.50 | ipv4 | analytics_subcluster
v_verticadb_node0006 | 10.11.12.60 | ipv4 | analytics_subcluster
(6 rows)
=> CREATE NETWORK ADDRESS node01 ON v_verticadb_node0001 WITH '10.11.12.10';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node02 ON v_verticadb_node0002 WITH '10.11.12.20';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node03 ON v_verticadb_node0003 WITH '10.11.12.30';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node04 ON v_verticadb_node0004 WITH '10.11.12.40';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node05 ON v_verticadb_node0005 WITH '10.11.12.50';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node06 ON v_verticadb_node0006 WITH '10.11.12.60';
CREATE NETWORK ADDRESS
=> CREATE LOAD BALANCE GROUP load_subcluster WITH SUBCLUSTER load_subcluster
FILTER '0.0.0.0/0';
CREATE LOAD BALANCE GROUP
=> CREATE LOAD BALANCE GROUP analytics_subcluster WITH SUBCLUSTER
analytics_subcluster FILTER '0.0.0.0/0';
CREATE LOAD BALANCE GROUP
负载均衡组有一个策略设置确定最初联系的节点如何从组中选择目标。CREATE LOAD BALANCE GROUP 支持以下三种策略:
ROUNDROBIN(默认值)将在负载均衡组的可用成员之间轮转。最初联系的节点会跟踪它上次选择的节点,并选择群集中的下一个节点。
RANDOM 从组中随机选择一个可用节点。
NONE 禁用负载均衡。
以下示例演示了如何使用 RANDOM 分布策略创建负载均衡组。
=> CREATE LOAD BALANCE GROUP group_random WITH ADDRESS node01, node02,
node03, node04 POLICY 'RANDOM';
CREATE LOAD BALANCE GROUP
在创建负载均衡组后,必须添加负载均衡路由规则,该规则通知 Vertica 如何将传入的连接重定向到组。请参阅创建负载均衡路由规则。
创建一个或多个连接负载均衡组后,便可以创建负载均衡路由规则了。这些规则通知 Vertica 如何根据 IP 地址重定向客户端连接。
您可以使用 CREATE ROUTING RULE 语句创建路由规则。向此语句传递以下内容:
规则的名称
规则适用的 CIDR 格式的源 IP 地址范围(IPv4 或 IPv6)
用来处理连接的负载均衡组的名称
以下示例创建两个规则。第一个将来自 IP 地址范围 192.168.1.0 到 192.168.1.255 的连接重定向到名为 group_1 的负载均衡组。第二个将来自 IP 范围 10.20.1.0 到 10.20.1.255 的连接路由到名为 group_2 的负载均衡组。
=> CREATE ROUTING RULE internal_clients ROUTE '192.168.1.0/24' TO group_1;
CREATE ROUTING RULE
=> CREATE ROUTING RULE external_clients ROUTE '10.20.1.0/24' TO group_2;
CREATE ROUTING RULE
Vertica 按照从最具针对性到最不具针对性的顺序应用路由规则。此行为可用于创建用来处理所有传入连接的“全方位”规则。然后,您可以针对特定用途创建规则来处理较小的 IP 地址范围。例如,假设您想创建一个全方位的规则与上例中创建的规则结合使用。然后,您可以创建一个新规则,将 0.0.0.0/0(所有 IP 地址的 CIDR 表示法)路由到一个组,该组应处理未由先前创建的任一规则处理的连接。例如:
=> CREATE LOAD BALANCE GROUP group_all WITH ADDRESS node01, node02, node03, node04;
CREATE LOAD BALANCE GROUP
=> CREATE ROUTING RULE catch_all ROUTE '0.0.0.0/0' TO group_all;
CREATE ROUTING RULE
在运行上述语句后,任何不是源自 IP 地址范围 192.168.1.* 或 10.20.1.* 的连接都会路由到 group_all 组。
创建路由规则后,您应当测试它们以验证它们是否按预期方式执行。测试规则的最佳方法是使用 IP 地址调用 DESCRIBE_LOAD_BALANCE_DECISION 函数。此函数评估路由规则并报告 Vertica 如何从 IP 地址路由客户端连接。它使用的逻辑与 Vertica 用来处理客户端连接的逻辑相同,因此结果反映了您将从客户端连接中看到的实际连接负载均衡结果。它还反映了 Vertica 群集的当前状态,因此它不会将连接重定向到已关闭的节点。
以下示例演示了如何测试一组规则。一条规则处理从 192.168.1.0 到 192.168.1.255 范围内的所有连接,而另一条规则处理源自 192 子网的所有连接。第三个调用演示了当没有规则适用于您提供的 IP 地址时会发生什么。
=> SELECT describe_load_balance_decision('192.168.1.25');
describe_load_balance_decision
--------------------------------------------------------------------------------
Describing load balance decision for address [192.168.1.25]
Load balance cache internal version id (node-local): [2]
Considered rule [etl_rule] source ip filter [10.20.100.0/24]... input address
does not match source ip filter for this rule.
Considered rule [internal_clients] source ip filter [192.168.1.0/24]... input
address matches this rule
Matched to load balance group [group_1] the group has policy [ROUNDROBIN]
number of addresses [2]
(0) LB Address: [10.20.100.247]:5433
(1) LB Address: [10.20.100.248]:5433
Chose address at position [1]
Routing table decision: Success. Load balance redirect to: [10.20.100.248] port [5433]
(1 row)
=> SELECT describe_load_balance_decision('192.168.2.25');
describe_load_balance_decision
--------------------------------------------------------------------------------
Describing load balance decision for address [192.168.2.25]
Load balance cache internal version id (node-local): [2]
Considered rule [etl_rule] source ip filter [10.20.100.0/24]... input address
does not match source ip filter for this rule.
Considered rule [internal_clients] source ip filter [192.168.1.0/24]... input
address does not match source ip filter for this rule.
Considered rule [subnet_192] source ip filter [192.0.0.0/8]... input address
matches this rule
Matched to load balance group [group_all] the group has policy [ROUNDROBIN]
number of addresses [3]
(0) LB Address: [10.20.100.247]:5433
(1) LB Address: [10.20.100.248]:5433
(2) LB Address: [10.20.100.249]:5433
Chose address at position [1]
Routing table decision: Success. Load balance redirect to: [10.20.100.248] port [5433]
(1 row)
=> SELECT describe_load_balance_decision('1.2.3.4');
describe_load_balance_decision
--------------------------------------------------------------------------------
Describing load balance decision for address [1.2.3.4]
Load balance cache internal version id (node-local): [2]
Considered rule [etl_rule] source ip filter [10.20.100.0/24]... input address
does not match source ip filter for this rule.
Considered rule [internal_clients] source ip filter [192.168.1.0/24]... input
address does not match source ip filter for this rule.
Considered rule [subnet_192] source ip filter [192.0.0.0/8]... input address
does not match source ip filter for this rule.
Routing table decision: No matching routing rules: input address does not match
any routing rule source filters. Details: [Tried some rules but no matching]
No rules matched. Falling back to classic load balancing.
Classic load balance decision: Classic load balancing considered, but either
the policy was NONE or no target was available. Details: [NONE or invalid]
(1 row)
DESCRIBE_LOAD_BALANCE_DECISION 函数还考虑了群集范围的经典负载均衡设置:
=> SELECT SET_LOAD_BALANCE_POLICY('ROUNDROBIN');
SET_LOAD_BALANCE_POLICY
--------------------------------------------------------------------------------
Successfully changed the client initiator load balancing policy to: roundrobin
(1 row)
=> SELECT DESCRIBE_LOAD_BALANCE_DECISION('1.2.3.4');
describe_load_balance_decision
--------------------------------------------------------------------------------
Describing load balance decision for address [1.2.3.4]
Load balance cache internal version id (node-local): [2]
Considered rule [etl_rule] source ip filter [10.20.100.0/24]... input address
does not match source ip filter for this rule.
Considered rule [internal_clients] source ip filter [192.168.1.0/24]... input
address does not match source ip filter for this rule.
Considered rule [subnet_192] source ip filter [192.0.0.0/8]... input address
does not match source ip filter for this rule.
Routing table decision: No matching routing rules: input address does not
match any routing rule source filters. Details: [Tried some rules but no matching]
No rules matched. Falling back to classic load balancing.
Classic load balance decision: Success. Load balance redirect to: [10.20.100.247]
port [5433]
(1 row)
此函数还可以帮助您调试在使用负载均衡策略后发现的连接问题。例如,如果您注意到一个节点正在处理大量客户端连接,您可以对照您的策略测试客户端 IP 地址,以了解连接不平衡的原因。
以下示例演示了连接负载均衡策略的一些常见用例。
假设您有一个可从两个(或更多)不同网络访问的 Vertica 群集。下面是这种情形的一些示例:
您有一个内部网络和一个外部网络。在这种配置中,您的数据库节点通常有两个或多个 IP 地址,每个地址只能从其中一个网络访问。在云环境中运行 Vertica 时,此配置很常见。在许多情况下,您可以创建适用于所有 IP 地址的全方位规则,然后为内部子网添加其他路由规则。
无论客户端使用 IPv4 还是 IPv6 协议,您都希望客户端进行负载均衡。从数据库的角度来看,IPv4 和 IPv6 连接是不同的网络,因为每个节点都有单独的 IPv4 和 IPv6 IP 地址。
在为可从多个网络访问的数据库创建负载均衡策略时,必须将客户端连接定向到网络上可供它们访问的 IP 地址。最好的解决方案是为分配给节点的每组 IP 地址创建负载均衡组。然后创建路由规则,将客户端连接重定向到可从其所在的网络访问的 IP 地址。
下面的例子:
创建两组网络地址:一组用于内部网络,另一组用于外部网络。
创建两个负载均衡组:一个用于内部网络,一个用于外部网络。
创建三个路由规则:一个用于内部网络,两个用于外部网络。内部路由规则涵盖其中一个外部规则涵盖的一部分网络。
使用内部和外部 IP 地址测试路由规则。
=> CREATE NETWORK ADDRESS node01_int ON v_vmart_node0001 WITH '192.168.0.1';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node01_ext ON v_vmart_node0001 WITH '203.0.113.1';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node02_int ON v_vmart_node0002 WITH '192.168.0.2';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node02_ext ON v_vmart_node0002 WITH '203.0.113.2';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node03_int ON v_vmart_node0003 WITH '192.168.0.3';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node03_ext ON v_vmart_node0003 WITH '203.0.113.3';
CREATE NETWORK ADDRESS
=> CREATE LOAD BALANCE GROUP internal_group WITH ADDRESS node01_int, node02_int, node03_int;
CREATE LOAD BALANCE GROUP
=> CREATE LOAD BALANCE GROUP external_group WITH ADDRESS node01_ext, node02_ext, node03_ext;
CREATE LOAD BALANCE GROUP
=> CREATE ROUTING RULE internal_rule ROUTE '192.168.0.0/24' TO internal_group;
CREATE ROUTING RULE
=> CREATE ROUTING RULE external_rule ROUTE '0.0.0.0/0' TO external_group;
CREATE ROUTING RULE
=> SELECT DESCRIBE_LOAD_BALANCE_DECISION('198.51.100.10');
DESCRIBE_LOAD_BALANCE_DECISION
-------------------------------------------------------------------------------
Describing load balance decision for address [198.51.100.10]
Load balance cache internal version id (node-local): [3]
Considered rule [internal_rule] source ip filter [192.168.0.0/24]... input
address does not match source ip filter for this rule.
Considered rule [external_rule] source ip filter [0.0.0.0/0]... input
address matches this rule
Matched to load balance group [external_group] the group has policy [ROUNDROBIN]
number of addresses [3]
(0) LB Address: [203.0.113.1]:5433
(1) LB Address: [203.0.113.2]:5433
(2) LB Address: [203.0.113.3]:5433
Chose address at position [2]
Routing table decision: Success. Load balance redirect to: [203.0.113.3] port [5433]
(1 row)
=> SELECT DESCRIBE_LOAD_BALANCE_DECISION('198.51.100.10');
DESCRIBE_LOAD_BALANCE_DECISION
-------------------------------------------------------------------------------
Describing load balance decision for address [192.168.0.79]
Load balance cache internal version id (node-local): [3]
Considered rule [internal_rule] source ip filter [192.168.0.0/24]... input
address matches this rule
Matched to load balance group [internal_group] the group has policy [ROUNDROBIN]
number of addresses [3]
(0) LB Address: [192.168.0.1]:5433
(1) LB Address: [192.168.0.3]:5433
(2) LB Address: [192.168.0.2]:5433
Chose address at position [2]
Routing table decision: Success. Load balance redirect to: [192.168.0.2] port
[5433]
(1 row)
您可能希望控制特定类型的客户端使用群集中的哪些节点。例如,您可能希望将执行数据加载任务的客户端限制为一组节点,并保留其余节点来运行查询。以这种方式分离工作负载对于 Eon 模式数据库尤为常见。有关在 Eon 模式数据库中使用负载均衡策略来控制客户端连接到哪个子群集的示例,请参阅控制查询的运行位置。
如果执行某些任务类型的客户端始终源自有限的 IP 地址范围,您可以创建支持工作负载隔离的客户端负载均衡策略。例如,如果用来将数据加载到系统中的客户端始终属于特定子网,则可以创建一个策略来限制这些客户端可以访问哪些节点。
在以下示例中:
通过两个容错组(group_a 和 group_b)来分离 Eon 模式数据库中的工作负载。这些组用作负载均衡组的基础。
ETL 客户端连接全部源自 203.0.113.0/24 子网。
用户连接源自 192.0.0.0 到 199.255.255.255 的范围。
=> CREATE NETWORK ADDRESS node01 ON v_vmart_node0001 WITH '192.0.2.1';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node02 ON v_vmart_node0002 WITH '192.0.2.2';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node03 ON v_vmart_node0003 WITH '192.0.2.3';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node04 ON v_vmart_node0004 WITH '192.0.2.4';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node05 ON v_vmart_node0005 WITH '192.0.2.5';
CREATE NETWORK ADDRESS
^
=> CREATE LOAD BALANCE GROUP lb_users WITH FAULT GROUP group_a FILTER '192.0.2.0/24';
CREATE LOAD BALANCE GROUP
=> CREATE LOAD BALANCE GROUP lb_etl WITH FAULT GROUP group_b FILTER '192.0.2.0/24';
CREATE LOAD BALANCE GROUP
=> CREATE ROUTING RULE users_rule ROUTE '192.0.0.0/5' TO lb_users;
CREATE ROUTING RULE
=> CREATE ROUTING RULE etl_rule ROUTE '203.0.113.0/24' TO lb_etl;
CREATE ROUTING RULE
=> SELECT DESCRIBE_LOAD_BALANCE_DECISION('198.51.200.129');
DESCRIBE_LOAD_BALANCE_DECISION
-------------------------------------------------------------------------------
Describing load balance decision for address [198.51.200.129]
Load balance cache internal version id (node-local): [6]
Considered rule [etl_rule] source ip filter [203.0.113.0/24]... input address
does not match source ip filter for this rule.
Considered rule [users_rule] source ip filter [192.0.0.0/5]... input address
matches this rule
Matched to load balance group [lb_users] the group has policy [ROUNDROBIN]
number of addresses [3]
(0) LB Address: [192.0.2.1]:5433
(1) LB Address: [192.0.2.2]:5433
(2) LB Address: [192.0.2.3]:5433
Chose address at position [1]
Routing table decision: Success. Load balance redirect to: [192.0.2.2] port
[5433]
(1 row)
=> SELECT DESCRIBE_LOAD_BALANCE_DECISION('203.0.113.24');
DESCRIBE_LOAD_BALANCE_DECISION
-------------------------------------------------------------------------------
Describing load balance decision for address [203.0.113.24]
Load balance cache internal version id (node-local): [6]
Considered rule [etl_rule] source ip filter [203.0.113.0/24]... input address
matches this rule
Matched to load balance group [lb_etl] the group has policy [ROUNDROBIN] number
of addresses [2]
(0) LB Address: [192.0.2.4]:5433
(1) LB Address: [192.0.2.5]:5433
Chose address at position [1]
Routing table decision: Success. Load balance redirect to: [192.0.2.5] port
[5433]
(1 row)
=> SELECT DESCRIBE_LOAD_BALANCE_DECISION('10.20.100.25');
DESCRIBE_LOAD_BALANCE_DECISION
-------------------------------------------------------------------------------
Describing load balance decision for address [10.20.100.25]
Load balance cache internal version id (node-local): [6]
Considered rule [etl_rule] source ip filter [203.0.113.0/24]... input address
does not match source ip filter for this rule.
Considered rule [users_rule] source ip filter [192.0.0.0/5]... input address
does not match source ip filter for this rule.
Routing table decision: No matching routing rules: input address does not match
any routing rule source filters. Details: [Tried some rules but no matching]
No rules matched. Falling back to classic load balancing.
Classic load balance decision: Classic load balancing considered, but either the
policy was NONE or no target was available. Details: [NONE or invalid]
(1 row)
如果没有其他负载均衡策略适用于传入连接并且未启用经典负载均衡,Vertica 会尝试应用默认的子群集内部负载均衡策略。有关此规则的说明,请参阅默认子群集内部负载均衡策略。
要启用默认子群集内部负载均衡,必须为子群集中的节点创建网络地址。创建地址后,Vertica 会在没有其他规则适用时尝试应用此规则来对子群集内的连接进行负载均衡。
以下示例确认数据库没有负载均衡组或规则。然后它将可公开访问的网络地址添加到主子群集中的节点。添加这些地址后,Vertica 应用默认的子群集内部负载均衡策略。
=> SELECT * FROM LOAD_BALANCE_GROUPS;
name | policy | filter | type | object_name
------+--------+--------+------+-------------
(0 rows)
=> SELECT * FROM ROUTING_RULES;
name | source_address | destination_name
------+----------------+------------------
(0 rows)
=> CREATE NETWORK ADDRESS node01_ext ON v_verticadb_node0001 WITH '203.0.113.1';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node02_ext ON v_verticadb_node0002 WITH '203.0.113.2';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node03_ext ON v_verticadb_node0003 WITH '203.0.113.3';
CREATE NETWORK ADDRESS
=> SELECT describe_load_balance_decision('11.0.0.100');
describe_load_balance_decision
-----------------------------------------------------------------------------------------------
Describing load balance decision for address [11.0.0.100] on subcluster [default_subcluster]
Load balance cache internal version id (node-local): [2]
Considered rule [auto_rr_default_subcluster] subcluster interior filter [default_subcluster]...
current subcluster matches this rule
Matched to load balance group [auto_lbg_sc_default_subcluster] the group has policy
[ROUNDROBIN] number of addresses [3]
(0) LB Address: [203.0.113.1]:5433
(1) LB Address: [203.0.113.2]:5433
(2) LB Address: [203.0.113.3]:5433
Chose address at position [1]
Routing table decision: Success. Load balance redirect to: [203.0.113.2] port [5433]
(1 row)
连接负载均衡策略将 IPv4 和 IPv6 连接视为不同的网络。要对两种类型的传入客户端连接进行负载均衡,请创建两组网络地址、至少两个负载均衡组和两个负载均衡,对于每个网络地址系列各创建一次。
以下示例为默认子群集创建两个负载均衡策略:一个用于 IPv4 网络地址(192.168.111.31 到 192.168.111.33),另一个用于 IPv6 网络地址(fd9b:1fcc:1dc4:78d3::31 到 fd9b:1fcc:1dc4:78d3::33)。
=> SELECT node_name,node_address,subcluster_name FROM NODES;
node_name | node_address | subcluster_name
----------------------+----------------+--------------------
v_verticadb_node0001 | 192.168.111.31 | default_subcluster
v_verticadb_node0002 | 192.168.111.32 | default_subcluster
v_verticadb_node0003 | 192.168.111.33 | default_subcluster
=> CREATE NETWORK ADDRESS node01 ON v_verticadb_node0001 WITH
'192.168.111.31';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node01_ipv6 ON v_verticadb_node0001 WITH
'fd9b:1fcc:1dc4:78d3::31';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node02 ON v_verticadb_node0002 WITH
'192.168.111.32';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node02_ipv6 ON v_verticadb_node0002 WITH
'fd9b:1fcc:1dc4:78d3::32';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node03 ON v_verticadb_node0003 WITH
'192.168.111.33';
CREATE NETWORK ADDRESS
=> CREATE NETWORK ADDRESS node03_ipv6 ON v_verticadb_node0003 WITH
'fd9b:1fcc:1dc4:78d3::33';
CREATE NETWORK ADDRESS
=> CREATE LOAD BALANCE GROUP group_ipv4 WITH SUBCLUSTER default_subcluster
FILTER '192.168.111.0/24';
CREATE LOAD BALANCE GROUP
=> CREATE LOAD BALANCE GROUP group_ipv6 WITH SUBCLUSTER default_subcluster
FILTER 'fd9b:1fcc:1dc4:78d3::0/64';
CREATE LOAD BALANCE GROUP
=> CREATE ROUTING RULE all_ipv4 route '0.0.0.0/0' TO group_ipv4;
CREATE ROUTING RULE
=> CREATE ROUTING RULE all_ipv6 route '0::0/0' TO group_ipv6;
CREATE ROUTING RULE
=> SELECT describe_load_balance_decision('203.0.113.50');
describe_load_balance_decision
-----------------------------------------------------------------------------------------------
Describing load balance decision for address [203.0.113.50] on subcluster [default_subcluster]
Load balance cache internal version id (node-local): [3]
Considered rule [all_ipv4] source ip filter [0.0.0.0/0]... input address matches this rule
Matched to load balance group [ group_ipv4] the group has policy [ROUNDROBIN] number of addresses [3]
(0) LB Address: [192.168.111.31]:5433
(1) LB Address: [192.168.111.32]:5433
(2) LB Address: [192.168.111.33]:5433
Chose address at position [2]
Routing table decision: Success. Load balance redirect to: [192.168.111.33] port [5433]
(1 row)
=> SELECT describe_load_balance_decision('2001:0DB8:EA04:8F2C::1');
describe_load_balance_decision
---------------------------------------------------------------------------------------------------------
Describing load balance decision for address [2001:0DB8:EA04:8F2C::1] on subcluster [default_subcluster]
Load balance cache internal version id (node-local): [3]
Considered rule [all_ipv4] source ip filter [0.0.0.0/0]... input address does not match source ip filter for this rule.
Considered rule [all_ipv6] source ip filter [0::0/0]... input address matches this rule
Matched to load balance group [ group_ipv6] the group has policy [ROUNDROBIN] number of addresses [3]
(0) LB Address: [fd9b:1fcc:1dc4:78d3::31]:5433
(1) LB Address: [fd9b:1fcc:1dc4:78d3::32]:5433
(2) LB Address: [fd9b:1fcc:1dc4:78d3::33]:5433
Chose address at position [2]
Routing table decision: Success. Load balance redirect to: [fd9b:1fcc:1dc4:78d3::33] port [5433]
(1 row)
有关使用连接负载均衡的其他示例,请参阅以下主题:
查询 V_CATALOG 架构中的以下系统表以查看数据库中定义的负载均衡策略:
NETWORK_ADDRESSES 列出数据库中定义的所有网络地址。
LOAD_BALANCE_GROUPS 列出负载均衡组的内容。
ROUTING_RULES 列出数据库中定义的所有路由规则。
此示例演示了如何查询每个负载均衡策略系统表。
=> \x
Expanded display is on.
=> SELECT * FROM V_CATALOG.NETWORK_ADDRESSES;
-[ RECORD 1 ]----+-----------------
name | node01
node | v_vmart_node0001
address | 10.20.100.247
port | 5433
address_family | ipv4
is_enabled | t
is_auto_detected | f
-[ RECORD 2 ]----+-----------------
name | node02
node | v_vmart_node0002
address | 10.20.100.248
port | 5433
address_family | ipv4
is_enabled | t
is_auto_detected | f
-[ RECORD 3 ]----+-----------------
name | node03
node | v_vmart_node0003
address | 10.20.100.249
port | 5433
address_family | ipv4
is_enabled | t
is_auto_detected | f
-[ RECORD 4 ]----+-----------------
name | alt_node1
node | v_vmart_node0001
address | 192.168.1.200
port | 8080
address_family | ipv4
is_enabled | t
is_auto_detected | f
-[ RECORD 5 ]----+-----------------
name | test_addr
node | v_vmart_node0001
address | 192.168.1.100
port | 4000
address_family | ipv4
is_enabled | t
is_auto_detected | f
=> SELECT * FROM LOAD_BALANCE_GROUPS;
-[ RECORD 1 ]----------------------
name | group_all
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node01
-[ RECORD 2 ]----------------------
name | group_all
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node02
-[ RECORD 3 ]----------------------
name | group_all
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node03
-[ RECORD 4 ]----------------------
name | group_1
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node01
-[ RECORD 5 ]----------------------
name | group_1
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node02
-[ RECORD 6 ]----------------------
name | group_2
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node01
-[ RECORD 7 ]----------------------
name | group_2
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node02
-[ RECORD 8 ]----------------------
name | group_2
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node03
-[ RECORD 9 ]----------------------
name | etl_group
policy | ROUNDROBIN
filter |
type | Network Address Group
object_name | node01
=> SELECT * FROM ROUTING_RULES;
-[ RECORD 1 ]----+-----------------
name | internal_clients
source_address | 192.168.1.0/24
destination_name | group_1
-[ RECORD 2 ]----+-----------------
name | etl_rule
source_address | 10.20.100.0/24
destination_name | etl_group
-[ RECORD 3 ]----+-----------------
name | subnet_192
source_address | 192.0.0.0/8
destination_name | group_all
创建负载均衡策略后,您可以使用以下语句对其进行维护:
ALTER NETWORK ADDRESS 允许您重命名和更改 IP 地址以及启用或禁用网络地址。
ALTER LOAD BALANCE GROUP 允许您重命名、添加或移除网络地址或容错组;更改容错组 IP 地址筛选;更改负载均衡组的策略。
ALTER ROUTING RULE 允许您重命名和更改源 IP 地址以及规则的目标负载均衡组。
有关示例,请参阅这些语句的参考页面。
您还可以使用以下语句删除现有的负载均衡策略对象:
与传统数据库将数据存储在表中不同,Vertica 会将表数据以物理方式存储在 投影(即表列的集合)中。
投影以优化查询执行的格式存储数据。与实体化视图类似,它们会将结果集存储在磁盘上,而不是每次在查询中使用时都进行计算。Vertica 会使用更新数据或新数据自动刷新这些结果集。
投影提供了以下优势:
对数据进行压缩和编码,以减少存储空间。Vertica 也会尽可能使用编码数据表示形式执行操作,以避免解码成本。这种结合了压缩和编码的方法可在优化磁盘空间的同时最大限度地提高查询性能。
有助于将数据分布到整个数据库群集中。根据大小不同,投影可通过分段或复制形式分布在各个群集节点中。例如,大型表的投影可分段后分布到所有节点上。小型表的未分段投影可以复制到所有节点上。
对最终用户透明。Vertica 查询优化器会自动选取最佳投影来执行给定查询。
提供高可用性和恢复能力。Vertica 会将表列复制到群集中的至少 K+1 个节点上。如果一台计算机在 K-Safe 环境中发生故障,则数据库会使用其余节点上复制的数据继续运行。当该节点恢复正常运行时,它会自动查询其他节点,以恢复数据和丢失的对象。有关详细信息,请参阅 使用容错组的高可用性和 使用投影的高可用性。
Vertica 表通常具有多个投影,每个投影都定义为包含不同的内容。对于给定表,投影内容的范围和组织可能会有所不同。这些差异通常可以分为以下几种投影类型:
对于数据库中的每个表,Vertica 需要至少一个包含表中所有列的超投影。在没有查询特定投影的情况下,Vertica 使用表的超投影,它可以支持任何查询和 DML 操作。
在某些条件下,Vertica 会在创建表时立即自动创建表的超投影。首次向该表加载数据时,如果其中不存在超投影,Vertica 还将创建一个超投影。
CREATE PROJECTION
如果它指定包括所有表列,则可以创建超投影。一个表可以有多个超投影。
虽然超投影可以支持对表的所有查询,但它们并不能促进特定查询的最佳执行。
查询特定的投影是仅包含处理给定查询所需的表列子集的投影。查询特定的投影可显著提高查询的性能,因为已针对查询优化这些投影。
当使用已包含聚合数据的投影时,包括表达式或聚合函数(例如 SUM 和 COUNT)的查询可以更有效地执行。对于涉及大量数据的查询,情况更是如此。
Vertica 提供几种类型的投影来存储从聚合函数或表达式中返回的数据。
Top-K 投影:一种实时聚合投影,用于从选定行的一个分区中返回前 k 行。创建一个满足 Top-K 查询条件的 Top-K 投影。
预聚合 UDTF 结果的投影:调用用户定义的转换函数 (UDTF) 的实时聚合投影。为了在查询这种类型的投影时最大限度降低开销,Vertica 会在后台处理 UDTF 函数,并将其结果存储在磁盘上。
包含表达式的投影:具有通过锚表列计算列值的列的投影。
有关详细信息,请参阅投影中的预聚合数据。
Vertica 支持两种投影创建方法:Database Designer 和 CREATE PROJECTION 语句。
Vertica 建议使用 Database Designer 设计物理架构,方法是在具有代表性的数据示例上运行它。Database Designer 生成用于创建投影的 SQL,如下所示:
有关详细信息,请参阅创建数据库设计。
CREATE PROJECTION
定义一个投影,如以下示例所示:
=> CREATE PROJECTION retail_sales_fact_p (
store_key ENCODING RLE,
pos_transaction_number ENCODING RLE,
sales_dollar_amount,
cost_dollar_amount )
AS SELECT
store_key,
pos_transaction_number,
sales_dollar_amount,
cost_dollar_amount
FROM store.store_sales_fact
ORDER BY store_key
SEGMENTED BY HASH(pos_transaction_number) ALL NODES;
投影定义包括以下组成部分:
SQL 语句的这一部分列出投影中的每一列,并定义每一列的编码。Vertica 支持编码数据,这有助于查询执行产生较少的磁盘 I/O。
CREATE PROJECTION retail_sales_fact_P (
store_key ENCODING RLE,
pos_transaction_number ENCODING RLE,
sales_dollar_amount,
cost_dollar_amount )
投影的基本查询子句确定要包含在投影中的列。
AS SELECT
store_key,
pos_transaction_number,
sales_dollar_amount,
cost_dollar_amount
投影的 ORDER BY
子句确定如何对投影数据进行排序。排序顺序可确定按照逻辑分组的值的位置,以便磁盘读取可以一次识别许多结果。为了获得最佳性能,请勿按 LONG VARBINARY 和 LONG VARCHAR 列对投影进行排序。有关详细信息,请参阅ORDER BY 子句。
ORDER BY store_key
投影的分段子句指定如何在数据库中的所有节点之间分布投影数据。均匀分布负载有助于最大限度地访问投影数据。对于大型表,使用 SEGMENTED BY HASH
按分段分布投影数据。例如:
SEGMENTED BY HASH(pos_transaction_number) ALL NODES;
对于小型表,使用 UNSEGMENTED
关键字复制表数据。Vertica 会在所有群集节点上创建未分段投影的相同副本。复制可确保较高的可用性和恢复能力。
为了获得最大性能,请勿按 LONG VARBINARY 和 LONG VARCHAR 列对投影进行分段。
有关设计注意事项的详细信息,请参阅创建自定义设计。
Vertica 根据以下约定标识投影,其中 proj‑basename 是由 CREATE PROJECTION 分配给此投影的名称。
未分段投影遵循以下命名约定:
在企业模式下,分段投影使用以下命名约定:
proj‑basename_boffset
此名称标识分段投影的伙伴实例投影,其中 offset 是投影相对于所有其他伙伴实例投影的节点位置。所有伙伴实例投影共享相同的投影基本名称。例如:
=> SELECT projection_basename, projection_name FROM projections WHERE anchor_table_name = 'store_orders';
projection_basename | projection_name
---------------------+-----------------
store_orders | store_orders_b0
store_orders | store_orders_b1
(2 rows)
一个例外情况是:Vertica 使用以下约定来命名实时聚合投影:proj‑basename、
proj‑basename_b1
,依此类推。
在 Eon 模式下,分段投影使用以下命名约定:
proj‑basename
Vertica 在以下两种情况下使用相同的逻辑重命名现有投影:
使用 ALTER TABLE...RENAME TABLE 重命名表。
使用 CREATE TABLE LIKE...INCLUDING PROJECTIONS 从现有表创建一个表。
在这两种情况下,Vertica 使用以下算法重命名投影:
循环访问锚定在重命名表或新表上的所有投影,并检查其名称是否以原始表名称为前缀:
否:保留投影名称
是:重命名投影
如果是,比较原始表名称和投影基本名称:
如果新的基本名称与原始表名称相同,则将基本名称替换为表和投影名称中的新表名称。
如果新的基本名称以原始表名称为前缀,则将前缀替换为新的表名称,移除附加到旧基本名称的任何版本字符串(例如
old‑basename_v1
),并使用新的基本名称生成投影名称。
检查新的投影名称是否已经存在。如果不存在,则保存它们。否则,通过根据需要将版本号附加到新的基本名称(
new‑basename_v1
、
new‑basename_v2
,依此类推)来解决名称冲突。
自动投影始终是超投影:
=> CREATE TABLE store.store_dimension
store_key int NOT NULL,
store_name varchar(64),
...
) UNSEGMENTED ALL NODES;
CREATE TABLE
=> COPY store.store_dim FROM '/home/dbadmin/store_dimension_data.txt';
50
=> SELECT anchor_table_name, projection_basename, projection_name FROM projections WHERE anchor_table_name = 'store_dimension';
anchor_table_name | projection_basename | projection_name
-------------------+---------------------+-----------------------
store_dimension | store_dimension | store_dimension_super
store_dimension | store_dimension | store_dimension_super
store_dimension | store_dimension | store_dimension_super
(3 rows)
未分段投影名称在所有节点上都具有 _unseg
后缀:
=> CREATE TABLE store.store_dimension( store_key int NOT NULL, store_name varchar(64), ... ); CREATE TABLE => CREATE PROJECTION store_dimension AS SELECT * FROM store.store_dimension UNSEGMENTED ALL NODES; WARNING 6922: Projection name was changed to store_dimension_unseg because it conflicts with the basename of the table store_dimension CREATE PROJECTION => SELECT anchor_table_name, projection_basename, projection_name FROM projections WHERE anchor_table_name = 'store_dimension'; anchor_table_name | projection_basename | projection_name -------------------+---------------------+----------------------- store_dimension | store_dimension | store_dimension_unseg store_dimension | store_dimension | store_dimension_unseg store_dimension | store_dimension | store_dimension_unseg (3 rows)
以下示例创建分段表 testRenameSeg
并使用数据填充它:
=> CREATE TABLE testRenameSeg (a int, b int);
CREATE TABLE
dbadmin=> INSERT INTO testRenameSeg VALUES (1,2);
OUTPUT
--------
1
(1 row)
dbadmin=> COMMIT;
COMMIT
Vertica 会为此表自动创建两个伙伴实例超投影:
=> \dj testRename*
List of projections
Schema | Name | Owner | Node | Comment
--------+-----------------------+---------+------------------+---------
public | testRenameSeg_b0 | dbadmin | |
public | testRenameSeg_b1 | dbadmin | |
以下 CREATE PROJECTION
语句显式为表创建额外的投影:
=> CREATE PROJECTION nameTestRenameSeg_p AS SELECT * FROM testRenameSeg;
=> CREATE PROJECTION testRenameSeg_p AS SELECT * FROM testRenameSeg;
=> CREATE PROJECTION testRenameSeg_pLap AS SELECT b, MAX(a) a FROM testRenameSeg GROUP BY b;
=> CREATE PROJECTION newTestRenameSeg AS SELECT * FROM testRenameSeg;
=> \dj *testRenameSeg*
List of projections
Schema | Name | Owner | Node | Comment
--------+------------------------+---------+------+---------
public | nameTestRenameSeg_p_b0 | dbadmin | |
public | nameTestRenameSeg_p_b1 | dbadmin | |
public | newTestRenameSeg_b0 | dbadmin | |
public | newTestRenameSeg_b1 | dbadmin | |
public | testRenameSeg_b0 | dbadmin | |
public | testRenameSeg_b1 | dbadmin | |
public | testRenameSeg_pLap | dbadmin | |
public | testRenameSeg_pLap_b1 | dbadmin | |
public | testRenameSeg_p_b0 | dbadmin | |
public | testRenameSeg_p_b1 | dbadmin | |
(10 rows)
如果重命名锚表,Vertica 还会重命名其投影:
=> ALTER TABLE testRenameSeg RENAME TO newTestRenameSeg;
ALTER TABLEn=> \dj *testRenameSeg*
List of projections
Schema | Name | Owner | Node | Comment
--------+--------------------------+---------+------+---------
public | nameTestRenameSeg_p_b0 | dbadmin | |
public | nameTestRenameSeg_p_b1 | dbadmin | |
public | newTestRenameSeg_b0 | dbadmin | |
public | newTestRenameSeg_b1 | dbadmin | |
public | newTestRenameSeg_pLap_b0 | dbadmin | |
public | newTestRenameSeg_pLap_b1 | dbadmin | |
public | newTestRenameSeg_p_b0 | dbadmin | |
public | newTestRenameSeg_p_b1 | dbadmin | |
public | newTestRenameSeg_v1_b0 | dbadmin | |
public | newTestRenameSeg_v1_b1 | dbadmin | |
(10 rows)
两组伙伴实例投影未重命名,因为它们的名称不以原始表名称为前缀:
nameTestRenameSeg_p_b0
nameTestRenameSeg_p_b1
newTestRenameSeg_b0
newTestRenameSeg_b1
当重命名其他投影时,Vertica 发现表的超投影(最初是 testRenameSeg
)与现有投影 newTestRenameSeg
之间存在潜在冲突。它通过将版本号 _v1
和 _v2
附加到超投影的新名称解决了此冲突:
newTestRenameSeg_v1_b0
newTestRenameSeg_v1_b1
自动投影是 Vertica 为临时表和持久表自动生成的 超投影。通常,如果没有为表定义任何投影,Vertica 会在您首次将数据加载到该表时自动为该表创建投影。以下规则适用于所有自动投影:
Vertica 在与表相同的架构中创建自动投影。
自动投影符合表的创建语句中指定的编码、排序顺序、分段和 K-safety。
如果表的创建语句包含 AS SELECT 子句,则 Vertica 使用投影定义的基础查询的某些属性。
创建自动投影的条件会依据表是临时表还是持久表而有所不同:
如果 CREATE TABLE
或 CREATE TEMPORARY TABLE
省略分段(SEGMENTED BY
或 UNSEGMENTED
)或者 ORDER BY
子句,则 Vertica 按如下方式对自动投影进行分段和排序:
如果表的创建语句省略分段(SEGMENTED BY 或 UNSEGMENTED
)子句,则 Vertica 检查配置参数 SegmentAutoProjection
以确定是创建分段的自动投影还是未分段的自动投影。默认情况下,此参数设置为 1(启用)。
如果启用了 SegmentAutoProjection
,并且表的创建语句也省略 ORDER BY
子句,则 Vertica 会根据表的创建方式对表的自动投影进行分段和排序:
如果 CREATE [TEMPORARY] TABLE
包含 AS SELECT 子句且查询输出已分段,则自动投影将使用同一分段。如果结果集已排序,则投影将使用相同的排列顺序。
在所有其他情况下,Vertica 会对表列约束求值,以确定如何对投影排序和分段,如下所示:
例如,下表是在没有主键或外键的情况下定义的:
=> CREATE TABLE testAutoProj(c10 char (10), v1 varchar(140) DEFAULT v2||v3, i int, c5 char(5), v3 varchar (80), d timestamp, v2 varchar(60), c1 char(1));
CREATE TABLE
=> INSERT INTO testAutoProj VALUES
('1234567890',
DEFAULT,
1,
'abcde',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ',
current_timestamp,
'incididunt ut labore et dolore magna aliqua. Eu scelerisque',
'a');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
在 INSERT 语句首次将数据加载到该表之前,Vertica 自动为该表创建一个超投影:
=> SELECT export_objects('', 'testAutoProj_b0');
--------------------------------------------------------
CREATE PROJECTION public.testAutoProj_b0 /*+basename(testAutoProj),createtype(L)*/
( c10, v1, i, c5, v3, d, v2, c1 )
AS
SELECT testAutoProj.c10,
testAutoProj.v1,
testAutoProj.i,
testAutoProj.c5,
testAutoProj.v3,
testAutoProj.d,
testAutoProj.v2,
testAutoProj.c1
FROM public.testAutoProj
ORDER BY testAutoProj.c10,
testAutoProj.v1,
testAutoProj.i,
testAutoProj.c5,
testAutoProj.v3,
testAutoProj.d,
testAutoProj.v2,
testAutoProj.c1
SEGMENTED BY hash(testAutoProj.i, testAutoProj.c5, testAutoProj.d, testAutoProj.c1, testAutoProj.c10, testAutoProj.v2, testAutoProj.v3, testAutoProj.v1) ALL NODES OFFSET 0;
SELECT MARK_DESIGN_KSAFE(1);
(1 row)
在许多情况下,维度表相对较小,因此您无需将它们分段。相应地,您应该设计一个 K-safe 数据库,以便可以复制其维度表的投影并且无需在所有群集节点上进行分段。您可以使用包括子句 UNSEGMENTED ALL NODES
的
CREATE PROJECTION
语句创建未分段投影。此子句用于指定在所有群集节点上创建投影的相同实例。
以下示例展示了如何为表 store.store_dimension
创建未分段投影:
=> CREATE PROJECTION store.store_dimension_proj (storekey, name, city, state)
AS SELECT store_key, store_name, store_city, store_state
FROM store.store_dimension
UNSEGMENTED ALL NODES;
CREATE PROJECTION
Vertica 使用相同的名称来标识未分段投影的所有实例 — 在此示例中为 store.store_dimension_proj
。关键字 ALL NODES
指定在所有节点上复制投影:
=> \dj store.store_dimension_proj
List of projections
Schema | Name | Owner | Node | Comment
--------+----------------------+---------+------------------+---------
store | store_dimension_proj | dbadmin | v_vmart_node0001 |
store | store_dimension_proj | dbadmin | v_vmart_node0002 |
store | store_dimension_proj | dbadmin | v_vmart_node0003 |
(3 rows)
有关投影名称约定的详细信息,请参阅投影命名。
通常,您会为大型事实表创建分段投影。Vertica 将分段投影拆分为大小相似的块(段),并将这些段均匀地分布在整个群集中。系统 K-safety 确定在不同节点上创建和维护每个段的多少个副本(伙伴示例)。
投影分段可实现以下目标:
确保高可用性和恢复。
将查询执行工作负载分散到多个节点上。
允许每个节点针对不同查询工作负载进行优化。
哈希分段
Vertica 使用哈希分段对大型投影进行分段。哈希分段可基于内置的哈希函数对投影进行分段。该内置哈希函数可使多个节点中的数据实现正态分布,从而优化查询的执行。在投影中,要进行哈希的数据由一列或多列值组成,每一列都包含大量唯一值,并且值的分布偏移程度在可接受的范围内。主键列通常满足这些条件,因此它们通常用作哈希函数实参。
可以使用包含 SEGMENTED BY
子句的
CREATE PROJECTION
语句创建分段投影。
以下 CREATE PROJECTION
语句将创建投影 public.employee_dimension_super
。它指定包含表 public.employee_dimension
中的所有列。哈希分段子句将调用 Vertica HASH
函数,对列 employee_key
中的投影数据进行分段;此外,它还包括 ALL NODES
子句,指定在群集中的所有节点上均匀分布投影数据:
=> CREATE PROJECTION public.employee_dimension_super
AS SELECT * FROM public.employee_dimension
ORDER BY employee_key
SEGMENTED BY hash(employee_key) ALL NODES;
如果数据库是 K-safe 的,Vertica 会为此投影创建多个伙伴实例,并将它们分布在群集中的不同节点上。在本例中,数据库 K-safety 设置为 1,因此 Vertica 会为此投影创建两个伙伴实例。它使用投影名称 employee_dimension_super
作为所创建的两个伙伴实例标识符的基本名称,在本例中为 employee_dimension_super_b0
和 employee_dimension_super_b1
:
=> SELECT projection_name FROM projections WHERE projection_basename='employee_dimension_super';
projection_name
-----------------------------
employee_dimension_super_b0
employee_dimension_super_b1
(2 rows)
对于分段投影和未分段投影,K-safety 的实施方式有所不同,如下所述。示例假定在 3 节点数据库中将数据库 K-safety 设置为 1,并对两个表使用投影:
store.store_orders_fact
是一个大型事实表。此表的投影应为分段投影。Vertica 会在群集中均匀地分布投影段。
store.store_dimension
是一个较小的维度表。此表的投影应为未分段投影。Vertica 会在每个群集节点上复制此投影的完整实例。
在 K-safe 数据库中,数据库需要每个投影段的 K+1 个实例或伙伴实例。例如,如果数据库 K-safety 设置为 1,则数据库要求每个投影段有两个实例或伙伴实例。
可以通过
CREATE PROJECTION
选项 KSAFE
设置各个分段投影的 K-safety。投影 K-safety 必须大于等于数据库 K-safety。如果省略设置 KSAFE
,投影将从数据库获取 K-safety。
以下
CREATE PROJECTION
为事实表 store.store_orders_fact
定义分段投影:
=> CREATE PROJECTION store.store_orders_fact
(prodkey, ordernum, storekey, total)
AS SELECT product_key, order_number, store_key, quantity_ordered*unit_price
FROM store.store_orders_fact
SEGMENTED BY HASH(product_key, order_number) ALL NODES KSAFE 1;
CREATE PROJECTION
CREATE PROJECTION
语句中的以下关键字与设置投影 K-safety 相关:
在 K-safe 数据库中,必须在所有节点上复制未分段投影。因此,未分段投影的 CREATE PROJECTION
语句必须包括分段子句 UNSEGMENTED ALL NODES
。这指示 Vertica 在所有群集节点上创建投影的相同实例(伙伴实例)。如果在单个节点上创建未分段投影,则 Vertica 会认为它不安全,并且不会使用它。
以下示例展示了如何为表 store.store_dimension
创建未分段投影:
=> CREATE PROJECTION store.store_dimension_proj (storekey, name, city, state)
AS SELECT store_key, store_name, store_city, store_state
FROM store.store_dimension
UNSEGMENTED ALL NODES;
CREATE PROJECTION
Vertica 使用相同的名称来标识未分段投影的所有实例 — 在此示例中为 store.store_dimension_proj
。关键字 ALL NODES
指定在所有节点上复制投影:
=> \dj store.store_dimension_proj
List of projections
Schema | Name | Owner | Node | Comment
--------+----------------------+---------+------------------+---------
store | store_dimension_proj | dbadmin | v_vmart_node0001 |
store | store_dimension_proj | dbadmin | v_vmart_node0002 |
store | store_dimension_proj | dbadmin | v_vmart_node0003 |
(3 rows)
有关投影名称约定的详细信息,请参阅投影命名。
Vertica 支持指定分区键范围的投影。默认情况下,投影存储分区表数据的所有行。随着时间的推移,此要求可能会产生越来越多的开销:
您可以通过为分区表创建投影,指定相对较窄的分区键范围,最大程度地减少这些问题。例如,表 store_orders
按 order_date
进行分区,如下所示:
=> CREATE TABLE public.store_orders(order_no int, order_date timestamp NOT NULL, shipper varchar(20), ship_date date);
CREATE TABLE
=> ALTER TABLE store_orders PARTITION BY order_date::DATE GROUP BY date_trunc('month', (order_date)::DATE);
ALTER TABLE
如果需要,可以创建 store_orders
的投影,指定表的连续分区键范围。在以下示例中,投影 ytd_orders
指定仅包括从一年的第一天开始下单的订单:
=> CREATE PROJECTION ytd_orders AS SELECT * FROM store_orders ORDER BY order_date
ON PARTITION RANGE BETWEEN date_trunc('year',now())::date AND NULL;
WARNING 4468: Projection <public.ytd_orders_b0> is not available for query processing. Execute the select start_refresh() function to copy data into this projection.
The projection must have a sufficient number of buddy projections and all nodes must be up before starting a refresh
WARNING 4468: Projection <public.ytd_orders_b1> is not available for query processing. Execute the select start_refresh() function to copy data into this projection.
The projection must have a sufficient number of buddy projections and all nodes must be up before starting a refresh
CREATE PROJECTION
=> SELECT refresh();
refresh
---------------------------------------------------------------------------------------
Refresh completed with the following outcomes:
Projection Name: [Anchor Table] [Status] [Refresh Method] [Error Count] [Duration (sec)]
----------------------------------------------------------------------------------------
"public"."ytd_orders_b1": [store_orders] [refreshed] [scratch] [0] [0]
"public"."ytd_orders_b0": [store_orders] [refreshed] [scratch] [0] [0]
(1 row)
每个 ytd_orders
伙伴实例投影仅需要每节点 7 个 ROS 容器,而锚表的超投影需要 77 个容器:
=> SELECT COUNT (DISTINCT ros_id) NumROS, projection_name, node_name FROM PARTITIONS WHERE projection_name ilike 'store_orders_b%' GROUP BY node_name, projection_name ORDER BY node_name;
NumROS | projection_name | node_name
--------+-----------------+------------------
77 | store_orders_b0 | v_vmart_node0001
77 | store_orders_b1 | v_vmart_node0001
77 | store_orders_b0 | v_vmart_node0002
77 | store_orders_b1 | v_vmart_node0002
77 | store_orders_b0 | v_vmart_node0003
77 | store_orders_b1 | v_vmart_node0003
(6 rows)
=> SELECT COUNT (DISTINCT ros_id) NumROS, projection_name, node_name FROM PARTITIONS WHERE projection_name ilike 'ytd_orders%' GROUP BY node_name, projection_name ORDER BY node_name;
NumROS | projection_name | node_name
--------+-----------------+------------------
7 | ytd_orders_b0 | v_vmart_node0001
7 | ytd_orders_b1 | v_vmart_node0001
7 | ytd_orders_b0 | v_vmart_node0002
7 | ytd_orders_b1 | v_vmart_node0002
7 | ytd_orders_b0 | v_vmart_node0003
7 | ytd_orders_b1 | v_vmart_node0003
(6 rows)
分区范围表达式必须符合适用于表级分区的要求,例如,分区键格式和数据类型验证。
以下要求和约束特别适用于分区范围投影:
锚表必须已经分区。
分区范围表达式必须与表的分区表达式兼容。
第一个范围表达式必须解析为小于或等于第二个表达式的分区键。
如果投影未分段,则锚表的至少一个超投影也必须未分段。如果不是,Vertica 会将投影添加到数据库编录,但会发出警告,指出此投影无法用于处理查询,直到您创建未分段的超投影。
分区范围表达式不支持子查询。
如前所述,分区范围投影取决于按同一表达式分区的锚表。如果从投影的锚表中移除表分区,Vertica 会删除从属投影。同样,如果修改锚表的 partition 子句,Vertica 也会删除投影。
以下例外情况适用:如果锚表的新 partition 子句保持分区表达式不变,则从属投影不会被删除,并且仍可用于查询。例如,表 store_orders
及其投影 ytd_orders
最初按如下方式分区:
=> ALTER TABLE store_orders PARTITION BY order_date::DATE GROUP BY DATE_TRUNC('month', (order_date)::DATE);
...
=> CREATE PROJECTION ytd_orders AS SELECT * FROM store_orders ORDER BY order_date
ON PARTITION RANGE BETWEEN date_trunc('year',now())::date AND NULL;
如果现在修改 store_orders
以使用分层分区,Vertica 会对表数据及其分区范围投影进行重新分区:
=> ALTER TABLE store_orders PARTITION BY order_date::DATE GROUP BY CALENDAR_HIERARCHY_DAY(order_date::DATE, 2, 2) REORGANIZE;
NOTICE 4785: Started background repartition table task
ALTER TABLE
由于 store_orders
和 ytd_orders
投影继续按照 order_date
列进行分区,因此 ytd_orders
投影仍然有效。此外,投影数据的范围保持不变,因此投影不需要刷新。但是在后台,Tuple Mover 会根据其锚表的新分层分区以静默方式重新组织投影 ROS 容器:
=> SELECT COUNT (DISTINCT ros_id) NumROS, projection_name, node_name FROM PARTITIONS WHERE projection_name ilike 'ytd_orders%' GROUP BY node_name, projection_name ORDER BY node_name;
NumROS | projection_name | node_name
--------+-----------------+------------------
38 | ytd_orders_b0 | v_vmart_node0001
38 | ytd_orders_b1 | v_vmart_node0001
38 | ytd_orders_b0 | v_vmart_node0002
38 | ytd_orders_b1 | v_vmart_node0002
38 | ytd_orders_b0 | v_vmart_node0003
38 | ytd_orders_b1 | v_vmart_node0003
(6 rows)
可以使用 ALTER PROJECTION 修改投影的分区范围。如果新范围位于前一个范围内,则不需要刷新。否则,需要先刷新,然后才能使用投影处理查询。
例如,投影 ytd_orders
之前指定了一个从当年第一天开始的分区范围。以下 ALTER PROJECTION 语句将范围更改为从去年 10 月 1 日开始。新范围先于前一个范围,因此 Vertica 会发出刷新指定的投影 ytd_orders_b0
及其伙伴实例投影 ytd_orders_b1
的警告:
=> ALTER PROJECTION ytd_orders_b0 ON PARTITION RANGE BETWEEN
add_months(date_trunc('year',now())::date, -3) AND NULL;
WARNING 10001: Projection "public.ytd_orders_b0" changed to out-of-date state as new partition range is not covered by existing partition range
HINT: Call refresh() or start_refresh() to refresh the projections
WARNING 10001: Projection "public.ytd_orders_b1" changed to out-of-date state as new partition range is not covered by existing partition range
HINT: Call refresh() or start_refresh() to refresh the projections
ALTER PROJECTION
投影的分区范围可以是静态范围,由始终解析为相同值的表达式设置。例如,以下投影指定一个介于 21/06/01 和 21/06/30 之间的静态范围:
=> CREATE PROJECTION last_month_orders AS SELECT * FROM store_orders ORDER BY order_date ON PARTITION RANGE BETWEEN
'2021-06-01' AND '2021-06-30';
...
CREATE PROJECTION
更典型的是,分区范围表达式使用稳定的日期函数(例如 ADD_MONTHS、DATE_TRUNC 和 NOW)来指定动态范围。在以下示例中,分区范围设置为从上个月的第一天开始。随着日历日期推进到下个月,分区范围也随之向前推进:
=> ALTER PROJECTION last_month_orders_b0 ON PARTITION RANGE BETWEEN
add_months(date_trunc('month', now())::date, -1) AND NULL;
ALTER PROJECTION
最佳实践是,始终通过将最大范围设置为 NULL 来将其保留为无限期,并依靠查询来确定要提取的最大数据量。例如,提取上个月下单的所有物料订单的查询可能如下所示:
=> SELECT * from store_orders WHERE order_date BETWEEN
add_months(date_trunc('month', now())::date, -1) AND
add_months(date_trunc('month', now())::date + dayofmonth(now()), -1);
为执行此查询而生成的查询计划表明它使用的是分区范围投影 last_month_orders
:
=> EXPLAIN SELECT * from store_orders WHERE order_date BETWEEN
add_months(date_trunc('month', now())::date, -1) AND
add_months(date_trunc('month', now())::date + dayofmonth(now()), -1);
Access Path:
+-STORAGE ACCESS for store_orders [Cost: 34, Rows: 763 (NO STATISTICS)] (PATH ID: 1)
| Projection: public.last_month_orders_b0
| Materialize: store_orders.order_date, store_orders.order_no, store_orders.shipper, store_orders.ship_date
| Filter: ((store_orders.order_date >= '2021-06-01 00:00:00'::timestamp(0)) AND (store_orders.order_date <= '2021-06-3
0 00:00:00'::timestamp(0)))
| Execute on: All Nodes
Projection Maintainer 是一项后台服务,它每小时使用投影范围表达式检查一次投影。如果投影中任一表达式的值发生变化,Projection Maintainer 将比较 PARTITION_RANGE_MIN
和 PARTITION_RANGE_MAX
中的新旧值,以确定分区范围是缩小还是扩大了:
如果分区范围在任一方向缩小(即 PARTITION_RANGE_MIN 大于其先前值或 PARTITION_RANGE_MAX 小于其先前值),则 Projection Maintainer 按照以下方式操作:
使用列 PARTITION_RANGE_MIN
和 PARTITION_RANGE_MAX
中的新值更新系统表 PROJECTIONS。
对 MERGEOUT 请求进行排队,以清除此范围内未使用的数据。投影仍然可用于在更新的范围内执行查询。
如果分区范围在任一方向扩大(即 PARTITION_RANGE_MIN 小于其先前值,或 PARTITION_RANGE_MAX 大于其先前值),则 Projection Maintainer 保持投影和 PROJECTIONS 表不变。由于分区范围保持不变,Vertica 将现有投影数据视为最新数据,因此也永远不会刷新。
例如,以下投影会创建一个分区范围,其中包括当前月的所有订单:
=> CREATE PROJECTION mtd_orders AS SELECT * FROM store_orders ON PARTITION RANGE BETWEEN
date_trunc('month', now())::date AND NULL;
如果您在 2021 年 7 月创建此分区,则最小分区范围表达式 date_trunc('month', now())::date
最初解析为该月的第一天:2021-07-01。在下个月开始时,在 2021‑08‑01 00:00
和 2021‑08‑01 01:00
之间的某个时间,Projection Maintainer 会将最小范围表达式与系统时间进行比较。然后,它按如下所示进行操作:
更新 PROJECTIONS 表,并将投影 mtd_orders
的 PARTITION_RANGE_MIN
设置为 2021-08-01
。
对 MERGEOUT 请求进行排队,以从此投影的分区范围中清除键早于 2021-08-01
的所有行。
鉴于上面显示的示例,您可以考虑按如下方式设置投影的最大分区范围表达式:
add_months(date_trunc('month', now()), 1) - 1
此表达式将始终解析为当前月的最后一天。对于每个新的月份,最大分区范围将比之前的值大一个月。如前所述,Projection Maintainer 会忽略分区范围的任何扩大,因此它将保持 mtd_orders
的最小和最大分区范围值不变。为避免此类问题,请始终将最大分区表达式设置为 NULL。
当为已包含数据的表创建投影时,Vertica 不会自动将该数据加载到新投影中。相反,您必须显式刷新该投影。在执行此操作之前,投影无法参与对其锚表执行查询。
可以使用以下函数之一刷新投影:
START_REFRESH 使用各自 锚表的最新数据刷新当前架构中的投影。START_REFRESH 会在后台异步运行。
REFRESH 会在前台同步刷新一个或多个表投影。
这两个函数都会更新维护有关投影刷新状态的信息的系统表:PROJECTION_REFRESHES、PROJECTIONS 和 PROJECTION_CHECKPOINT_EPOCHS。
如果刷新会违反表或架构磁盘配额,则操作将失败。有关详细信息,请参阅磁盘配额。
可以查询 PROJECTION_REFRESHES 和 PROJECTIONS 系统表来查看刷新操作的进度。还可以调用 GET_PROJECTIONS 函数来查看给定表的投影刷新的最终状态:
=> SELECT GET_PROJECTIONS('customer_dimension');
GET_PROJECTIONS
----------------------------------------------------------------------------------------------------------
Current system K is 1.
# of Nodes: 3.
Table public.customer_dimension has 2 projections.
Projection Name: [Segmented] [Seg Cols] [# of Buddies] [Buddy Projections] [Safe] [UptoDate] [Stats]
----------------------------------------------------------------------------------------------------
public.customer_dimension_b1 [Segmented: Yes] [Seg Cols: "public.customer_dimension.customer_key"] [K: 1]
[public.customer_dimension_b0] [Safe: Yes] [UptoDate: Yes] [Stats: RowCounts]
public.customer_dimension_b0 [Segmented: Yes] [Seg Cols: "public.customer_dimension.customer_key"] [K: 1]
[public.customer_dimension_b1] [Safe: Yes] [UptoDate: Yes] [Stats: RowCounts]
(1 row)
Vertica 可以刷新其伙伴实例之一中的投影(如有)。在这种情况下,目标投影将获取源伙伴实例的历史数据。否则,将使用刷新操作时的最新时期数据从头开始刷新投影。在这种情况下,投影不能参与刷新操作之前的任何时期的历史查询。
要确定用来刷新给定投影的方法,请查询 PROJECTION_REFRESHES 系统表中的 REFRESH_METHOD 列。
可以通过 DROP PROJECTION 语句显式删除投影。当删除投影的锚表时,也会隐式删除投影。
数据分区定义为表属性,并在该表的所有投影上实施。在所有加载、刷新和恢复操作中,Vertica Tuple Mover 会将数据自动分区到不同的 ROS 容器中。每个 ROS 容器包含单个分区或分区组的数据;根据空间要求,一个分区或分区组可以跨多个 ROS 容器。
例如,按时间段对数据进行分区很常见。如果表包含数十年的数据,则可以按年份对其进行分区。如果表仅包含一年的数据,则可以按月份对其进行分区。
数据的逻辑划分可以显著改善查询执行。例如,如果对表的 partition 子句中的列查询表,则查询优化器可以快速隔离相关的 ROS 容器(请参阅分区修剪)。
分区还可以促进 DML 操作。例如,给定一个按月份分区的表,便可以在新月份开始时删除最旧月份的所有数据。在这种情况下,Vertica 可以轻松识别存储要删除的分区数据的 ROS 容器。有关详细信息,请参阅管理分区。
可以为新表和现有表指定分区:
使用 CREATE TABLE 为表定义分区。
通过使用 ALTER TABLE 修改定义,为现有表指定分区。
创建分区组以将分区合并为逻辑子集,从而最大限度减少 ROS 存储的使用。
使用 CREATE TABLE 对新表进行分区,由 PARTITION BY 子句指定:
CREATE TABLE table-name... PARTITION BY partition‑expression [ GROUP BY group‑expression ] [ REORGANIZE ];
以下语句创建 store_orders
表并将数据加载到其中。CREATE TABLE 语句包含一个简单的分区子句,指定按年份对数据进行分区:
=> CREATE TABLE public.store_orders
(
order_no int,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date
)
UNSEGMENTED ALL NODES
PARTITION BY YEAR(order_date);
CREATE TABLE
=> COPY store_orders FROM '/home/dbadmin/export_store_orders_data.txt';
41834
当 COPY 将新表数据加载到 ROS 存储中时,Tuple Mover 会将每年的订单划分为单独的分区,且将这些分区合并到 ROS 容器中,以执行表的分区子句。
在这种情况下,Tuple Mover 为加载的数据创建四个分区键(2017、2016、2015 和 2014),并将数据相应划分到单独的 ROS 容器中:
=> SELECT dump_table_partition_keys('store_orders');
... Partition keys on node v_vmart_node0001
Projection 'store_orders_super'
Storage [ROS container]
No of partition keys: 1
Partition keys: 2017
Storage [ROS container]
No of partition keys: 1
Partition keys: 2016
Storage [ROS container]
No of partition keys: 1
Partition keys: 2015
Storage [ROS container]
No of partition keys: 1
Partition keys: 2014
Partition keys on node v_vmart_node0002
Projection 'store_orders_super'
Storage [ROS container]
No of partition keys: 1
Partition keys: 2017
...
(1 row)
当新数据加载到 store_orders
中时,Tuple Mover 会将其合并到适当的分区中,以此根据需要创建新的分区键。
使用 ALTER TABLE 对现有表进行分区或重新分区,由 PARTITION BY
子句指定:
ALTER TABLE table-name PARTITION BY partition‑expression [ GROUP BY group‑expression ] [ REORGANIZE ];
例如,可以对前面定义的 store_orders
表进行重新分区。下面的 ALTER TABLE 将每年的所有 store_orders
数据划分为每月分区,每个分区键标识订单日期的年份和月份:
=> ALTER TABLE store_orders
PARTITION BY EXTRACT(YEAR FROM order_date)*100 + EXTRACT(MONTH FROM order_date)
GROUP BY EXTRACT(YEAR from order_date)*100 + EXTRACT(MONTH FROM order_date);
NOTICE 8364: The new partitioning scheme will produce partitions in 42 physical storage containers per projection
WARNING 6100: Using PARTITION expression that returns a Numeric value
HINT: This PARTITION expression may cause too many data partitions. Use of an expression that returns a more accurate value, such as a regular VARCHAR or INT, is encouraged
WARNING 4493: Queries using table "store_orders" may not perform optimally since the data may not be repartitioned in accordance with the new partition expression
HINT: Use "ALTER TABLE public.store_orders REORGANIZE;" to repartition the data
执行此语句后,Vertica 会删除现有的分区键。但是,partition 子句会省略 REORGANIZE,以便现有数据仍然根据前一个 partition 子句进行存储。这可能会使表分区处于不一致状态,并对查询性能、DROP_PARTITIONS 和节点恢复产生负面影响。在这种情况下,必须通过以下方式之一显式请求 Vertica 将现有数据重新组织到新分区中:
发出 ALTER TABLE...REORGANIZE:
ALTER TABLE table-name REORGANIZE;
调用 Vertica 元函数 PARTITION_TABLE。
例如:
=> ALTER TABLE store_orders REORGANIZE;
NOTICE 4785: Started background repartition table task
ALTER TABLE
ALTER TABLE...REORGANIZE 和 PARTITION_TABLE 的运行方式相同:两者都会拆分分区键与新 partition 子句不相符的任何 ROS 容器。在执行下一次合并时,Tuple Mover 会将分区合并到相应的 ROS 容器中。
分区组将分区合并为逻辑子集,从而最大限度减少 ROS 存储的使用。减少存储分区数据的 ROS 容器数有助于促进 DELETE
和 UPDATE
等 DML 操作,并避免 ROS 推回。例如,可以按年份对日期分区进行分组。通过这样做,Tuple Mover 会为每个年份组分配 ROS 容器,并相应地将各个分区合并到这些 ROS 容器中。
可以通过使用 GROUP BY
子句限定 PARTITION BY
子句来创建分区组:
ALTER TABLE table-name PARTITION BY partition‑expression [ GROUP BY group‑expression ]
GROUP BY
子句指定如何将分区键合并到组中,其中每个组由唯一的分区组键标识。例如,以下
ALTER TABLE
语句指定按订单日期对 store_orders
表进行重新分区(如对新表进行分区中所示),按年份对分区键进行分组。组表达式 DATE_TRUNC('year', (order_date)::DATE)
使用分区表达式 order_date::DATE 生成分区组键:
=> ALTER TABLE store_orders
PARTITION BY order_date::DATE GROUP BY DATE_TRUNC('year', (order_date)::DATE) REORGANIZE;
NOTICE 8364: The new partitioning scheme will produce partitions in 4 physical storage containers per projection
NOTICE 4785: Started background repartition table task
在这种情况下,order_date
列的日期跨越四年。Tuple Mover 会创建四个分区组键,并相应地将 store_orders
分区合并到组特定的 ROS 存储容器中:
=> SELECT DUMP_TABLE_PARTITION_KEYS('store_orders');
...
Partition keys on node v_vmart_node0001
Projection 'store_orders_super'
Storage [ROS container]
No of partition keys: 173
Partition keys: 2017-01-02 2017-01-03 2017-01-04 ... 2017-09-25 2017-09-26 2017-09-27
Storage [ROS container]
No of partition keys: 212
Partition keys: 2016-01-01 2016-01-04 2016-01-05 ... 2016-11-23 2016-11-24 2016-11-25
Storage [ROS container]
No of partition keys: 213
Partition keys: 2015-01-01 2015-01-02 2015-01-05 ... 2015-11-23 2015-11-24 2015-11-25
2015-11-26 2015-11-27
Storage [ROS container]
No of partition keys: 211
Partition keys: 2014-01-01 2014-01-02 2014-01-03 ... 2014-11-25 2014-11-26 2014-11-27
Projection 'store_orders_super'
Storage [ROS container]
No of partition keys: 173
...
此示例演示了分区分组如何促进更有效地使用 ROS 存储。但是,将所有分区分组到多个大型静态 ROS 容器中可能会对性能产生负面影响,特别是对于频繁执行 DML 操作的表。特别是,频繁的加载操作可能会产生相当大的合并开销,进而降低性能。
Vertica 建议使用
CALENDAR_HIERARCHY_DAY
作为 partition 子句的组表达式。此函数自动将 DATE
分区键分组为年、月和日的动态层次结构。这样做有助于最大限度地减少与合并相关的问题。有关详细信息,请参阅分层分区。
可以使用各种分区管理函数(例如
DROP_PARTITIONS
或
MOVE_PARTITIONS_TO_TABLE
),来定位给定分区组内或跨多个分区组的订单日期范围。在前面的示例中,每个组都包含给定年份内不同日期的分区键。可以使用 DROP_PARTITIONS
删除跨越两年(2014 年和 2015 年)的订单日期:
=> SELECT DROP_PARTITIONS('store_orders', '2014-05-30', '2015-01-15', 'true');
元函数 CALENDAR_HIERARCHY_DAY 利用分区分组。您可以将此函数指定为分区 GROUP BY
表达式。CALENDAR_HIERARCHY_DAY 将表的日期分区组织成组的层次结构:最早的日期分区按年分组,较新的分区按月分组,最近的日期分区保持未分组状态。分组动态进行:随着最近的数据老化,Tuple Mover 将它们的分区合并到月份组中,并最终合并到年份组中。
分区合并策略对于带时间戳的数据管理尤其重要,因为分区数量可能会迅速升级并面临 ROS 推回风险。例如,以下语句创建 store_orders
表并将数据加载到其中。CREATE TABLE 语句包含一个简单的 partition 子句,它指定按日期对数据进行分区:
=> DROP TABLE IF EXISTS public.store_orders CASCADE;
=> CREATE TABLE public.store_orders
(
order_no int,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date
)
UNSEGMENTED ALL NODES PARTITION BY order_date::DATE;
CREATE TABLE
=> COPY store_orders FROM '/home/dbadmin/export_store_orders_data.txt';
41834
(1 row)
当 COPY 将新表数据加载到 ROS 存储中时,它通过将每日订单划分为不同的分区(在本例中为 809 个分区)来执行该表的 partition 子句,其中每个分区都需要有自己的 ROS 容器:
=> SELECT COUNT (DISTINCT ros_id) NumROS, node_name FROM PARTITIONS
WHERE projection_name ilike '%store_orders_super%' GROUP BY node_name ORDER BY node_name;
NumROS | node_name
--------+------------------
809 | v_vmart_node0001
809 | v_vmart_node0002
809 | v_vmart_node0003
(3 rows)
这远远高于每个投影最多 50 个分区的建议值。这一数字也接近每个投影 1024 个 ROS 容器的默认系统限制,从而面临不久的将来发生 ROS 推回的风险。
您可以通过多种方式解决此问题:
考虑将表数据合并到更大的分区中,例如,按月而不是按天进行分区。但是,在此级别对数据进行分区可能会限制分区管理函数的有效使用。
定期存档旧分区,从而最大限度地减少累积分区的数量。但是,这需要多一层数据管理,并且还要禁止访问历史数据。
或者,可以使用 CALENDAR_HIERARCHY_DAY 将分区自动合并到基于日期的分区组层次结构中。每个分区组都存储在其自己的一组 ROS 容器中,与其他组区别开来。您可以在表 partition 子句中指定此函数,如下所示:
PARTITION BY partition‑expression
GROUP BY CALENDAR_HIERARCHY_DAY( partition‑expression [, active‑months[, active‑years] ] )
在 partition 子句中使用 CALENDAR_HIERARCHY_DAY 有两个要求:
partition-expression 必须是 DATE。
PARTITION BY
子句和 CALENDAR_HIERARCHY_DAY 指定的分区表达式必须相同。
例如,给定上一个表,您可以按如下方式对其进行重新分区:
=> ALTER TABLE public.store_orders
PARTITION BY order_date::DATE
GROUP BY CALENDAR_HIERARCHY_DAY(order_date::DATE, 2, 2) REORGANIZE;
CALENDAR_HIERARCHY_DAY 会创建分区组的层次结构,并将分区合并到相应的组中。为此,它使用以下算法计算每个表行的分区表达式,来确定其分区组键:
GROUP BY (
CASE WHEN DATEDIFF('YEAR', partition-expression, NOW()::TIMESTAMPTZ(6)) >= active-years
THEN DATE_TRUNC('YEAR', partition-expression::DATE)
WHEN DATEDIFF('MONTH', partition-expression, NOW()::TIMESTAMPTZ(6)) >= active-months
THEN DATE_TRUNC('MONTH', partition-expression::DATE)
ELSE DATE_TRUNC('DAY', partition-expression::DATE) END);
在此示例中,算法将每个 store_orders
行中的 order_date
与当前日期进行比较,如下所示:
确定 order_date
是否处于非活动年份。
如果 order_date
处于非活动年份,则该行的分区组键解析为该年份。该行将被合并到该年份的 ROS 容器中。
如果 order_date
处于活动年份,则 CALENDAR_HIERARCHY_DAY 会计算 order_date
以确定它是否处于非活动月份。
如果 order_date
处于非活动月份,则该行的分区组键解析为该月份。该行将被合并到该月份的 ROS 容器中。
如果 order_date
处于活动月份,则该行的分区组键解析为 order_date
日期。该行将被合并到该日期的 ROS 容器中。order_date
是未来日期的任何行都将以相同的方式处理。
CALENDAR_HIERARCHY_DAY 算法假定大多数表活动都集中在最近的日期。将 active‑years 和 active‑months 设置为 ≥ 2 的较小数字,有助于将大多数合并活动隔离到特定于日期的容器,并产生最小的开销。Vertica 建议您对 active‑years 和 active‑months 使用默认设置 2。对于大多数用户,这些设置会在 ROS 存储和性能之间实现最佳平衡。
最佳实践是,永远不要将 active-years 和 active-months 设置为 0。
例如,如果当前日期是 2017-09-26,则 CALENDAR_HIERARCHY_DAY 会将 active-years 和 active-months 解析为以下日期范围:
active‑years:2016-01-01 到 2017-12-31。处于活动年份的分区将被分组到每月的 ROS 容器中,或合并到每日的 ROS 容器中。早期年份的分区被认为是非活动年份,合并到年度 ROS 容器中。
active‑months:2017/8/1 到 2017/9/30。处于活动月份的分区将被合并到每日的 ROS 容器中。
现在,每个投影的 ROS 容器总数减少到 40 个:
=> SELECT COUNT (DISTINCT ros_id) NumROS, node_name FROM PARTITIONS
WHERE projection_name ilike '%store_orders_super%' GROUP BY node_name ORDER BY node_name;
NumROS | node_name
--------+------------------
40 | v_vmart_node0001
40 | v_vmart_node0002
40 | v_vmart_node0003
(3 rows)
如前所述,CALENDAR_HIERARCHY_DAY 在创建分区组键和合并分区时引用当前日期。随着日历向前推进,Tuple Mover 会重新计算使用此函数分区的表的分区组键,并根据需要将分区移动到不同的 ROS 容器中。
因此,给定前面的示例,在 2017-10-01,Tuple Mover 会为 8 月分区创建一个月度 ROS 容器。2017-08-01 和 2017-08-31 之间的所有分区键都将合并到新的 ROS 容器 2017-08 中:
同样,在 2018-01-01,Tuple Mover 会为 2016 年分区创建一个 ROS 容器。之前按月份分组的 2016-01-01 和 2016-12-31 之间的所有分区键都将合并到新的年度 ROS 容器中:
Vertica 提供了一个函数 CALENDAR_HIERARCHY_DAY,以方便分层分区。Vertica 将 GROUP BY
子句存储为 CASE 语句,您可以对其进行编辑以满足自己的要求。
例如,Vertica 按如下所示存储 store_orders
partition 子句:
=> ALTER TABLE public.store_orders
PARTITION BY order_date::DATE
GROUP BY CALENDAR_HIERARCHY_DAY(order_date::DATE, 2, 2);
=> select export_tables('','store_orders');
...
CREATE TABLE public.store_orders ( ... )
PARTITION BY ((store_orders.order_date)::date)
GROUP BY (
CASE WHEN ("datediff"('year', (store_orders.order_date)::date, ((now())::timestamptz(6))::date) >= 2)
THEN (date_trunc('year', (store_orders.order_date)::date))::date
WHEN ("datediff"('month', (store_orders.order_date)::date, ((now())::timestamptz(6))::date) >= 2)
THEN (date_trunc('month', (store_orders.order_date)::date))::date
ELSE (store_orders.order_date)::date END);
您可以修改 CASE 语句,以自定义分区组的层次结构。例如,以下 CASE 语句创建月、日和小时的层次结构:
=> ALTER TABLE store_orders
PARTITION BY (store_orders.order_date)
GROUP BY (
CASE WHEN DATEDIFF('MONTH', store_orders.order_date, NOW()::TIMESTAMPTZ(6)) >= 2
THEN DATE_TRUNC('MONTH', store_orders.order_date::DATE)
WHEN DATEDIFF('DAY', store_orders.order_date, NOW()::TIMESTAMPTZ(6)) >= 2
THEN DATE_TRUNC('DAY', store_orders.order_date::DATE)
ELSE DATE_TRUNC('hour', store_orders.order_date::DATE) END);
在 Vertica 中,分区和分段是不同的概念,可实现不同的数据本地化目标:
分段 是指跨群集节点组织和分发数据,以便实现快速数据清除和查询性能。分段的目的是使数据在多个数据库节点上均匀分布,以便所有节点均参与查询执行。可通过
CREATE PROJECTION
语句的哈希分段子句来指定分段。
分区 指定如何在各个节点内组织数据以进行分布式计算。通过节点分区,可以轻松地标识要删除的数据和帮助回收磁盘空间。可以通过
CREATE TABLE
语句的 PARTITION BY
子句来指定分区。
例如:按年对数据进行分区对于保留和删除年度数据意义重大。然而,按年对相同数据分段则效率低下,因为与其他节点相比,包含本年数据的节点所要响应的查询可能要多得多。
下图显示了四节点数据库群集的分段和分区流:
示例表数据
按 HASH(order_id)
分段的数据
按哈希跨四个节点分段的数据
按年在单个节点上分区的数据
尽管分区发生在所有四个节点上,但为了简单起见,示意图仅显示一个节点上的分区数据。
使用
DROP_PARTITIONS
函数删除给定表的一个或多个分区键。可以指定单个分区键或分区键范围。
例如,对新表进行分区中显示的表按列 order_date
进行分区:
=> CREATE TABLE public.store_orders
(
order_no int,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date
)
PARTITION BY YEAR(order_date);
鉴于此表定义,Vertica 会为每个唯一的 order_date
年份(在本例中为 2017、2016、2015 和 2014 年)创建一个分区键,并相应地将数据划分到不同的 ROS 容器中。
以下 DROP_PARTITIONS
语句从表 store_orders
中删除与分区键 2014 关联的所有订单记录:
=> SELECT DROP_PARTITIONS ('store_orders', 2014, 2014);
Partition dropped
如果表 partition 子句包括 GROUP BY
子句,则分区将按其分区组键在 ROS 中合并。 DROP_PARTITIONS
随后可以指定给定分区组内或跨多个分区组的分区键范围。无论哪种情况,删除操作都要求 Vertica 拆分存储这些分区的 ROS 容器。为此,该函数的 force_split
参数必须设置为 true。
例如,上面显示的 store_orders
表可以使用 GROUP BY
子句重新分区,如下所示:
=> ALTER TABLE store_orders
PARTITION BY order_date::DATE GROUP BY DATE_TRUNC('year', (order_date)::DATE) REORGANIZE;
由于之前删除了 2014 年的所有订单记录,order_date
值现在跨越三个年份(2017 年、2016 年和 2015 年)。因此,Tuple Mover 为每年创建三个分区组键,并为每个组指定一个或多个 ROS 容器。然后,它将 store_orders
分区合并到相应的组中。
以下 DROP_PARTITIONS
语句指定删除跨越两个年份(2014 年和 2015 年)的订单日期:
=> SELECT DROP_PARTITIONS('store_orders', '2015-05-30', '2016-01-16', 'true');
Partition dropped
删除操作要求 Vertica 从两个分区组(2015 和 2016)删除分区。这些组至少跨越两个 ROS 容器,必须拆分这些容器才能移除目标分区。因此,将函数的 force_split
参数设置为 true。
如果硬件具有固定的磁盘空间,则可能需要配置一个定期执行的进程,以便通过删除分区来迁出旧数据。
例如,如果空间只够将数据存储固定的天数,请将 Vertica 配置为删除最旧的分区键。为此,请创建一个基于时间的作业调度程序(例如 cron
),以计划在低负载期间删除分区键。
如果数据输入速率具有峰值和谷值,则可以使用以下两种技术来管理分区键删除方式:
设置一个定期(每天)检查磁盘空间的进程。如果已用磁盘空间百分比超出特定阈值(例如 80%),则删除最旧的分区键。
在分区中添加一个可根据行计数等指标递增的假列。例如,每次行计数增加 100 行时,该列可能会递增。设置一个定期(每天)查询此列的进程。如果新列中的值超出特定阈值(例如 100),请删除最旧的分区键,并将该列的值设置回 0。
DROP_PARTITIONS
会在目标表上获取一个独占的 O 锁,以阻止可能会影响表数据的任何 DML 操作(DELETE
、UPDATE
、INSERT
或 COPY
)。该锁还会阻止在 SERIALIZABLE 隔离级别发出的 SELECT
语句。
如果操作无法在目标表上获取 O lock,Vertica 将尝试关闭该表上运行的任何内部 tuple mover 会话。如果成功,则可以继续操作。在用户会话中运行的显式 Tuple Mover 操作不会关闭。如果显式 Tuple Mover 操作在表上运行,则该操作仅在 Tuple Mover 操作完成后继续。
可以使用 Vertica 函数
MOVE_PARTITIONS_TO_TABLE
在表之间移动分区。此函数对于在下列过程中存档旧分区非常有用:
标识要存档的分区,并使用
MOVE_PARTITIONS_TO_TABLE
将其移动到临时暂存表。
可以随时还原已存档的分区。
存档历史数据的方法是标识要从表中移除的分区。然后,将每个分区(或分区组)移动到临时表。
在调用 MOVE_PARTITIONS_TO_TABLE
之前:
以下建议适用于临时表:
为了方便备份过程,为每个存档操作的临时表创建唯一架构。
为临时表指定新名称。这可确保它们不包含先前移动操作中的分区。
如果该表不存在,Vertica 将根据源表的定义,通过调用
CREATE TABLE
包含 LIKE
和 INCLUDING PROJECTIONS
子句来创建一个表。该新表会从源表中继承所有权。有关详细信息,请参阅复制表。
使用能够让其他用户轻松标识分区内容的临时名称。例如,如果表按日期分区,则使用指定日期或日期范围的名称。
在以下示例中,MOVE_PARTITIONS_TO_TABLE
指定将单个分区移动到临时表 partn_backup.tradfes_200801
。
=> SELECT MOVE_PARTITIONS_TO_TABLE (
'prod_trades',
'200801',
'200801',
'partn_backup.trades_200801');
MOVE_PARTITIONS_TO_TABLE
-------------------------------------------------
1 distinct partition values moved at epoch 15.
(1 row)
在创建临时表后,可以使用
vbr
配置文件通过对象级备份来存档它。有关详细信息,请参阅备份和还原数据库。
在备份完成后,可以按删除表中所述删除临时表。
您可以还原之前移至中间表、存档为对象级别备份后删除的分区。
还原已存档分区的步骤如下:
还原在移动一个或多个待存档分区时所保存的中间表的备份(请参阅存档分区)。
将还原后的分区从中间表移至原始表。
删除中间表。
SWAP_PARTITIONS_BETWEEN_TABLES 将 DROP_PARTITIONS 和 MOVE_PARTITIONS_TO_TABLE 的操作合并为单个事务。如果定期将已分区数据从一个表加载到另一个表,并且需要刷新第二个表中的分区,则 SWAP_PARTITIONS_BETWEEN_TABLES 很有用。
例如,您可能具有一个按日期分区的收入表,而且经常将数据从临时表移动到该表中。临时表有时包含目标表中已有日期的数据。在这种情况下,首先必须从目标表中删除这些日期的分区,然后将其替换为临时表中的相应分区。可以通过调用一次 SWAP_PARTITIONS_BETWEEN_TABLES 来完成这两个任务。
通过将删除和移动操作包装在单个事务中,SWAP_PARTITIONS_BETWEEN_TABLES 可保持交换数据的完整性。如果交换操作中的任何任务失败,整个操作则会失败并回退。
以下示例创建两个已分区表,然后在两者之间交换某些分区。
这两个表的定义相同,且具有不同 year
值的分区。交换 year
= 2008 和 year
= 2009 的分区。这两个表都至少有两行要交换。
创建 customer_info
表:
=> CREATE TABLE customer_info (
customer_id INT NOT NULL,
first_name VARCHAR(25),
last_name VARCHAR(35),
city VARCHAR(25),
year INT NOT NULL)
ORDER BY last_name
PARTITION BY year;
向 customer_info
表中插入数据:
INSERT INTO customer_info VALUES
(1,'Joe','Smith','Denver',2008),
(2,'Bob','Jones','Boston',2008),
(3,'Silke','Muller','Frankfurt',2007),
(4,'Simone','Bernard','Paris',2014),
(5,'Vijay','Kumar','New Delhi',2010);
OUTPUT
--------
5
(1 row)
=> COMMIT;
查看表数据:
=> SELECT * FROM customer_info ORDER BY year DESC;
customer_id | first_name | last_name | city | year
-------------+------------+-----------+-----------+------
4 | Simone | Bernard | Paris | 2014
5 | Vijay | Kumar | New Delhi | 2010
2 | Bob | Jones | Boston | 2008
1 | Joe | Smith | Denver | 2008
3 | Silke | Muller | Frankfurt | 2007
(5 rows)
创建第二个表 member_info
,其具有与 customer_info
相同的定义:
=> CREATE TABLE member_info LIKE customer_info INCLUDING PROJECTIONS;
CREATE TABLE
向 member_info
表中插入数据:
=> INSERT INTO member_info VALUES
(1,'Jane','Doe','Miami',2001),
(2,'Mike','Brown','Chicago',2014),
(3,'Patrick','OMalley','Dublin',2008),
(4,'Ana','Lopez','Madrid',2009),
(5,'Mike','Green','New York',2008);
OUTPUT
--------
5
(1 row)
=> COMMIT;
COMMIT
查看 member_info
表中的数据:
=> SELECT * FROM member_info ORDER BY year DESC;
customer_id | first_name | last_name | city | year
-------------+------------+-----------+----------+------
2 | Mike | Brown | Chicago | 2014
4 | Ana | Lopez | Madrid | 2009
5 | Mike | Green | New York | 2008
3 | Patrick | OMalley | Dublin | 2008
1 | Jane | Doe | Miami | 2001
(5 rows)
要交换分区,请运行 SWAP_PARTITIONS_BETWEEN_TABLES 函数:
=> SELECT SWAP_PARTITIONS_BETWEEN_TABLES('customer_info', 2008, 2009, 'member_info');
SWAP_PARTITIONS_BETWEEN_TABLES
----------------------------------------------------------------------------------------------
1 partition values from table customer_info and 2 partition values from table member_info are swapped at epoch 1045.
(1 row)
查询这两个表以确认它们交换了各自的 2008 年和 2009 年记录:
=> SELECT * FROM customer_info ORDER BY year DESC;
customer_id | first_name | last_name | city | year
-------------+------------+-----------+-----------+------
4 | Simone | Bernard | Paris | 2014
5 | Vijay | Kumar | New Delhi | 2010
4 | Ana | Lopez | Madrid | 2009
3 | Patrick | OMalley | Dublin | 2008
5 | Mike | Green | New York | 2008
3 | Silke | Muller | Frankfurt | 2007
(6 rows)
=> SELECT * FROM member_info ORDER BY year DESC;
customer_id | first_name | last_name | city | year
-------------+------------+-----------+---------+------
2 | Mike | Brown | Chicago | 2014
2 | Bob | Jones | Boston | 2008
1 | Joe | Smith | Denver | 2008
1 | Jane | Doe | Miami | 2001
(4 rows)
默认情况下,Vertica 最多支持 1024 个 ROS 容器来存储给定投影的分区(请参阅投影参数)。ROS 容器包含共享相同分区键或相同分区组键的数据。根据每个分区的数据量,一个分区或分区组可以跨多个 ROS 容器。
鉴于此限制,不建议按高度精细的数据(例如,按 TIMESTAMP 列)对表进行分区。这样做会生成非常多的分区。如果分区数量需要超过 1024 个 ROS 容器,Vertica 会发出 ROS 推回警告并拒绝加载更多表数据。大量 ROS 容器也会对 DML 操作(例如 DELETE
)产生负面影响,这需要 Vertica 打开所有 ROS 容器。
在实践中,您不太可能接近此最大值。为了获得最佳性能,Vertica 建议未分组分区的数量介于 10 到 20 之间,并且不超过 50。此范围通常适用于大多数业务需求。
Vertica 提供了多种方式来查看表分区的组织和存储方式:
查询
PARTITIONS
系统表。
转储分区键。
下面的表和投影定义按订单日期对 store_order
数据进行分区,并将同一年份的分区分组在一起:
=> CREATE TABLE public.store_orders
(order_no int, order_date timestamp NOT NULL, shipper varchar(20), ship_date date)
PARTITION BY ((order_date)::date) GROUP BY (date_trunc('year', (order_date)::date));
=> CREATE PROJECTION public.store_orders_super
AS SELECT order_no, order_date, shipper, ship_date FROM store_orders
ORDER BY order_no, order_date, shipper, ship_date UNSEGMENTED ALL NODES;
=> COPY store_orders FROM '/home/dbadmin/export_store_orders_data.txt';
在将数据加载到此表后,可以查询 PARTITIONS
表,以确定所有节点上有多少个 ROS 容器存储投影 store_orders_unseg
的已分组分区。每个节点有八个 ROS 容器,每个容器存储一个分区组的分区:
=> SELECT COUNT (partition_key) NumPartitions, ros_id, node_name FROM PARTITIONS
WHERE projection_name ilike 'store_orders%' GROUP BY ros_id, node_name ORDER BY node_name, NumPartitions;
NumPartitions | ros_id | node_name
---------------+-------------------+------------------
173 | 45035996274562779 | v_vmart_node0001
211 | 45035996274562791 | v_vmart_node0001
212 | 45035996274562783 | v_vmart_node0001
213 | 45035996274562787 | v_vmart_node0001
173 | 49539595901916471 | v_vmart_node0002
211 | 49539595901916483 | v_vmart_node0002
212 | 49539595901916475 | v_vmart_node0002
213 | 49539595901916479 | v_vmart_node0002
173 | 54043195529286985 | v_vmart_node0003
211 | 54043195529286997 | v_vmart_node0003
212 | 54043195529286989 | v_vmart_node0003
213 | 54043195529286993 | v_vmart_node0003
(12 rows)
Vertica 提供了多个函数,允许您在多个级别检查各个分区在群集上的存储方式:
DUMP_PARTITION_KEYS
转储系统中所有投影的分区键。
DUMP_TABLE_PARTITION_KEYS
转储指定表的所有投影的分区键。
DUMP_PROJECTION_PARTITION_KEYS
转储指定投影的分区键。
给定前面的表和投影,DUMP_PROJECTION_PARTITION_KEYS
显示每个节点上四个 ROS 容器的内容:
=> SELECT DUMP_PROJECTION_PARTITION_KEYS('store_orders_super');
...
Partition keys on node v_vmart_node0001
Projection 'store_orders_super'
Storage [ROS container]
No of partition keys: 173
Partition keys: 2017-01-02 2017-01-03 2017-01-04 2017-01-05 2017-01-06 2017-01-09 2017-01-10
2017-01-11 2017-01-12 2017-01-13 2017-01-16 2017-01-17 2017-01-18 2017-01-19 2017-01-20 2017-01-23
2017-01-24 2017-01-25 2017-01-26 2017-01-27 2017-02-01 2017-02-02 2017-02-03 2017-02-06 2017-02-07
2017-02-08 2017-02-09 2017-02-10 2017-02-13 2017-02-14 2017-02-15 2017-02-16 2017-02-17 2017-02-20
...
2017-09-01 2017-09-04 2017-09-05 2017-09-06 2017-09-07 2017-09-08 2017-09-11 2017-09-12 2017-09-13
2017-09-14 2017-09-15 2017-09-18 2017-09-19 2017-09-20 2017-09-21 2017-09-22 2017-09-25 2017-09-26 2017-09-27
Storage [ROS container]
No of partition keys: 212
Partition keys: 2016-01-01 2016-01-04 2016-01-05 2016-01-06 2016-01-07 2016-01-08 2016-01-11
2016-01-12 2016-01-13 2016-01-14 2016-01-15 2016-01-18 2016-01-19 2016-01-20 2016-01-21 2016-01-22
2016-01-25 2016-01-26 2016-01-27 2016-02-01 2016-02-02 2016-02-03 2016-02-04 2016-02-05 2016-02-08
2016-02-09 2016-02-10 2016-02-11 2016-02-12 2016-02-15 2016-02-16 2016-02-17 2016-02-18 2016-02-19
...
2016-11-01 2016-11-02 2016-11-03 2016-11-04 2016-11-07 2016-11-08 2016-11-09 2016-11-10 2016-11-11
2016-11-14 2016-11-15 2016-11-16 2016-11-17 2016-11-18 2016-11-21 2016-11-22 2016-11-23 2016-11-24 2016-11-25
Storage [ROS container]
No of partition keys: 213
Partition keys: 2015-01-01 2015-01-02 2015-01-05 2015-01-06 2015-01-07 2015-01-08 2015-01-09
2015-01-12 2015-01-13 2015-01-14 2015-01-15 2015-01-16 2015-01-19 2015-01-20 2015-01-21 2015-01-22
2015-01-23 2015-01-26 2015-01-27 2015-02-02 2015-02-03 2015-02-04 2015-02-05 2015-02-06 2015-02-09
2015-02-10 2015-02-11 2015-02-12 2015-02-13 2015-02-16 2015-02-17 2015-02-18 2015-02-19 2015-02-20
...
2015-11-02 2015-11-03 2015-11-04 2015-11-05 2015-11-06 2015-11-09 2015-11-10 2015-11-11 2015-11-12
2015-11-13 2015-11-16 2015-11-17 2015-11-18 2015-11-19 2015-11-20 2015-11-23 2015-11-24 2015-11-25
2015-11-26 2015-11-27
Storage [ROS container]
No of partition keys: 211
Partition keys: 2014-01-01 2014-01-02 2014-01-03 2014-01-06 2014-01-07 2014-01-08 2014-01-09
2014-01-10 2014-01-13 2014-01-14 2014-01-15 2014-01-16 2014-01-17 2014-01-20 2014-01-21 2014-01-22
2014-01-23 2014-01-24 2014-01-27 2014-02-03 2014-02-04 2014-02-05 2014-02-06 2014-02-07 2014-02-10
2014-02-11 2014-02-12 2014-02-13 2014-02-14 2014-02-17 2014-02-18 2014-02-19 2014-02-20 2014-02-21
...
2014-11-04 2014-11-05 2014-11-06 2014-11-07 2014-11-10 2014-11-11 2014-11-12 2014-11-13 2014-11-14
2014-11-17 2014-11-18 2014-11-19 2014-11-20 2014-11-21 2014-11-24 2014-11-25 2014-11-26 2014-11-27
Storage [ROS container]
No of partition keys: 173
...
Tuple Mover 假定分区表的所有加载和更新都是针对一个或多个标识为活动的分区。通常,具有最大分区键的分区(通常是最近创建的分区)均视为活动分区。随着分区的老化,其工作负载通常会缩小,并大多变为只读状态。
您可以按优先级的升序指定两个级别的分区表的活动分区数:
配置参数 ActivePartitionCount 将确定数据库中分区表的活动分区数。默认情况下,ActivePartitionCount 设置为 1。Tuple Mover 会将此设置应用于自身未设置活动分区计数的所有表。
通过使用 CREATE TABLE 和 ALTER TABLE 设置各个表自身的活动分区计数,可以取代各个表的 ActivePartitionCount。
同一数据库中的分区表可能会受到更新和加载活动的不同分布的影响。当这些差异显著时,某些表设置自己的活动分区计数便可能有了意义。
例如,表 store_orders
按月分区,并通过配置参数 ActivePartitionCount
获取其活动分区计数。如果该参数设置为 1,则 Tuple Mover 会将最近一个月(通常是当前月份)标识为表的活动分区。如果 store_orders
面临当前月份和上一个月的频繁数据活动,您可能希望该表取代此配置参数,将其活动分区计数设置为 2:
ALTER TABLE public.store_orders SET ACTIVEPARTITIONCOUNT 2;
Tuple Mover 通常将活动分区标识为最近创建的分区。Vertica 使用以下算法来确定哪些分区比其他分区较旧:
如果分区 X 是在分区 Y 之前创建的,则分区 X 较旧。
如果分区 X 和 Y 是同时创建的,但分区 X 上次更新是在分区 Y 之前,则分区 X 较旧。
如果分区 X 和 Y 是同时创建的且上次更新时间相同,则具有较小键的分区较旧。
您可以通过联接系统表
PARTITIONS
和
STRATA
并查询其投影来获取表的活动分区。例如,以下查询获取投影 store_orders_super
的活动分区:
=> SELECT p.node_name, p.partition_key, p.ros_id, p.ros_size_bytes, p.ros_row_count, ROS_container_count
FROM partitions p JOIN strata s ON p.partition_key = s.stratum_key AND p.node_name=s.node_name
WHERE p.projection_name = 'store_orders_super' ORDER BY p.node_name, p.partition_key;
node_name | partition_key | ros_id | ros_size_bytes | ros_row_count | ROS_container_count
------------------+---------------+-------------------+----------------+---------------+---------------------
v_vmart_node0001 | 2017-09-01 | 45035996279322851 | 6905 | 960 | 1
v_vmart_node0002 | 2017-09-01 | 49539595906590663 | 6905 | 960 | 1
v_vmart_node0003 | 2017-09-01 | 54043195533961159 | 6905 | 960 | 1
(3 rows)
如果表的 partition 子句包括 GROUP BY
表达式,Vertica 会将该表的活动分区计数应用于其最大分区组键,并将该组中的所有分区视为活动分区。如果使用 Vertica 元函数
CALENDAR_HIERARCHY_DAY
对分区进行分组,则最近日期的分区也会按天分组。因此,最大分区组键和最大分区键相同。实际上,这意味着只有最近的分区是活动分区。
如果查询谓词指定了分区表达式,则查询优化器会根据已分区数据的 ROS 容器计算谓词。每个 ROS 容器维护其分区键数据的最小值和最大值。查询优化器使用此元数据来确定执行查询所需的 ROS 容器,并从查询计划中省略或修剪其余容器。通过最大限度地减少它必须扫描的 ROS 容器的数量,查询优化器可以更快地执行查询。
例如,表可能按年份进行分区,如下所示:
=> CREATE TABLE ... PARTITION BY EXTRACT(year FROM date);
给定此表定义,其投影数据根据年份划分到 ROS 容器中,每个年份一个容器,在本例中为 2007 年、2008 年、2009 年。
以下查询指定分区表达式 date
:
=> SELECT ... WHERE date = '12-2-2009';
给定此查询,包含 2007 年和 2008 年数据的 ROS 容器不在所请求年份(2009 年)的范围内。在查询执行之前,查询优化器会从查询计划中修剪这些容器:
假定表按时间分区,并且它将使用按时间限制数据的查询。
=> CREATE TABLE time ( tdate DATE NOT NULL, tnum INTEGER)
PARTITION BY EXTRACT(year FROM tdate);
=> CREATE PROJECTION time_p (tdate, tnum) AS
=> SELECT * FROM time ORDER BY tdate, tnum UNSEGMENTED ALL NODES;
=> INSERT INTO time VALUES ('03/15/04' , 1);
=> INSERT INTO time VALUES ('03/15/05' , 2);
=> INSERT INTO time VALUES ('03/15/06' , 3);
=> INSERT INTO time VALUES ('03/15/06' , 4);
在前面的一系列命令中插入的数据将加载到三个 ROS 容器(每个年份一个容器),因为这就是数据的分区方式:
=> SELECT * FROM time ORDER BY tnum;
tdate | tnum
------------+------
2004-03-15 | 1 --ROS1 (min 03/01/04, max 03/15/04)
2005-03-15 | 2 --ROS2 (min 03/15/05, max 03/15/05)
2006-03-15 | 3 --ROS3 (min 03/15/06, max 03/15/06)
2006-03-15 | 4 --ROS3 (min 03/15/06, max 03/15/06)
(4 rows)
当查询 time
表时,将发生以下操作:
在此查询中,Vertica 可以省略 ROS2 容器,因为它仅查找 2004 年:
=> SELECT COUNT(*) FROM time WHERE tdate = '05/07/2004';
在下一个查询中,Vertica 可以省略两个容器 ROS1 和 ROS3:
=> SELECT COUNT(*) FROM time WHERE tdate = '10/07/2005';
以下查询有一个针对 tnum
列的附加谓词,且该列中没有保留最小/最大值。此外,由于不支持使用逻辑运算符 OR,因此没有消除 ROS:
=> SELECT COUNT(*) FROM time WHERE tdate = '05/07/2004' OR tnum = 7;
约束将设置表格列中允许哪些数据的相关规则。使用约束有助于维护数据完整性。例如,您可以将列限制为仅允许唯一值或禁止 NULL 值。主键等约束也有助于优化器生成查询计划,促进数据访问更快捷,特别是对于联接更是如此。
您可分别使用
CREATE TABLE
和
ALTER TABLE...ADD CONSTRAINT
对新表和现有表设置约束。
Vertica 支持标准 SQL 约束,如此部分中所述。
主键包含一列或多列基元类型,其值可以唯一标识表行。一个表只能指定一个主键。您可以在创建表时或在现有表中使用
ALTER TABLE
标识表的主键。您不能将具有集合类型的列指定为键。
例如,下面的 CREATE TABLE
语句将 order_no
列定义为 store_orders
表的主键:
=> CREATE TABLE public.store_orders(
order_no int PRIMARY KEY,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date,
product_key int,
product_version int
)
PARTITION BY ((date_part('year', order_date))::int);
CREATE TABLE
一个主键可以包含多列。在这种情况下,CREATE TABLE
语句必须在定义所有列之后指定约束,如下所示:
=> CREATE TABLE public.product_dimension(
product_key int,
product_version int,
product_description varchar(128),
sku_number char(32) UNIQUE,
category_description char(32),
CONSTRAINT pk PRIMARY KEY (product_key, product_version) ENABLED
);
CREATE TABLE
或者,您可以使用单独的
ALTER TABLE...ADD CONSTRAINT
语句指定表的主键,如下所示:
=> ALTER TABLE product_dimension ADD CONSTRAINT pk PRIMARY KEY (product_key, product_version) ENABLED;
ALTER TABLE
您可以通过强制执行主键约束来防止将重复值加载到主键中。这样做可以让您在主键和外键上联接表。当查询将维度表联接到事实表时,维度表中的每个主键都必须唯一地匹配事实表中的每个外键值。否则,尝试联接这些表会返回键强制执行错误。
您可以使用配置参数 EnableNewPrimaryKeysByDefault
全局强制执行主键约束。还可以通过使用关键字 ENABLED
限定约束来对特定表强制执行主键约束约束。在这两种情况下,Vertica 都会在将键值加载到表中时检查键值,并在有任何约束违规时返回错误。或者,在更新表内容后使用
ANALYZE_CONSTRAINTS
验证主键。有关详细信息,请参阅约束强制执行。
在定义主键时,Vertica 会自动将主键列设置为 NOT NULL
。例如,当您按前面所示创建 product_dimension
表时,Vertica 会将主键列 product_key
和 product_version
设置为 NOT NULL
,并将它们相应地存储在编录中:
> SELECT EXPORT_TABLES('','product_dimension');
...
CREATE TABLE public.product_dimension
(
product_key int NOT NULL,
product_version int NOT NULL,
product_description varchar(128),
sku_number char(32),
category_description char(32),
CONSTRAINT C_UNIQUE UNIQUE (sku_number) DISABLED,
CONSTRAINT pk PRIMARY KEY (product_key, product_version) ENABLED
);
(1 row)
如果您使用 ALTER TABLE
为现有表指定主键,Vertica 会通知您将主键列设置为 NOT NULL
:
WARNING 2623: Column "column-name" definition changed to NOT NULL
外键通过引用其主键将一个表联接到另一个表。外键约束指定外键只能包含被引用主键中的值,从而确保在两个键上联接的数据的引用完整性。
您可以在创建表时标识表的外键,也可以在现有表中使用
ALTER TABLE
标识表的外键。例如,下面的 CREATE TABLE
语句定义了两个外键约束: fk_store_orders_store
和 fk_store_orders_vendor
:
=> CREATE TABLE store.store_orders_fact(
product_key int NOT NULL,
product_version int NOT NULL,
store_key int NOT NULL CONSTRAINT fk_store_orders_store REFERENCES store.store_dimension (store_key),
vendor_key int NOT NULL CONSTRAINT fk_store_orders_vendor REFERENCES public.vendor_dimension (vendor_key),
employee_key int NOT NULL,
order_number int NOT NULL,
date_ordered date,
date_shipped date,
expected_delivery_date date,
date_delivered date,
quantity_ordered int,
quantity_delivered int,
shipper_name varchar(32),
unit_price int,
shipping_cost int,
total_order_cost int,
quantity_in_stock int,
reorder_level int,
overstock_ceiling int
);
下面的 ALTER TABLE
语句在同一个表中添加外键约束 fk_store_orders_employee
:
=> ALTER TABLE store.store_orders_fact ADD CONSTRAINT fk_store_orders_employee
FOREIGN KEY (employee_key) REFERENCES public.employee_dimension (employee_key);
REFERENCES
子句可以省略被引用列的名称,但前提是被引用列与外键列同名。例如,下面的 ALTER TABLE
语句等效于上面的语句:
=> ALTER TABLE store.store_orders_fact ADD CONSTRAINT fk_store_orders_employee
FOREIGN KEY (employee_key) REFERENCES public.employee_dimension;
如果外键引用的主键包含多列,则外键必须包含相同数量的列。例如,public.product_dimension
表的主键包含两列:product_key
和 product_version
。在这种情况下,CREATE TABLE
可以定义一个引用该主键的外键约束,如下所示:
=> CREATE TABLE store.store_orders_fact3(
product_key int NOT NULL,
product_version int NOT NULL,
...
CONSTRAINT fk_store_orders_product
FOREIGN KEY (product_key, product_version) REFERENCES public.product_dimension (product_key, product_version)
);
CREATE TABLE
CREATE TABLE
只有在定义所有表列之后才能指定多列外键。还可以使用单独的
ALTER TABLE...ADD CONSTRAINT
语句指定表的外键:
=> ALTER TABLE store.store_orders_fact ADD CONSTRAINT fk_store_orders_product
FOREIGN KEY (product_key, product_version) REFERENCES public.product_dimension (product_key, product_version);
在这两个示例中,约束都指定被引用表中的列。如果被引用列与外键列同名,则 REFERENCES
子句可以省略它们。例如,下面的 ALTER TABLE 语句等效于上面的语句:
=> ALTER TABLE store.store_orders_fact ADD CONSTRAINT fk_store_orders_product
FOREIGN KEY (product_key, product_version) REFERENCES public.product_dimension;
其列省略 NOT NULL 的外键可以包含 NULL 值,即使主键不包含 NULL 值也是如此。因此,即使不知道外键,也可以将行插入到表中。
您可以在列上指定唯一约束,以便该列中的每个值在所有其他值之间都是唯一的。您可以在创建表时定义唯一约束,也可以使用
ALTER TABLE
向现有表添加唯一约束。您不能对具有集合类型的列使用唯一性约束。
例如,下面的 ALTER TABLE
语句将 product_dimensions
表中的 sku_number
列定义为唯一列:
=> ALTER TABLE public.product_dimension ADD UNIQUE(sku_number);
WARNING 4887: Table product_dimension has data. Queries using this table may give wrong results
if the data does not satisfy this constraint
HINT: Use analyze_constraints() to check constraint violation on data
您可以使用配置参数 EnableNewUniqueKeysByDefault
全局强制执行唯一约束。还可以通过使用关键字 ENABLED
限定唯一约束来对特定表强制执行唯一约束。在这两种情况下,Vertica 都会在将值加载到唯一列中时检查值,并在有任何约束违规时返回错误。或者,可以在更新表内容后使用
ANALYZE_CONSTRAINTS
验证唯一约束。有关详细信息,请参阅约束强制执行。
例如,前面的示例不在 sku_number
列中强制执行唯一约束。以下语句启用此约束:
=> ALTER TABLE public.product_dimension ALTER CONSTRAINT C_UNIQUE ENABLED;
ALTER TABLE
您可以定义由多个列组成的唯一约束。下面的 CREATE TABLE
语句指定每行中 c1 和 c2 列的组合值在所有其他行中必须唯一:
CREATE TABLE dim1 (c1 INTEGER,
c2 INTEGER,
c3 INTEGER,
UNIQUE (c1, c2) ENABLED
);
检查约束指定一个布尔表达式,它计算每一行的列值。如果给定行的表达式求值结果为 false,则该列值被视为违反约束。
例如,下表指定了两个命名检查约束:
IsYear2018
指定在 order_date
列中仅允许使用 2018 年的日期。
Ship5dAfterOrder
指定检查每个 ship_date
值是否在 order_date
之后不超过 5 天。
CREATE TABLE public.store_orders_2018 (
order_no int CONSTRAINT pk PRIMARY KEY,
product_key int,
product_version int,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date,
CONSTRAINT IsYear2018 CHECK (DATE_PART('year', order_date)::int = 2018),
CONSTRAINT Ship5dAfterOrder CHECK (DAYOFYEAR(ship_date) - DAYOFYEAR(order_date) <=5)
);
当 Vertica 检查 store_orders_2018
中的数据是否出现约束违规时,它会对每一行的 order_date
和 ship_date
求值,以确定它们是否遵守各自的检查约束。
检查表达式只能引用当前表行,无法访问存储在其他表或数据库对象(如序列)中的数据,也无法访问其他表行中的数据。
检查约束表达式可以包括:
算法和串联字符串运算符
逻辑运算符,例如 AND
、OR
、 NOT
WHERE 介词,例如 CASE
、IN
、LIKE
、BETWEEN
、 IS [NOT] NULL
调用以下函数类型:
不可变的内置 SQL 函数,例如
LENGTH
不可变的 SQL 宏(请参阅下面的检查约束和 SQL 宏)
在组件工厂中标记为不可变的、由用户定义的标量函数(请参阅下面的检查约束和 UDSF)
以下检查表达式假设表包含所有被引用列,还假设这些列具有适当的数据类型:
CONSTRAINT chk_pos_quant CHECK (quantity > 0)
CONSTRAINT chk_pqe CHECK (price*quantity = extended_price)
CONSTRAINT size_sml CHECK (size in ('s', 'm', 'l', 'xl'))
CHECK ( regexp_like(dept_name, '^[a-z]+$', 'i') OR (dept_name = 'inside sales'))
检查表达式的求值结果必须为布尔值。但是,Vertica 不支持隐式转换为布尔值。例如,以下检查表达式无效:
CHECK (1)
CHECK ('hello')
检查表达式不能包含下列元素:
子查询 — 例如, CHECK (dept_id in (SELECT id FROM dept))
聚合 — 例如, CHECK (quantity < sum(quantity)/2)
窗口函数 — 例如, CHECK (RANK() over () < 3)
SQL 元函数 — 例如, CHECK (START_REFRESH('') = 0)
引用时期列
引用其他表或对象(如序列)或者系统上下文
调用在时间和空间上不可变的函数
您可以使用配置参数 EnableNewCheckConstraintsByDefault
全局强制执行检查约束。还可以通过使用关键字 ENABLED
限定唯一约束来对特定表强制执行检查约束。在这两种情况下,Vertica 都会在将新值加载到表中时对检查约束求值,并在有任何约束违规时返回错误。或者,可以在更新表内容后使用
ANALYZE_CONSTRAINTS
来验证检查约束。有关详细信息,请参阅约束强制执行。
例如,可以使用 ALTER TABLE...ALTER CONSTRAINT
启用前面显示的约束:
=> ALTER TABLE store_orders_2018 ALTER CONSTRAINT IsYear2018 ENABLED;
ALTER TABLE
=> ALTER TABLE store_orders_2018 ALTER CONSTRAINT Ship5dAfterOrder ENABLED;
ALTER TABLE
对于给定行,如果由于检查表达式中的某个列包含 NULL 而导致该表达式的求值结果未知,则该行将通过约束条件。Vertica 会对表达式进行求值,如果求值结果为 true 或未知,则它会认为该表达式符合条件。例如,如果 quantity
为 NULL,则 check (quantity > 0)
将通过验证。此结果不同于 WHERE
子句的工作原理。使用 WHERE 子句时,该行将不会包含在结果集中。
可以通过在检查约束表达式中显式包含 NULL 检查,在检查约束中禁止 NULL。例如: CHECK (quantity IS NOT NULL AND (quantity > 0))
NOT NULL
约束。
如果 SQL 宏(用 SQL 编写的函数)不可变,检查约束可以调用该宏。不可变宏始终会为一组给定实参返回相同值。
当 DDL 语句在检查表达式中指定宏时,Vertica 会确定它是否不可变。如果不是,Vertica 会回退该 DDL 语句。
以下示例创建宏 mycompute
,然后在检查表达式中使用它:
=> CREATE OR REPLACE FUNCTION mycompute(j int, name1 varchar)
RETURN int AS BEGIN RETURN (j + length(name1)); END;
=> ALTER TABLE sampletable
ADD CONSTRAINT chk_compute
CHECK(mycompute(weekly_hours, name1))<50);
检查约束可以调用用户定义的标量函数 (UDSF)。需要满足以下要求:
UDSF 必须在 UDx 工厂中标记为不可变。
约束正确处理 NULL 值。
有关使用示例,请参阅 C++ 示例:从检查约束调用 UDSF。
NOT NULL> 约束指定列不能包含 NULL 值。在进行所有表更新时,都必须在具有此约束的列中指定值。您可以在创建表时对列设置 NOT NULL
约束,也可以使用 ALTER TABLE 对现有表设置约束。
以下 CREATE TABLE 语句将三列定义为 NOT NULL。您不能在这些列中存储任何 NULL 值。
=> CREATE TABLE inventory ( date_key INTEGER NOT NULL,
product_key INTEGER NOT NULL,
warehouse_key INTEGER NOT NULL, ... );
以下 ALTER TABLE 语句将 product_dimensions 表中的 sku_number 列定义为 NOT NULL:
=> ALTER TABLE public.product_dimension ALTER COLUMN sku_number SET NOT NULL;
ALTER TABLE
您不能对 NOT NULL 约束启用强制执行。您必须使用 ANALYZE_CONSTRAINTS 确定列数据是否包含 NULL 值,然后手动修复该函数发现的任何约束违规。
在定义主键时,Vertica 会自动将主键列设置为 NOT NULL。如果删除主键约束,构成它的列仍设置为 NOT NULL。只能通过 ALTER TABLE...ALTER COLUMN 显式移除此约束。
您可以使用
CREATE TABLE
和
分别对新表和现有表设置约束ALTER TABLE...ADD CONSTRAINT
。
CREATE TABLE
可以通过两种方式指定约束:作为列定义的一部分,或遵循所有列定义。
例如,以下 CREATE TABLE
语句对 sku_number
、NOT NULL
和 UNIQUE
列设置两个约束。在定义了所有列之后,该语句还设置一个由以下两列组成的主键:product_key
和 product_version
:
=> CREATE TABLE public.prod_dimension(
product_key int,
product_version int,
product_description varchar(128),
sku_number char(32) NOT NULL UNIQUE,
category_description char(32),
CONSTRAINT pk PRIMARY KEY (product_key, product_version) ENABLED
);
CREATE TABLE
ALTER TABLE...ADD CONSTRAINT
向现有表添加约束。例如,以下语句为 product_version
列指定唯一值:
=> ALTER TABLE prod_dimension ADD CONSTRAINT u_product_versions UNIQUE (product_version) ENABLED;
ALTER TABLE
在已包含数据的列上添加约束时,如果以下条件都成立,Vertica 会立即验证列值:
如果其中任何一个条件不成立,Vertica 不会验证列值。在这种情况下,您必须调用
ANALYZE_CONSTRAINTS
来查找约束违规。否则,查询可能会返回意外结果。有关详细信息,请参阅检测约束违规。
无论您是在列定义中还是在表中指定约束,Vertica 都会将表 DDL 存储为 CREATE
语句的一部分,并将约束导出。有一个例外:外键作为 ALTER TABLE
语句存储和导出。
例如:
=> SELECT EXPORT_TABLES('','prod_dimension');
...
CREATE TABLE public.prod_dimension
(
product_key int NOT NULL,
product_version int NOT NULL,
product_description varchar(128),
sku_number char(32) NOT NULL,
category_description char(32),
CONSTRAINT C_UNIQUE UNIQUE (sku_number) DISABLED,
CONSTRAINT pk PRIMARY KEY (product_key, product_version) ENABLED,
CONSTRAINT u_product_versions UNIQUE (product_version) ENABLED
);
(1 row)
ALTER TABLE
通过两种方式从表中删除约束:
ALTER TABLE...DROP CONSTRAINT
移除命名表的约束。
ALTER TABLE...ALTER COLUMN
移除列的 NOT NULL
约束。
例如,表 store_orders_2018
指定以下约束:
命名约束 pk
将列 order_no
标识为主键。
命名约束 IsYear2018
指定的检查约束在 order_date
列中仅允许使用 2018 年的日期。
命名约束 Ship5dAfterOrder
指定的检查约束禁止使用 order_date
之后 5 天以上的任何 ship_date
值。
order_no
和 order_date
列设置为 NOT NULL
。
CREATE TABLE public.store_orders_2018 (
order_no int NOT NULL CONSTRAINT pk PRIMARY KEY,
product_key int,
product_version int,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date,
CONSTRAINT IsYear2018 CHECK (DATE_PART('year', order_date)::int = 2018),
CONSTRAINT Ship5dAfterOrder CHECK (DAYOFYEAR(ship_date) - DAYOFYEAR(order_date) <=5)
);
您可以使用
ALTER TABLE...DROP CONSTRAINT
移除主键、外键、检查和唯一约束,这需要您提供它们的名称。例如,您按如下方式移除表 store_orders_2018
中的主键约束:
=> ALTER TABLE store_orders_2018 DROP CONSTRAINT pk;
ALTER TABLE
=> SELECT export_tables('','store_orders_2018');
export_tables
---------------------------------------------------------------------------------------------------------------------------------------
CREATE TABLE public.store_orders_2018
(
order_no int NOT NULL,
product_key int,
product_version int,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date,
CONSTRAINT IsYear2018 CHECK (((date_part('year', store_orders_2018.order_date))::int = 2018)) ENABLED,
CONSTRAINT Ship5dAfterOrder CHECK (((dayofyear(store_orders_2018.ship_date) - dayofyear(store_orders_2018.order_date)) <= 5)) ENABLED
);
使用
ALTER TABLE...ALTER COLUMN
删除列的 NOT NULL
约束,如以下示例所示:
=> ALTER TABLE store_orders_2018 ALTER COLUMN order_date DROP NOT NULL;
ALTER TABLE
如果某个主键约束被另一个表中的外键约束引用,则不能删除该主键约束。若要删除主键,必须首先删除所有引用它的外键。
如果您尝试删除由同一表中的约束引用的列,则删除操作会返回错误。例如,检查约束 Ship5dAfterOrder
会引用两列:order_date
和 ship_date
。如果您尝试删除其中的任一列,Vertica 会返回以下错误消息:
=> ALTER TABLE public.store_orders_2018 DROP COLUMN ship_date;
ROLLBACK 3128: DROP failed due to dependencies
DETAIL:
Constraint Ship5dAfterOrder references column ship_date
HINT: Use DROP .. CASCADE to drop or modify the dependent objects
在这种情况下,您必须使用 CASCADE
选项限定 DROP COLUMN
子句,该选项指定要删除列及其依赖对象 — 在这种情况下,约束 Ship5dAfterOrder
:
=> ALTER TABLE public.store_orders_2018 DROP COLUMN ship_date CASCADE;
ALTER TABLE
调用 Vertica 函数 EXPORT_TABLES
可确认列和约束是否均已移除:
=> ALTER TABLE public.store_orders_2018 DROP COLUMN ship_date CASCADE;
ALTER TABLE
dbadmin=> SELECT export_tables('','store_orders_2018');
export_tables
---------------------------------------------------------------------------------------------------------
CREATE TABLE public.store_orders_2018
(
order_no int NOT NULL,
product_key int,
product_version int,
order_date timestamp,
shipper varchar(20),
CONSTRAINT IsYear2018 CHECK (((date_part('year', store_orders_2018.order_date))::int = 2018)) ENABLED
);
(1 row)
必须对以下约束进行命名。
PRIMARY KEY
REFERENCES
(外键)
CHECK
UNIQUE
在定义这些约束时对它们进行命名。如果您未分配名称,Vertica 会自动分配名称。
当您使用
CREATE TABLE
或
ALTER TABLE...ADD CONSTRAINT
定义约束时,您可以为约束分配名称。例如,以下 CREATE TABLE
语句分别对主键约束和检查约束 pk
和 date_c
进行命名:
=> CREATE TABLE public.store_orders_2016
(
order_no int CONSTRAINT pk PRIMARY KEY,
product_key int,
product_version int,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date,
CONSTRAINT date_c CHECK (date_part('year', order_date)::int = 2016)
)
PARTITION BY ((date_part('year', order_date))::int);
CREATE TABLE
以下 ALTER TABLE
语句添加外键约束 fk
:
=> ALTER TABLE public.store_orders_2016 ADD CONSTRAINT fk
FOREIGN KEY (product_key, product_version)
REFERENCES public.product_dimension (product_key, product_version);
对约束进行命名是可选的。如果您不为约束分配名称,Vertica 将使用以下约定分配自己的名称:
C_constraint-type[_integer]
例如,下表定义了两个名为 a
和 b
列,并将它们限制为仅包含唯一值:
=> CREATE TABLE t1 (a int UNIQUE, b int UNIQUE );
CREATE TABLE
当您使用
EXPORT_TABLES
导出表的 DDL 时,该函数的输出中显示 Vertica 已为 a
列分配约束名称 C_UNIQUE
,已为 b
列分配约束名称 C_UNIQUE_1
:
=> SELECT EXPORT_TABLES('','t1');
CREATE TABLE public.t1
(
a int,
b int,
CONSTRAINT C_UNIQUE UNIQUE (a) DISABLED,
CONSTRAINT C_UNIQUE_1 UNIQUE (b) DISABLED
);
(1 row)
您可以通过使用
EXPORT_TABLES
导出表的 DDL 来查看表约束的名称,如前面所示。您还可以查询以下系统表:
例如,以下查询获取 online_sales
架构中所有主键约束和外键约束的名称:
=> SELECT table_name, constraint_name, column_name, constraint_type FROM constraint_columns
WHERE constraint_type in ('p','f') AND table_schema='online_sales'
ORDER BY table_name, constraint_type, constraint_name;
table_name | constraint_name | column_name | constraint_type
-----------------------+---------------------------+-----------------+-----------------
call_center_dimension | C_PRIMARY | call_center_key | p
online_page_dimension | C_PRIMARY | online_page_key | p
online_sales_fact | fk_online_sales_cc | call_center_key | f
online_sales_fact | fk_online_sales_customer | customer_key | f
online_sales_fact | fk_online_sales_op | online_page_key | f
online_sales_fact | fk_online_sales_product | product_version | f
online_sales_fact | fk_online_sales_product | product_key | f
online_sales_fact | fk_online_sales_promotion | promotion_key | f
online_sales_fact | fk_online_sales_saledate | sale_date_key | f
online_sales_fact | fk_online_sales_shipdate | ship_date_key | f
online_sales_fact | fk_online_sales_shipping | shipping_key | f
online_sales_fact | fk_online_sales_warehouse | warehouse_key | f
(12 rows)
您必须引用约束名称才能执行以下任务:
启用或禁用约束强制执行。
删除约束。
例如,以下 ALTER TABLE
语句在 store_orders_2016
表中启用对 pk
约束的强制执行:
=> ALTER TABLE public.store_orders_2016 ALTER CONSTRAINT pk ENABLED;
ALTER TABLE
以下语句删除同一个表中的另一个约束:
=> ALTER TABLE public.store_orders_2016 DROP CONSTRAINT date_c;
ALTER TABLE
ANALYZE_CONSTRAINTS
分析和报告给定架构中的表约束违规。您可以使用 ANALYZE_CONSTRAINTS
来分析单个表、表中的特定列或架构中的所有表。您通常针对未强制执行主键、唯一性或检查约束的表使用此函数。您还可以使用 ANALYZE_CONSTRAINTS
来检查外键的引用完整性。
在最简单的用例中,ANALYZE_CONSTRAINTS
是包含两个步骤的过程:
针对所需的表运行 ANALYZE_CONSTRAINTS
。 ANALYZE_CONSTRAINTS
报告所有约束违规。
使用报告来修复违规。
您还可以在以下情况下使用 ANALYZE_CONSTRAINTS
:
分析已强制执行约束的表。
检测由 COPY
操作引入的约束违规,并在提交复制事务之前解决它们。
如果对表强制执行约束,而且 DML 操作返回约束违规,则 Vertica 会在回退操作之前报告有限数量的约束违规。当您尝试加载大量包含多个约束违规(例如,键值重复)的数据时,这可能会出现问题。在这种情况下,按如下方式使用 ANALYZE_CONSTRAINTS
:
针对目标表暂时禁用所有约束的强制执行。
运行 DML 操作。
操作返回后,对表运行 ANALYZE_CONSTRAINTS
。 ANALYZE_CONSTRAINTS
报告所有约束违规。
使用报告来修复违规。
对表重新启用约束强制执行。
使用 ANALYZE_CONSTRAINTS
来检测和解决由
COPY
操作引入的约束违规,如下所示:
使用 COPY...NO COMMIT
将源数据复制到目标表中。
调用 ANALYZE_CONSTRAINTS
以检查目标表及其未提交的更新。
如果 ANALYZE_CONSTRAINTS
报告约束违规,则回退复制事务。
使用报告修复违规,然后重新执行复制操作。
有关使用 COPY...NO COMMIT
的详细信息,请参阅使用事务暂存加载。
ANALYZE_CONSTRAINTS
作为原子操作运行 — 也就是说,它在计算指定范围内的所有约束之后才会返回。例如,如果您对表运行 ANALYZE_CONSTRAINTS
,则该函数仅在针对列数据计算所有列约束后才返回。如果表中包含大量带有约束的列,并且包含非常大的数据集,则 ANALYZE_CONSTRAINTS
可能会耗尽所有可用内存并返回内存不足错误。同时对多个表或整个数据库运行 ANALYZE_CONSTRAINTS
会增加这种风险。
您可以通过将配置参数 MaxConstraintChecksPerQuery 设置为正整数(默认值为 ‑1)来最大程度地降低内存不足错误所带来的风险。例如,如果此参数设置为 20,并且您对包含 38 个列约束的表运行 ANALYZE_CONSTRAINTS
,则该函数会将其工作分成两个单独的查询。 ANALYZE_CONSTRAINTS
创建一个临时表来加载和编译来自两个查询的结果,然后返回复合结果集。
MaxConstraintChecksPerQuery 只能在数据库级别进行设置,并且会产生一定的开销。设置后,提交到由 ANALYZE_CONSTRAINTS
创建的临时表会导致所有挂起的数据库事务自动提交。将此参数设置为合理的数值(例如 20)应该可以最大限度地减少对性能的影响。
您可以强制执行以下约束:
PRIMARY KEY
UNIQUE
CHECK
对表启用约束强制执行之后,Vertica 会立即将该约束应用到表的当前内容,以及稍后添加或更新的所有内容。
以下 DDL 和 DML 操作可调用约束强制执行:
ALTER TABLE...ADD CONSTRAINT
和
ALTER TABLE...ALTER CONSTRAINT
COPY
UPDATE
MERGE
分区函数:
-
COPY_PARTITIONS_TO_TABLE
-
MOVE_PARTITIONS_TO_TABLE
-
SWAP_PARTITIONS_BETWEEN_TABLES
启用约束强制执行有助于最大程度地减少加载后的维护任务,例如:使用
ANALYZE_CONSTRAINTS
单独验证数据,然后处理其返回的约束违规问题。
强制执行键约束,尤其是启用主键约束,可以帮助优化器更快地生成查询计划,特别是联接的查询计划。当对表实施主键约束时,优化器假定该表中的所有行均未包含重复的键值。
在某些情况下,大量的约束强制执行(尤其是在大型数据表中)可能会导致系统开销显著增加。有关详细信息,请参阅约束强制执行和性能。
可以在两个级别强制执行约束:
[表约束](#Table Co)
Vertica 支持通过以下三个布尔参数来强制执行约束:
可以通过使用关键字 ENABLED
或 DISABLED
限定约束,借助于
CREATE TABLE
和
ALTER TABLE
对表设置约束强制执行。以下 CREATE TABLE
语句在其 order_qty
列的定义中对检查约束启用强制执行:
=> CREATE TABLE new_orders (
cust_id int,
order_date timestamp DEFAULT CURRENT_TIMESTAMP,
product_id varchar(12),
order_qty int CHECK(order_qty > 0) ENABLED,
PRIMARY KEY(cust_id, order_date) ENABLED
);
CREATE TABLE
ALTER TABLE
可以对现有约束启用强制执行。以下语句通过对指定的约束 C_UNIQUE
启用强制执行来修改表 customer_dimension
:
=> ALTER TABLE public.customer_dimension ALTER CONSTRAINT C_UNIQUE ENABLED;
ALTER TABLE
表和列的强制执行设置优先于强制执行参数设置。如果表或列约束省略 ENABLED
或 DISABLED
,Vertica 将使用相关配置参数的当前设置。
ENABLED
或 DISABLED
的现有表约束没有影响。这些表约束将保留它们先前获取的强制执行设置。您只能使用
ALTER TABLE...ALTER CONSTRAINT
更改这些约束的强制执行设置。
下面的 CREATE TABLE
语句创建其中包含列 order_id
和 order_qty
的表 new_sales
,这两列分别定义有约束 PRIMARY KEY
和 CHECK
:
=> CREATE TABLE new_sales ( order_id int PRIMARY KEY, order_qty int CHECK (order_qty > 0) );
这两个约束都没有显式启用或禁用,因此 Vertica 使用配置参数 EnableNewPrimaryKeysByDefault
和 EnableNewCheckConstraintsByDefault
在表定义中设置强制执行:
=> SHOW CURRENT EnableNewPrimaryKeysByDefault, EnableNewCheckConstraintsByDefault;
level | name | setting
---------+------------------------------------+---------
DEFAULT | EnableNewPrimaryKeysByDefault | 0
DEFAULT | EnableNewCheckConstraintsByDefault | 1
(2 rows)
=> SELECT EXPORT_TABLES('','new_sales');
...
CREATE TABLE public.new_sales
(
order_id int NOT NULL,
order_qty int,
CONSTRAINT C_PRIMARY PRIMARY KEY (order_id) DISABLED,
CONSTRAINT C_CHECK CHECK ((new_sales.order_qty > 0)) ENABLED
);
(1 row)
在这种情况下,将 EnableNewPrimaryKeysByDefault
更改为 1(启用)对表 new_sales
中的 C_PRIMARY
约束没有影响。可以使用 ALTER TABLE...ALTER CONSTRAINT
强制执行此约束:
=> ALTER TABLE public.new_sales ALTER CONSTRAINT C_PRIMARY ENABLED;
ALTER TABLE
SHOW CURRENT 可以返回约束强制执行参数的设置:
=> SHOW CURRENT EnableNewCheckConstraintsByDefault, EnableNewUniqueKeysByDefault, EnableNewPrimaryKeysByDefault;
level | name | setting
----------+------------------------------------+---------
DEFAULT | EnableNewCheckConstraintsByDefault | 1
DEFAULT | EnableNewUniqueKeysByDefault | 0
DATABASE | EnableNewPrimaryKeysByDefault | 1
(3 rows)
您还可以查询下面的系统表以检查表强制执行设置:
例如,以下语句查询 TABLE_CONSTRAINTS
并返回数据库表中的所有约束。对于所有可以启用或禁用的约束(PRIMARY KEY
、UNIQUE
和 CHECK
),is_enabled
列设置为 true 或 false:
=> SELECT constraint_name, table_name, constraint_type, is_enabled FROM table_constraints ORDER BY is_enabled, table_name;
constraint_name | table_name | constraint_type | is_enabled
---------------------------+-----------------------+-----------------+------------
C_PRIMARY | call_center_dimension | p | f
C_PRIMARY | date_dimension | p | f
C_PRIMARY | employee_dimension | p | f
C_PRIMARY | online_page_dimension | p | f
C_PRIMARY | product_dimension | p | f
C_PRIMARY | promotion_dimension | p | f
C_PRIMARY | shipping_dimension | p | f
C_PRIMARY | store_dimension | p | f
C_UNIQUE_1 | tabletemp | u | f
C_PRIMARY | vendor_dimension | p | f
C_PRIMARY | warehouse_dimension | p | f
C_PRIMARY | customer_dimension | p | t
C_PRIMARY | new_sales | p | t
C_CHECK | new_sales | c | t
fk_inventory_date | inventory_fact | f |
fk_inventory_product | inventory_fact | f |
fk_inventory_warehouse | inventory_fact | f |
...
以下查询返回具有主键、唯一性和检查约束的所有表,并显示是否启用了约束:
=> SELECT table_name, constraint_name, constraint_type, is_enabled FROM constraint_columns
WHERE constraint_type in ('p', 'u', 'c')
ORDER BY table_name, constraint_type;
=> SELECT table_name, constraint_name, constraint_type, is_enabled FROM constraint_columns WHERE constraint_type in ('p', 'u', 'c') ORDER BY table_name, constraint_type;
table_name | constraint_name | constraint_type | is_enabled
-----------------------+-----------------+-----------------+------------
call_center_dimension | C_PRIMARY | p | f
customer_dimension | C_PRIMARY | p | t
customer_dimension2 | C_PRIMARY | p | t
customer_dimension2 | C_PRIMARY | p | t
date_dimension | C_PRIMARY | p | f
employee_dimension | C_PRIMARY | p | f
new_sales | C_CHECK | c | t
new_sales | C_PRIMARY | p | t
...
Vertica 在以下两种情况下报告约束违规:
ALTER TABLE
尝试对已包含数据的表启用约束强制执行,但数据不符合约束。
DML 操作尝试在已强制执行约束的表上添加或更新数据,但新数据不符合一个或多个约束。
当您使用
ALTER TABLE...ADD CONSTRAINT
或
ALTER TABLE...ALTER CONSTRAINT
对现有表启用约束强制执行时,Vertica 会将该约束立即应用于表的当前内容。如果 Vertica 检测到约束违规,Vertica 会返回一个报告违规的错误,然后回退 ALTER TABLE
语句。
例如:
=> ALTER TABLE public.customer_dimension ADD CONSTRAINT unique_cust_types UNIQUE (customer_type) ENABLED;
ERROR 6745: Duplicate key values: 'customer_type=Company'
-- violates constraint 'public.customer_dimension.unique_cust_types'
DETAIL: Additional violations:
Constraint 'public.customer_dimension.unique_cust_types':
duplicate key values: 'customer_type=Individual'
当您调用 DML 操作以在已强制执行约束的表上添加或更新数据时,Vertica 会检查新数据是否符合这些约束。如果 Vertica 检测到约束违规,此操作会返回一个报告违规的错误,然后执行回退。
例如,使用相同的主键和检查约束来定义 store_orders
和 store_orders_2015
表。这两个表都可以强制执行主键约束;只有 store_orders_2015
强制执行检查约束:
CREATE TABLE public.store_orders
(
order_no int NOT NULL,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date
)
PARTITION BY ((date_part('year', store_orders.order_date))::int);
ALTER TABLE public.store_orders ADD CONSTRAINT C_PRIMARY PRIMARY KEY (order_no) ENABLED;
ALTER TABLE public.store_orders ADD CONSTRAINT C_CHECK CHECK (((date_part('year', store_orders.order_date))::int = 2014)) DISABLED;
CREATE TABLE public.store_orders_2015
(
order_no int NOT NULL,
order_date timestamp NOT NULL,
shipper varchar(20),
ship_date date
)
PARTITION BY ((date_part('year', store_orders_2015.order_date))::int);
ALTER TABLE public.store_orders_2015 ADD CONSTRAINT C_PRIMARY PRIMARY KEY (order_no) ENABLED;
ALTER TABLE public.store_orders_2015 ADD CONSTRAINT C_CHECK CHECK (((date_part('year', store_orders_2015.order_date))::int = 2015)) ENABLED;
如果您尝试将具有重复键值的数据插入 store_orders
中,则插入操作会返回错误消息。错误消息包含有关第一次违规的详细信息。它还返回有关后续违规(最多前 30 个)的简短信息。如有必要,错误消息还会包括一条说明违规次数超过 30 的注释:
=> INSERT INTO store_orders SELECT order_number, date_ordered, shipper_name, date_shipped FROM store.store_orders_fact;
ERROR 6745: Duplicate key values: 'order_no=10' -- violates constraint 'public.store_orders.C_PRIMARY'
DETAIL: Additional violations:
Constraint 'public.store_orders.C_PRIMARY':
duplicate key values:
'order_no=11'; 'order_no=12'; 'order_no=13'; 'order_no=14'; 'order_no=15'; 'order_no=17';
'order_no=21'; 'order_no=23'; 'order_no=26'; 'order_no=27'; 'order_no=29'; 'order_no=33';
'order_no=35'; 'order_no=38'; 'order_no=39'; 'order_no=4'; 'order_no=41'; 'order_no=46';
'order_no=49'; 'order_no=6'; 'order_no=62'; 'order_no=67'; 'order_no=68'; 'order_no=70';
'order_no=72'; 'order_no=75'; 'order_no=76'; 'order_no=77'; 'order_no=79';
Note: there were additional errors
同样,以下将数据从 store_orders
复制到 store_orders_2015
的尝试违反了表的检查约束。它返回一条类似以上消息的错误消息:
=> SELECT COPY_TABLE('store_orders', 'store_orders_2015');
NOTICE 7636: Validating enabled constraints on table 'public.store_orders_2015'...
ERROR 7231: Check constraint 'public.store_orders_2015.C_CHECK' ((date_part('year', store_orders_2015.order_date))::int = 2015)
violation in table 'public.store_orders_2015': 'order_no=101,order_date=2007-05-02 00:00:00'
DETAIL: Additional violations:
Check constraint 'public.store_orders_2015.C_CHECK':violations:
'order_no=106,order_date=2016-07-01 00:00:00'; 'order_no=119,order_date=2016-01-04 00:00:00';
'order_no=14,order_date=2016-07-01 00:00:00'; 'order_no=154,order_date=2016-11-06 00:00:00';
'order_no=156,order_date=2016-04-10 00:00:00'; 'order_no=171,order_date=2016-10-08 00:00:00';
'order_no=203,order_date=2016-03-01 00:00:00'; 'order_no=204,order_date=2016-06-09 00:00:00';
'order_no=209,order_date=2016-09-07 00:00:00'; 'order_no=214,order_date=2016-11-02 00:00:00';
'order_no=223,order_date=2016-12-08 00:00:00'; 'order_no=227,order_date=2016-08-02 00:00:00';
'order_no=240,order_date=2016-03-09 00:00:00'; 'order_no=262,order_date=2016-02-09 00:00:00';
'order_no=280,order_date=2016-10-10 00:00:00';
Note: there were additional errors
用来添加或更新表内容的分区管理函数也必须遵守目标表中强制执行的约束。例如,以下
MOVE_PARTITIONS_TO_TABLE
操作尝试将分区从 store_orders
移至 store_orders_2015
。但是,源分区包含违反目标表检查约束的数据。因此,该函数返回的结果表明它未能移动任何数据:
=> SELECT MOVE_PARTITIONS_TO_TABLE ('store_orders','2014','2014','store_orders_2015');
NOTICE 7636: Validating enabled constraints on table 'public.store_orders_2015'...
MOVE_PARTITIONS_TO_TABLE
--------------------------------------------------
0 distinct partition values moved at epoch 204.
对于需要验证启用的主键和唯一约束的 DML 操作,Vertica 使用插入/验证 (IV) 锁。
在强制执行主键或唯一键约束的表上运行此类操作时,Vertica 将按以下方式对表设置锁:
设置 I(插入)锁以加载数据。多个会话可以同时获取同一张表的 I 锁,并同时加载数据。
设置表的 IV 锁,可针对表的主约束和唯一约束验证加载的数据。一次只有一个会话可以获取给定表的 IV 锁。其他需要访问该表的会话将被阻止,直到 IV 锁释放为止。 会话将保留其 IV 锁,直到出现以下两个事件之一:
验证已完成且已提交 DML 操作。
检测到约束违规且已回滚操作。
在上述任一情况下,Vertica 均会释放 IV 锁。
Vertica 验证表的主键或唯一键约束时,将暂时阻止对表执行其他 DML 操作。当多个会话同时尝试对同一个表上的数据进行大量更改时,此类延迟可能会特别明显。
例如,在三个并发会话中,每个会话均尝试将数据加载到表 t1
中,如下所示:
所有三个会话均获取 t1
的 I 锁,并开始将数据加载到表中。
会话 2 获取 t1
的排他 IV 锁,以验证其加载数据的表约束。一次只有一个会话可以获取表的 IV 锁,因此会话 1 和 3 必须等待会话 2 完成验证,然后才能开始自己的验证。
会话 2 成功验证它加载到 t1
中的所有数据。提交其加载事务之后,它将释放表的 IV 锁。
会话 1 获取 t1
的 IV 锁,并开始验证其加载的数据。在此示例中,Vertica 检测到违反约束并回滚加载事务。会话 1 释放其 t1
IV 锁。
现在,会话 3 获取 t1
的 IV 锁,并开始验证其加载的数据。完成验证后,会话 3 提交其加载事务,并释放 t1
的 IV 锁。该表现在可用于其他 DML 操作。
有关锁定架构和兼容性以及转换矩阵的信息,请参阅 锁定模式。另请参阅 LOCKS 和 LOCK_USAGE。
在某些情况下,强制执行约束会显著影响整体系统性能。当对需要频繁进行并发批量更新的大型事实表强制执行约束时尤其如此。每个调用约束强制执行的更新操作都要求 Vertica 检查每个表行是否存在所有约束违规。因此,对具有大量数据的表强制执行多个约束可能会导致显著延迟。
为了最大限度地减少因强制执行约束而产生的开销,请忽略对经常更新的大型表强制执行约束。您可以通过在非高峰时段运行
ANALYZE_CONSTRAINTS
来评估这些表是否出现约束违规。
约束强制执行的多个方面会对系统性能产生特定的影响。这些包括:
如果针对表强制执行约束,Vertica 在 DML 操作期间会在该表上设置插入/验证 (IV) 锁定,同时进行验证。一次只有一个会话可以获取该表上的 IV 锁定。只要会话持有这个锁定,其他会话就不能访问该表。长时间加载容易导致性能瓶颈,尤其是当多个会话尝试同时加载同一个表时。有关详细信息,请参阅约束强制执行和锁定。
为了强制执行主键和唯一约束,Vertica 创建了用于验证数据的特殊投影。根据锚表中的数据量,创建投影可能会产生巨大的系统开销。
Vertica 针对每个 SQL 语句验证强制执行的约束,并回退遇到约束违规的每个语句。您无法将约束强制执行延迟到事务提交后再执行。因此,如果多个 DML 语句包含一个事务,Vertica 会分别验证每个语句以确保符合约束,并回退任何未通过验证的语句。它仅在其中的所有语句都返回后才提交事务。
例如,您可能在一个针对其中一列强制执行 UNIQUE
的表上通过单个事务发出十条 INSERT
语句。如果第六条语句尝试在该列中插入重复值,则会回退该语句。但是,可以提交其他语句。
为了强制执行主键和唯一约束,Vertica 创建了特殊的约束强制执行投影,用于验证新数据和更新后的数据。如果您在空表上添加约束,Vertica 仅在向该表添加数据时才会为该表创建约束强制执行投影。如果您向填充有内容的表添加主键或唯一约束并启用强制执行,Vertica 会选择现有投影来强制执行约束(如果存在)。否则,Vertica 会为该约束创建投影。如果出现约束违规,Vertica 会回退语句以及它为约束创建的任何投影。
如果您删除已强制执行的主键或唯一约束,Vertica 会自动删除与该约束关联的投影。您还可以使用
DROP PROJECTION
显式删除约束投影。如果该语句省略了 CASCADE
,Vertica 会发出一条警告,提示为已启用的约束删除此投影;否则,它会静默删除此投影。无论哪种情况,下次 Vertica 需要强制执行此约束时,它都会重新创建投影。根据锚表中的数据量,创建投影可能会产生巨大的开销。
您可以查询系统表
PROJECTIONS
的布尔列 IS_KEY_CONSTRAINT_PROJECTION
以获得特定于约束的投影。
ANALYZE_CONSTRAINTS
显著促进约束分析。
Vertica 不支持对外键或外部表强制执行约束。限制也适用于临时表。
Vertica 不支持强制执行外键和引用完整性。因此,在以下情况下,在加载数据时可能会返回错误:
处理内部联接查询时。
由于存在外键而将外部联接视为内部联接时。
要验证外键约束,请使用
ANALYZE_CONSTRAINTS
。
Vertica 不支持对外部表自动强制执行约束。
ALTER TABLE
仅当表中不包含数据时,才能对本地临时表中的主键或唯一约束设置强制执行。如果您尝试在包含数据的表中强制执行约束,ALTER TABLE
会返回错误。
在全局临时表中,只能使用 CREATE TEMPORARY TABLE 对主键或唯一约束设置强制执行。 ALTER TABLE
在您尝试对现有表(无论已填充还是为空)中的主键或唯一约束设置强制执行时返回错误。
ALTER TABLE...DROP CONSTRAINT
来禁用本地和全局临时表中的主键和唯一键约束。
提交查询时,查询优化器序会迅速选择要使用的投影,优化并计划查询执行,以及将 SQL 语句记录到其日志中。此计划会产生一个用来映射出查询所执行步骤的查询计划。
查询计划是一系列类似步骤的 路径,Vertica 基于成本的查询优化器使用这些路径执行查询。Vertica 可以为给定查询生成不同的查询计划。对于每个查询计划,查询优化器都评估要查询的数据:行数、列统计,如不同值的数量(基数)、数据在各个节点上的分布。它还评估可用资源,如 CPU 和网络拓扑以及其他环境因素。查询优化器使用此信息制定多个潜在计划。然后它比较各个计划并选择一个,该计划通常为具有最低成本的计划。
优化器将查询计划细分为更小的本地计划并将它们分发到 执行程序节点
在查询计划的最后执行阶段,启动程序节点执行以下任务:
通过分组操作将结果组合起来。
将来自所有执行程序的多个已排序部分结果集合并起来。
设置结果格式,以返回给客户端。
可以用以下两种方式获得查询计划:
管理控制台提供用于查看查询计划的图形界面。有关详细信息,请参阅在 MC 中使用查询计划。
您还可以通过查询系统表
QUERY_PLAN_PROFILES
,观察流经查询计划的实时数据流。有关详细信息,请参阅分析查询计划。
默认情况下,EXPLAIN
输出将查询计划表示为层次结构,其中的每个级别或
路径都表示优化器用于执行查询的一个数据库操作。 EXPLAIN
输出还会附加 DOT 语言源,因此您可以使用开源 Graphviz 工具以图形方式显示此输出。
EXPLAIN
支持生成详细和 JSON 输出的选项。还可以显示分配给每个节点的本地查询计划,它们共同组成整个(全局)查询计划。
EXPLAIN
还支持 ANNOTATED
选项。 EXPLAIN ANNOTATED
返回带有嵌入式优化器提示的查询,这些提示会封装此查询的查询计划。有关使用示例,请参阅结合使用优化器生成的定向查询和自定义定向查询。
EXPLAIN 返回用于执行指定查询的优化器查询计划。例如:
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT customer_name, customer_state FROM customer_dimension WHERE customer_state IN ('MA','NH') AND customer_gender='Male' ORDER BY customer_name LIMIT 10;
Access Path:
+-SELECT LIMIT 10 [Cost: 365, Rows: 10] (PATH ID: 0)
| Output Only: 10 tuples
| Execute on: Query Initiator
| +---> SORT [TOPK] [Cost: 365, Rows: 544] (PATH ID: 1)
| | Order: customer_dimension.customer_name ASC
| | Output Only: 10 tuples
| | Execute on: Query Initiator
| | +---> STORAGE ACCESS for customer_dimension [Cost: 326, Rows: 544] (PATH ID: 2)
| | | Projection: public.customer_dimension_DBD_1_rep_VMartDesign_node0001
| | | Materialize: customer_dimension.customer_state, customer_dimension.customer_name
| | | Filter: (customer_dimension.customer_gender = 'Male')
| | | Filter: (customer_dimension.customer_state = ANY (ARRAY['MA', 'NH']))
| | | Execute on: Query Initiator
| | | Runtime Filter: (SIP1(TopK): customer_dimension.customer_name)
您可以使用 EXPLAIN
评估优化器做出的关于给定查询的选择。如果您认为查询性能未达到最佳效果,请通过 Database Designer 运行该查询。有关详细信息,请参阅增量设计和缩短查询运行时间。
EXPLAIN JSON
以 JSON 格式返回查询计划。例如:
=> EXPLAIN JSON SELECT customer_name, customer_state FROM customer_dimension
WHERE customer_state IN ('MA','NH') AND customer_gender='Male' ORDER BY customer_name LIMIT 10;
------------------------------
{
"PARAMETERS" : {
"QUERY_STRING" : "EXPLAIN JSON SELECT customer_name, customer_state FROM customer_dimension \n
WHERE customer_state IN ('MA','NH') AND customer_gender='Male' ORDER BY customer_name LIMIT 10;"
},
"PLAN" : {
"PATH_ID" : 0,
"PATH_NAME" : "SELECT",
"EXTRA" : " LIMIT 10",
"COST" : 2114.000000,
"ROWS" : 10.000000,
"COST_STATUS" : "NO_STATISTICS",
"TUPLE_LIMIT" : 10,
"EXECUTE_NODE" : "Query Initiator",
"INPUT" : {
"PATH_ID" : 1,
"PATH_NAME" : "SORT",
"EXTRA" : "[TOPK]",
"COST" : 2114.000000,
"ROWS" : 49998.000000,
"COST_STATUS" : "NO_STATISTICS",
"ORDER" : ["customer_dimension.customer_name", "customer_dimension.customer_state"],
"TUPLE_LIMIT" : 10,
"EXECUTE_NODE" : "All Nodes",
"INPUT" : {
"PATH_ID" : 2,
"PATH_NAME" : "STORAGE ACCESS",
"EXTRA" : "for customer_dimension",
"COST" : 252.000000,
"ROWS" : 49998.000000,
"COST_STATUS" : "NO_STATISTICS",
"TABLE" : "public.customer_dimension",
"PROJECTION" : "public.customer_dimension_b0",
"MATERIALIZE" : ["customer_dimension.customer_name", "customer_dimension.customer_state"],
"FILTER" : ["(customer_dimension.customer_state = ANY (ARRAY['MA', 'NH']))", "(customer_dimension.customer_gender = 'Male')"],
"EXECUTE_NODE" : "All Nodes"
"SIP" : "Runtime Filter: (SIP1(TopK): customer_dimension.customer_name)"
}
}
}
}
(40 rows)
可使用 VERBOSE
选项对 EXPLAIN
进行限定。此选项对默认和 JSON 输出有效,可增加所呈现的查询计划的详细信息量
例如,以下 EXPLAIN
语句指定生成详细输出。添加的信息将以粗体显示:
=> EXPLAIN VERBOSE SELECT customer_name, customer_state FROM customer_dimension
WHERE customer_state IN ('MA','NH') AND customer_gender='Male' ORDER BY customer_name LIMIT 10;
QUERY PLAN DESCRIPTION:
------------------------------
Opt Vertica Options
--------------------
PLAN_OUTPUT_SUPER_VERBOSE
EXPLAIN VERBOSE SELECT customer_name, customer_state FROM customer_dimension
WHERE customer_state IN ('MA','NH') AND customer_gender='Male'
ORDER BY customer_name LIMIT 10;
Access Path:
+-SELECT LIMIT 10 [Cost: 756.000000, Rows: 10.000000 Disk(B): 0.000000 CPU(B): 0.000000 Memory(B): 0.000000 Netwrk(B): 0.000000 Parallelism: 1.000000] [OutRowSz (B): 274](PATH ID: 0)
| Output Only: 10 tuples
| Execute on: Query Initiator
| Sort Key: (customer_dimension.customer_name)
| LDISTRIB_UNSEGMENTED
| +---> SORT [TOPK] [Cost: 756.000000, Rows: 9998.000000 Disk(B): 0.000000 CPU(B): 34274697.123457 Memory(B): 2739452.000000 Netwrk(B): 0.000000 Parallelism: 4.000000 (NO STATISTICS)] [OutRowSz (B): 274] (PATH ID: 1)
| | Order: customer_dimension.customer_name ASC
| | Output Only: 10 tuples
| | Execute on: Query Initiator
| | Sort Key: (customer_dimension.customer_name)
| | LDISTRIB_UNSEGMENTED
| | +---> STORAGE ACCESS for customer_dimension [Cost: 513.000000, Rows: 9998.000000 Disk(B): 0.000000 CPU(B): 0.000000 Memory(B): 0.000000 Netwrk(B): 0.000000 Parallelism: 4.000000 (NO STATISTICS)] [OutRowSz (B): 274] (PATH ID: 2)
| | | Column Cost Aspects: [ Disk(B): 7371817.156569 CPU(B): 4914708.578284 Memory(B): 2659466.004399 Netwrk(B): 0.000000 Parallelism: 4.000000 ]
| | | Projection: public.customer_dimension_P1
| | | Materialize: customer_dimension.customer_state, customer_dimension.customer_name
| | | Filter: (customer_dimension.customer_gender = 'Male')/* sel=0.999800 ndv= 500 */
| | | Filter: (customer_dimension.customer_state = ANY (ARRAY['MA', 'NH']))/* sel=0.999800 ndv= 500 */
| | | Execute on: All Nodes
| | | Runtime Filter: (SIP1(TopK): customer_dimension.customer_name)
| | | Sort Key: (customer_dimension.household_id, customer_dimension.customer_key, customer_dimension.store_membership_card, customer_dimension.customer_type, customer_dimension.customer_region, customer_dimension.title, customer_dimension.number_of_children)
| | | LDISTRIB_SEGMENTED
EXPLAIN LOCAL
(在多节点数据库中)显示分配给每一节点的本地查询计划,它们共同组成整个(全局)查询计划。如果您省略此选项,Vertica 将仅显示全局查询计划。本地查询计划仅以可在 Graphviz 中呈现的 DOT 语言源显示。
例如,以下 EXPLAIN
语句包括 LOCAL
选项:
=> EXPLAIN LOCAL SELECT store_name, store_city, store_state
FROM store.store_dimension ORDER BY store_state ASC, store_city ASC;
该输出包括 GraphViz 源,用于描述分配给每个节点的本地查询计划。例如,在三节点数据库上,此语句的输出包括一个节点 (v_vmart_node0003
) 的以下查询计划的 GraphViz 描述:
-----------------------------------------------
PLAN: v_vmart_node0003 (GraphViz Format)
-----------------------------------------------
digraph G {
graph [rankdir=BT, label = "v_vmart_node0003\n", labelloc=t, labeljust=l ordering=out]
0[label = "NewEENode \nOutBlk=[UncTuple(3)]", color = "green", shape = "box"];
1[label = "Send\nSend to: v_vmart_node0001\nNet id: 1000\nMerge\n\nUnc: Char(2)\nUnc: Varchar(64)\nUnc: Varchar(64)", color = "green", shape = "box"];
2[label = "Sort: (keys = A,A,N)\nUnc: Char(2)\nUnc: Varchar(64)\nUnc: Varchar(64)", color = "green", shape = "box"];
3[label = "ExprEval: \n store_dimension.store_state\n store_dimension.store_city\n store_dimension.store_name\nUnc: Char(2)\nUnc: Varchar(64)\nUnc: Varchar(64)
", color = "green", shape = "box"];
4[label = "StorageUnionStep: store_dimension_p_b0\nUnc: Varchar(64)\nUnc: Varchar(64)\nUnc: Char(2)", color = "purple", shape = "box"];
5[label = "ScanStep: store_dimension_p_b0\nstore_key (not emitted)\nstore_name\nstore_city\nstore_state\nUnc: Varchar(64)\nUnc: Varchar(64)\nUnc: Char(2)", color
= "brown", shape = "box"];
1->0 [label = "0",color = "blue"];
2->1 [label = "0",color = "blue"];
3->2 [label = "0",color = "blue"];
4->3 [label = "0",color = "blue"];
5->4 [label = "0",color = "blue"];
}
GraphViz 呈现的此输出如下所示:
查询优化器根据成本预估选择查询计划,并使用来自各种来源的信息开发潜在计划和确定其相对成本。这些包括:
表的行数
列统计信息,包括:不同值(基数)的数量、最小/最大值、值分布和磁盘空间使用情况
可能需要的 I/O 操作最少、CPU 最低、内存最少、网络使用最少的访问路径
可用的合适投影
联接选项:联接类型(合并与散列联接)、联接顺序
查询谓词
群集节点上的数据分段
许多重要的优化器决策均依赖于统计信息,查询优化器使用这些信息来确定执行查询的最终计划。因此,统计信息保持最新非常重要。没有合理的准确统计信息,优化器可能选择不理想的计划,从而导致查询性能受影响。
Vertica 提供有关查询计划中的统计信息的提示。请参阅查询计划统计信息。
尽管成本与查询运行时间相关,但它们并不能 预估 出实际运行时间。例如,如果优化器确定计划 A 的成本为计划 B 的两倍,则计划 A 的运行时间可能会更长。但是,此成本预估并不一定表示计划 A 的运行时间是计划 B 的两倍。
此外,不同查询的计划成本不能直接相比。例如,如果查询 1 的计划 X 的估计成本高于查询 2 的计划 Y 的成本,计划 X 的运行时间不一定就比计划 Y 的运行时间长。
根据查询和数据库架构的情况,EXPLAIN
输出会包含以下信息:
语句引用的表
估计成本
估计行基数
路径 ID,一个用于链接至错误消息和分析计数器的整数,以便您可以更轻松地解决性能问题。有关详细信息,请参阅分析查询计划。
数据操作,如 SORT
、FILTER
、LIMIT
和 GROUP BY
使用的投影
关于统计的信息—例如,它们是当前的还是超出范围
查询中为操作选择的算法,如 HASH
/MERGE
或 GROUPBY HASH
/GROUPBY PIPELINED
群集节点间的数据再分发(广播、分段)
在下面的 EXPLAIN
输出中,优化器用三个步骤处理查询,每个步骤通过唯一路径 ID 标识:
0:限制
1:排序
2:存储空间访问和筛选
SELECT
列表中更多的列—例如,WHERE
子句中引用的列。
如果您查询统计信息不可用或已过时的表,优化器可能会选择不理想的查询计划。
可通过调用
ANALYZE_STATISTICS
来解决与表统计信息相关的许多问题。此函数可用于更新不同范围的统计信息:一个或多个表列、单个表或所有数据库表。
如果您更新统计信息后发现查询执行仍不理想,请通过 Database Designer 运行查询并选择增量设计作为设计类型。
有关更新数据库统计信息的详细信息,请参阅收集数据库统计信息。
查询计划可通过以下两个提示包含表统计信息相关信息: NO STATISTICS
和 STALE STATISTICS
。例如,以下查询计划片段包括 NO STATISTICS
,表示直方图不可用:
| | +-- Outer -> STORAGE ACCESS for fact [Cost: 604, Rows: 10K (NO STATISTICS)]
以下查询计划片段包括 STALE STATISTICS
,表示谓词已超出直方图范围:
| | +-- Outer -> STORAGE ACCESS for fact [Cost: 35, Rows: 1 (STALE STATISTICS)]
以下 EXPLAIN 输出显示 Cost
运算符:
Access Path: +-SELECT LIMIT 10 [Cost: 370, Rows: 10] (PATH ID: 0)
| Output Only: 10 tuples
| Execute on: Query Initiator
| +---> SORT [Cost: 370, Rows: 544] (PATH ID: 1)
| | Order: customer_dimension.customer_name ASC
| | Output Only: 10 tuples
| | Execute on: Query Initiator
| | +---> STORAGE ACCESS for customer_dimension [Cost: 331, Rows: 544] (PATH ID: 2)
| | | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | | Materialize: customer_dimension.customer_state, customer_dimension.customer_name
| | | Filter: (customer_dimension.customer_gender = 'Male')
| | | Filter: (customer_dimension.customer_state = ANY (ARRAY['MA', 'NH']))
| | | Execute on: Query Initiator
Row
运算符是优化器估计查询将要返回的行数。数字后面的字母表示度量单位(K=千、M=百万、B=十亿、T=万亿),因此,以下查询的输出指示要返回的行数为 50 K。
=> EXPLAIN SELECT customer_gender FROM customer_dimension;
Access Path:
+-STORAGE ACCESS for customer_dimension [Cost: 17, Rows: 50K (3 RLE)] (PATH ID: 1)
| Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| Materialize: customer_dimension.customer_gender
| Execute on: Query Initiator
STORAGE ACCESS 路径中对 (3 RLE) 的引用表明优化器估计存储访问运算符返回的行数为 50K。由于列为运行长度编码 (RLE),因此返回的 RLE 实际行数仅为三行:
1 行用于女性
1 行用于男性
1 行用于表示未知 (NULL) 性别
通过查看文本输出中的 Projection
路径,您可以了解优化器为查询计划选择了哪些
投影:
EXPLAIN SELECT
customer_name,
customer_state
FROM customer_dimension
WHERE customer_state in ('MA','NH')
AND customer_gender = 'Male'
ORDER BY customer_name
LIMIT 10;
Access Path:
+-SELECT LIMIT 10 [Cost: 370, Rows: 10] (PATH ID: 0)
| Output Only: 10 tuples
| Execute on: Query Initiator
| +---> SORT [Cost: 370, Rows: 544] (PATH ID: 1)
| | Order: customer_dimension.customer_name ASC
| | Output Only: 10 tuples
| | Execute on: Query Initiator
| | +---> STORAGE ACCESS for customer_dimension [Cost: 331, Rows: 544] (PATH ID: 2)
| | | Projection: public.customer_dimension_DBD_1_rep_vmart_vmart_node0001
| | | Materialize: customer_dimension.customer_state, customer_dimension.customer_name
| | | Filter: (customer_dimension.customer_gender = 'Male')
| | | Filter: (customer_dimension.customer_state = ANY (ARRAY['MA', 'NH']))
| | | Execute on: Query Initiator
查询优化器会自动选取最佳投影,但如果没有合理准确的统计信息,则优化器为查询选择的投影或联接顺序可能会不太理想。有关详细信息,请参阅收集统计信息。
Vertica 在为计划选择投影时会考虑以下几个方面:
如何在查询中联接各个列
如何对投影进行分组或排序
是否应用 SQL 分析操作
磁盘上投影存储中的所有列信息
当 Vertica 评估每个计划的可能性时,初始成本较高的投影可能会进入到最终计划之中,因为这些投影会减少联接开销。例如,可能会为某个查询提供许多可能的计划,优化器在从中作出选择之前会对这些计划进行评估。为提高效率,优化器会使用复杂的算法删除成本较高的中间部分计划片段。优化器知道中间计划片段可能最初看起来很糟糕(由于存储访问成本很高),但可能会因为它所允许的其他优化而产生出色的最终计划。
如果在统计信息已是最新的情况下查询仍然执行欠佳,请通过 Database Designer 运行该查询。有关详细信息,请参阅增量设计。
要对已分段的不同投影进行测试,请在查询中按名称引用投影。
要获得最佳性能,请将查询编写为将列按照投影列的排序方式进行排序。
与引用两个或更多表的联接查询相似,查询计划中的 Join
步骤有两个输入分支:
左输入,其为联接的外部表
右输入,其为联接的内部表
在以下查询中,T1
表为左输入,因为它在 JOIN 关键字的左侧;T2
表为右输入,因为它在 JOIN 关键字的右侧:
SELECT * FROM T1 JOIN T2 ON T1.x = T2.x;
如果使用小型表作为联接的内部输入,则查询性能会更好。查询优化器会自动对联接的输入进行重新排序以确保这种情况,除非相关联接为外部联接。
以下示例显示左外部联接的查询及其计划:
=> EXPLAIN SELECT CD.annual_income,OSI.sale_date_key
-> FROM online_sales.online_sales_fact OSI
-> LEFT OUTER JOIN customer_dimension CD ON CD.customer_key = OSI.customer_key;
Access Path:
+-JOIN HASH [LeftOuter] [Cost: 4K, Rows: 5M] (PATH ID: 1)
| Join Cond: (CD.customer_key = OSI.customer_key)
| Materialize at Output: OSI.sale_date_key
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for OSI [Cost: 3K, Rows: 5M] (PATH ID: 2)
| | Projection: online_sales.online_sales_fact_DBD_12_seg_vmartdb_design_vmartdb_design
| | Materialize: OSI.customer_key
| | Execute on: All Nodes
| +-- Inner -> STORAGE ACCESS for CD [Cost: 264, Rows: 50K] (PATH ID: 3)
| | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | Materialize: CD.annual_income, CD.customer_key
| | Execute on: All Nodes
以下示例显示完整外部联接的查询及其计划:
=> EXPLAIN SELECT CD.annual_income,OSI.sale_date_key
-> FROM online_sales.online_sales_fact OSI
-> FULL OUTER JOIN customer_dimension CD ON CD.customer_key = OSI.customer_key;
Access Path:
+-JOIN HASH [FullOuter] [Cost: 18K, Rows: 5M] (PATH ID: 1) Outer (RESEGMENT) Inner (FILTER)
| Join Cond: (CD.customer_key = OSI.customer_key)
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for OSI [Cost: 3K, Rows: 5M] (PATH ID: 2)
| | Projection: online_sales.online_sales_fact_DBD_12_seg_vmartdb_design_vmartdb_design
| | Materialize: OSI.sale_date_key, OSI.customer_key
| | Execute on: All Nodes
| +-- Inner -> STORAGE ACCESS for CD [Cost: 264, Rows: 50K] (PATH ID: 3)
| | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | Materialize: CD.annual_income, CD.customer_key
| | Execute on: All Nodes
Vertica 有两个联接算法可供选择:合并联接和散列联接。系统中给定查询和投影时,优化器会自动选择最适合的算法。
对于以下查询,优化器会选择散列联接。
=> EXPLAIN SELECT CD.annual_income,OSI.sale_date_key
-> FROM online_sales.online_sales_fact OSI
-> INNER JOIN customer_dimension CD ON CD.customer_key = OSI.customer_key;
Access Path:
+-JOIN HASH [Cost: 4K, Rows: 5M] (PATH ID: 1)
| Join Cond: (CD.customer_key = OSI.customer_key)
| Materialize at Output: OSI.sale_date_key
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for OSI [Cost: 3K, Rows: 5M] (PATH ID: 2)
| | Projection: online_sales.online_sales_fact_DBD_12_seg_vmartdb_design_vmartdb_design
| | Materialize: OSI.customer_key
| | Execute on: All Nodes
| +-- Inner -> STORAGE ACCESS for CD [Cost: 264, Rows: 50K] (PATH ID: 3)
| | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | Materialize: CD.annual_income, CD.customer_key
| | Execute on: All Nodes
customer_key
)。为了促进合并联接,您可能需要创建在联接列上排序的其他投影。
在下一个示例中,优化器会选择合并联接。优化器的首次传递会执行合并联接,因为输入已预先排序,然后它再执行散列联接。
=> EXPLAIN SELECT count(*) FROM online_sales.online_sales_fact OSI
-> INNER JOIN customer_dimension CD ON CD.customer_key = OSI.customer_key
-> INNER JOIN product_dimension PD ON PD.product_key = OSI.product_key;
Access Path:
+-GROUPBY NOTHING [Cost: 8K, Rows: 1] (PATH ID: 1)
| Aggregates: count(*)
| Execute on: All Nodes
| +---> JOIN HASH [Cost: 7K, Rows: 5M] (PATH ID: 2)
| | Join Cond: (PD.product_key = OSI.product_key)
| | Materialize at Input: OSI.product_key
| | Execute on: All Nodes
| | +-- Outer -> JOIN MERGEJOIN(inputs presorted) [Cost: 4K, Rows: 5M] (PATH ID: 3)
| | | Join Cond: (CD.customer_key = OSI.customer_key)
| | | Execute on: All Nodes
| | | +-- Outer -> STORAGE ACCESS for OSI [Cost: 3K, Rows: 5M] (PATH ID: 4)
| | | | Projection: online_sales.online_sales_fact_DBD_12_seg_vmartdb_design_vmartdb_design
| | | | Materialize: OSI.customer_key
| | | | Execute on: All Nodes
| | | +-- Inner -> STORAGE ACCESS for CD [Cost: 132, Rows: 50K] (PATH ID: 5)
| | | | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | | | Materialize: CD.customer_key
| | | | Execute on: All Nodes
| | +-- Inner -> STORAGE ACCESS for PD [Cost: 152, Rows: 60K] (PATH ID: 6)
| | | Projection: public.product_dimension_DBD_2_rep_vmartdb_design_vmartdb_design_node0001
| | | Materialize: PD.product_key
| | | Execute on: All Nodes
Vertica 使用等于谓词处理联接的效率非常高。查询计划将等于联接谓词显示为联接条件 (Join Cond
)。
=> EXPLAIN SELECT CD.annual_income, OSI.sale_date_key
-> FROM online_sales.online_sales_fact OSI
-> INNER JOIN customer_dimension CD
-> ON CD.customer_key = OSI.customer_key;
Access Path:
+-JOIN HASH [Cost: 4K, Rows: 5M] (PATH ID: 1)
| Join Cond: (CD.customer_key = OSI.customer_key)
| Materialize at Output: OSI.sale_date_key
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for OSI [Cost: 3K, Rows: 5M] (PATH ID: 2)
| | Projection: online_sales.online_sales_fact_DBD_12_seg_vmartdb_design_vmartdb_design
| | Materialize: OSI.customer_key
| | Execute on: All Nodes
| +-- Inner -> STORAGE ACCESS for CD [Cost: 264, Rows: 50K] (PATH ID: 3)
| | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | Materialize: CD.annual_income, CD.customer_key
| | Execute on: All Nodes
但是,对不等于联接的处理方式与交叉联接类似,其运行效率较低,您可以通过查看两个查询之间的成本变化对此进行了解:
=> EXPLAIN SELECT CD.annual_income, OSI.sale_date_key
-> FROM online_sales.online_sales_fact OSI
-> INNER JOIN customer_dimension CD
-> ON CD.customer_key < OSI.customer_key;
Access Path:
+-JOIN HASH [Cost: 98M, Rows: 5M] (PATH ID: 1)
| Join Filter: (CD.customer_key < OSI.customer_key)
| Materialize at Output: CD.annual_income
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for CD [Cost: 132, Rows: 50K] (PATH ID: 2)
| | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | Materialize: CD.customer_key
| | Execute on: All Nodes
| +-- Inner -> STORAGE ACCESS for OSI [Cost: 3K, Rows: 5M] (PATH ID: 3)
| | Projection: online_sales.online_sales_fact_DBD_12_seg_vmartdb_design_vmartdb_design
| | Materialize: OSI.sale_date_key, OSI.customer_key
| | Execute on: All Nodes
事件序列联接由 INTERPOLATED
路径指示。
=> EXPLAIN SELECT * FROM hTicks h FULL OUTER JOIN aTicks a -> ON (h.time INTERPOLATE PREVIOUS
Access Path:
+-JOIN (INTERPOLATED) [FullOuter] [Cost: 31, Rows: 4 (NO STATISTICS)] (PATH ID: 1)
Outer (SORT ON JOIN KEY) Inner (SORT ON JOIN KEY)
| Join Cond: (h."time" = a."time")
| Execute on: Query Initiator
| +-- Outer -> STORAGE ACCESS for h [Cost: 15, Rows: 4 (NO STATISTICS)] (PATH ID: 2)
| | Projection: public.hTicks_node0004
| | Materialize: h.stock, h."time", h.price
| | Execute on: Query Initiator
| +-- Inner -> STORAGE ACCESS for a [Cost: 15, Rows: 4 (NO STATISTICS)] (PATH ID: 3)
| | Projection: public.aTicks_node0004
| | Materialize: a.stock, a."time", a.price
| | Execute on: Query Initiator
PATH ID
是 Vertica 为查询计划内的每个操作(路径)所分配的唯一标识符。以下项共享同一标识符:
联接错误消息
路径 ID 可帮助您追踪问题的根本原因。例如,如果查询返回联接错误,请在查询前加上 EXPLAIN
并在查询计划中查找
PATH ID n
,以查看查询中的哪个联接有问题。
例如,下面的 EXPLAIN
输出显示优化器查询计划中每个路径的路径 ID:
=> EXPLAIN SELECT * FROM fact JOIN dim ON x=y JOIN ext on y=z;
Access Path:
+-JOIN MERGEJOIN(inputs presorted) [Cost: 815, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
| Join Cond: (dim.y = ext.z)
| Materialize at Output: fact.x
| Execute on: All Nodes
| +-- Outer -> JOIN MERGEJOIN(inputs presorted) [Cost: 408, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Join Cond: (fact.x = dim.y)
| | Execute on: All Nodes
| | +-- Outer -> STORAGE ACCESS for fact [Cost: 202, Rows: 10K (NO STATISTICS)] (PATH ID: 3)
| | | Projection: public.fact_super
| | | Materialize: fact.x
| | | Execute on: All Nodes
| | +-- Inner -> STORAGE ACCESS for dim [Cost: 202, Rows: 10K (NO STATISTICS)] (PATH ID: 4)
| | | Projection: public.dim_super
| | | Materialize: dim.y
| | | Execute on: All Nodes
| +-- Inner -> STORAGE ACCESS for ext [Cost: 202, Rows: 10K (NO STATISTICS)] (PATH ID: 5)
| | Projection: public.ext_super
| | Materialize: ext.z
| | Execute on: All Nodes
Filter
步骤对单个表上的谓词求值。它接受一组行、清除其中的某些行(基于查询中提供的条件)并返回剩余行。例如,优化器可以筛选将要与其他重新分段的联接输入进行联接的联接输入的本地数据。
以下语句查询 customer_dimension
表,并使用 WHERE 子句仅筛选马萨诸塞州和新罕布什尔州的男性客户结果。
EXPLAIN SELECT
CD.customer_name,
CD.customer_state,
AVG(CD.customer_age) AS avg_age,
COUNT(*) AS count
FROM customer_dimension CD
WHERE CD.customer_state in ('MA','NH') AND CD.customer_gender = 'Male'
GROUP BY CD.customer_state, CD.customer_name;
查询计划输出如下:
Access Path:
+-GROUPBY HASH [Cost: 378, Rows: 544] (PATH ID: 1)
| Aggregates: sum_float(CD.customer_age), count(CD.customer_age), count(*)
| Group By: CD.customer_state, CD.customer_name
| Execute on: Query Initiator
| +---> STORAGE ACCESS for CD [Cost: 372, Rows: 544] (PATH ID: 2)
| | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | Materialize: CD.customer_state, CD.customer_name, CD.customer_age
| | Filter: (CD.customer_gender = 'Male')
| | Filter: (CD.customer_state = ANY (ARRAY['MA', 'NH']))
| | Execute on: Query Initiator
GROUP BY 操作具有两种算法:
GROUPBY HASH 输入不按组列排序,因此 Vertica 用这些组列构建一个哈希表,以便处理聚合和 Group By 表达式。
GROUPBY PIPELINED 需要在组中指定的列上对输入进行预分类,这意味着 Vertica 仅需要在内存中保留当前组中的数据。GROUPBY PIPELINED 操作是首选算法,因为它们通常比 GROUPBY HASH 更快并且需要的内存更少。GROUPBY PIPELINED 对按列或 DISTINCT
聚合处理大量高基数组的查询尤为有用。
如果可能,查询优化器会选择更快的算法 GROUPBY PIPELINED 而非 GROUPBY HASH。
下面是一个关于 GROUPBY HASH
操作在 EXPLAIN
输出中的样式的示例。
=> EXPLAIN SELECT COUNT(DISTINCT annual_income)
FROM customer_dimension
WHERE customer_region='NorthWest';
该输出显示优化器选择了效率更低的 GROUPBY HASH
方法,这意味着投影未在 annual_income
列上预分类。如果此类投影可用,优化器将选择 GROUPBY PIPELINED
算法。
Access Path:
+-GROUPBY NOTHING [Cost: 256, Rows: 1 (NO STATISTICS)] (PATH ID: 1)
| Aggregates: count(DISTINCT customer_dimension.annual_income)
| +---> GROUPBY HASH (LOCAL RESEGMENT GROUPS) [Cost: 253, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Group By: customer_dimension.annual_income
| | +---> STORAGE ACCESS for customer_dimension [Cost: 227, Rows: 50K (NO STATISTICS)] (PATH ID: 3)
| | | Projection: public.customer_dimension_super
| | | Materialize: customer_dimension.annual_income
| | | Filter: (customer_dimension.customer_region = 'NorthWest'
...
如果投影已按 customer_gender
列排序,优化器将选择更快的 GROUPBY PIPELINED
操作:
=> EXPLAIN SELECT COUNT(distinct customer_gender) from customer_dimension;
Access Path:
+-GROUPBY NOTHING [Cost: 22, Rows: 1] (PATH ID: 1)
| Aggregates: count(DISTINCT customer_dimension.customer_gender)
| Execute on: Query Initiator
| +---> GROUPBY PIPELINED [Cost: 20, Rows: 10K] (PATH ID: 2)
| | Group By: customer_dimension.customer_gender
| | Execute on: Query Initiator
| | +---> STORAGE ACCESS for customer_dimension [Cost: 17, Rows: 50K (3 RLE)] (PATH ID: 3)
| | | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | | Materialize: customer_dimension.customer_gender
| | | Execute on: Query Initiator
同样,使用相等谓词(如在以下查询中)将保留 GROUPBY PIPELINED
:
=> EXPLAIN SELECT COUNT(DISTINCT annual_income) FROM customer_dimension
WHERE customer_gender = 'Female';
Access Path: +-GROUPBY NOTHING [Cost: 161, Rows: 1] (PATH ID: 1)
| Aggregates: count(DISTINCT customer_dimension.annual_income)
| +---> GROUPBY PIPELINED [Cost: 158, Rows: 10K] (PATH ID: 2)
| | Group By: customer_dimension.annual_income
| | +---> STORAGE ACCESS for customer_dimension [Cost: 144, Rows: 47K] (PATH ID: 3)
| | | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | | Materialize: customer_dimension.annual_income
| | | Filter: (customer_dimension.customer_gender = 'Female')
EXPLAIN
报告 GROUPBY HASH
,请修改投影设计,强制其使用 GROUPBY PIPELINED
。
SORT
运算符根据指定的列列表来排序数据。EXPLAIN 输出指示排序表达式以及排列顺序是升序 (ASC) 还是降序 (DESC)。
例如,以下查询计划显示 SORT 运算符的列列表性质:
EXPLAIN SELECT
CD.customer_name,
CD.customer_state,
AVG(CD.customer_age) AS avg_age,
COUNT(*) AS count
FROM customer_dimension CD
WHERE CD.customer_state in ('MA','NH')
AND CD.customer_gender = 'Male'
GROUP BY CD.customer_state, CD.customer_name
ORDER BY avg_age, customer_name;
Access Path:
+-SORT [Cost: 422, Rows: 544] (PATH ID: 1)
| Order: (<SVAR> / float8(<SVAR>)) ASC, CD.customer_name ASC
| Execute on: Query Initiator
| +---> GROUPBY HASH [Cost: 378, Rows: 544] (PATH ID: 2)
| | Aggregates: sum_float(CD.customer_age), count(CD.customer_age), count(*)
| | Group By: CD.customer_state, CD.customer_name
| | Execute on: Query Initiator
| | +---> STORAGE ACCESS for CD [Cost: 372, Rows: 544] (PATH ID: 3)
| | | Projection: public.customer_dimension_DBD_1_rep_vmart_vmart_node0001
| | | Materialize: CD.customer_state, CD.customer_name, CD.customer_age
| | | Filter: (CD.customer_gender = 'Male')
| | | Filter: (CD.customer_state = ANY (ARRAY['MA', 'NH']))
| | | Execute on: Query Initiator
如果将排序顺序更改为降序,查询计划将发生变化:
EXPLAIN SELECT
CD.customer_name,
CD.customer_state,
AVG(CD.customer_age) AS avg_age,
COUNT(*) AS count
FROM customer_dimension CD
WHERE CD.customer_state in ('MA','NH')
AND CD.customer_gender = 'Male'
GROUP BY CD.customer_state, CD.customer_name
ORDER BY avg_age DESC, customer_name;
Access Path:
+-SORT [Cost: 422, Rows: 544] (PATH ID: 1)
| Order: (<SVAR> / float8(<SVAR>)) DESC, CD.customer_name ASC
| Execute on: Query Initiator
| +---> GROUPBY HASH [Cost: 378, Rows: 544] (PATH ID: 2)
| | Aggregates: sum_float(CD.customer_age), count(CD.customer_age), count(*)
| | Group By: CD.customer_state, CD.customer_name
| | Execute on: Query Initiator
| | +---> STORAGE ACCESS for CD [Cost: 372, Rows: 544] (PATH ID: 3)
| | | Projection: public.customer_dimension_DBD_1_rep_vmart_vmart_node0001
| | | Materialize: CD.customer_state, CD.customer_name, CD.customer_age
| | | Filter: (CD.customer_gender = 'Male')
| | | Filter: (CD.customer_state = ANY (ARRAY['MA', 'NH']))
| | | Execute on: Query Initiator
LIMIT
路径根据查询中的 LIMIT 子句限制结果行数。在具有数千行的查询中使用 LIMIT
子句可能会提高查询性能。
优化器在查询中尽可能向下推动 LIMIT
操作。查询中的一条 LIMIT
子句可以生成多个 Output Only
计划注释。
=> EXPLAIN SELECT COUNT(DISTINCT annual_income) FROM customer_dimension LIMIT 10;
Access Path:
+-SELECT LIMIT 10 [Cost: 161, Rows: 10] (PATH ID: 0)
| Output Only: 10 tuples
| +---> GROUPBY NOTHING [Cost: 161, Rows: 1] (PATH ID: 1)
| | Aggregates: count(DISTINCT customer_dimension.annual_income)
| | Output Only: 10 tuples
| | +---> GROUPBY HASH (SORT OUTPUT) [Cost: 158, Rows: 10K] (PATH ID: 2)
| | | Group By: customer_dimension.annual_income
| | | +---> STORAGE ACCESS for customer_dimension [Cost: 132, Rows: 50K] (PATH ID: 3)
| | | | Projection: public.customer_dimension_DBD_1_rep_vmartdb_design_vmartdb_design_node0001
| | | | Materialize: customer_dimension.annual_income
优化器可通过两种方式重新分布联接数据:
广播
重新分段
广播操作会将中间结果的完整副本发送到群集中的所有节点。在以下情况下,可对联接使用广播。
一个表与其他表相比很小(通常指内部表)。
Vertica 可避免其他大型上游重新分段操作。
外部联接或子查询的语义需要复制联接一侧的内容。
例如:
=> EXPLAIN SELECT * FROM T1 LEFT JOIN T2 ON T1.a > T2.y;
Access Path:
+-JOIN HASH [LeftOuter] [Cost: 40K, Rows: 10K (NO STATISTICS)] (PATH ID: 1) Inner (BROADCAST)
| Join Filter: (T1.a > T2.y)
| Materialize at Output: T1.b
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for T1 [Cost: 151, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Projection: public.T1_b0
| | Materialize: T1.a
| | Execute on: All Nodes
| +-- Inner -> STORAGE ACCESS for T2 [Cost: 302, Rows: 10K (NO STATISTICS)] (PATH ID: 3)
| | Projection: public.T2_b0
| | Materialize: T2.x, T2.y
| | Execute on: All Nodes
重新分段操作会获取现有投影或中间关系,并在所有群集节点之间均匀地对数据进行重新分段。在重新分段操作结束时,输入关系中的每一行仅位于一个节点上。如果本地联接数据尚未进行分段,则重新分段将是对 Vertica 中的分布式联接最常使用的操作。有关更多详细信息,请参阅相同分段。
例如:
=> CREATE TABLE T1 (a INT, b INT) SEGMENTED BY HASH(a) ALL NODES;
=> CREATE TABLE T2 (x INT, y INT) SEGMENTED BY HASH(x) ALL NODES;
=> EXPLAIN SELECT * FROM T1 JOIN T2 ON T1.a = T2.y;
------------------------------ QUERY PLAN DESCRIPTION: ------------------------------
Access Path:
+-JOIN HASH [Cost: 639, Rows: 10K (NO STATISTICS)] (PATH ID: 1) Inner (RESEGMENT)
| Join Cond: (T1.a = T2.y)
| Materialize at Output: T1.b
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for T1 [Cost: 151, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Projection: public.T1_b0
| | Materialize: T1.a
| | Execute on: All Nodes
| +-- Inner -> STORAGE ACCESS for T2 [Cost: 302, Rows: 10K (NO STATISTICS)] (PATH ID: 3)
| | Projection: public.T2_b0
| | Materialize: T2.x, T2.y
| | Execute on: All Nodes
Vertica 通过将同一查询中的多个 SQL-99 分析函数分组到 Analytic Group
区域,来尝试对这些分析函数进行优化。
对于每个分析组,Vertica 将根据需要执行数据分布式排序和重新分段。
可根据查询计划判断需要多少次排序和重新分段。
例如,以下查询计划显示
FIRST_VALUE
和
LAST_VALUE
函数在同一分析组中,因为它们的 OVER
子句相同。相反,ROW_NUMBER()
具有不同的 ORDER BY
子句,因此它在其他分析组中。由于这两个组共享同一 PARTITION BY deal_stage
子句,因此无需在组间对数据重新分段:
EXPLAIN SELECT
first_value(deal_size) OVER (PARTITION BY deal_stage
ORDER BY deal_size),
last_value(deal_size) OVER (PARTITION BY deal_stage
ORDER BY deal_size),
row_number() OVER (PARTITION BY deal_stage
ORDER BY largest_bill_amount)
FROM customer_dimension;
Access Path:
+-ANALYTICAL [Cost: 1K, Rows: 50K] (PATH ID: 1)
| Analytic Group
| Functions: row_number()
| Group Sort: customer_dimension.deal_stage ASC, customer_dimension.largest_bill_amount ASC NULLS LAST
| Analytic Group
| Functions: first_value(), last_value()
| Group Filter: customer_dimension.deal_stage
| Group Sort: customer_dimension.deal_stage ASC, customer_dimension.deal_size ASC NULL LAST
| Execute on: All Nodes
| +---> STORAGE ACCESS for customer_dimension [Cost: 263, Rows: 50K]
(PATH ID: 2)
| | Projection: public.customer_dimension_DBD_1_rep_vmart_vmart_node0001
| | Materialize: customer_dimension.largest_bill_amount,
customer_dimension.deal_stage, customer_dimension.deal_size
| | Execute on: All Nodes
Vertica 在群集节点出现故障时对性能进行优化,方法是将故障节点的工作均匀分摊到整个群集的可用节点上。
当群集中的某个节点出现故障时,查询计划会识别将在哪个节点执行查询。为了帮助您快速识别大型群集上的故障节点,当正在运行的节点数少于或等于六个时,EXPLAIN
输出最多列出六个节点;当正在运行的节点数多于六个时,将仅列出故障节点。
下表提供了更多详细信息:
在以下示例中,故障节点为 v_vmart_node0005
,节点 v_vmart_node0006
将执行查询的此次运行。
=> EXPLAIN SELECT * FROM test;
QUERY PLAN
-----------------------------------------------------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT * FROM my1table;
Access Path:
+-STORAGE ACCESS for my1table [Cost: 10, Rows: 2] (PATH ID: 1)
| Projection: public.my1table_b0
| Materialize: my1table.c1, my1table.c2
| Execute on: All Except v_vmart_node0005
+-STORAGE ACCESS for my1table (REPLACEMENT FOR DOWN NODE) [Cost: 66, Rows: 2]
| Projection: public.my1table_b1
| Materialize: my1table.c1, my1table.c2
| Execute on: v_vmart_node0006
以下示例片段中的 All Permanent Nodes
输出指示节点列表仅适用于永久(非短暂)节点:
=> EXPLAIN SELECT * FROM my2table;
Access Path:
+-STORAGE ACCESS for my2table [Cost: 18, Rows:6 (NO STATISTICS)] (PATH ID: 1)
| Projection: public.my2tablee_b0
| Materialize: my2table.x, my2table.y, my2table.z
| Execute on: All Permanent Nodes
如果
MERGE
语句及其表满足 MERGE 优化中所述的标准,则 Vertica 会为该语句准备优化的查询计划。
使用
EXPLAIN
关键字可以确定 Vertica 是否可以为给定的 MERGE
语句生成优化的查询计划。如果可以进行优化,EXPLAIN
生成的输出将包含 [Semi]
路径,如以下示例片段所示:
...
Access Path:
+-DML DELETE [Cost: 0, Rows: 0]
| Target Projection: public.A_b1 (DELETE ON CONTAINER)
| Target Prep:
| Execute on: All Nodes
| +---> JOIN MERGEJOIN(inputs presorted) [Semi] [Cost: 6, Rows: 1 (NO STATISTICS)] (PATH ID: 1)
Inner (RESEGMENT)
| | Join Cond: (A.a1 = VAL(2))
| | Execute on: All Nodes
| | +-- Outer -> STORAGE ACCESS for A [Cost: 2, Rows: 2 (NO STATISTICS)] (PATH ID: 2)
...
相反,如果 Vertica 无法创建优化计划,EXPLAIN
生成的输出将包含 RightOuter
路径:
...
Access Path: +-DML MERGE
| Target Projection: public.locations_b1
| Target Projection: public.locations_b0
| Target Prep:
| Execute on: All Nodes
| +---> JOIN MERGEJOIN(inputs presorted) [RightOuter] [Cost: 28, Rows: 3 (NO STATISTICS)] (PATH ID: 1) Outer (RESEGMENT) Inner (RESEGMENT)
| | Join Cond: (locations.user_id = VAL(2)) AND (locations.location_x = VAL(2)) AND (locations.location_y = VAL(2))
| | Execute on: All Nodes
| | +-- Outer -> STORAGE ACCESS for <No Alias> [Cost: 15, Rows: 2 (NO STATISTICS)] (PATH ID: 2)
...
定向查询封装优化器可用于创建查询计划的信息。定向查询可以实现以下目标:
在计划的升级前保留当前查询计划。大多数情况下,升级 Vertica 后,查询的执行效率会变得更高。在少数并非如此的情况下,您可以使用升级之前所创建的定向查询,以便重新创建早期版本的查询计划。
使您能够创建改善优化工具性能的查询计划。有时,您可能希望影响优化器,使其在执行给定查询时做出更好的选择。例如,您可以选择不同的投影,或强制执行不同的联接顺序。在这种情况下,可使用定向查询来创建一个查询计划,替代优化器可能另外创建的计划。
将输入查询重定向到使用不同语义的查询 — 例如,将联接查询映射到用来查询扁平表的 SELECT 语句。
定向查询为两个组件配对:
输入查询:在此定向查询处于活动状态时,触发使用它的查询。
带注释的查询:具有嵌入式优化器提示的 SQL 语句,其中的提示指导优化器如何为指定的输入查询创建查询计划。这些提示指定重要的查询计划元素,如联接顺序和投影选择。
Vertica 提供两种创建定向查询的方法:
优化器可以从给定输入查询创建带注释的查询,并将这两个查询配对为定向查询。
您可以编写自己的带注释查询并将其与输入查询配对。
有关这两种方法的描述,请参阅创建定向查询。
CREATE DIRECTED QUERY 将输入查询与使用优化器提示进行注释的查询相关联。它将此关联存储在一个唯一标识符下。CREATE DIRECTED QUERY 有两种变体:
CREATE DIRECTED QUERY OPTIMIZER 指示查询优化器从指定的输入查询生成带注释的 SQL。带注释的查询包含一些提示,优化器可使用这些提示为输入查询重新创建其当前查询计划。
CREATE DIRECTED QUERY CUSTOM 指定用户提供的带注释的查询。Vertica 将带注释的查询与最后一个 SAVE QUERY 语句指定的输入查询相关联。
在这两种情形中,Vertica 都会将带注释的查询与输入查询相关联,并将其关联注册在 query_name
下的系统表 DIRECTED_QUERIES 中。
这两种方法可以一起使用:您可以使用优化器创建的带注释 SQL 作为创建自己的(自定义)定向查询的基础。
CREATE DIRECTED QUERY OPTIMIZER 将输入查询传递给优化器,优化器从自己的查询计划生成带注释的查询。然后,它将输入查询和带注释的查询配对并将其保存为定向查询。此定向查询可用于处理其他除了作为查询结果筛选依据的谓词字符串以外完全相同的查询。
可在升级之前使用优化器生成的定向查询来捕获查询计划。如果在升级后检测到给定查询的性能下降,这样做将会非常有用。在这种情况下,可使用相应的定向查询重新创建早期查询计划,并将其性能与当前优化器生成的计划相对比。
以下 SQL 语句创建和激活定向查询 findEmployeesCityJobTitle_OPT
:
=> CREATE DIRECTED QUERY OPTIMIZER 'findEmployeesCityJobTitle_OPT'
SELECT employee_first_name, employee_last_name FROM public.employee_dimension
WHERE employee_city='Boston' and job_title='Cashier' ORDER BY employee_last_name, employee_first_name;
CREATE DIRECTED QUERY
=> ACTIVATE DIRECTED QUERY findEmployeesCityJobTitle_OPT;
ACTIVATE DIRECTED QUERY
在激活此定向查询计划后,优化器会使用它为此输入查询的所有后续调用以及其他类似调用生成查询计划。可通过调用 GET DIRECTED QUERY 或查询系统表 DIRECTED_QUERIES 来查看优化器生成的带注释查询:
=> SELECT input_query, annotated_query FROM V_CATALOG.DIRECTED_QUERIES WHERE query_name = 'findEmployeesCityJobTitle_OPT';
-[ RECORD 1 ]---+----------------------------------------------------------------------------
input_query | SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension
WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/))
ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name
annotated_query | SELECT /*+verbatim*/ employee_dimension.employee_first_name AS employee_first_name, employee_dimension.employee_last_name AS employee_last_name FROM public.employee_dimension AS employee_dimension/*+projs('public.employee_dimension')*/
WHERE (employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/)
ORDER BY 2 ASC, 1 ASC
带注释的查询包括以下提示:
/*+verbatim*/
指定完全按编写执行带注释的查询并相应地生成查询计划。/*+projs('public.Emp_Dimension')*/
指示优化器创建使用投影 public.Emp_Dimension
的查询计划。/*+:v({{< codevar >}}n{{< /codevar >}})*/
(alias of
/*+IGNORECONST(n)*/
) 多次包含在带注释查询和输入查询中。这些提示限定查询谓词中的两个常量: Boston
和 Cashier
. Each :v
提示有一个整数实参 n,该实参将输入查询和带注释查询中的对应常量进行配对: *+:v(1)*/
(用于 Boston
)和 /*+:v(2)*/
(用于 Cashier
)。这些提示告诉优化器在决定是否将此定向查询应用于其他类似的输入查询时忽略这些常量。因此,忽略常量提示可让您对不同的输入查询使用相同的定向查询。以下查询对 employee_city
和 job_title
列使用不同的值,但在其他方面与定向查询 EmployeesCityJobTitle_OPT
的原始输入查询相同:
=> SELECT employee_first_name, employee_last_name FROM public.employee_dimension
WHERE employee_city = 'San Francisco' and job_title = 'Branch Manager' ORDER BY employee_last_name, employee_first_name;
如果定向查询 EmployeesCityJobTitle_OPT
处于活动状态,则优化器可以将其用于此查询:
=> EXPLAIN SELECT employee_first_name, employee_last_name FROM employee_dimension WHERE employee_city='San Francisco' AND job_title='Branch Manager' ORDER BY employee_last_name, employee_first_name;
...
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT employee_first_name, employee_last_name FROM employee_dimension WHERE employee_city='San Francisco' AND job_title='Branch Manager' ORDER BY employee_last_name, employee_first_name;
The following active directed query(query name: findEmployeesCityJobTitle_OPT) is being executed:
SELECT /*+verbatim*/ employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension employee_dimension/*+projs('public.employee_dimension')*/
WHERE ((employee_dimension.employee_city = 'San Francisco'::varchar(13)) AND (employee_dimension.job_title = 'Branch Manager'::varchar(14)))
ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name
Access Path:
+-SORT [Cost: 222, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
| Order: employee_dimension.employee_last_name ASC, employee_dimension.employee_first_name ASC
| Execute on: All Nodes
| +---> STORAGE ACCESS for employee_dimension [Cost: 60, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Projection: public.employee_dimension_super
| | Materialize: employee_dimension.employee_first_name, employee_dimension.employee_last_name
| | Filter: (employee_dimension.employee_city = 'San Francisco')
| | Filter: (employee_dimension.job_title = 'Branch Manager')
| | Execute on: All Nodes
...
CREATE DIRECTED QUERY CUSTOM 指定带注释的查询并将其与 SAVE QUERY 先前保存的输入查询配对。必须在同一用户会话中发出这两个语句。
例如,您可能希望某查询使用特定投影:
使用 SAVE QUERY 指定查询:
=> SAVE QUERY SELECT employee_first_name, employee_last_name FROM employee_dimension
WHERE employee_city='Boston' AND job_title='Cashier';
SAVE QUERY
:v
提示。
使用 CREATE DIRECTED QUERY CUSTOM 创建自定义定向查询,它指定带注释的查询并将其与保存的查询相关联。带注释的查询包含一个 /*+projs*/
提示,它指示优化器在用户调用保存的查询时使用投影 public.emp_dimension_unseg
:
=> CREATE DIRECTED QUERY CUSTOM 'findBostonCashiers_CUSTOM'
SELECT employee_first_name, employee_last_name
FROM employee_dimension /*+Projs('public.emp_dimension_unseg')*/
WHERE employee_city='Boston' AND job_title='Cashier';
CREATE DIRECTED QUERY
激活此定向查询:
=> ACTIVATE DIRECTED QUERY findBostonCashiers_CUSTOM;
ACTIVATE DIRECTED QUERY
激活后,优化器将使用此定向查询为其输入查询的所有后续调用生成查询计划。下面的 EXPLAIN 输出验证优化器使用此定向查询的情况及其指定的投影:
=> EXPLAIN SELECT employee_first_name, employee_last_name FROM employee_dimension
WHERE employee_city='Boston' AND job_title='Cashier';
QUERY PLAN
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT employee_first_name, employee_last_name FROM employee_dimension where employee_city='Boston' AND job_title='Cashier';
The following active directed query(query name: findBostonCashiers_CUSTOM) is being executed:
SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name
FROM public.employee_dimension/*+Projs('public.emp_dimension_unseg')*/
WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6)) AND (employee_dimension.job_title = 'Cashier'::varchar(7)))
Access Path:
+-STORAGE ACCESS for employee_dimension [Cost: 158, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
| Projection: public.emp_dimension_unseg
| Materialize: employee_dimension.employee_first_name, employee_dimension.employee_last_name
| Filter: (employee_dimension.employee_city = 'Boston')
| Filter: (employee_dimension.job_title = 'Cashier')
| Execute on: Query Initiator
可以根据优化器所创建的带注释 SQL 来创建自己的自定义定向查询。当评估优化器创建用于处理给定查询的计划以及测试计划修改内容时,此方法特别有用。
例如,您可能希望修改优化器对以下查询的实施方式:
=> SELECT COUNT(customer_name) Total, customer_region Region
FROM (store_sales s JOIN customer_dimension c ON c.customer_key = s.customer_key)
JOIN product_dimension p ON s.product_key = p.product_key
WHERE p.category_description ilike '%Medical%'
AND p.product_description ilike '%antibiotics%'
AND c.customer_age <= 30 AND YEAR(s.sales_date)=2017
GROUP BY customer_region;
对此查询运行 EXPLAIN 时,您会发现优化器将投影 customers_proj_age
用于 customer_dimension
表。此投影按列 customer_age
进行排序。因此,优化器将根据 customer_key
对表 store_sales
和 customer_dimension
进行哈希联接。
分析 customer_dimension
表数据后,您观察到大部分客户的年龄不到 30 岁,因此对按 customer_key
排序的 customer_dimension
表使用投影 customer_proj_id
更有意义:
您可以创建一个用于封装此更改的定向查询,如下所示:
使用 EXPLAIN ANNOTATED 获取由优化器生成的查询注释:
=> \o annotatedQuery
=> EXPLAIN ANNOTATED SELECT COUNT(customer_name) Total, customer_region Region
FROM (store_sales s JOIN customer_dimension c ON c.customer_key = s.customer_key)
JOIN product_dimension p ON s.product_key = p.product_key
WHERE p.category_description ilike '%Medical%'
AND p.product_description ilike '%antibiotics%'
AND c.customer_age <= 30 AND YEAR(s.sales_date)=2017
GROUP BY customer_region;
=> \o
=> \! cat annotatedQuery
...
SELECT /*+syntactic_join,verbatim*/ count(c.customer_name) AS Total, c.customer_region AS Region
FROM ((public.store_sales AS s/*+projs('public.store_sales_super')*/
JOIN /*+Distrib(L,B),JType(H)*/ public.customer_dimension AS c/*+projs('public.customers_proj_age')*/
ON (c.customer_key = s.customer_key))
JOIN /*+Distrib(L,B),JType(M)*/ public.product_dimension AS p/*+projs('public.product_dimension')*/
ON (s.product_key = p.product_key))
WHERE ((date_part('year'::varchar(4), (s.sales_date)::timestamp(0)))::int = 2017)
AND (c.customer_age <= 30)
AND ((p.category_description)::varchar(32) ~~* '%Medical%'::varchar(9))
AND (p.product_description ~~* '%antibiotics%'::varchar(13))
GROUP BY /*+GByType(Hash)*/ 2
(4 rows)
修改带注释的查询:
SELECT /*+syntactic_join,verbatim*/ count(c.customer_name) AS Total, c.customer_region AS Region
FROM ((public.store_sales AS s/*+projs('public.store_sales_super')*/
JOIN /*+Distrib(L,B),JType(H)*/ public.customer_dimension AS c/*+projs('public.customer_proj_id')*/
ON (c.customer_key = s.customer_key))
JOIN /*+Distrib(L,B),JType(H)*/ public.product_dimension AS p/*+projs('public.product_dimension')*/
ON (s.product_key = p.product_key))
WHERE ((date_part('year'::varchar(4), (s.sales_date)::timestamp(0)))::int = 2017)
AND (c.customer_age <= 30)
AND ((p.category_description)::varchar(32) ~~* '%Medical%'::varchar(9))
AND (p.product_description ~~* '%antibiotics%'::varchar(13))
GROUP BY /*+GByType(Hash)*/ 2
使用修改后的带注释查询来创建所需的定向查询:
使用 SAVE QUERY 保存所需的输入查询:
=> SAVE QUERY SELECT COUNT(customer_name) Total, customer_region Region
FROM (store_sales s JOIN customer_dimension c ON c.customer_key = s.customer_key)
JOIN product_dimension p ON s.product_key = p.product_key
WHERE p.category_description ilike '%Medical%'
AND p.product_description ilike '%antibiotics%'
AND c.customer_age <= 30 AND YEAR(s.sales_date)=2017
GROUP BY customer_region;
创建一个自定义定向查询,将保存的输入查询与修改后的带注释查询相关联:
=> CREATE DIRECTED QUERY CUSTOM 'getCustomersUnder31'
SELECT /*+syntactic_join,verbatim*/ count(c.customer_name) AS Total, c.customer_region AS Region
FROM ((public.store_sales AS s/*+projs('public.store_sales_super')*/
JOIN /*+Distrib(L,B),JType(H)*/ public.customer_dimension AS c/*+projs('public.customer_proj_id')*/
ON (c.customer_key = s.customer_key))
JOIN /*+Distrib(L,B),JType(H)*/ public.product_dimension AS p/*+projs('public.product_dimension')*/
ON (s.product_key = p.product_key))
WHERE ((date_part('year'::varchar(4), (s.sales_date)::timestamp(0)))::int = 2017)
AND (c.customer_age <= 30)
AND ((p.category_description)::varchar(32) ~~* '%Medical%'::varchar(9))
AND (p.product_description ~~* '%antibiotics%'::varchar(13))
GROUP BY /*+GByType(Hash)*/ 2;
CREATE DIRECTED QUERY
激活此定向查询:
=> ACTIVATE DIRECTED QUERY getCustomersUnder31;
ACTIVATE DIRECTED QUERY
当优化器处理与此定向查询的输入查询相匹配的查询时,它会使用此定向查询的带注释查询来生成查询计划:
=> EXPLAIN SELECT COUNT(customer_name) Total, customer_region Region
FROM (store_sales s JOIN customer_dimension c ON c.customer_key = s.customer_key)
JOIN product_dimension p ON s.product_key = p.product_key
WHERE p.category_description ilike '%Medical%'
AND p.product_description ilike '%antibiotics%'
AND c.customer_age <= 30 AND YEAR(s.sales_date)=2017
GROUP BY customer_region;
The following active directed query(query name: getCustomersUnder31) is being executed:
...
定向查询的带注释查询中的提示为优化器提供关于如何执行输入查询的说明。带注释查询支持以下提示:
联接提示 用于指定联接顺序、联接类型和联接数据分布:SYNTACTIC_JOINDISTRIB、JTYPE、UTYPE。
表提示 用于指定在查询计划中包括和排除哪些投影:PROJS、SKIP_PROJS。
:v 及其别名 IGNORECONSTANT 标记谓词字符串常量,当优化器决定是否对给定输入查询使用定向查询时,您希望优化器忽略这些常量。有关详细信息,请参阅忽略定向查询中的常量。
VERBATIM 完全按编写的那样强制执行带注释查询。
带注释查询中的其他提示(如 DIRECT 或 LABEL)不起作用。
您可以在 vsql 查询中使用与带注释查询中相同的提示,只有两个例外情况: :v
(IGNORECONSTANT
) 和 VERBATIM
。
优化器生成的定向查询通常包括一个或多个
:v
(IGNORECONSTANT
的别名)提示,这些提示标记谓词字符串常量,当优化器决定是否对给定输入查询使用定向查询时,您希望优化器忽略这些常量。 :v
提示使多个查询能够使用同一个定向查询,但前提是这些查询在除了其谓词字符串以外的所有其他方面都相同。
例如,在以下两个查询中,除了分别为 employee_city
和 job_title
列指定的字符串常量 Boston|San Francisco
和 Cashier|Branch Manager
外,所有其他方面都相同:
=> SELECT employee_first_name, employee_last_name FROM public.employee_dimension
WHERE employee_city='Boston' and job_title ='Cashier' ORDER BY employee_last_name, employee_first_name;
=> SELECT employee_first_name, employee_last_name FROM public.employee_dimension
WHERE employee_city = 'San Francisco' and job_title = 'Branch Manager' ORDER BY employee_last_name, employee_first_name;
在这种情况下,您从一个查询创建的、由优化器生成的定向查询可用于这两个查询:
=> CREATE DIRECTED QUERY OPTIMIZER 'findEmployeesCityJobTitle_OPT'
SELECT employee_first_name, employee_last_name FROM public.employee_dimension
WHERE employee_city='Boston' and job_title='Cashier' ORDER BY employee_last_name, employee_first_name;
CREATE DIRECTED QUERY
=> ACTIVATE DIRECTED QUERY findEmployeesCityJobTitle_OPT;
ACTIVATE DIRECTED QUERY
定向查询的输入查询和带注释查询都包含 :v
提示:
=> SELECT input_query, annotated_query FROM V_CATALOG.DIRECTED_QUERIES WHERE query_name = 'findEmployeesCityJobTitle_OPT';
-[ RECORD 1 ]---+----------------------------------------------------------------------------
input_query | SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension
WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/))
ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name
annotated_query | SELECT /*+verbatim*/ employee_dimension.employee_first_name AS employee_first_name, employee_dimension.employee_last_name AS employee_last_name FROM public.employee_dimension AS employee_dimension/*+projs('public.employee_dimension')*/
WHERE (employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/)
ORDER BY 2 ASC, 1 ASC
输入查询和带注释查询中的提示实参将两个谓词常量配对:
/*+:v(1)*/
将 employee_city
的输入查询和带注释查询设置配对。
/*+:v(2)*/
将 job_title
的输入查询和带注释查询设置配对。
:v
提示告诉优化器在决定是否可以将此定向查询用于给定输入查询时忽略这两列的值。
例如,以下查询虽然对 employee_city
和 job_title
使用不同的值,但在其他方面与用于创建定向查询 EmployeesCityJobTitle_OPT
的查询相同:
=> SELECT employee_first_name, employee_last_name FROM public.employee_dimension
WHERE employee_city = 'San Francisco' and job_title = 'Branch Manager' ORDER BY employee_last_name, employee_first_name;
如果定向查询 EmployeesCityJobTitle_OPT
处于活动状态,则优化器可以将该定向查询用在以下查询的查询计划中:
=> EXPLAIN SELECT employee_first_name, employee_last_name FROM employee_dimension WHERE employee_city='San Francisco' AND job_title='Branch Manager' ORDER BY employee_last_name, employee_first_name;
...
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT employee_first_name, employee_last_name FROM employee_dimension WHERE employee_city='San Francisco' AND job_title='Branch Manager' ORDER BY employee_last_name, employee_first_name;
The following active directed query(query name: findEmployeesCityJobTitle_OPT) is being executed:
SELECT /*+verbatim*/ employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension employee_dimension/*+projs('public.employee_dimension')*/
WHERE ((employee_dimension.employee_city = 'San Francisco'::varchar(13)) AND (employee_dimension.job_title = 'Branch Manager'::varchar(14)))
ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name
Access Path:
+-SORT [Cost: 222, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
| Order: employee_dimension.employee_last_name ASC, employee_dimension.employee_first_name ASC
| Execute on: All Nodes
| +---> STORAGE ACCESS for employee_dimension [Cost: 60, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Projection: public.employee_dimension_super
| | Materialize: employee_dimension.employee_first_name, employee_dimension.employee_last_name
| | Filter: (employee_dimension.employee_city = 'San Francisco')
| | Filter: (employee_dimension.job_title = 'Branch Manager')
| | Execute on: All Nodes
...
目前所显示的示例演示了 :v
提示的一对一配对。您也可以使用 :v
提示将一个输入常数映射到带注释查询中的多个常数。当您想为优化器提供关于如何执行查询(用来联接表)的显式说明时,这种方法特别有用。
例如,以下查询联接了两个表:
SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 8;
在这种情况下,优化器可以推断 S.a
和 T.b
具有相同的值,并相应地实施联接。
<a name="simpleJoinExample"></a>=> CREATE DIRECTED QUERY OPTIMIZER simpleJoin SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 8;
CREATE DIRECTED QUERY
=> SELECT input_query, annotated_query FROM directed_queries WHERE query_name = 'simpleJoin';
-[ RECORD 1 ]---+---------------------------------------------------------------------------------------------------------------------
input_query | SELECT S.a, T.b FROM (public.S JOIN public.T ON ((S.a = T.b))) WHERE (S.a = 8 /*+:v(1)*/)
annotated_query | SELECT /*+syntactic_join,verbatim*/ S.a AS a, T.b AS b
FROM (public.S AS S/*+projs('public.S')*/ JOIN /*+Distrib(L,L),JType(M)*/ public.T AS T/*+projs('public.T')*/ ON (S.a = T.b))
WHERE (S.a = 8 /*+:v(1)*/) AND (T.b = 8 /*+:v(1)*/)
(1 row)
=> ACTIVATE DIRECTED QUERY simpleJoin;
ACTIVATED DIRECTED QUERY
现在,以下列输入查询为例:
SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 3;
优化器忽略联接谓词常量并使用其查询计划中的定向查询 simpleJoin
:
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 3;
The following active directed query(query name: simpleJoin) is being executed:
SELECT /*+syntactic_join,verbatim*/ S.a, T.b FROM (public.S S/*+projs('public.S')*/ JOIN /*+Distrib('L', 'L'), JType('
M')*/public.T T/*+projs('public.T')*/ ON ((S.a = T.b))) WHERE ((S.a = 3) AND (T.b = 3))
Access Path:
+-JOIN MERGEJOIN(inputs presorted) [Cost: 21, Rows: 4 (NO STATISTICS)] (PATH ID: 1)
| Join Cond: (S.a = T.b)
| Execute on: Query Initiator
| +-- Outer -> STORAGE ACCESS for S [Cost: 12, Rows: 4 (NO STATISTICS)] (PATH ID: 2)
| | Projection: public.S_b0
| | Materialize: S.a
| | Filter: (S.a = 3)
| | Execute on: Query Initiator
| | Runtime Filter: (SIP1(MergeJoin): S.a)
| +-- Inner -> STORAGE ACCESS for T [Cost: 8, Rows: 3 (NO STATISTICS)] (PATH ID: 3)
| | Projection: public.T_b0
| | Materialize: T.b
| | Filter: (T.b = 3)
| | Execute on: Query Initiator
...
默认情况下,优化器生成的定向查询在谓词常量上设置 :v
提示。您可以覆盖此行为,方法是使用
:c
提示来标记不得忽略的谓词常量。例如,以下语句创建一个定向查询,该定向查询只能用于联接谓词常量 8
与原始输入查询中相同的输入查询:
=> CREATE DIRECTED QUERY OPTIMIZER simpleJoin_KeepPredicateConstant SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 8 /*+:c*/;
CREATE DIRECTED QUERY
=> ACTIVATE DIRECTED QUERY simpleJoin_KeepPredicateConstant;
以下对系统表 DIRECTED_QUERIES 的查询对定向查询 simpleJoin
(在前面的示例中创建)和 simpleJoin_KeepPredicateConstant
进行了比较。与 simpleJoin
不同,simpleJoin_KeepPredicateConstant
的输入查询和带注释查询的联接谓词省略了 :v
提示:
=> SELECT query_name, input_query, annotated_query FROM directed_queries WHERE query_name ILIKE'%simpleJoin%';
-[ RECORD 1 ]---+
query_name | simpleJoin
input_query | SELECT S.a, T.b FROM (public.S JOIN public.T ON ((S.a = T.b))) WHERE (S.a = 8 /*+:v(1)*/)
annotated_query | SELECT /*+syntactic_join,verbatim*/ S.a AS a, T.b AS b
FROM (public.S AS S/*+projs('public.S')*/ JOIN /*+Distrib(L,L),JType(M)*/ public.T AS T/*+projs('public.T')*/ ON (S.a = T.b))
WHERE (S.a = 8 /*+:v(1)*/) AND (T.b = 8 /*+:v(1)*/)
-[ RECORD 2 ]---+
query_name | simpleJoin_KeepPredicateConstant
input_query | SELECT S.a, T.b FROM (public.S JOIN public.T ON ((S.a = T.b))) WHERE (S.a = 8)
annotated_query | SELECT /*+syntactic_join,verbatim*/ S.a AS a, T.b AS b
FROM (public.S AS S/*+projs('public.S')*/ JOIN /*+Distrib(L,L),JType(M)*/ public.T AS T/*+projs('public.T')*/ ON (S.a = T.b))
WHERE (S.a = 8) AND (T.b = 8)
如果停用定向查询 simpleJoin
(否则该定向查询会优先于 simpleJoin_KeepPredicateConstant
,因为前者创建的早),Vertica 仅将 simpleJoin_KeepPredicateConstant
应用于输入查询,这里联接谓词常量与原始输入查询中相同。例如,比较以下两个查询计划:
=> EXPLAIN SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 8;
...
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 8;
The following active directed query(query name: simpleJoin_KeepPredicateConstant) is being executed:
SELECT /*+syntactic_join,verbatim*/ S.a, T.b FROM (public.S S/*+projs('public.S')*/ JOIN /*+Distrib('L', 'L'), JType('
M')*/public.T T/*+projs('public.T')*/ ON ((S.a = T.b))) WHERE ((S.a = 8) AND (T.b = 8))
Access Path:
+-JOIN MERGEJOIN(inputs presorted) [Cost: 21, Rows: 4 (NO STATISTICS)] (PATH ID: 1)
| Join Cond: (S.a = T.b)
...
=> EXPLAIN SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 3
...
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT * FROM S JOIN T ON S.a = T.b WHERE S.a = 3;
Access Path:
+-JOIN MERGEJOIN(inputs presorted) [Cost: 21, Rows: 4 (NO STATISTICS)] (PATH ID: 1)
| Join Cond: (S.a = T.b)
...
您可以使用定向查询来更改给定查询的语义,即用一个查询替换另一个查询。当您对 Vertica 数据库处理的输入查询的内容和格式没有或几乎没有控制权时,这一点尤其重要。您可以将这些查询映射到定向查询,从而重写原始输入以实现最佳执行效果。
以下部分介绍了两个用例:
许多输入查询联接多个表。您已经确定,在许多情况下,更高效的做法是对多个扁平表中的大量数据进行反向标准化并直接查询这些表。您不能修改输入查询本身。但是,您可以使用定向查询将联接查询重定向到扁平表数据。
例如,以下查询通过联接 VMart 数据库中的三个表来聚合葡萄酒产品的区域销售额:
=> SELECT SD.store_region AS Region, SD.store_city AS City, SUM(SF.gross_profit_dollar_amount) Total
FROM store.store_sales_fact SF
JOIN store.store_dimension SD ON SF.store_key=SD.store_key
JOIN product_dimension P ON SF.product_key||SF.product_version=P.product_key||P.product_version
WHERE P.product_description ILIKE '%wine%'
GROUP BY ROLLUP (SD.store_region, SD.store_city)
ORDER BY Region,Total DESC;
您可以将联接的表数据合并到一个扁平表中,然后改为查询该表。通过这样做,您可以更快地访问同一数据。您可以使用以下 DDL 语句创建扁平表:
=> CREATE TABLE store.store_sales_wide AS SELECT * FROM store.store_sales_fact;
=> ALTER TABLE store.store_sales_wide ADD COLUMN store_name VARCHAR(64)
SET USING (SELECT store_name FROM store.store_dimension WHERE store.store_sales_wide.store_key=store.store_dimension.store_key);
=> ALTER TABLE store.store_sales_wide ADD COLUMN store_city varchar(64)
SET USING (SELECT store_city FROM store.store_dimension WHERE store.store_sales_wide.store_key=store.store_dimension.store_key);
=> ALTER TABLE store.store_sales_wide ADD COLUMN store_state char(2)
SET USING (SELECT store_state char FROM store.store_dimension WHERE store.store_sales_wide.store_key=store.store_dimension.store_key)
=> ALTER TABLE store.store_sales_wide ADD COLUMN store_region varchar(64)
SET USING (SELECT store_region FROM store.store_dimension WHERE store.store_sales_wide.store_key=store.store_dimension.store_key);
=> ALTER TABLE store.store_sales_wide ADD column product_description VARCHAR(128)
SET USING (SELECT product_description FROM public.product_dimension
WHERE store_sales_wide.product_key||store_sales_wide.product_version = product_dimension.product_key||product_dimension.product_version);
=> ALTER TABLE store.store_sales_wide ADD COLUMN sku_number char(32)
SET USING (SELECT sku_number char FROM product_dimension
WHERE store_sales_wide.product_key||store_sales_wide.product_version = product_dimension.product_key||product_dimension.product_version);
=> SELECT REFRESH_COLUMNS ('store.store_sales_wide','', 'rebuild');
创建此表并刷新其 SET USING
列后,可以重写前面的查询,如下所示:
=> SELECT SD.store_region AS Region, SD.store_city AS City, SUM(SF.gross_profit_dollar_amount) Total
FROM store.store_sales_fact SF
JOIN store.store_dimension SD ON SF.store_key=SD.store_key
JOIN product_dimension P ON SF.product_key||SF.product_version=P.product_key||P.product_version
WHERE P.product_description ILIKE '%wine%'
GROUP BY ROLLUP (SD.store_region, SD.store_city)
ORDER BY Region,Total DESC;
Region | City | Total
-----------+------------------+---------
East | | 1679788
East | Boston | 138494
East | Elizabeth | 138071
East | Sterling Heights | 137719
East | Allentown | 137122
East | New Haven | 117751
East | Lowell | 102670
East | Washington | 84595
East | Charlotte | 83255
East | Waterbury | 81516
East | Erie | 80784
East | Stamford | 59935
East | Hartford | 59843
East | Baltimore | 55873
East | Clarksville | 54117
East | Nashville | 53757
East | Manchester | 53290
East | Columbia | 52799
East | Memphis | 52648
East | Philadelphia | 29711
East | Portsmouth | 29316
East | New York | 27033
East | Cambridge | 26111
East | Alexandria | 23378
MidWest | | 1073224
MidWest | Lansing | 145616
MidWest | Livonia | 129349
--More--
查询扁平表更高效;但是,您仍然必须考虑继续使用早期联接语法的输入查询。这可以通过创建自定义定向查询来完成,这会将这些输入查询重定向到以扁平表为目标的语法:
=> SAVE QUERY SELECT SD.store_region AS Region, SD.store_city AS City, SUM(SF.gross_profit_dollar_amount) Total
FROM store.store_sales_fact SF
JOIN store.store_dimension SD ON SF.store_key=SD.store_key
JOIN product_dimension P ON SF.product_key||SF.product_version=P.product_key||P.product_version
WHERE P.product_description ILIKE '%wine%'
GROUP BY ROLLUP (SD.store_region, SD.store_city)
ORDER BY Region,Total DESC;
SAVE QUERY
将保存的查询映射到具有所需语法的定向查询,然后激活定向查询:
=> CREATE DIRECTED QUERY CUSTOM 'RegionalSalesWine'
SELECT store_region AS Region,
store_city AS City,
SUM(gross_profit_dollar_amount) AS Total
FROM store.store_sales_wide
WHERE product_description ILIKE '%wine%'
GROUP BY ROLLUP (region, city)
ORDER BY Region,Total DESC;
CREATE DIRECTED QUERY
=> ACTIVATE DIRECTED QUERY RegionalSalesWine;
ACTIVATE DIRECTED QUERY
当定向查询 RegionalSalesWine
处于活动状态时,查询优化器会将所有与原始输入格式相匹配的查询映射到定向查询,如下面的查询计划所示:
=> EXPLAIN SELECT SD.store_region AS Region, SD.store_city AS City, SUM(SF.gross_profit_dollar_amount) Total
FROM store.store_sales_fact SF
JOIN store.store_dimension SD ON SF.store_key=SD.store_key
JOIN product_dimension P ON SF.product_key||SF.product_version=P.product_key||P.product_version
WHERE P.product_description ILIKE '%wine%'
GROUP BY ROLLUP (SD.store_region, SD.store_city)
ORDER BY Region,Total DESC;
...
The following active directed query(query name: RegionalSalesWine) is being executed:
SELECT store_sales_wide.store_region AS Region, store_sales_wide.store_city AS City, sum(store_sales_wide.gross_profit_dollar_amount) AS Total
FROM store.store_sales_wide WHERE (store_sales_wide.product_description ~~* '%wine%'::varchar(6))
GROUP BY GROUPING SETS((store_sales_wide.store_region, store_sales_wide.store_city), (store_sales_wide.store_region),())
ORDER BY store_sales_wide.store_region, sum(store_sales_wide.gross_profit_dollar_amount) DESC
Access Path:
+-SORT [Cost: 2K, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
| Order: store_sales_wide.store_region ASC, sum(store_sales_wide.gross_profit_dollar_amount) DESC
| Execute on: All Nodes
| +---> GROUPBY HASH (GLOBAL RESEGMENT GROUPS) (LOCAL RESEGMENT GROUPS) [Cost: 2K, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Aggregates: sum(store_sales_wide.gross_profit_dollar_amount)
| | Group By: store_sales_wide.store_region, store_sales_wide.store_city
| | Grouping Sets: (store_sales_wide.store_region, store_sales_wide.store_city, <SVAR>), (store_sales_wide.store_region, <SVAR>), (<SVAR>)
| | Execute on: All Nodes
| | +---> STORAGE ACCESS for store_sales_wide [Cost: 864, Rows: 10K (NO STATISTICS)] (PATH ID: 3)
| | | Projection: store.store_sales_wide_b0
| | | Materialize: store_sales_wide.gross_profit_dollar_amount, store_sales_wide.store_city, store_sales_wide.store_region
| | | Filter: (store_sales_wide.product_description ~~* '%wine%')
| | | Execute on: All Nodes
要对定向查询和原始输入查询的执行成本进行比较,请停用定向查询并对原始输入查询使用 EXPLAIN。优化器恢复到为输入查询创建计划,这会产生高得多的成本 - 188K 相对于 2K:
=> DEACTIVATE DIRECTED QUERY RegionalSalesWine;
DEACTIVATE DIRECTED QUERY
=> EXPLAIN SELECT SD.store_region AS Region, SD.store_city AS City, SUM(SF.gross_profit_dollar_amount) Total
FROM store.store_sales_fact SF
JOIN store.store_dimension SD ON SF.store_key=SD.store_key
JOIN product_dimension P ON SF.product_key||SF.product_version=P.product_key||P.product_version
WHERE P.product_description ILIKE '%wine%'
GROUP BY ROLLUP (SD.store_region, SD.store_city)
ORDER BY Region,Total DESC;
...
Access Path:
+-SORT [Cost: 188K, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
| Order: SD.store_region ASC, sum(SF.gross_profit_dollar_amount) DESC
| Execute on: All Nodes
| +---> GROUPBY HASH (GLOBAL RESEGMENT GROUPS) (LOCAL RESEGMENT GROUPS) [Cost: 188K, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Aggregates: sum(SF.gross_profit_dollar_amount)
| | Group By: SD.store_region, SD.store_city
| | Grouping Sets: (SD.store_region, SD.store_city, <SVAR>), (SD.store_region, <SVAR>), (<SVAR>)
| | Execute on: All Nodes
| | +---> JOIN HASH [Cost: 12K, Rows: 5M (NO STATISTICS)] (PATH ID: 3) Inner (BROADCAST)
| | | Join Cond: (concat((SF.product_key)::varchar, (SF.product_version)::varchar) = concat((P.product_key)::varchar, (P.product_version)::varchar))
| | | Materialize at Input: SF.product_key, SF.product_version
| | | Materialize at Output: SF.gross_profit_dollar_amount
| | | Execute on: All Nodes
| | | +-- Outer -> JOIN HASH [Cost: 2K, Rows: 5M (NO STATISTICS)] (PATH ID: 4) Inner (BROADCAST)
| | | | Join Cond: (SF.store_key = SD.store_key)
| | | | Execute on: All Nodes
| | | | +-- Outer -> STORAGE ACCESS for SF [Cost: 1K, Rows: 5M (NO STATISTICS)] (PATH ID: 5)
| | | | | Projection: store.store_sales_fact_super
| | | | | Materialize: SF.store_key
| | | | | Execute on: All Nodes
| | | | | Runtime Filters: (SIP2(HashJoin): SF.store_key), (SIP1(HashJoin): concat((SF.product_key)::varchar, (SF.product_version)::varchar))
| | | | +-- Inner -> STORAGE ACCESS for SD [Cost: 13, Rows: 250 (NO STATISTICS)] (PATH ID: 6)
| | | | | Projection: store.store_dimension_super
| | | | | Materialize: SD.store_key, SD.store_city, SD.store_region
| | | | | Execute on: All Nodes
| | | +-- Inner -> STORAGE ACCESS for P [Cost: 201, Rows: 60K (NO STATISTICS)] (PATH ID: 7)
| | | | Projection: public.product_dimension_super
| | | | Materialize: P.product_key, P.product_version
| | | | Filter: (P.product_description ~~* '%wine%')
| | | | Execute on: All Nodes
您可以使用定向查询来实施除了用于筛选查询结果的谓词字符串以外都完全相同的多个查询。例如,定向查询 RegionalSalesWine
仅处理对包含字符串 wine
的 product_description
值进行筛选的输入查询。您可以创建此定向查询的修改版本,使其匹配多个输入查询的语法,这些输入查询仅在输入值(例如 tuna
)上有所不同。
按以下步骤创建此查询模板:
创建两个优化器生成的定向查询:
从对联接表的原始查询:
=> CREATE DIRECTED QUERY OPTIMIZER RegionalSalesProducts_JoinTables
SELECT SD.store_region AS Region, SD.store_city AS City, SUM(SF.gross_profit_dollar_amount) Total
FROM store.store_sales_fact SF
JOIN store.store_dimension SD ON SF.store_key=SD.store_key
JOIN product_dimension P ON SF.product_key||SF.product_version=P.product_key||P.product_version
WHERE P.product_description ILIKE '%wine%'
GROUP BY ROLLUP (SD.store_region, SD.store_city)
ORDER BY Region,Total DESC;
CREATE DIRECTED QUERY
从对扁平表的查询:
=> CREATE DIRECTED QUERY OPTIMIZER RegionalSalesProduct
SELECT store_region AS Region, store_city AS City, SUM(gross_profit_dollar_amount) AS Total
FROM store.store_sales_wide
WHERE product_description ILIKE '%wine%'
GROUP BY ROLLUP (region, city)
ORDER BY Region,Total DESC;
CREATE DIRECTED QUERY
查询系统表 DIRECTED_QUERIES 并复制定向查询 RegionalSalesProducts_JoinTables
的输入查询:
SELECT input_query FROM directed_queries WHERE query_name = 'RegionalSalesProducts_JoinTables';
通过 SAVE QUERY 使用复制的输入查询:
SAVE QUERY SELECT SD.store_region AS Region, SD.store_city AS City, sum(SF.gross_profit_dollar_amount) AS Total
FROM ((store.store_sales_fact SF
JOIN store.store_dimension SD ON ((SF.store_key = SD.store_key)))
JOIN public.product_dimension P ON ((concat((SF.product_key)::varchar, (SF.product_version)::varchar) = concat((P.product_key)::varchar, (P.product_version)::varchar))))
WHERE (P.product_description ~~* '%wine%'::varchar(6) /*+:v(1)*/)
GROUP BY GROUPING SETS((SD.store_region, SD.store_city), (SD.store_region), ())
ORDER BY SD.store_region, sum(SF.gross_profit_dollar_amount) DESC
(1 row)
查询系统表 DIRECTED_QUERIES 并复制定向查询 RegionalSalesProducts_FlatTables
的带注释查询:
SELECT input_query FROM directed_queries WHERE query_name = 'RegionalSalesProducts_JoinTables';
使用复制的带注释查询创建自定义定向查询:
=> CREATE DIRECTED QUERY CUSTOM RegionalSalesProduct SELECT /*+verbatim*/ store_sales_wide.store_region AS Region, store_sales_wide.store_city AS City, sum(store_sales_wide.gross_profit_dollar_amount) AS Total
FROM store.store_sales_wide AS store_sales_wide/*+projs('store.store_sales_wide')*/
WHERE (store_sales_wide.product_description ~~* '%wine%'::varchar(6) /*+:v(1)*/)
GROUP BY /*+GByType(Hash)*/ GROUPING SETS((1, 2), (1), ())
ORDER BY 1 ASC, 3 DESC;
CREATE DIRECTED QUERY
激活此定向查询:
ACTIVATE DIRECTED QUERY RegionalSalesProduct;
激活此定向查询后,Vertica 可以将其用于和模板相匹配的输入查询中,不同之处仅在于 product_description
的谓词值:
=> EXPLAIN SELECT SD.store_region AS Region, SD.store_city AS City, SUM(SF.gross_profit_dollar_amount) Total
FROM store.store_sales_fact SF
JOIN store.store_dimension SD ON SF.store_key=SD.store_key
JOIN product_dimension P ON SF.product_key||SF.product_version=P.product_key||P.product_version
WHERE P.product_description ILIKE '%tuna%'
GROUP BY ROLLUP (SD.store_region, SD.store_city)
ORDER BY Region,Total DESC;
...
The following active directed query(query name: RegionalSalesProduct) is being executed:
SELECT /*+verbatim*/ store_sales_wide.store_region AS Region, store_sales_wide.store_city AS City, sum(store_sales_wide.gross_profit_dollar_amount) AS Total
FROM store.store_sales_wide store_sales_wide/*+projs('store.store_sales_wide')*/
WHERE (store_sales_wide.product_description ~~* '%tuna%'::varchar(6))
GROUP BY /*+GByType(Hash)*/ GROUPING SETS((store_sales_wide.store_region, store_sales_wide.store_city), (store_sales_wide.store_region), ())
ORDER BY store_sales_wide.store_region, sum(store_sales_wide.gross_profit_dollar_amount) DESC
Access Path:
+-SORT [Cost: 2K, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
| Order: store_sales_wide.store_region ASC, sum(store_sales_wide.gross_profit_dollar_amount) DESC
| Execute on: All Nodes
| +---> GROUPBY HASH (GLOBAL RESEGMENT GROUPS) (LOCAL RESEGMENT GROUPS) [Cost: 2K, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| | Aggregates: sum(store_sales_wide.gross_profit_dollar_amount)
| | Group By: store_sales_wide.store_region, store_sales_wide.store_city
| | Grouping Sets: (store_sales_wide.store_region, store_sales_wide.store_city, <SVAR>), (store_sales_wide.store_region, <SVAR>), (<SVAR>)
| | Execute on: All Nodes
| | +---> STORAGE ACCESS for store_sales_wide [Cost: 864, Rows: 10K (NO STATISTICS)] (PATH ID: 3)
| | | Projection: store.store_sales_wide_b0
| | | Materialize: store_sales_wide.gross_profit_dollar_amount, store_sales_wide.store_city, store_sales_wide.store_region
| | | Filter: (store_sales_wide.product_description ~~* '%tuna%')
| | | Execute on: All Nodes
当您执行此查询时,它会返回以下结果:
Region | City | Total
-----------+------------------+---------
East | | 1564582
East | Elizabeth | 131328
East | Allentown | 129704
East | Boston | 128188
East | Sterling Heights | 125877
East | Lowell | 112133
East | New Haven | 101161
East | Waterbury | 85401
East | Washington | 76127
East | Erie | 73002
East | Charlotte | 67850
East | Memphis | 53650
East | Clarksville | 53416
East | Hartford | 52583
East | Columbia | 51950
East | Nashville | 50031
East | Manchester | 48607
East | Baltimore | 48108
East | Stamford | 47302
East | New York | 30840
East | Portsmouth | 26485
East | Alexandria | 26391
East | Philadelphia | 23092
East | Cambridge | 21356
MidWest | | 980209
MidWest | Lansing | 130044
MidWest | Livonia | 118740
--More--
可按以下两种方式获取有关定向查询的编录信息:
GET DIRECTED QUERY 在指定输入查询上查询系统表 DIRECTED_QUERIES。它返回映射到输入查询的所有定向查询的详细信息。
以下 GET DIRECTED QUERY 语句返回映射到以下输入查询 findEmployeesCityJobTitle_OPT
的定向查询:
=> GET DIRECTED QUERY SELECT employee_first_name, employee_last_name from employee_dimension where employee_city='Boston' AND job_title='Cashier' order by employee_last_name, employee_first_name;
-[ RECORD 1 ]---+
query_name | findEmployeesCityJobTitle_OPT
is_active | t
vertica_version | Vertica Analytic Database v11.0.1-20210815
comment | Optimizer-generated directed query
creation_date | 2021-08-20 14:53:42.323963
annotated_query | SELECT /*+verbatim*/ employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension employee_dimension/*+projs('public.employee_dimension')*/ WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/)) ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name
可以直接查询系统表 DIRECTED_QUERIES。例如:
=> SELECT query_name, is_active FROM V_CATALOG.DIRECTED_QUERIES WHERE query_name ILIKE '%findEmployeesCityJobTitle%';
query_name | is_active
-------------------------------+-----------
findEmployeesCityJobTitle_OPT | t
(1 row)
字段 INPUT_QUERY 和 ANNOTATED_QUERY 的查询结果在 ~32K 个字符之后会被截断。可以使用两种方法获取这两个字段的完整内容:
使用语句 GET DIRECTED QUERY。
使用 EXPORT_CATALOG 导出定向查询。
多个定向查询可以映射到同一输入查询。系统表 DIRECTED_QUERIES 中的 is_active
列标识处于活动状态的定向查询。如果多个定向查询对于同一输入查询均处于活动状态,优化器则使用要创建的第一个定向查询。在这种情况下,可使用 EXPLAIN 识别哪个定向查询处于活动状态。
DIRECTED_QUERIES 上的以下查询查找所有处于活动状态的定向查询,且其中的输入查询包含被查询表 employee_dimension
的名称:
=> SELECT * FROM directed_queries WHERE input_query ILIKE ('%employee_dimension%') AND is_active='t';
-[ RECORD 1 ]------+
query_name | findEmployeesCityJobTitle_OPT
is_active | t
vertica_version | Vertica Analytic Database v11.0.1-20210815
comment | Optimizer-generated directed query
save_plans_version | 0
username | dbadmin
creation_date | 2021-08-20 14:53:42.323963
since_date |
input_query | SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/)) ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name
annotated_query | SELECT /*+verbatim*/ employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension employee_dimension/*+projs('public.employee_dimension')*/ WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/)) ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name
如果对输入查询运行 EXPLAIN,它将返回一个确认使用 findEmployeesCityJobTitle_OPT
作为活动定向查询的查询计划:
=> EXPLAIN SELECT employee_first_name, employee_last_name FROM employee_dimension WHERE employee_city='Boston' AND job_title ='Cashier' ORDER BY employee_last_name, employee_first_name;
The following active directed query(query name: findEmployeesCityJobTitle_OPT) is being executed:
SELECT /*+verbatim*/ employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension employee_dimension/*+projs('public.employee_dimension')*/ WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6)) AND (employee_dimension.job_title = 'Cashier'::varchar(7))) ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name
优化器仅使用处于活动状态的定向查询。如果多个定向查询共享同一输入查询,优化器将使用要创建的第一个查询。
定向查询分别使用 ACTIVATE DIRECTED QUERY 和 DEACTIVATE DIRECTED QUERY 来激活和停用。例如,以下 ACTIVATE DIRECTED QUERY 语句停用 RegionalSalesProducts_JoinTables
并激活 RegionalSalesProduct
:
=> DEACTIVATE DIRECTED QUERY RegionalSalesProducts_JoinTables;
DEACTIVATE DIRECTED QUERY;
=> ACTIVATE DIRECTED QUERY RegionalSalesProduct;
ACTIVATE DIRECTED QUERY;
Vertica 为所有会话中的给定查询使用处于活动状态的定向查询,除非它被 DEACTIVATE DIRECTED QUERY 明确停用或被 DROP DIRECTED QUERY 从存储空间中移除。如果在数据库关闭时定向查询处于活动状态,当您重新启动数据库时,Vertica 会自动将其重新激活。
定向查询停用后,查询优化器通过使用另一个定向查询(如果存在这样的定向查询)处理输入查询的后续调用。否则,它会生成自己的查询计划。
升级到新版本 Vertica 之前,您可以导出定向查询,从而将其用于实现最佳性能对系统而言至关重要的查询。
配合使用 EXPORT_CATALOG 和实参 DIRECTED_QUERIES
从数据库编录导出所有当前定向查询及其当前激活状态:
=> SELECT EXPORT_CATALOG('../../export_directedqueries', 'DIRECTED_QUERIES');
EXPORT_CATALOG
-------------------------------------
Catalog data exported successfully
EXPORT_CATALOG 创建一个脚本以重新创建定向查询,如以下示例所示:
SAVE QUERY SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/)) ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name;
CREATE DIRECTED QUERY CUSTOM findEmployeesCityJobTitle_OPT COMMENT 'Optimizer-generated directed query' OPTVER 'Vertica Analytic Database v11.0.1-20210815' PSDATE '2021-08-20 14:53:42.323963' SELECT /*+verbatim*/ employee_dimension.employee_first_name, employee_dimension.employee_last_name
FROM public.employee_dimension employee_dimension/*+projs('public.employee_dimension')*/
WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/))
ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name;
ACTIVATE DIRECTED QUERY findEmployeesCityJobTitle_OPT;
EXPORT_CATALOG
创建的脚本指定使用 CREATE DIRECTED QUERY CUSTOM 重新创建所有定向查询,而不管其最初是如何创建的。
升级完成后,使用 DROP DIRECTED QUERY 从数据库编录中移除每个定向查询。或者,编辑导出脚本并在每个 CREATE DIRECTED QUERY 语句之前插入 DROP DIRECTED QUERY 语句。例如,您可以使用粗体显示的更改来修改先前生成的脚本:
SAVE QUERY SELECT employee_dimension.employee_first_name, ... ;
DROP DIRECTED QUERY findEmployeesCityJobTitle_OPT;
CREATE DIRECTED QUERY CUSTOM findEmployeesCityJobTitle_OPT COMMENT 'Optimizer-generated ... ;
ACTIVATE DIRECTED QUERY findEmployeesCityJobTitle_OPT;
当您运行此脚本时,Vertica 将重新创建定向查询并还原其激活状态:
=> \i /home/dbadmin/export_directedqueries
SAVE QUERY
DROP DIRECTED QUERY
CREATE DIRECTED QUERY
ACTIVATE DIRECTED QUERY
DROP DIRECTED QUERY 从数据库编录中移除指定的定向查询。如果该定向查询处于活动状态,Vertica 会在移除前将其停用。
例如:
=> DROP DIRECTED QUERY findBostonCashiers_CUSTOM;
DROP DIRECTED QUERY
在升级到新 Vertica 版本之前,您可能希望使用定向查询来保存查询计划,以便可能在新数据库中重新使用。您无法预测哪些查询计划可能会重新使用,因此可能要保存很多(或全部)数据库查询的查询计划。但是,每天运行的查询有数百个。通过重复调用 CREATE DIRECTED QUERY 将每个查询计划保存到数据库编录是不切实际的。此外,这样做会显著增加编录大小,可能还会影响性能。
在此情况下,可绕过数据库编录并将查询计划作为定向查询批量导出到外部 SQL 文件。通过为查询计划存储卸载,可以保存当前数据库中任意数量的查询计划,而不影响编录大小和性能。升级之后,您可以决定在新数据库中保留哪些查询计划,并有选择地导入相应的定向查询。
Vertica 提供一组支持此方法的元函数:
EXPORT_DIRECTED_QUERIES 从一组输入查询生成查询计划,并编写用于创建封装这些计划的定向查询的 SQL。
IMPORT_DIRECTED_QUERIES 将定向查询从由 EXPORT_DIRECTED_QUERIES 生成的 SQL 文件导入数据库编录中。
可以将任意数量的查询计划作为定向查询批量导出到外部 SQL 文件中,如下所示:
创建一个 SQL 文件,其中包含要保存其查询计划的输入查询。请参阅下文的输入格式。
对该 SQL 文件调用元函数 EXPORT_DIRECTED_QUERIES。此元函数具有两个实参:
例如,以下 EXPORT_DIRECTED_QUERIES 语句指定了输入文件 inputQueries
和输出文件 outputQueries
:
=> SELECT EXPORT_DIRECTED_QUERIES('/home/dbadmin/inputQueries','/home/dbadmin/outputQueries');
EXPORT_DIRECTED_QUERIES
---------------------------------------------------------------------------------------------
1 queries successfully exported.
Queries exported to /home/dbadmin/outputQueries.
(1 row)
您提供给 EXPORT_DIRECTED_QUERIES 的输入文件包含一个或多个输入查询。对于每个输入查询,您可以选择指定两个字段,这两个字段将用于生成的定向查询中:
DirQueryName
提供了定向查询的唯一标识符,即一个符合标识符中所述惯例的字符串。
DirQueryComment
指定了一个最多 128 个字符并以引号分隔的字符串。
您可以按如下方式设置每个输入查询的格式:
--DirQueryName=query-name
--DirQueryComment='comment'
input-query
例如,一个文件可以指定一个输入查询,如下所示:
/* Query: findEmployeesCityJobTitle_OPT */
/* Comment: This query finds all employees of a given city and job title, ordered by employee name */
SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name, employee_dimension.job_title FROM public.employee_dimension WHERE (employee_dimension.employee_city = 'Boston'::varchar(6)) ORDER BY employee_dimension.job_title;
EXPORT_DIRECTED_QUERIES
会将生成用于创建定向查询的 SQL,并将此 SQL 写入到指定文件或标准输出中。对于这两种情况,输出都遵循以下格式:
/* Query: directed-query-name */
/* Comment: directed-query-comment */
SAVE QUERY input-query;
CREATE DIRECTED QUERY CUSTOM 'directed-query-name'
COMMENT 'directed-query-comment'
OPTVER 'vertica-release-num'
PSDATE 'timestamp'
annotated-query
例如,给定前面的输入时,Vertica 会将以下输出写入 /home/dbadmin/outputQueries
:
/* Query: findEmployeesCityJobTitle_OPT */
/* Comment: This query finds all employees of a given city and job title, ordered by employee name */
SAVE QUERY SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension WHERE ((employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/)) ORDER BY employee_dimension.employee_last_name, employee_dimension.employee_first_name;
CREATE DIRECTED QUERY CUSTOM 'findEmployeesCityJobTitle_OPT'
COMMENT 'This query finds all employees of a given city and job title, ordered by employee name'
OPTVER 'Vertica Analytic Database v11.1.0-20220102'
PSDATE '2022-01-06 13:45:17.430254'
SELECT /*+verbatim*/ employee_dimension.employee_first_name AS employee_first_name, employee_dimension.employee_last_name AS employee_last_name
FROM public.employee_dimension AS employee_dimension/*+projs('public.employee_dimension')*/
WHERE (employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) AND (employee_dimension.job_title = 'Cashier'::varchar(7) /*+:v(2)*/)
ORDER BY 2 ASC, 1 ASC;
如果给定的输入查询省略了 DirQueryName
和 DirQueryComment
字段,则 EXPORT_DIRECTED_QUERIES 会自动生成以下输出:
/* Query: Autoname:
时间戳
.n */
,其中 n 为基于零的整数索引,用于确保具有相同时间戳的自动生成名称具有唯一性。
/* Comment: Optimizer-generated directed query */
例如,以下输入文件包含一个 SELECT
语句,并且省略 DirQueryName
和 DirQueryComment
字段:
SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name
FROM public.employee_dimension WHERE (employee_dimension.employee_city = 'Boston'::varchar(6))
ORDER BY employee_dimension.job_title;
给定这个文件,EXPORT_DIRECTED_QUERIES 会返回一个关于缺少输入字段的警告,它还会将该警告写入错误文件:
> SELECT EXPORT_DIRECTED_QUERIES('/home/dbadmin/inputQueries2','/home/dbadmin/outputQueries3');
EXPORT_DIRECTED_QUERIES
--------------------------------------------------------------------------------------------------------------
1 queries successfully exported.
1 warning message was generated.
Queries exported to /home/dbadmin/outputQueries3.
See error report, /home/dbadmin/outputQueries3.err for details.
(1 row)
输出文件包含以下内容:
/* Query: Autoname:2022-01-06 14:11:23.071559.0 */
/* Comment: Optimizer-generated directed query */
SAVE QUERY SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name FROM public.employee_dimension WHERE (employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/) ORDER BY employee_dimension.job_title;
CREATE DIRECTED QUERY CUSTOM 'Autoname:2022-01-06 14:11:23.071559.0'
COMMENT 'Optimizer-generated directed query'
OPTVER 'Vertica Analytic Database v11.1.0-20220102'
PSDATE '2022-01-06 14:11:23.071559'
SELECT /*+verbatim*/ employee_dimension.employee_first_name AS employee_first_name, employee_dimension.employee_last_name AS employee_last_name
FROM public.employee_dimension AS employee_dimension/*+projs('public.employee_dimension')*/
WHERE (employee_dimension.employee_city = 'Boston'::varchar(6) /*+:v(1)*/)
ORDER BY employee_dimension.job_title ASC;
如果在执行 EXPORT_DIRECTED_QUERIES 期间发生任何错误或警告,它会返回一条类似于以下内容的消息:
1 queries successfully exported.
1 warning message was generated.
Queries exported to /home/dbadmin/outputQueries.
See error report, /home/dbadmin/outputQueries.err for details.
EXPORT_DIRECTED_QUERIES 会将所有错误或警告都写入到一个在与输出文件相同的路径下所创建的文件中,并使用输出文件的基名。
在上一示例中,输出文件名为 /home/dbadmin/outputQueries
,因此 EXPORT_DIRECTED_QUERIES 会将错误写入 /home/dbadmin/outputQueries.err
。
错误文件可以捕获许多错误和警告,例如,可以捕获 EXPORT_DIRECTED_QUERIES 无法创建定向查询的所有实例。在以下示例中,错误文件包含一个警告,指示没有为指定的输入查询提供名称字段,并且还记录了自动为该查询生成的名称:
----------------------------------------------------------------------------------------------------
WARNING: Name field not supplied. Using auto-generated name: 'Autoname:2016-10-13 09:44:33.527548.0'
Input Query: SELECT employee_dimension.employee_first_name, employee_dimension.employee_last_name, employee_dimension.job_title FROM public.employee_dimension WHERE (employee_dimension.employee_city = 'Boston'::varchar(6)) ORDER BY employee_dimension.job_title;
END WARNING
确定您希望在当前数据库中使用的已导出查询计划后,使用 IMPORT_DIRECTED_QUERIES 将其导入。您为此函数提供您使用 EXPORT_DIRECTED_QUERIES 创建的导出文件的名称以及您希望导入的定向查询的名称。例如:
=> SELECT IMPORT_DIRECTED_QUERIES('/home/dbadmin/outputQueries','FindEmployeesBoston');
IMPORT_DIRECTED_QUERIES
------------------------------------------------------------------------------------------
1 directed queries successfully imported.
To activate a query named 'my_query1':
=> ACTIVATE DIRECTED QUERY 'my_query1';
(1 row)
导入所需的定向查询后,必须使用 ACTIVATE DIRECTED QUERY 将其激活,然后才能使用它们创建查询计划。
Vertica 优化器在定向查询中使用多个关键字来重新创建交叉联接和半联接子查询。它还支持使用一组额外的关键字来表示复杂交叉联接和半联接。您还可以在直接从 vsql 中执行的查询中使用这些关键字。
有关详细信息,请参阅以下主题:
Vertica 优化器在定向查询中使用多个关键字来重新创建与特定搜索运算符的半联接子查询,例如
ANY
或
NOT IN
:
[SEMI JOIN](#SEMI)
[NULLAWARE ANTI JOIN](#NULLAWARE)
[SEMIALL JOIN](#SEMIALL)
[ANTI JOIN](#ANTI)
重新创建用来执行 semi-join 的查询,其中包含以
IN
、
EXIST
或
ANY
运算符开头的子查询。
输入查询
SELECT product_description FROM product_dimension
WHERE product_dimension.product_key IN (SELECT qty_in_stock from inventory_fact);
查询计划
QUERY PLAN DESCRIPTION:
------------------------------
explain SELECT product_description FROM product_dimension WHERE product_dimension.product_key IN (SELECT qty_in_stock from inventory_fact);
Access Path:
+-JOIN HASH [Semi] [Cost: 1K, Rows: 30K] (PATH ID: 1) Outer (FILTER) Inner (RESEGMENT)
| Join Cond: (product_dimension.product_key = VAL(2))
| Materialize at Output: product_dimension.product_description
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for product_dimension [Cost: 152, Rows: 60K] (PATH ID: 2)
| | Projection: public.product_dimension
| | Materialize: product_dimension.product_key
| | Execute on: All Nodes
| | Runtime Filter: (SIP1(HashJoin): product_dimension.product_key)
| +-- Inner -> SELECT [Cost: 248, Rows: 300K] (PATH ID: 3)
| | Execute on: All Nodes
| | +---> STORAGE ACCESS for inventory_fact [Cost: 248, Rows: 300K] (PATH ID: 4)
| | | Projection: public.inventory_fact_b0
| | | Materialize: inventory_fact.qty_in_stock
| | | Execute on: All Nodes
优化器生成的带注释查询
SELECT /*+ syntactic_join */ product_dimension.product_description AS product_description
FROM (public.product_dimension AS product_dimension/*+projs('public.product_dimension')*/
SEMI JOIN /*+Distrib(F,R),JType(H)*/ (SELECT inventory_fact.qty_in_stock AS qty_in_stock
FROM public.inventory_fact AS inventory_fact/*+projs('public.inventory_fact')*/) AS subQ_1
ON (product_dimension.product_key = subQ_1.qty_in_stock))
重新创建用来执行 null-aware anti-join 的查询,其中包含以
NOT IN
或 !=ALL
运算符开头的子查询。
输入查询
SELECT product_description FROM product_dimension
WHERE product_dimension.product_key NOT IN (SELECT qty_in_stock from inventory_fact);
查询计划
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT product_description FROM product_dimension WHERE product_dimension.product_key not IN (SELECT qty_in_sto
ck from inventory_fact);
Access Path:
+-JOIN HASH [Anti][NotInAnti] [Cost: 7K, Rows: 30K] (PATH ID: 1) Inner (BROADCAST)
| Join Cond: (product_dimension.product_key = VAL(2))
| Materialize at Output: product_dimension.product_description
| Execute on: Query Initiator
| +-- Outer -> STORAGE ACCESS for product_dimension [Cost: 152, Rows: 60K] (PATH ID: 2)
| | Projection: public.product_dimension_DBD_2_rep_VMartDesign
| | Materialize: product_dimension.product_key
| | Execute on: Query Initiator
| +-- Inner -> SELECT [Cost: 248, Rows: 300K] (PATH ID: 3)
| | Execute on: All Nodes
| | +---> STORAGE ACCESS for inventory_fact [Cost: 248, Rows: 300K] (PATH ID: 4)
| | | Projection: public.inventory_fact_DBD_9_seg_VMartDesign_b0
| | | Materialize: inventory_fact.qty_in_stock
| | | Execute on: All Nodes
优化器生成的带注释查询
SELECT /*+ syntactic_join */ product_dimension.product_description AS product_description
FROM (public.product_dimension AS product_dimension/*+projs('public.product_dimension')*/
NULLAWARE ANTI JOIN /*+Distrib(L,B),JType(H)*/ (SELECT inventory_fact.qty_in_stock AS qty_in_stock
FROM public.inventory_fact AS inventory_fact/*+projs('public.inventory_fact')*/) AS subQ_1
ON (product_dimension.product_key = subQ_1.qty_in_stock))
重新创建用来执行 semi-all join 的查询,其中包含以
ALL
运算符开头的子查询。
输入查询
SELECT product_key, product_description FROM product_dimension
WHERE product_dimension.product_key > ALL (SELECT product_key from inventory_fact);
查询计划
QUERY PLAN DESCRIPTION:
------------------------------
explain SELECT product_key, product_description FROM product_dimension WHERE product_dimension.product_key > ALL (SELECT product_key from inventory_fact);
Access Path:
+-JOIN HASH [Semi][All] [Cost: 7M, Rows: 30K] (PATH ID: 1) Outer (FILTER) Inner (BROADCAST)
| Join Filter: (product_dimension.product_key > VAL(2))
| Materialize at Output: product_dimension.product_description
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for product_dimension [Cost: 152, Rows: 60K] (PATH ID: 2)
| | Projection: public.product_dimension
| | Materialize: product_dimension.product_key
| | Execute on: All Nodes
| +-- Inner -> SELECT [Cost: 248, Rows: 300K] (PATH ID: 3)
| | Execute on: All Nodes
| | +---> STORAGE ACCESS for inventory_fact [Cost: 248, Rows: 300K] (PATH ID: 4)
| | | Projection: public.inventory_fact_b0
| | | Materialize: inventory_fact.product_key
| | | Execute on: All Nodes
优化器生成的带注释查询
SELECT /*+ syntactic_join */ product_dimension.product_key AS product_key, product_dimension.product_description AS product_description
FROM (public.product_dimension AS product_dimension/*+projs('public.product_dimension')*/
SEMIALL JOIN /*+Distrib(F,B),JType(H)*/ (SELECT inventory_fact.product_key AS product_key FROM public.inventory_fact AS inventory_fact/*+projs('public.inventory_fact')*/) AS subQ_1
ON (product_dimension.product_key > subQ_1.product_key))
重新创建用来执行 anti-join 的查询,其中包含以
NOT EXISTS
运算符开头的子查询。
输入查询
SELECT product_key, product_description FROM product_dimension
WHERE NOT EXISTS (SELECT inventory_fact.product_key FROM inventory_fact
WHERE inventory_fact.product_key=product_dimension.product_key);
查询计划
QUERY PLAN DESCRIPTION:
------------------------------
explain SELECT product_key, product_description FROM product_dimension WHERE NOT EXISTS (SELECT inventory_fact.product_
key FROM inventory_fact WHERE inventory_fact.product_key=product_dimension.product_key);
Access Path:
+-JOIN HASH [Anti] [Cost: 703, Rows: 30K] (PATH ID: 1) Outer (FILTER)
| Join Cond: (VAL(1) = product_dimension.product_key)
| Materialize at Output: product_dimension.product_description
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for product_dimension [Cost: 152, Rows: 60K] (PATH ID: 2)
| | Projection: public.product_dimension_DBD_2_rep_VMartDesign
| | Materialize: product_dimension.product_key
| | Execute on: All Nodes
| +-- Inner -> SELECT [Cost: 248, Rows: 300K] (PATH ID: 3)
| | Execute on: All Nodes
| | +---> STORAGE ACCESS for inventory_fact [Cost: 248, Rows: 300K] (PATH ID: 4)
| | | Projection: public.inventory_fact_DBD_9_seg_VMartDesign_b0
| | | Materialize: inventory_fact.product_key
| | | Execute on: All Nodes
优化器生成的带注释查询
SELECT /*+ syntactic_join */ product_dimension.product_key AS product_key, product_dimension.product_description AS product_description
FROM (public.product_dimension AS product_dimension/*+projs('public.product_dimension')*/
ANTI JOIN /*+Distrib(F,L),JType(H)*/ (SELECT inventory_fact.product_key AS "inventory_fact.product_key"
FROM public.inventory_fact AS inventory_fact/*+projs('public.inventory_fact')*/) AS subQ_1
ON (subQ_1."inventory_fact.product_key" = product_dimension.product_key))
优化器使用一组关键字来表达复杂交叉联接和半联接。所有复杂联接都由关键字 COMPLEX
指示,该关键字插入在关键字 JOIN
之前,例如 CROSS COMPLEX JOIN
。复杂半联接的语义有一个额外的要求,在下面有详细说明。
Vertica 使用关键字短语 CROSS COMPLEX JOIN
来描述所有的复杂交叉联接。例如:
输入查询
SELECT
(SELECT max(sales_quantity) FROM store.store_sales_fact) *
(SELECT max(sales_quantity) FROM online_sales.online_sales_fact);
查询计划
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT
(SELECT max(sales_quantity) FROM store.store_sales_fact) *
(SELECT max(sales_quantity) FROM online_sales.online_sales_fact);
Access Path:
+-JOIN (CROSS JOIN) [Cost: 4K, Rows: 1 (NO STATISTICS)] (PATH ID: 1)
| Execute on: Query Initiator
| +-- Outer -> JOIN (CROSS JOIN) [Cost: 2K, Rows: 1 (NO STATISTICS)] (PATH ID: 2)
| | Execute on: Query Initiator
| | +-- Outer -> STORAGE ACCESS for dual [Cost: 10, Rows: 1] (PATH ID: 3)
| | | Projection: v_catalog.dual_p
| | | Materialize: dual.dummy
| | | Execute on: Query Initiator
| | +-- Inner -> SELECT [Cost: 2K, Rows: 1 (NO STATISTICS)] (PATH ID: 4)
| | | Execute on: Query Initiator
| | | +---> GROUPBY NOTHING [Cost: 2K, Rows: 1 (NO STATISTICS)] (PATH ID: 5)
| | | | Aggregates: max(store_sales_fact.sales_quantity)
| | | | Execute on: All Nodes
| | | | +---> STORAGE ACCESS for store_sales_fact [Cost: 1K, Rows: 5M (NO STATISTICS)] (PATH ID: 6)
| | | | | Projection: store.store_sales_fact_super
| | | | | Materialize: store_sales_fact.sales_quantity
| | | | | Execute on: All Nodes
| +-- Inner -> SELECT [Cost: 2K, Rows: 1 (NO STATISTICS)] (PATH ID: 7)
| | Execute on: Query Initiator
| | +---> GROUPBY NOTHING [Cost: 2K, Rows: 1 (NO STATISTICS)] (PATH ID: 8)
| | | Aggregates: max(online_sales_fact.sales_quantity)
| | | Execute on: All Nodes
| | | +---> STORAGE ACCESS for online_sales_fact [Cost: 1K, Rows: 5M (NO STATISTICS)] (PATH ID: 9)
| | | | Projection: online_sales.online_sales_fact_super
| | | | Materialize: online_sales_fact.sales_quantity
| | | | Execute on: All Nodes
优化器生成的带注释查询
以下带注释的查询返回与前面显示的输入查询相同的结果。与优化器生成的所有带注释查询一样,您可以直接在 vsql 中执行此查询(无论是按照编写还是修改后):
SELECT /*+syntactic_join,verbatim*/ (subQ_1.max * subQ_2.max) AS "?column?"
FROM ((v_catalog.dual AS dual CROSS COMPLEX JOIN /*+Distrib(L,L),JType(H)*/
(SELECT max(store_sales_fact.sales_quantity) AS max
FROM store.store_sales_fact AS store_sales_fact/*+projs('store.store_sales_fact')*/) AS subQ_1 )
CROSS COMPLEX JOIN /*+Distrib(L,L),JType(H)*/ (SELECT max(online_sales_fact.sales_quantity) AS max
FROM online_sales.online_sales_fact AS online_sales_fact/*+projs('online_sales.online_sales_fact')*/) AS subQ_2 )
复杂半联接由以下关键字之一表达:
SEMI COMPLEX JOIN
NULLAWARE ANTI COMPLEX JOIN
SEMIALL COMPLEX JOIN
ANTI COMPLEX JOIN
额外要求适用于所有复杂半联接:每个子查询的 SELECT
列表都以调用 Vertica 元函数 complex_join_marker()
的虚拟列(标记为 false
)结尾。当子查询处理每一行时,complex_join_marker()
返回 true
或 false
以指示结果集中包含或排除该行。结果集与这个标志一起返回到外部查询,外部查询可以使用来自这个和其他子查询中的标志来筛选它自己的结果集。
例如,查询优化器将以下输入查询重写为 NULLAWARE ANTI COMPLEX JOIN
。该联接返回子查询中其 complex_join_marker()
标志设置为适当布尔值的所有行。
输入查询
SELECT product_dimension.product_description FROM public.product_dimension
WHERE (NOT (product_dimension.product_key NOT IN (SELECT inventory_fact.qty_in_stock FROM public.inventory_fact)));
查询计划
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT product_dimension.product_description FROM public.product_dimension
WHERE (NOT (product_dimension.product_key NOT IN (SELECT inventory_fact.qty_in_stock FROM public.inventory_fact)));
Access Path:
+-JOIN HASH [Anti][NotInAnti] [Cost: 3K, Rows: 30K] (PATH ID: 1) Inner (BROADCAST)
| Join Cond: (product_dimension.product_key = VAL(2))
| Materialize at Output: product_dimension.product_description
| Filter: (NOT VAL(2))
| Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for product_dimension [Cost: 56, Rows: 60K] (PATH ID: 2)
| | Projection: public.product_dimension_super
| | Materialize: product_dimension.product_key
| | Execute on: All Nodes
| +-- Inner -> SELECT [Cost: 248, Rows: 300K] (PATH ID: 3)
| | Execute on: All Nodes
| | +---> STORAGE ACCESS for inventory_fact [Cost: 248, Rows: 300K] (PATH ID: 4)
| | | Projection: public.inventory_fact_super
| | | Materialize: inventory_fact.qty_in_stock
| | | Execute on: All Nodes
优化器生成的带注释查询
以下带注释的查询返回与前面显示的输入查询相同的结果。与优化器生成的所有带注释查询一样,您可以直接在 vsql 中执行此查询(无论是按照编写还是修改后)。例如,您可以通过修改外部查询的谓词对 subQ_1."false"
标志的求值方式来控制外部查询的输出。
SELECT /*+syntactic_join,verbatim*/ product_dimension.product_description AS product_description
FROM (public.product_dimension AS product_dimension/*+projs('public.product_dimension')*/
NULLAWARE ANTI COMPLEX JOIN /*+Distrib(L,B),JType(H)*/
(SELECT inventory_fact.qty_in_stock AS qty_in_stock, complex_join_marker() AS "false"
FROM public.inventory_fact AS inventory_fact/*+projs('public.inventory_fact')*/) AS subQ_1
ON (product_dimension.product_key = subQ_1.qty_in_stock)) WHERE (NOT subQ_1."false")
定向查询支持各种查询,但也有一些例外。Vertica 通过优化器生成的警告处理所有例外。以下部分将这些限制分为几类。
存在以下限制:
优化器生成的定向查询不支持引用系统表或数据收集器表的查询。例外情况:V_CATALOG.DUAL 的显式和隐式引用。
优化器生成的定向查询不支持所含表内具有访问策略的查询。
定向查询不支持不含投影的表。
不支持包括以下函数的查询:
Vertica 元函数
GROUPING_ID (无实参)
不支持包括以下内容的查询:
WITH
子句(启用实体化时)
包括日期/时间字面量的查询,这些字面量引用当前时间,如 NOW
或 YESTERDAY
不支持包括以下数据类型的查询:
多个用户会话中的 事务并发访问相同数据时,会话范围的隔离级别决定了每个事务可访问哪些数据。
事务将保留其隔离级别直至其完成,即使在事务处理期间会话的隔离级别发生更改也是如此。Vertica 内部进程(例如 Tuple Mover 和 刷新操作)以及 DDL 操作始终以 SERIALIZABLE 隔离级别运行以确保一致性。
Vertica 查询解析器支持标准 ANSI SQL-92 隔离级别,如下所示:
READ COMMITTED(默认值)
READ UNCOMMITTED:自动解释为 READ COMMITTED。
REPEATABLE READ:自动解释为 SERIALIZABLE
事务隔离级别 READ COMMITTED 与 SERIALIZABLE 的区别如下:
您可以为数据库和各个事务设置单独的隔离级别。
Vertica 支持具有标准 ACID 属性的传统 SQL 事务:
ANSI SQL 92 风格隐式事务。您无需运行 BEGIN 或 START TRANSACTION 命令。
没有重做/撤消日志或两阶段提交。
COPY 命令会自动提交自身及所有当前事务(加载临时表时除外)。一般来说,在使用 COPY 之前提交或回退当前事务是一种很好的做法。此步骤对 DDL 语句来说是可选的,因为此类语句会自动提交。
事务回退通过放弃该事务所做的更改将数据库还原到先前的状态。语句级回退仅放弃已还原的语句所启动的更改。事务级回退放弃该事务所做的所有更改。
使用 ROLLBACK
语句,您可以明确回退到事务内的给定保存点,或者放弃整个事务。在以下两种情况下,Vertica 还可以启动自动回退:
单个语句返回 ERROR
消息。这种情况下,Vertica 会回退该语句。
DDL 错误、系统故障、死锁和资源约束返回 ROLLBACK
消息。这种情况下,Vertica 会回退整个事务。
明确回退和自动回退始终会释放该事务持有的任何锁定。
保存点是事务内的一个特殊标记,允许在回退该保存点之后执行命令。事务恢复到该保存点之前的状态。
Vertica 支持以下两种类型的保存点:
隐式保存点是在事务内成功执行每个命令之后自动建立的。此保存点用于回退下一个语句(如果该语句返回一个错误)。事务会保留一个隐式保存点,该保存点会通过每个成功的语句向前滚动。隐式保存点只能用于 Vertica,不能被直接引用。
给定保存点是您通过 SAVEPOINT 语句在事务内设置的带标签的标记。随后可以通过 RELEASE SAVEPOINT(销毁保存点)和 ROLLBACK TO SAVEPOINT(回退在该保存点之后执行的所有操作)在同一事务中引用某个给定保存点。给定保存点在嵌套事务中尤为有用:可根据需要整体回退以某个保存点开始的嵌套事务。
使用隔离级别 READ COMMITTED
时,一个 SELECT
查询会获取在事务开始时已提交的数据的备份。在当前事务期间内执行的后续查询也会查看已在同一个事务内执行的未提交更新的结果。
使用 DML 语句时,您的查询会获取写入锁,以防止其他 READ COMMITTED
事务修改相同的数据。但是,需要注意的是,SELECT
语句不会获取锁,因此并发事务可以获得对相同所选内容的读取和写入访问权限。
READ COMMITTED
是默认隔离级别。对大多数查询来说,此隔离级别可在数据库一致性和并发性之间取得平衡。但是,此隔离级别允许在一个事务正在访问数据期间,另一个事务仍可更改这些数据。此类更改会产生
不可重复的读取和
虚拟读取。您的应用程序可能有着复杂的查询和更新要求,需要获得更一致的数据库视图。如果是这样,请改用 SERIALIZABLE 隔离。
下图说明了 READ COMMITTED
隔离如何控制并发事务读取和写入相同数据:
READ COMMITTED
隔离会在事务结束之前保持独占写入锁,如下图所示:
SERIALIZABLE
是最严格的 SQL 事务隔离级别。尽管此隔离级别允许事务并发运行,但在它的影响下,事务实际上会按序列顺序运行。事务会为读取和写入操作获取锁。因此,一个事务内的连续 SELECT
命令始终会产生相同的结果。由于 SERIALIZABLE
隔离提供了一致的数据视图,它对要求复杂查询和更新的应用程序十分有用。但是,可序列化隔离会降低并发性。例如,它会在批量加载期间禁止执行查询操作。
SERIALIZABLE
隔离会建立以下锁:
表级别读取锁:Vertica 会在所选表上获取表级别读取锁,并在事务结束时释放这些锁。此行为可防止在一个事务读取行期间,另一个事务修改行。
表级别写入锁:Vertica 会在更新时获取表级别写入锁,并在事务结束时释放这些锁。此行为可防止在一个事务对行的更改尚未提交时,另一个事务读取这些行更改。
在事务开始时,SELECT
语句会获取所选内容中已提交数据的备份。事务也会查看在事务内运行的更新操作的结果,然后再提交这些更新。
下图显示了都具有 SERIALIZABLE
隔离级别的并发事务如何处理锁定:
使用 SERIALIZABLE
的应用程序必须准备好在序列化失败后重试事务。此类失败通常是由于死锁导致的。死锁发生时,等待锁的所有事务都会在 5 分钟后自动超时。下图显示了死锁的发生方式以及 Vertica 如何处理死锁:
SERIALIZABLE
隔离不会应用于临时表。这些表不需要锁,因为它们被隔离在各自的事务范围内。
当多个用户并发访问同一数据库信息时,数据处理可能会导致冲突并破坏数据完整性。发生这些冲突的原因是,某些事务会在事务完成前阻止其他操作。由于同时提交的事务应产生一致的结果,因此,Vertica 使用各种锁来维持数据并发性和一致性。Vertica 会根据对象状态,通过限制用户可对该对象执行的操作来自动控制锁定情况。
Vertica 使用对象锁和系统锁。对象锁用于对象,如表和投影。系统锁包括全局编录锁、本地编录锁和弹性群集锁。Vertica 支持全系列的标准 SQL 锁定模式,例如共享 (S) 和独占 (X)。
有关不同事务隔离级别中锁使用情况的相关信息,请参阅 READ COMMITTED 隔离和 SERIALIZABLE 隔离。
Vertica 具有不同的锁定模式,这些锁定模式决定了锁在对象上如何发挥作用。每个锁定模式都具有一种锁定兼容性和锁定强度,反映了它在同一个环境中如何与其他锁交互。
锁兼容性是指两个锁能在同一个对象上同时生效。
此一览表显示了哪些锁可在同一个对象上同时使用。
当锁定模式在标有项目符号 (•) 的单元格中相交时,它们兼容。如果请求的两个模式在空单元格相交,那么在第一个请求释放锁之前,不会向第二个模式授予锁。
此一览表显示了对象锁如何响应 INSERT 请求。
如果对象具有一个 S 锁,而您希望执行 INSERT,您的事务将请求 SI 锁。但是,如果某个对象具有 S 锁,而您希望执行要求 S 锁的操作,则不会发出锁定请求。
锁强度是指一个锁定模式与另一个锁定模式交互的能力。O 锁是最强的锁,并且与所有其他锁不兼容。相反,U 锁是最弱的锁,可以与除 O 锁之外的所有其他锁同时运行。
下图描述了锁定模式的强度:
另请参阅:
在此示例中,两个会话 A 和 B 处于活动状态,并尝试处理表 T1。在示例开始时,表 T1 具有一列 (C1),但没有任何行。
这里的步骤说明的是从会话 A 和会话 B 可执行的一系列事务:
两个会话中的事务都获取了 S 锁,以便从表 T1 进行读取。
会话 B 释放了其针对 COMMIT 语句的 S 锁定。
会话 A 可升级到 SI 锁并插入到 T1,因为会话 B 释放了其 S 锁。
执行 COMMIT 语句后,会话 A 释放了其 SI 锁。
两个会话都可以获取 S 锁,因为会话 A 释放了其 SI 锁。
会话 A 无法获取 SI 锁,因为会话 B 未释放其 S 锁。SI 锁与 S 锁不兼容。
会话 B 释放了其针对 COMMIT 语句的 S 锁定。
会话 A 现在可以升级到 SI 锁并插入 T1。
会话 B 会尝试从 T1 删除一个行,但无法获取 X 锁,因为会话 A 未释放其 SI 锁。SI 锁与 X 锁不兼容。
会话 A 继续向 T1 插入内容。
会话 A 释放其 SI 锁。
会话 B 现在可以获取 X 锁并执行删除操作。
此图说明了前述步骤:
LOCKS 和 LOCK_USAGE 系统表有助于排查在使用 Vertica 数据库锁期间遇到的问题。
此示例显示了来自 LOCKS 系统表的一行。在此表中,您可以看到哪些类型的锁正在特定对象和节点上处于活动状态。
=> SELECT node_names, object_name, lock_mode, lock_scope FROM LOCKS;
node_names | object_name | lock_mode | lock_scope
-------------------+---------------------------------+-----------+-----------
v_vmart_node0001 | Table:public.customer_dimension | X | TRANSACTION
此示例显示了来自 LOCKS_USAGE 系统表的两行。您也可使用此表查看哪些锁正在特定对象和节点上使用。
=> SELECT node_name, object_name, mode FROM LOCK_USAGE;
node_name | object_name | mode
------------------+------------------+-------
v_vmart_node0001 | Cluster Topology | S
v_vmart_node0001 | Global Catalog | X
文本搜索可用于在表中快速搜索单个 CHAR、VARCHAR、LONG VARCHAR、VARBINARY 或 LONG VARBINARY 字段的内容,以查找特定关键字。
可以对要重复查询其内容的列使用此功能。创建文本索引之后,对源表的 DML 操作会变得稍慢。这种性能变化是对文本索引和源表进行同步所导致的。每次对源表执行操作时,文本索引都会在后台进行更新。对源表的定期查询不受影响。
文本索引包含源表文本字段以及在创建索引期间包含的任何其他额外列中的所有单词。不会为额外列编制索引,仅将其值传递到文本索引中。文本索引就像任何其他 Vertica 表一样,除了它在内部链接至源表。
首先对计划搜索的表创建文本索引。然后,在对表创建索引后,针对文本索引运行查询,以查找特定关键字。此查询会为每个关键字实例返回一个 doc_id。与直接在源表中查询其文本字段的内容相比,在查询文本索引之后将文本索引联接回源表的性能会有显著提高。
在以下示例中,可以使用名为 t_log 的源表执行文本搜索。该源表包含两个列:
一个列包含表的主键
另一个列包含日志文件信息
必须将投影与源表相关联。使用按主键排序并按哈希 (id) 分段或未分段的投影。可以在源表上定义此投影以及任何其他现有投影。
对希望执行文本搜索的表创建文本索引。
=> CREATE TEXT INDEX text_index ON t_log (id, text);
文本索引包含两列:
doc_id 使用源表中的唯一标识符。
token 使用来自源表的指定列的文本字符串填充。字词列通过对文本列中的单词进行分词和词干提取获得。
如果您的表已分区,您的文本索引还将包含名为 partition 的第三个列。
=> SELECT * FROM text_index;
token | doc_id | partition
------------------------+--------+-----------
<info> | 6 | 2014
<warning> | 2 | 2014
<warning> | 3 | 2014
<warning> | 4 | 2014
<warning> | 5 | 2014
database | 6 | 2014
execute: | 6 | 2014
object | 4 | 2014
object | 5 | 2014
[catalog] | 4 | 2014
[catalog] | 5 | 2014
您一次只能在源表上创建一个文本索引。在未来,您将不必在每次更新或更改源表时重新创建文本索引。
通过在源表上运行的任何操作,您的文本索引将与源表的内容保持同步。这些操作包括但不限于:
COPY
INSERT
UPDATE
DELETE
DROP PARTITION
MOVE_PARTITIONS_TO_TABLE
当您在编制了索引的源表中移动或交换分区时,验证目标表是否已存在并且以相同的方式编制了索引。
在以下示例中,您将对 Flex 表创建文本索引。本例假定您已创建一个名为 mountains 的 Flex 表。要创建本示例中使用的 Flex 表,请参阅入门中的“使用 Flex 表”。
若要对 Flex 表创建文本索引,首先需要向 Flex 表添加主键约束。
=> ALTER TABLE mountains ADD PRIMARY KEY (__identity__);
对希望执行文本搜索的表创建文本索引。使用 FlexTokenizer 对 __raw__column 进行分词,并将数据类型指定为 LONG VARBINARY。由于 __raw__column 的数据类型不同于默认的 StringTokenizer,因此务必要使用 FlexTokenizer 来对 Flex 表创建文本索引。
=> CREATE TEXT INDEX flex_text_index ON mountains(__identity__, __raw__) TOKENIZER public.FlexTokenizer(long varbinary);
文本索引包含两列:
doc_id 使用源表中的唯一标识符。
token 使用来自源表的指定列的文本字符串填充。字词列通过对文本列中的单词进行分词和词干提取获得。
如果您的表已分区,您的文本索引还将包含名为 partition 的第三个列。
=> SELECT * FROM flex_text_index;
token | doc_id
-------------+--------
50.6 | 5
Mt | 5
Washington | 5
mountain | 5
12.2 | 3
15.4 | 2
17000 | 3
29029 | 2
Denali | 3
Helen | 2
Mt | 2
St | 2
mountain | 3
volcano | 2
29029 | 1
34.1 | 1
Everest | 1
mountain | 1
14000 | 4
Kilimanjaro | 4
mountain | 4
(21 rows)
您一次只能在源表上创建一个文本索引。在未来,您将不必在每次更新或更改源表时重新创建文本索引。
通过在源表上运行的任何操作,您的文本索引将与源表的内容保持同步。这些操作包括但不限于:
COPY
INSERT
UPDATE
DELETE
DROP PARTITION
MOVE_PARTITIONS_TO_TABLE
当您在编制了索引的源表中移动或交换分区时,验证目标表是否已存在并且以相同的方式编制了索引。
创建文本索引后,请编写一个查询,并对该索引运行查询,从而搜索特定关键字。
在下面的示例中,可以使用 WHERE 子句搜索文本索引中的关键字 <WARNING>。WHERE 子句应使用创建文本索引时使用的词干分析器。使用 STEMMER 关键字时,它会分析关键字词干,以便与文本索引中的关键字匹配。如果未使用 STEMMER 关键字,则默认词干分析器为 v_txtindex.StemmerCaseInsensitive。如果使用了 STEMMER NONE,则可以省略 WHERE 子句中的 STEMMER 关键字。
=> SELECT * FROM text_index WHERE token = v_txtindex.StemmerCaseInsensitive('<WARNING>');
token | doc_id
-----------+--------
<warning> | 2
<warning> | 3
<warning> | 4
<warning> | 5
(4 rows)
接下来,编写查询,显示与在文本索引中搜索的关键字匹配的所有源表内容。
=> SELECT * FROM t_log WHERE id IN (SELECT doc_id FROM text_index WHERE token = v_txtindex.StemmerCaseInsensitive('<WARNING>'));
id | date | text
---+------------+-----------------------------------------------------------------------------------------------
4 | 2014-06-04 | 11:00:49.568 unknown:0x7f9207607700 [Catalog] <WARNING> validateDependencies: Object 45035968
5 | 2014-06-04 | 11:00:49.568 unknown:0x7f9207607700 [Catalog] <WARNING> validateDependencies: Object 45030
2 | 2013-06-04 | 11:00:49.568 unknown:0x7f9207607700 [Catalog] <WARNING> validateDependencies: Object 4503
3 | 2013-06-04 | 11:00:49.568 unknown:0x7f9207607700 [Catalog] <WARNING> validateDependencies: Object 45066
(4 rows)
使用 doc_id 可以查找源表中关键字的确切位置。doc_id 会与源表的唯一标识符匹配。此匹配有助于迅速找出表中的关键字实例。
文本索引会经过优化,以便根据词干分析器与所有单词实例匹配。默认情况下,不区分大小写的词干分析器会应用于未指定词干分析器的所有文本索引。因此,如果您计划针对文本索引编写的查询区分大小写,则 Vertica 建议使用区分大小写的词干分析器来构建文本索引。
以下示例显示了执行文本搜索时,可用来在区分大小写和不区分大小写的情况下与单词匹配的查询。
此查询会在不区分大小写的文本索引中查找不区分大小写的记录:
=> SELECT * FROM t_log WHERE id IN (SELECT doc_id FROM text_index WHERE token = v_txtindex.StemmerCaseInsensitive('warning'));
此查询会在区分大小写的文本索引中查找区分大小写的记录:
=> SELECT * FROM t_log_case_sensitive WHERE id IN (SELECT doc_id FROM text_index WHERE token = v_txtindex.StemmerCaseSensitive('Warning'));
使用文本索引时也可以执行更详细的查询,找出多个关键字或忽略含有其他关键字的结果。以下示例显示了一个更详细的查询,您可以在执行文本搜索时使用该查询。
在此示例中,t_log 是源表,text_index 是文本索引。查询会查找含有以下任一内容的记录:
同时含有 '<WARNING>’ 和 'validate' 单词的记录
只含有 '[Log]' 单词且不含有 'validateDependencies' 的记录
SELECT * FROM t_log where (
id IN (SELECT doc_id FROM text_index WHERE token = v_txtindex.StemmerCaseSensitive('<WARNING>'))
AND ( id IN (SELECT doc_id FROM text_index WHERE token = v_txtindex.StemmerCaseSensitive('validate')
OR id IN (SELECT doc_id FROM text_index WHERE token = v_txtindex.StemmerCaseSensitive('[Log]')))
AND NOT (id IN (SELECT doc_id FROM text_index WHERE token = v_txtindex.StemmerCaseSensitive('validateDependencies'))));
此查询会返回以下结果:
id | date | text
----+------------+------------------------------------------------------------------------------------------------
11 | 2014-05-04 | 11:00:49.568 unknown:0x7f9207607702 [Log] <WARNING> validate: Object 4503 via fld num_all_roles
13 | 2014-05-04 | 11:00:49.568 unknown:0x7f9207607706 [Log] <WARNING> validate: Object 45035 refers to root_i3
14 | 2014-05-04 | 11:00:49.568 unknown:0x7f9207607708 [Log] <WARNING> validate: Object 4503 refers to int_2
17 | 2014-05-04 | 11:00:49.568 unknown:0x7f9207607700 [Txn] <WARNING> Begin validate Txn: fff0ed17 catalog editor
(4 rows)
删除文本索引将从数据库中删除指定的文本索引。
在以下情况下可以删除文本索引:
不再经常查询。
管理任务需要在源表上执行,且需要删除文本索引。
删除文本索引不会删除与该文本索引关联的源表。但如果删除与文本索引关联的源表,该文本索引也会被删除。Vertica 会将文本索引视为依赖对象。
以下示例说明了如何删除名为 text_index 的文本索引:
=> DROP TEXT INDEX text_index;
DROP INDEX
Vertica 提供默认词干分析器和分词器。您也可以创建自己的自定义词干分析器和分词器。以下主题介绍了默认词干分析器和分词器,以及在 Vertica 中创建自定义词干分析器和分词器的要求。
Vertica 词干分析器采用 Porter 词干抽取算法来查找从同一基词/根词派生的词语。例如,在文本索引中搜索关键字 database 时,您可能还希望获得包含 databases 一词的结果。
为了实现这种类型的匹配,当使用任何 v_txtindex 词干分析器时,Vertica 都会以已抽取词干的形式存储词语。
Vertica Analytics Platform 提供了以下词干分析器:
以下示例显示了创建文本索引时如何使用词干分析器。
使用 StemmerCaseInsensitive 词干分析器创建文本索引:
=> CREATE TEXT INDEX idx_100 ON top_100 (id, feedback) STEMMER v_txtindex.StemmerCaseInsensitive(long varchar)
TOKENIZER v_txtindex.StringTokenizer(long varchar);
使用 StemmerCaseSensitive 词干分析器创建文本索引:
=> CREATE TEXT INDEX idx_unstruc ON unstruc_data (__identity__, __raw__) STEMMER v_txtindex.StemmerCaseSensitive(long varchar)
TOKENIZER public.FlexTokenizer(long varbinary);
在不使用词干分析器的情况下创建文本索引:
=> CREATE TEXT INDEX idx_logs FROM sys_logs ON (id, message) STEMMER NONE TOKENIZER v_txtindex.StringTokenizer(long varchar);
分词器执行以下操作:
接收字符流。
将该流分解为通常对应于各个单词的各个标记。
返回标记流。
Vertica Analytics Platform 提供以下预配置的分词器:
Vertica 还提供了以下未预配置的分词器:
以下示例显示了在创建文本索引时如何使用预配置的分词器。
使用 StringTokenizer 从 top_100 创建索引:
=> CREATE TEXT INDEX idx_100 FROM top_100 on (id, feedback)
TOKENIZER v_txtindex.StringTokenizer(long varchar)
STEMMER v_txtindex.StemmerCaseInsensitive(long varchar);
使用 FlexTokenizer 从非结构化数据创建索引:
=> CREATE TEXT INDEX idx_unstruc FROM unstruc_data on (__identity__, __raw__)
TOKENIZER public.FlexTokenizer(long varbinary)
STEMMER v_txtindex.StemmerCaseSensitive(long varchar);
使用 StringTokenizerDelim 在指定的分隔符处拆分字符串:
=> CREATE TABLE string_table (word VARCHAR(100), delim VARCHAR);
CREATE TABLE
=> COPY string_table FROM STDIN DELIMITER ',';
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>>
>> SingleWord,dd
>> Break On Spaces,' '
>> Break:On:Colons,:
>> \.
=> SELECT * FROM string_table;
word | delim
-----------------+-------
SingleWord | dd
Break On Spaces |
Break:On:Colons | :
(3 rows)
=> SELECT v_txtindex.StringTokenizerDelim(word,delim) OVER () FROM string_table;
words
-----------------
Break
On
Colons
SingleWor
Break
On
Spaces
(7 rows)
=> SELECT v_txtindex.StringTokenizerDelim(word,delim) OVER (PARTITION BY word), word as input FROM string_table;
words | input
-----------------+-----------------
Break | Break:On:Colons
On | Break:On:Colons
Colons | Break:On:Colons
SingleWor | SingleWord
Break | Break On Spaces
On | Break On Spaces
Spaces | Break On Spaces
(7 rows)
返回可包含二级分隔符的令牌。在令牌由空格或多种标点分隔的情况下可以使用此分词器。在通过添加二级分隔符定义分隔符方面,高级日志分词器能比基本日志分词器提供更大的粒度。这一方法通常适用于分析日志文件。
以下示例显示了如何在没有词干分析器的情况下使用高级日志分词器从表 foo 创建文本索引。
=> CREATE TABLE foo (id INT PRIMARY KEY NOT NULL,text VARCHAR(250));
=> COPY foo FROM STDIN;
End with a backslash and a period on a line by itself.
>> 1|2014-05-10 00:00:05.700433 %ASA-6-302013: Built outbound TCP connection 9986454 for outside:101.123.123.111/443 (101.123.123.111/443)
>> \.
=> CREATE PROJECTION foo_projection AS SELECT * FROM foo ORDER BY id
SEGMENTED BY HASH(id) ALL NODES KSAFE;
=> CREATE TEXT INDEX indexfoo_AdvancedLogTokenizer ON foo (id, text)
TOKENIZER v_txtindex.AdvancedLogTokenizer(LONG VARCHAR) STEMMER NONE;
=> SELECT * FROM indexfoo_AdvancedLogTokenizer;
token | doc_id
-----------------------------+--------
%ASA-6-302013: | 1
00 | 1
00:00:05.700433 | 1
05 | 1
10 | 1
101 | 1
101.123.123.111/443 | 1
111 | 1
123 | 1
2014 | 1
2014-05-10 | 1
302013 | 1
443 | 1
700433 | 1
9986454 | 1
ASA | 1
Built | 1
TCP | 1
connection | 1
for | 1
outbound | 1
outside | 1
outside:101.123.123.111/443 | 1
(23 rows)
返回不包含指定二级分隔符的令牌。在令牌由空格或多种标点分隔的情况下可以使用此分词器。这一方法通常适用于分析日志文件。
以下示例显示了如何在没有词干分析器的情况下使用基本日志分词器从表 foo 创建文本索引。
=> CREATE TABLE foo (id INT PRIMARY KEY NOT NULL,text VARCHAR(250));
=> COPY foo FROM STDIN;
End with a backslash and a period on a line by itself.
>> 1|2014-05-10 00:00:05.700433 %ASA-6-302013: Built outbound TCP connection 9986454 for outside:101.123.123.111/443 (101.123.123.111/443)
>> \.
=> CREATE PROJECTION foo_projection AS SELECT * FROM foo ORDER BY id
SEGMENTED BY HASH(id) ALL NODES KSAFE;
=> CREATE TEXT INDEX indexfoo_BasicLogTokenizer ON foo (id, text)
TOKENIZER v_txtindex.BasicLogTokenizer(LONG VARCHAR) STEMMER NONE;
=> SELECT * FROM indexfoo_BasicLogTokenizer;
token | doc_id
-----------------------------+--------
%ASA-6-302013: | 1
00:00:05.700433 | 1
101.123.123.111/443 | 1
2014-05-10 | 1
9986454 | 1
Built | 1
TCP | 1
connection | 1
for | 1
outbound | 1
outside:101.123.123.111/443 | 1
(11 rows)
仅返回由空格所包围的令牌。如果您只希望用空格字符来分隔源文档中的令牌,可以使用此分词器。使用这种方法时,您仍然能够设置非索引字和令牌长度限制。
以下示例显示了如何在没有词干分析器的情况下使用空格日志分词器从表 foo 创建文本索引。
=> CREATE TABLE foo (id INT PRIMARY KEY NOT NULL,text VARCHAR(250));
=> COPY foo FROM STDIN;
End with a backslash and a period on a line by itself.
>> 1|2014-05-10 00:00:05.700433 %ASA-6-302013: Built outbound TCP connection 998 6454 for outside:101.123.123.111/443 (101.123.123.111/443)
>> \.
=> CREATE PROJECTION foo_projection AS SELECT * FROM foo ORDER BY id
SEGMENTED BY HASH(id) ALL NODES KSAFE;
=> CREATE TEXT INDEX indexfoo_WhitespaceLogTokenizer ON foo (id, text)
TOKENIZER v_txtindex.WhitespaceLogTokenizer(LONG VARCHAR) STEMMER NONE;
=> SELECT * FROM indexfoo_WhitespaceLogTokenizer;
token | doc_id
-----------------------------+--------
%ASA-6-302013: | 1
(101.123.123.111/443) | 1
00:00:05.700433 | 1
2014-05-10 | 1
6454 | 1
998 | 1
Built | 1
TCP | 1
connection | 1
for | 1
outbound | 1
outside:101.123.123.111/443 | 1
(12 rows)
支持多种语言。可以使用此分词器识别非英语语言中的单词边界,包括未用空格分隔的亚洲语言。
ICU 分词器未预先配置。可以通过首先创建用户定义的转换函数 (UDTF) 来配置分词器。然后设置参数 locale 来标识分词器的语言。
以下示例步骤显示了如何将 ICU 分词器配置为简体中文,然后从包含中文字符的表 foo 中创建文本索引。
有关如何配置分词器的详细信息,请参阅配置分词器。
使用 UDTF 创建分词器。将示例分词器命名为 ICUChineseTokenizer。
VMart=> CREATE OR REPLACE TRANSFORM FUNCTION v_txtindex.ICUChineseTokenizer AS LANGUAGE 'C++' NAME 'ICUTokenizerFactory' LIBRARY v_txtindex.logSearchLib NOT FENCED;
CREATE TRANSFORM FUNCTION
获取分词器的过程 ID。
VMart=> SELECT proc_oid from vs_procedures where procedure_name = 'ICUChineseTokenizer';
proc_oid
-------------------
45035996280452894
(1 row)
将参数 locale 设置为简体中文。使用过程 ID 标识分词器。
VMart=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('locale','zh_CN' using parameters proc_oid='45035996280452894');
SET_TOKENIZER_PARAMETER
-------------------------
t
(1 row)
锁定分词器。
VMart=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('used','true' using parameters proc_oid='45035996273762696');
SET_TOKENIZER_PARAMETER
-------------------------
t
(1 row)
创建示例表 foo,其中包含要编制索引的简体中文文本。
VMart=> CREATE TABLE foo(doc_id integer primary key not null,text varchar(250));
CREATE TABLE
VMart=> INSERT INTO foo values(1, u&'\4E2D\534E\4EBA\6C11\5171\548C\56FD');
OUTPUT
--------
1
对表 foo 创建索引 index_example。该示例在没有词干分析器的情况下创建索引;Vertica 词干分析器仅适用于英语文本。对非英语文本使用英语词干分析器可能会导致分词不正确。
VMart=> CREATE TEXT INDEX index_example ON foo (doc_id, text) TOKENIZER v_txtindex.ICUChineseTokenizer(long varchar) stemmer none;
CREATE INDEX
查看新索引。
VMart=> SELECT * FROM index_example ORDER BY token,doc_id;
token | doc_id
--------+--------
中华 | 1
人民 | 1
共和国 | 1
(3 rows)
可以通过使用 v_txtindex.AdvTxtSearchLib
库中的两个基本用户定义的转换函数 (UDTF) 之一创建 UDTF,来配置分词器。库中包含两个基本分词器:一个用于 Log Words,一个用于 Ngrams。可以配置每个具有或不具有位置相关性的基本函数。
可以在多个不同的分词器基本配置中进行选择:
创建没有位置相关性的 logWord 分词器:
=> CREATE TRANSFORM FUNCTION v_txtindex.fooTokenizer AS LANGUAGE 'C++' NAME 'logWordITokenizerFactory' LIBRARY v_txtindex.logSearchLib NOT FENCED;
创建分词器之后,Vertica 将名称和 proc_oid 写入系统表 vs_procedures 中。必须检索分词器的 proc_oid 以执行其他配置。
输入以下查询,替换您自己的分词器名称:
=> SELECT proc_oid FROM vs_procedures WHERE procedure_name = 'fooTokenizer';
使用分词器的 proc_oid 可以配置分词器。有关获取分词器 proc_oid 的详细信息,请参阅配置分词器。以下示例显示了如何配置各个分词器参数:
配置非索引字:
=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('stopwordscaseinsensitive','for,the' USING PARAMETERS proc_oid='45035996274128376');
配置主要分隔符:
=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('majorseparators', E'{}()&[]' USING PARAMETERS proc_oid='45035996274128376');
配置二级分隔符:
=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('minorseparators', '-,$' USING PARAMETERS proc_oid='45035996274128376');
配置最小长度:
=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('minlength', '1' USING PARAMETERS proc_oid='45035996274128376');
配置最大长度:
=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('maxlength', '140' USING PARAMETERS proc_oid='45035996274128376');
配置 ngramssize:
=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('ngramssize', '2' USING PARAMETERS proc_oid='45035996274128376');
配置完分词器时,请将参数 used 设置为 True
。更改此设置后,将不能再更改分词器的参数。此时即可使用分词器来创建文本索引。
配置 used 参数:
=> SELECT v_txtindex.SET_TOKENIZER_PARAMETER('used', 'True' USING PARAMETERS proc_oid='45035996274128376');
创建自定义分词器之后,您可以通过以下两种任一方式查看分词器的参数设置:
使用 GET_TOKENIZER_PARAMETER — 查看单个分词器参数设置。
使用 READ_CONFIG_FILE — 查看所有分词器参数设置。
如果您需要查看分词器的单个参数设置,可以使用 GET_TOKENIZER_PARAMETER 查看特定的分词器参数设置:
=> SELECT v_txtindex.GET_TOKENIZER_PARAMETER('majorseparators' USING PARAMETERS proc_oid='45035996274126984');
getTokenizerParameter
-----------------------
{}()&[]
(1 row)
有关详细信息,请参阅GET_TOKENIZER_PARAMETER。
如果您需要查看分词器的所有参数,可以使用 READ_CONFIG_FILE 查看分词器的所有参数设置:
=> SELECT v_txtindex.READ_CONFIG_FILE( USING PARAMETERS proc_oid='45035996274126984') OVER();
config_key | config_value
--------------------------+---------------
majorseparators | {}()&[]
maxlength | 140
minlength | 1
minorseparators | -,$
stopwordscaseinsensitive | for,the
type | 1
used | true
(7 rows)
如果 used 参数设置为 False
,则只能查看已对分词器应用的参数。
有关详细信息,请参阅READ_CONFIG_FILE。
使用 DELETE_TOKENIZER_CONFIG_FILE 函数删除分词器配置文件。此函数不会删除用户定义的转换函数 (UDTF)。而是仅删除与 UDTF 关联的配置文件。
当参数“已使用 (used)”设置为 False
时,删除分词器配置文件:
=> SELECT v_txtindex.DELETE_TOKENIZER_CONFIG_FILE(USING PARAMETERS proc_oid='45035996274127086');
删除参数“确认 (confirm)”设置为 True
的分词器配置文件。此设置将强制执行配置文件删除操作,即使参数“已使用 (used)”也设置为 True
:
=> SELECT v_txtindex.DELETE_TOKENIZER_CONFIG_FILE(USING PARAMETERS proc_oid='45035996274126984', confirm='true');
有关详细信息,请参阅DELETE_TOKENIZER_CONFIG_FILE。
有时,您需要的特定分词或词干抽取行为可能与 Vertica 所提供的不同。在这种情况下,您可以实施自己的自定义“用户定义的扩展 (UDx)”,以取代词干分析器或分词器。有关构建自定义 UDx 的详细信息,请参阅开发用户定义的扩展 (UDx)。
在 Vertica 中实现自定义词干分析器或分词器之前,请验证 UDx 扩展是否满足以下要求。
创建自定义词干分析器时,应遵循以下要求:
必须是用户定义的标量函数 (UDSF) 或 SQL 函数
可使用 C++、Java 或 R 编写
波动性设置为稳定或不可变
支持的数据输入类型:
Varchar
Long varchar
支持的数据输出类型:
Varchar
Long varchar
若要创建自定义分词器,请遵循以下要求:
必须是用户定义的转换函数 (UDTF)
可使用 C++、Java 或 R 编写
输入类型必须与输入文本的类型相匹配
支持的数据输入类型:
Char
Varchar
Long varchar
Varbinary
Long varbinary
支持的数据输出类型:
Varchar
Long varchar
Vertica 存储位置是您指定存储数据和临时文件的文件目的地的路径。每个群集节点至少需要两个存储位置:一个用于存储数据,另一个用于存储数据库编录文件。您可以在安装和设置期间设置这些位置。(有关磁盘空间要求的信息,请参阅 准备磁盘存储位置)。
当您向数据库添加数据或执行 DML 操作时,新数据将作为 ROS 容器添加到磁盘上的存储位置。根据数据库的配置,系统中可能存在许多 ROS 容器。
您可以标记自己创建的存储位置,以便在对象存储策略中引用它们。如果对象没有与之关联的存储策略,Vertica 将使用默认存储算法将数据存储在可用存储位置。如果对象具有存储策略,Vertica 会将数据存储在对象的指定存储位置。您可以在不再需要时停用或删除存储位置。
默认情况下,Vertica 将数据存储在每个节点的唯一位置。每个位置都位于节点可以访问的文件系统目录中,而且通常位于节点的自有文件系统中。您可以为群集中的单个节点或全部节点创建本地存储位置。群集范围的存储位置是最常用的存储类型。Vertica 默认使用本地群集范围的存储位置来存储所有数据。如果要以不同方式存储数据,您必须创建其他存储位置。
您可以创建共享存储位置,其中数据存储在群集中的所有节点都可以访问的单个文件系统上。此共享文件系统通常托管在群集之外,例如托管在分布式文件系统(如 HDFS)上。当前,Vertica 仅支持 HDFS 共享存储位置。除非使用 MapR 挂载点,否则不能将 NFS 用作 Vertica 共享存储位置。有关详细信息,请参阅适用于 HDFS 的 Vertica 存储位置。
创建用于 DATA 和/或 TEMP 用途的共享存储位置时,Vertica 群集中的每个节点都会在该共享位置下创建自己的子目录。创建不同的目录可防止节点覆盖彼此的数据。
对于在 Eon 模式下运行的数据库,STORAGE_LOCATIONS 系统表会显示第三种类型的位置:公共。
您可以监控有关可用存储位置标签和当前存储策略的信息。
在 V_MONITOR.DISK_STORAGE 系统表中查询每个数据库节点上的磁盘存储信息。有关详细信息,请参阅使用系统表和 更改位置用途。V_MONITOR.DISK_STORAGE
系统表包括 CATALOG 注释,指示该位置用于存储编录文件。
以下三个系统表在 location_labels
列中包含有关存储位置标签的信息:
storage_containers
storage_locations
partitions
对 storage_containers
系统表的相关列使用类似于以下内容的查询:
VMART=> select node_name,projection_name, location_label from v_monitor.storage_containers;
node_name | projection_name | location_label
------------------+----------------------+-----------------
v_vmart_node0001 | states_p |
v_vmart_node0001 | states_p |
v_vmart_node0001 | t1_b1 |
v_vmart_node0001 | newstates_b0 | FAST3
v_vmart_node0001 | newstates_b0 | FAST3
v_vmart_node0001 | newstates_b1 | FAST3
v_vmart_node0001 | newstates_b1 | FAST3
v_vmart_node0001 | newstates_b1 | FAST3
.
.
.
对 v_catalog.storage_locations
系统表的列使用类似于以下内容的查询:
VMart=> select node_name, location_path, location_usage, location_label from storage_locations;
node_name | location_path | location_usage | location_label
------------------+-------------------------------------------+----------------+----------------
v_vmart_node0001 | /home/dbadmin/VMart/v_vmart_node0001_data | DATA,TEMP |
v_vmart_node0001 | home/dbadmin/SSD/schemas | DATA |
v_vmart_node0001 | /home/dbadmin/SSD/tables | DATA | SSD
v_vmart_node0001 | /home/dbadmin/SSD/schemas | DATA | Schema
v_vmart_node0002 | /home/dbadmin/VMart/v_vmart_node0002_data | DATA,TEMP |
v_vmart_node0002 | /home/dbadmin/SSD/tables | DATA |
v_vmart_node0002 | /home/dbadmin/SSD/schemas | DATA |
v_vmart_node0003 | /home/dbadmin/VMart/v_vmart_node0003_data | DATA,TEMP |
v_vmart_node0003 | /home/dbadmin/SSD/tables | DATA |
v_vmart_node0003 | /home/dbadmin/SSD/schemas | DATA |
(10 rows)
对 v_monitor.partitions
系统表的列使用类似于以下内容的查询:
VMART=> select partition_key, projection_name, location_label from v_monitor.partitions;
partition_key | projection_name | location_label
---------------+----------------------+---------------
NH | states_b0 | FAST3
MA | states_b0 | FAST3
VT | states_b1 | FAST3
ME | states_b1 | FAST3
CT | states_b1 | FAST3
.
.
.
查询 storage_tiers
系统表以查看带标签和不带标签的存储容器及其相关信息:
VMart=> select * from v_monitor.storage_tiers;
location_label | node_count | location_count | ros_container_count | total_occupied_size
----------------+------------+----------------+---------------------+---------------------
| 1 | 2 | 17 | 297039391
SSD | 1 | 1 | 9 | 1506
Schema | 1 | 1 | 0 | 0
(3 rows)
查询 storage_policies
系统表以查看适当位置的当前存储策略。
VMART=> select * from v_monitor.storage_policies;
schema_name | object_name | policy_details | location_label
-------------+-------------+------------------+-----------------
| public | Schema | F4
public | lineorder | Partition [4, 4] | M3
(2 rows)
可以使用 CREATE LOCATION 添加和配置存储位置(不同于所需的默认值),以便提供用于以下用途的存储:
将执行引擎临时文件与数据文件隔离开。
创建 带标签的位置,以便在存储策略中使用。
根据预测或测量的访问模式创建存储位置。
为特定用户或用户组创建 USER 存储位置。
可以将新存储位置从一个节点添加到另一个节点,或从单个节点添加到所有群集节点。但是请勿在一个节点上使用供其他群集节点访问的共享目录。
在添加存储位置之前,请执行以下步骤:
验证计划用于存储位置目标的目录是否是对 Vertica 进程具有写入权限的空目录。
规划标签,以便在创建位置时为位置添加标签时。
确定要在存储位置中存储的信息类型:
DATA,TEMP
(默认值):存储位置可以存储 DML 生成的永久和临时数据,以及临时表的数据。
TEMP
:path 指定的位置,用于存储 DML 生成的临时数据。如果 path 设置为 S3,则仅当 RemoteStorageForTemp 配置参数设置为 1 且 TEMP
必须使用 ALL NODES SHARED 加以限定。有关详细信息,请参阅临时数据的 S3 存储。
DATA
:存储位置只能存储永久数据。
DEPOT
:该存储位置用于在
Eon 模式中存储存储库。仅在本地 Linux 文件系统上创建 DEPOT
存储位置。
Vertica 允许每个节点有单个 DEPOT
存储位置。如果要将存储库移动到不同的位置(例如,在不同的文件系统上),您必须首先删除旧的存储库存储位置,然后创建新位置。
在不同存储位置中存储临时和数据文件极为有利,因为两种数据类型具有不同的磁盘 I/O 访问模式。临时文件根据可用存储空间在各个位置中分布。但是,数据文件可以根据存储策略存储在不同的存储位置,以反映预测或测量的访问模式。
如果您计划在 HDFS 上放置存储位置,请参阅HDFS 存储位置的要求了解其他要求。
本示例显示了一个三节点群集,其中每个节点具有一个用于存储的 vertica/SSD
目录。
在群集中的每个节点上,创建供节点存储其数据的目录。例如:
$ mkdir /home/dbadmin/vertica/SSD
Vertica 建议在每个节点上创建相同的目录路径。在创建存储位置时使用此路径。
使用 CREATE LOCATION 语句添加存储位置。指定以下信息:
节点上供 Vertica 存储数据的路径
在上面有位置可用的节点,或 ALL NODES。如果指定 ALL NODES,该语句将在单个事务中在群集的所有节点上创建存储位置。
要存储的信息的类型。
要授予非特权(非 dbadmin)Linux 用户对数据的访问权限,您必须创建一个 USER 存储位置。还可以使用 USER 存储位置为没有自己凭据的用户提供对共享文件系统和对象存储(如 HDFS 和 S3)的访问权限。请参阅创建供 USER 访问的存储位置。
以下示例显示了如何添加在所有节点上可用的位置以仅存储数据:
=> CREATE LOCATION '/home/dbadmin/vertica/SSD/' ALL NODES USAGE 'DATA';
以下示例显示了如何添加 v_vmart_node0001 节点上可用于存储数据和临时文件的位置:
=> CREATE LOCATION '/home/dbadmin/vertica/SSD/' NODE 'v_vmart_node0001';
假设您正在使用适用于数据文件的存储位置,且要创建排名的存储位置。在此排名中,列根据其测量的性能存储在不同磁盘上。要创建带排名的存储位置,请参阅测量存储性能和设置存储性能。
创建存储位置之后,可以使用一些限制,更改其存储的信息类型。请参阅更改位置用途。
不能在现有存储位置的子目录中创建存储位置。这样做会导致出现与以下内容类似的错误:
=> CREATE LOCATION '/tmp/myloc' ALL NODES USAGE 'TEMP';
CREATE LOCATION
=> CREATE LOCATION '/tmp/myloc/ssd' ALL NODES USAGE 'TEMP';
ERROR 5615: Location [/tmp/myloc/ssd] conflicts with existing location
[/tmp/myloc] on node v_vmart_node0001
可以使用 CREATE LOCATION 语句的 LABEL 关键字添加带描述性标签的存储位置。可以使用带标签的位置设置存储策略。请参阅创建存储策略。
以下示例显示了如何在 v_mart_node0002 上创建带标签 SSD 的存储位置:
=> CREATE LOCATION '/home/dbadmin/SSD/schemas' NODE 'v_vmart_node0002'
USAGE 'DATA' LABEL 'SSD';
以下示例显示了如何在所有节点上创建存储位置。指定 ALL NODES 关键字可通过单个事务将存储位置添加到所有节点:
=> CREATE LOCATION '/home/dbadmin/SSD/schemas' ALL NODES
USAGE 'DATA' LABEL 'SSD';
新存储位置列在 DISK_STORAGE 系统表中:
=> SELECT * FROM v_monitor.disk_storage;
.
.
-[ RECORD 7 ]-----------+-----------------------------------------------------
node_name | v_vmart_node0002
storage_path | /home/dbadmin/SSD/schemas
storage_usage | DATA
rank | 0
throughput | 0
latency | 0
storage_status | Active
disk_block_size_bytes | 4096
disk_space_used_blocks | 1549437
disk_space_used_mb | 6053
disk_space_free_blocks | 13380004
disk_space_free_mb | 52265
disk_space_free_percent | 89%
...
要授予非特权(非 dbadmin)Linux 用户对数据的访问权限,您必须创建一个 USER 存储位置。
默认情况下,Vertica 使用用户提供的凭据来访问外部文件系统,例如 HDFS 和云对象存储。您可以覆盖此默认设置并创建一个 USER 存储位置来管理对这些位置的访问。要覆盖默认设置,请设置 UseServerIdentityOverUserIdentity 配置参数。
创建 USER 存储位置之后,可以授予一个或多个用户访问该位置的权限。USER 存储位置仅授予对数据文件的访问权限,而不授予对临时文件的访问权限。不能将 USER 存储位置指定给存储策略。不能将现有存储位置更改为具有 USER 访问权限。
以下示例显示了如何在特定节点上创建 USER 存储位置:
=> CREATE LOCATION '/home/dbadmin/UserStorage/BobStore' NODE
'v_mcdb_node0007' USAGE 'USER';
以下示例显示了如何为特定用户授予对该位置的读写权限:
=> GRANT ALL ON LOCATION '/home/dbadmin/UserStorage/BobStore' TO Bob;
GRANT PRIVILEGE
以下示例显示了如何使用 USER 存储位置授予对 S3 上位置的访问权限。Vertica 使用服务器凭据来访问该位置:
--- set database-level credential (once):
=> ALTER DATABASE DEFAULT SET AWSAuth = 'myaccesskeyid123456:mysecretaccesskey123456789012345678901234';
=> CREATE LOCATION 's3://datalake' SHARED USAGE 'USER' LABEL 's3user';
=> CREATE ROLE ExtUsers;
--- Assign users to this role using GRANT (Role).
=> GRANT READ ON LOCATION 's3://datalake' TO ExtUsers;
有关配置用户权限的详细信息,请参阅数据库用户和权限,以及 GRANT(存储位置)和 REVOKE(存储位置)参考页面。
SHARED 关键字指示该位置被所有节点共享。大多数远程文件系统(如 HDFS 和 S3)都是共享的。对于这些文件系统,path 实参表示远程文件系统中供所有节点存储数据的单个位置。每个节点在共享存储位置中为其各自的文件创建一个相应的子目录。这样做可以防止一个节点覆盖属于其他节点的文件。
如果使用远程文件系统,必须指定 SHARED,即使对于单节点群集也是如此。如果位置被声明为 USER,Vertica 不会为每个节点都创建子目录。USER 的设置优先于 SHARED。
如果在创建位置时省略此关键字,则新存储位置将被视为本地位置。每个节点都必须具有对指定路径的唯一访问权限。该位置通常是节点自己的文件系统中的一个路径。位于每个节点本地的文件系统(如 Linux)中的存储位置始终是本地位置。
如果在 Eon 模式下使用 Vertica 并且本地磁盘空间有限,则该空间可能不足以处理某些 DML 操作可能生成的大量临时数据。对于大型加载操作和刷新操作尤其如此。
您可以利用 S3 存储来处理临时数据,如下所示:
使用 CREATE LOCATION 创建远程存储位置,其中 path 设置为 S3,如下所示:
=> CREATE LOCATION 's3://bucket/path' ALL NODES SHARED USAGE 'TEMP';
将 RemoteStorageForTemp 会话配置参数设置为 1:
=> ALTER SESSION SET RemoteStorageForTemp= 1;
在将此参数设置为 1 之前,S3 上必须已经存在临时存储位置;否则,Vertica 会引发错误并提示创建存储位置。
运行需要额外临时存储的查询。
将 RemoteStorageForTemp 重置为其默认值:
=> ALTER SESSION DEFAULT CLEAR RemoteStorageForTemp;
当设置 RemoteStorageForTemp 时,Vertica 会将所有 DML 操作的临时数据重定向到指定的远程位置。在被显式重置为其默认值 (0) 或当前会话结束之前,该参数设置将一直有效。
除了本地 Linux 文件系统之外,还可以将存储位置放在 HDFS 中。因为 HDFS 存储位置不在本地,所以查询它们可能会更慢。您可以将 HDFS 存储位置用于优先级较低的数据或很少查询的数据(冷数据)。将优先级较低的数据移至 HDFS 可以释放 Vertica 群集上的空间以存储优先级较高的数据。
如果在 Apache Hadoop 上使用 Vertica for SQL,则通常仅将存储位置放在 HDFS 上。
要将 Vertica 的数据存储在 HDFS 上,请执行以下操作:
确认您的 Hadoop 群集已启用 WebHDFS。
确认 Vertica 群集中的所有节点均可连接到 Hadoop 群集中的所有节点。两个群集之间的任何防火墙都必须允许在 HDFS 使用的端口上建立连接。
在 HDFS 群集不安全时,确认您有一个 Hadoop 用户,其用户名与 Vertica 数据库超级用户(通常名为 dbadmin)的名称匹配。该 Hadoop 用户必须具有您希望 Vertica 用于存储其数据的 HDFS 目录的读写权限。
确认在 HDFS 群集使用 Kerberos 身份验证时:
您拥有 Vertica 的 Kerberos 主体,该主体对将用于存储位置的 HDFS 目录具备读写权限。有关说明,请参阅下面的 Kerberos。
Kerberos KDC 正在运行。
确认您的 HDFS 群集具有足以容纳 Vertica 数据的存储空间。有关详细信息,请参阅下面的空间要求。
确认您在支持 HDFS 的存储位置存储的数据不会将数据库大小扩展到超出 Vertica 许可证中规定的任何数据限额。Vertica 将支持 HDFS 的存储位置中存储的数据视为许可证规定的任何数据限额的一部分。有关详细信息,请参阅《管理员指南》中的管理许可证。
备份/还原还有其他要求。
如果 Vertica 数据库属于 K-safe,基于 HDFS 的存储位置包含您在该位置中存储的数据的两个副本。一个副本为主投影,另一个为同伴投影。如果您已启用 HDFS 的数据冗余功能,Hadoop 会将这两个投影存储多次。这种重复可能看似有些过多。但它与 RAID 1 级或更高级别存储主投影和伙伴实例投影的冗余副本的方式类似。冗余副本还可通过启用多个节点处理文件请求,帮助提高 HDFS 的性能。
验证 HDFS 安装有足够的空间可用于冗余存储 K-safe 数据的主投影和同伴投影。您可以通过设置 HadoopFSReplication
配置参数,调整 HDFS 存储的副本数量。有关详细信息,请参阅对 HDFS 存储位置进行故障排除。
要将 HDFS 中的存储位置与 Kerberos 一起使用,请执行以下附加步骤:
为每个 Vertica 节点创建 Kerberos 主体,如将 Kerberos 与 Vertica 结合使用中所述。
授予所有节点主体对将用作存储位置的 HDFS 目录的读写权限。
如果您计划使用 vbr
来备份和还原位置,请参阅备份和还原 HDFS 存储位置的要求中的其他要求。
如果将节点添加到 Vertica 群集中,它们不会自动拥有对现有 HDFS 存储位置的访问权限。您必须使用 CREATE LOCATION 语句,为新节点手动创建存储位置。请不要在此语句中使用 ALL NODES 选项,而应该使用 NODE 选项提供新节点的名称,以告知 Vertica 只有该节点需要添加共享位置。
请考虑一个借助于以下语句在三节点群集上创建的 HDFS 存储位置:
=> CREATE LOCATION 'hdfs://hadoopNS/vertica/colddata' ALL NODES SHARED
USAGE 'data' LABEL 'coldstorage';
=> SELECT SET_OBJECT_STORAGE_POLICY('SchemaName','coldstorage');
以下示例显示了如何将存储位置添加到新群集节点:
=> CREATE LOCATION 'hdfs://hadoopNS/vertica/colddata' NODE 'v_vmart_node0004'
SHARED USAGE 'data' LABEL 'coldstorage';
当您创建 HDFS 存储位置时,群集中的所有活动备用节点均将自动创建其自己的位置实例。而当备用节点替代出现故障的节点后,它会采用 HDFS 存储策略,使用自己的位置实例存储对象数据。请将存储位置创建后添加的备用节点视为任何其他新节点。您必须手动定义 HDFS 存储位置。
Vertica 将数据存储在 HDFS 上的存储位置与它在 Linux 文件系统中存储数据的方式相似。如果您在 HDFS 上创建存储位置,Vertica 会将容纳其数据的 ROS 容器存储在 HDFS 上。您可以选择让哪些数据使用 HDFS 存储位置:从仅单个表或分区的数据到数据库的所有数据。
当 Vertica 从 HDFS 存储位置读取数据或向 HDFS 存储位置写入数据时,存储或检索该数据的节点会直接连接 Hadoop 群集以传输数据。如果一个单 ROS 容器文件拆分到多个 HDFS 节点,Vertica 节点将连接到每个节点。Vertica 节点将检索文件片段,然后重新组合文件。由于每个节点直接从来源提取其自己的数据,因此将并行传输数据,从而提高传输效率。让 Vertica 节点直接检索文件拆分片段还可以降低对 Hadoop 群集的影响。
请用 HDFS 存储位置只存储数据。您不能在 HDFS 存储位置中存储编录信息。
由于 Vertica 使用存储位置以专用格式存储 ROS 容器,因此 MapReduce 和其他 Hadoop 组件无法访问 HDFS 中存储的 Vertica ROS 数据。切勿允许具有 HDFS 访问权限的其他程序写入到 ROS 文件。在外部对这些文件所做的任何修改均可能导致数据损坏和丢失。应用程序必须使用 Vertica 客户端库来访问 Vertica 数据。如果您想与其他 Hadoop 组件共享 ROS 数据,可以将其导出(请参阅文件导出)。
如果您在 Apache Hadoop 产品上使用 Vertica for SQL,Vertica 建议针对存储位置采用以下最佳实践:
只将数据类型存储位置放在 HDFS 存储上。
将临时空间直接放在本地 Linux 文件系统(而不是 HDFS)中。
为获得最佳性能,请将 Vertica 编录直接放在本地 Linux 文件系统上。
首先在本地 Linux 文件系统上创建数据库。然后,您可以将数据库扩展至 HDFS 存储位置,并设置只将数据块放在 HDFS 存储位置的存储策略。
如果仅在部分 HDFS 节点上运行 Vertica,请勿在这些节点上运行 HDFS 平衡器,以便获得更好的性能。HDFS 平衡器会将数据块移动至较远的距离,导致 Vertica 在执行查询期间读取非本地数据。不需要执行网络 I/O 时,查询运行速度更快。
一般来说,HDFS 要为群集中的每个节点准备 2 GB 左右的内存。要在 Vertica 配置中支持此要求:
创建一个 2 GB 资源池。
不要为此池分配任何 Vertica 执行资源。此方法可保留 HDFS 使用的空间。
或者,使用 Ambari 或 Cloudera Manager 查找 HDFS 所需的最大堆大小并将资源池的大小设置为该值。
有关如何配置资源池的详细信息,请参阅管理工作负载。
本主题将介绍 HDFS 存储位置的一些常见问题。
默认情况下,HDFS 为其存储的每个文件制作三个副本。这种复制有助于防止由磁盘或系统故障导致的数据丢失。还可以通过允许多个节点处理一个文件请求,来帮助提高性能。
K-Safety 值为 1 或更大的 Vertica 数据库还使用伙伴实例投影以冗余方式存储其数据。
当 K-Safe Vertica 数据库将数据存储在 HDFS 存储位置时,其数据冗余会因为 HDFS 的冗余而增加。HDFS 将存储主投影数据的三个副本外加伙伴实例投影的三个副本,从而总共存储数据的六个副本。
如果您想降低 HDFS 位置使用的磁盘存储量,您可以更改 HDFS 存储的数据副本数量。名为 HadoopFSReplication 的 Vertica 配置参数控制 HDFS 存储的数据副本数量。
您可以通过登录到 Hadoop NameNode 并发出以下命令,确定当前的 HDFS 磁盘使用情况:
$ hdfs dfsadmin -report
此命令可打印出整个 HDFS 存储的使用情况,后面则是 Hadoop 群集中各个节点的详细信息。下面的示例显示了此命令输出的开头部分,其中磁盘总空间已突出显示:
$ hdfs dfsadmin -report
Configured Capacity: 51495516981 (47.96 GB)
Present Capacity: 32087212032 (29.88 GB)
DFS Remaining: 31565144064 (29.40 GB)
DFS Used: 522067968 (497.88 MB)
DFS Used%: 1.63%
Under replicated blocks: 0
Blocks with corrupt replicas: 0
Missing blocks: 0
. . .
将一个简单的百万行表加载到 HDFS 存储位置中存储的一个表之后,报告将显示更大的磁盘使用量:
Configured Capacity: 51495516981 (47.96 GB)
Present Capacity: 32085299338 (29.88 GB)
DFS Remaining: 31373565952 (29.22 GB)
DFS Used: 711733386 (678.76 MB)
DFS Used%: 2.22%
Under replicated blocks: 0
Blocks with corrupt replicas: 0
Missing blocks: 0
. . .
以下 Vertica 示例演示了下列操作:
在 HDFS 上创建存储位置。
删除 Vertica 中的表。
将 HadoopFSReplication 配置选项设置为 1。这将规定 HDFS 存储 HDFS 存储位置数据的一个副本。
重新创建表并重新加载数据。
=> CREATE LOCATION 'hdfs://hadoopNS/user/dbadmin' ALL NODES SHARED
USAGE 'data' LABEL 'hdfs';
CREATE LOCATION
=> DROP TABLE messages;
DROP TABLE
=> ALTER DATABASE DEFAULT SET PARAMETER HadoopFSReplication = 1;
=> CREATE TABLE messages (id INTEGER, text VARCHAR);
CREATE TABLE
=> SELECT SET_OBJECT_STORAGE_POLICY('messages', 'hdfs');
SET_OBJECT_STORAGE_POLICY
----------------------------
Object storage policy set.
(1 row)
=> COPY messages FROM '/home/dbadmin/messages.txt';
Rows Loaded
-------------
1000000
现在在 Hadoop 上运行 HDFS 报告将显示磁盘空间使用量减少:
$ hdfs dfsadmin -report
Configured Capacity: 51495516981 (47.96 GB)
Present Capacity: 32086278190 (29.88 GB)
DFS Remaining: 31500988416 (29.34 GB)
DFS Used: 585289774 (558.18 MB)
DFS Used%: 1.82%
Under replicated blocks: 0
Blocks with corrupt replicas: 0
Missing blocks: 0
. . .
将数据加载到小型 Hadoop 群集(数据节点不超过 5 个)上的存储位置时,您可能会遇到错误 6966。此错误是由 HDFS 管理写入管道和复制的方式引起的。您可以通过减少副本数量来缓解此问题,如 [HDFS 存储磁盘消耗](#HDFS Sto)中所述。有关可以在 Hadoop 群集中进行的配置更改,请参阅 Hortonworks 中的这篇博文。
如果 HDFS 使用 Kerberos 身份验证,那么 CREATE LOCATION 语句将使用 Vertica keytab 主体而不是执行操作的用户的主体进行身份验证。如果因为身份验证错误导致创建失败,请验证您已遵循 Kerberos 中所述的步骤来配置此主体。
如下面的示例所示,使用 Kerberos 在 Hadoop 群集上创建 HDFS 存储位置时,CREATE LOCATION 可以报告正在使用的主体:
=> CREATE LOCATION 'hdfs://hadoopNS/user/dbadmin' ALL NODES SHARED
USAGE 'data' LABEL 'coldstorage';
NOTICE 0: Performing HDFS operations using kerberos principal [vertica/hadoop.example.com]
CREATE LOCATION
有关 HDFS 存储位置的备份/还原问题,请参阅备份和还原故障排除。
ALTER_LOCATION_USE
可用于更改 Vertica 在存储位置存储的文件类型。您通常仅将标签用于 DATA 存储位置,而不用于 TEMP 存储位置。
以下示例显示了如何将 v_vmartdb_node0004
上的存储位置更改为仅存储数据文件:
=> SELECT ALTER_LOCATION_USE ('/thirdVerticaStorageLocation/' , 'v_vmartdb_node0004' , 'DATA');
更改 HDFS 存储位置时,必须对 Vertica 群集中的所有节点进行更改。为此,请指定节点值 '',如以下示例所示:
=> SELECT ALTER_LOCATION_USE('hdfs:///user/dbadmin/v_vmart',
'','TEMP');
您不能更改 USER 使用类型的存储位置(如果存储位置是以这种方式创建的),也不能将存储位置更改为 USER 类型(如果存储位置不是以这种方式创建的)。可以更改 USER 存储位置以指定 DATA(不支持存储 TEMP 文件)。但是,这样做不会影响 USER 存储位置的主要目标可由具有已分配权限的非 dbadmin 用户访问。
您不能将存储位置从 SHARED TEMP 或 SHARED USER 更改为 SHARED DATA,反之亦然。
在更改存储位置用途类型之前,注意必须保留至少一个位置用来存储节点上的数据文件和临时文件。可将数据文件和临时文件存储在相同或单独的存储位置中。
更改现有存储位置具有以下影响:
ALTER_LOCATION_LABEL
可用于以多种方式更改存储位置的标签:
您可以在单个节点或群集范围内执行这些操作。
如果为已包含数据的现有存储位置添加标签,然后在一个或多个存储策略中包含带标签的位置,则现有数据可能会被移动。如果 Tuple Mover 确定存储在带标签的位置中的数据不符合存储策略,则它会将数据移动到其他位置。
使用 ALTER_LOCATION_LABEL
为不带标签的存储位置添加位置标签。例如,在三节点群集上定义有不带标签的存储位置 /home/dbadmin/Vertica/SSD
:
您可以在所有节点上将此存储位置标记为 SSD
,如下所示:
=> SELECT ALTER_LOCATION_LABEL('/home/dbadmin/vertica/SSD', '', 'SSD');
只有当这两个条件都为真时,您才能移除位置标签:
未在数据库对象的存储策略中指定标签。
标记的位置不是其关联对象的最后一个可用存储。
以下语句从所有节点上的指定存储位置移除 SSD
标签:
=> SELECT ALTER_LOCATION_LABEL('/home/dbadmin/SSD/tables','', '');
ALTER_LOCATION_LABEL
------------------------------------------
/home/dbadmin/SSD/tables label changed.
(1 row)
更改位置标签会产生以下影响:
Vertica 元函数
SET_OBJECT_STORAGE_POLICY
可以创建
存储策略,将数据库对象与带标签的存储位置相关联。当对象具有存储策略时,Vertica 使用带标签的位置作为对象数据的默认存储位置。
可以为任何数据库、架构、表和分区范围创建存储策略。每个对象都可以与一个存储策略相关联。每次加载和更新数据时,Vertica 都会检查对象是否具有存储策略。如果有,Vertica 将自动使用带标签的存储位置。如果对象或其父代实体没有存储策略,数据存储处理将在可用的存储位置上使用标准存储算法继续。如果所有存储位置都带标签,Vertica 将使用其中之一。
存储策略可用于确定存储重要数据的位置。例如,可以创建带标签 SSD
的存储位置,用以表示群集节点上最快可用的存储位置。随后可以创建存储策略以将表与此带标签的位置相关联。例如,下面的 SET_OBJECT_STORAGE_POLICY
语句将 test
表上的存储策略设置为使用带有 SSD
标签的存储作为其默认位置:
=> SELECT SET_OBJECT_STORAGE_POLICY('test','ssd', true);
SET_OBJECT_STORAGE_POLICY
--------------------------------------------------
Object storage policy set.
Task: moving storages
(Table: public.test) (Projection: public.test_b0)
(Table: public.test) (Projection: public.test_b1)
(1 row)
创建一个或多个存储策略并不需要所有数据库对象都存在策略。站点可以支持具有或不具有存储策略的对象。可以为离散的优先级对象集添加存储策略,同时允许存在其他没有策略的对象,以便其使用可用的存储空间。
可以测量任何磁盘存储位置的性能(请参阅测量存储性能)。然后使用性能测量值设置存储位置性能。Vertica 使用您设置的性能测量值为其存储位置排名,并通过排名确定哪些键投影列存储在性能更高的位置,如设置存储性能中所述。
如果已设置站点存储位置的性能,并决定使用存储策略,则具有关联策略的任何存储位置的优先级都高于存储排名设置。
您可以使用存储策略将旧数据移至成本较低的存储位置,同时保持其可用于查询。请参阅为低优先级数据创建存储策略。
Vertica 根据下面的存储策略层次结构确定对象数据的存储位置,下面列出的存储位置按优先级升序排列:
数据库
架构
表
表分区
如果对象缺少自己的存储策略,它将使用其父对象的存储策略。例如,数据库 Sales
中的表 Region.Income
按月分区。带标签的存储策略 FAST
和 STANDARD
分别分配给表和数据库。没有为表的分区或其父架构分配存储策略,因此这些分区或架构分别使用其父对象 FAST
和 STANDARD
的存储策略:
当发生 Tuple Mover 操作(如合并)时,所有 Income
数据都会移至 FAST
存储位置。Region 架构中的其他表使用其各自的存储策略。如果 Region
表缺少自己的存储策略,Tuple Mover 将使用它上面的下一个存储策略 — 在这种情况下,它使用数据库存储策略并将表数据移至 STANDARD
。
可以查询在 STORAGE_CONTAINERS 系统表的 location_label
列中列出的现有存储策略:
=> SELECT node_name, projection_name, location_label FROM v_monitor.storage_containers;
node_name | projection_name | location_label
------------------+----------------------+----------------
v_vmart_node0001 | states_p |
v_vmart_node0001 | states_p |
v_vmart_node0001 | t1_b1 |
v_vmart_node0001 | newstates_b0 | LEVEL3
v_vmart_node0001 | newstates_b0 | LEVEL3
v_vmart_node0001 | newstates_b1 | LEVEL3
v_vmart_node0001 | newstates_b1 | LEVEL3
v_vmart_node0001 | newstates_b1 | LEVEL3
v_vmart_node0001 | states_p_v1_node0001 | LEVEL3
v_vmart_node0001 | states_p_v1_node0001 | LEVEL3
v_vmart_node0001 | states_p_v1_node0001 | LEVEL3
v_vmart_node0001 | states_p_v1_node0001 | LEVEL3
v_vmart_node0001 | states_p_v1_node0001 | LEVEL3
v_vmart_node0001 | states_p_v1_node0001 | LEVEL3
v_vmart_node0001 | states_b0 | SSD
v_vmart_node0001 | states_b0 | SSD
v_vmart_node0001 | states_b1 | SSD
v_vmart_node0001 | states_b1 | SSD
v_vmart_node0001 | states_b1 | SSD
...
默认情况下,Tuple Mover 在所有挂起的合并操作完成后强制执行对象存储策略。 SET_OBJECT_STORAGE_POLICY
立即将现有数据存储移至新位置,但前提是它的 enforce-storage-move 参数设置为 true
。如果要移动的数据是旧数据,您可能需要强制移动,即使这意味着需要等待操作完成才能继续也是如此。Tuple Mover 在旧数据上的运行频率较低。
true
,SET_OBJECT_STORAGE_POLICY
将执行群集范围的操作。如果任意节点上出现错误,该函数将显示警告消息并跳过此节点。然后在剩余节点上继续执行操作。
如果您的某些数据位于分区表中,您可以将查询较少的分区移至成本较低的存储,例如 HDFS。您仍然可以通过查询访问这些数据,只不过访问的速度更慢。在这种情况下,速度更快的存储通常被称为“热存储”,速度较慢的存储被称为“冷存储”。
假设您有一个名为 messages 的表,其中包含已按照时间戳的年份和月份分区的社交媒体消息。您可以通过查询 PARTITIONS 系统表列出表中的分区。
=> SELECT partition_key, projection_name, node_name, location_label FROM partitions
ORDER BY partition_key;
partition_key | projection_name | node_name | location_label
--------------+-----------------+------------------+----------------
201309 | messages_b1 | v_vmart_node0001 |
201309 | messages_b0 | v_vmart_node0003 |
201309 | messages_b1 | v_vmart_node0002 |
201309 | messages_b1 | v_vmart_node0003 |
201309 | messages_b0 | v_vmart_node0001 |
201309 | messages_b0 | v_vmart_node0002 |
201310 | messages_b0 | v_vmart_node0002 |
201310 | messages_b1 | v_vmart_node0003 |
201310 | messages_b0 | v_vmart_node0001 |
. . .
201405 | messages_b0 | v_vmart_node0002 |
201405 | messages_b1 | v_vmart_node0003 |
201405 | messages_b1 | v_vmart_node0001 |
201405 | messages_b0 | v_vmart_node0001 |
(54 rows)
接下来,假设您发现,对此表执行的大多数查询仅访问最近一两个月的数据。您可能会决定将旧数据移至基于 HDFS 的存储位置中的冷存储。数据在移动后仍可用于查询,但查询性能会降低。
要将分区移至 HDFS 存储位置,请在 SET_OBJECT_STORAGE_POLICY 函数调用中提供最低和最高的待移动分区键值。以下示例演示了如何移动两个日期之间的数据。在此示例中:
分区键值 201309 代表 2013 年 9 月。
分区键值 201403 代表 2014 年 3 月。
名称 coldstorage 是基于 HDFS 的存储位置的标签。
最后一个可选的实参是 true
,这意味着在移动完成之前,该函数不会返回。默认情况下,该函数立即返回,并在下次运行 Tuple Mover 时移动数据。但是,当数据较旧时,Tuple Mover 的运行频率会降低,这会延迟原始存储空间的恢复。
=> SELECT SET_OBJECT_STORAGE_POLICY('messages','coldstorage', '201309', '201403', 'true');
下次运行 Tuple Mover 时,指定范围内的分区将移至带有 coldstorage 标签的 HDFS 存储位置。这个位置的名称现在显示在 PARTITIONS 系统表的 location_label 列中。
=> SELECT partition_key, projection_name, node_name, location_label
FROM partitions ORDER BY partition_key;
partition_key | projection_name | node_name | location_label
--------------+-----------------+------------------+----------------
201309 | messages_b0 | v_vmart_node0003 | coldstorage
201309 | messages_b1 | v_vmart_node0001 | coldstorage
201309 | messages_b1 | v_vmart_node0002 | coldstorage
201309 | messages_b0 | v_vmart_node0001 | coldstorage
. . .
201403 | messages_b0 | v_vmart_node0002 | coldstorage
201404 | messages_b0 | v_vmart_node0001 |
201404 | messages_b0 | v_vmart_node0002 |
201404 | messages_b1 | v_vmart_node0001 |
201404 | messages_b1 | v_vmart_node0002 |
201404 | messages_b0 | v_vmart_node0003 |
201404 | messages_b1 | v_vmart_node0003 |
201405 | messages_b0 | v_vmart_node0001 |
201405 | messages_b1 | v_vmart_node0002 |
201405 | messages_b0 | v_vmart_node0002 |
201405 | messages_b0 | v_vmart_node0003 |
201405 | messages_b1 | v_vmart_node0001 |
201405 | messages_b1 | v_vmart_node0003 |
(54 rows)
完成首次数据移动之后,您可以定期将额外的数据移至该 HDFS 存储位置。您可以使用相同的方法将个别分区或一系列分区从“热”存储移至“冷”存储位置:
=> SELECT SET_OBJECT_STORAGE_POLICY('messages', 'coldstorage', '201404', '201404', 'true');
=> SELECT projection_name, node_name, location_label
FROM PARTITIONS WHERE PARTITION_KEY = '201404';
projection_name | node_name | location_label
-----------------+------------------+----------------
messages_b0 | v_vmart_node0002 | coldstorage
messages_b0 | v_vmart_node0003 | coldstorage
messages_b1 | v_vmart_node0003 | coldstorage
messages_b0 | v_vmart_node0001 | coldstorage
messages_b1 | v_vmart_node0002 | coldstorage
messages_b1 | v_vmart_node0001 | coldstorage
(6 rows)
将分区从热存储移至冷存储的另一种方法是,将分区的数据移至其他存储位置中单独的表中。这种方法将数据分成两个表,一个包含热数据,另一个则包含冷数据。如果希望防止查询无意中访问存储在冷存储中的数据,您可以使用这种方法。要查询旧数据,您必须显式查询冷表。
要移动分区:
创建一个架构与现有已分区表的架构相匹配的新表。
将新表的存储策略设置为使用基于 HDFS 的存储位置。
使用 MOVE_PARTITIONS_TO_TABLE 函数,将一系列分区从热表移至冷表。这些分区将在下次运行 Tuple Mover 时迁移。
下面的示例演示了上述步骤。您首先创建一个名为 cold_messages 的表。然后向其分配名为 coldstorage 的基于 HDFS 的存储位置;最后,移动一系列分区。
=> CREATE TABLE cold_messages LIKE messages INCLUDING PROJECTIONS;
=> SELECT SET_OBJECT_STORAGE_POLICY('cold_messages', 'coldstorage');
=> SELECT MOVE_PARTITIONS_TO_TABLE('messages','201309','201403','cold_messages');
SET_OBJECT_STORAGE_POLICY 将数据存储从现有位置(带标签和不带标签)移至另一个带标签的位置。该函数执行两个任务:
为对象创建存储策略,或更改其当前策略。
将指定对象的所有现有数据移至目标存储位置。
在该函数将对象数据移至指定的目标位置之前,Vertica 会计算所需的存储并检查目标处的可用空间。在调用 SET_OBJECT_STORAGE_POLICY 之前,请检查新目标位置上的可用空间。请注意,检查并不能保证在 Tuple Mover 实际执行移动时该空间仍然可用。如果存储位置没有足够的可用空间,该函数将返回错误。
默认情况下,Tuple Mover 在所有待定的合并任务返回后才将对象数据移至新的存储位置。您可以通过将函数的 enforce-storage-move 实参设置为 true 来强制立即移动数据。例如,以下语句设置表的存储策略并立即实施移动操作:
=> SELECT SET_OBJECT_STORAGE_POLICY('states', 'SSD', 'true');
SET_OBJECT_STORAGE_POLICY
------------------------------------------------------------------------------------------------
Object storage policy set.
Task: moving storages
(Table: public.states) (Projection: public.states_p1)
(Table: public.states) (Projection: public.states_p2)
(Table: public.states) (Projection: public.states_p3)
(1 row)
CLEAR_OBJECT_STORAGE_POLICY 元函数从数据库、架构、表或表分区中清除存储策略。例如,以下语句清除表的存储策略:
=> SELECT CLEAR_OBJECT_STORAGE_POLICY ('store.store_sales_fact');
CLEAR_OBJECT_STORAGE_POLICY
--------------------------------
Object storage policy cleared.
(1 row)
Tuple Mover 会将现有存储容器移至父存储策略的位置,若无父策略,则移至默认存储位置。默认情况下,此移动发生在所有待定的合并任务返回之后。
您可以通过将函数的 enforce-storage-move 实参设置为 true 来强制立即移动数据。例如,以下语句清除表的存储策略并立即执行移动操作:
=> SELECT CLEAR_OBJECT_STORAGE_POLICY ('store.store_orders_fact', 'true');
CLEAR_OBJECT_STORAGE_POLICY
-----------------------------------------------------------------------------
Object storage policy cleared.
Task: moving storages
(Table: store.store_orders_fact) (Projection: store.store_orders_fact_b0)
(Table: store.store_orders_fact) (Projection: store.store_orders_fact_b1)
(1 row)
清除一个级别(如表)的存储策略不一定会影响其他级别(如该表的分区)的存储策略。
例如,lineorder
表有一个存储策略用来将表数据存储在带有 F2
标签的位置。为该表中的各个分区单独分配各自的存储位置,通过查询 STORAGE_POLICIES 系统表进行验证:
=> SELECT * from v_monitor.storage_policies;
schema_name | object_name | policy_details | location_label
-------------+-------------+------------------+----------------
| public | Schema | F4
public | lineorder | Partition [0, 0] | F1
public | lineorder | Partition [1, 1] | F2
public | lineorder | Partition [2, 2] | F4
public | lineorder | Partition [3, 3] | M1
public | lineorder | Partition [4, 4] | M3
(6 rows)
从 lineorder
表中清除当前的存储策略对其各个分区的存储策略没有影响。例如,对于以下 CLEAR_OBJECT_STORAGE_POLICY 语句:
=> SELECT CLEAR_OBJECT_STORAGE_POLICY ('lineorder');
CLEAR_OBJECT_STORAGE_POLICY
-------------------------------------
Default storage policy cleared.
(1 row)
表中的各个分区保留其存储策略:
=> SELECT * from v_monitor.storage_policies;
schema_name | object_name | policy_details | location_label
-------------+-------------+------------------+----------------
| public | Schema | F4
public | lineorder | Partition [0, 0] | F1
public | lineorder | Partition [1, 1] | F2
public | lineorder | Partition [2, 2] | F4
public | lineorder | Partition [3, 3] | M1
public | lineorder | Partition [4, 4] | M3
(6 rows)
如果您从表中的一个分区键范围中清除存储策略,则父对象和其他分区范围的存储策略不受影响。例如,以下语句从分区键 0 到 3 中清除存储策略:
=> SELECT CLEAR_OBJECT_STORAGE_POLICY ('lineorder','0','3');
clear_object_storage_policy
-------------------------------------
Default storage policy cleared.
(1 row)
=> SELECT * from storage_policies;
schema_name | object_name | policy_details | location_label
-------------+-------------+------------------+----------------
| public | Schema | F4
public | lineorder | Table | F2
public | lineorder | Partition [4, 4] | M3
(2 rows)
Vertica 可用于测量站点上任何存储位置的磁盘 I/O 性能。可使用返回的测量值设置性能,从而自动进行排名。根据您的存储需求,也可以使用性能确定作为站点存储策略一部分的关键数据所需的存储位置。存储性能测量仅适用于数据存储位置,不适用于临时存储位置。
测量存储位置性能会计算在磁盘中读写 1 MB 数据所需的时间,计算公式如下:
IO time = time to read/write 1MB + time to seek = 1/throughput + 1/Latency
吞吐量为顺序读写的平均吞吐量(以每秒兆字节表示)。
延迟适用于随机读取(仅在查找中,单位为每秒查找次数)。
因此,存储位置较快的 I/O 时间要比存储位置较慢的 I/O 时间短。
Vertica 提供两种测量存储位置性能的方法,具体视数据库是否正在运行而定。您可以执行以下操作之一:
在正在运行的数据库上测量性能。
设置群集之前测量性能。
这两种方法都将返回存储位置的吞吐量和延迟。记录或捕获吞吐量和延迟信息,以便您可以使用它来设置位置性能(请参阅设置存储性能)。
使用 MEASURE_LOCATION_PERFORMANCE() 函数可在数据库正在运行时测量存储位置的性能。此函数具有以下要求:
存储路径必须已存在于数据库中。
存储位置中需要有 RAM*2 的可用磁盘空间来测量其性能。例如,如果 RAM 为 16 GB,则需 32 GB 的可用磁盘空间。如果磁盘空间不足,函数将返回一个错误。
使用系统表 DISK_STORAGE 可获取每个数据库节点上的磁盘存储的相关信息。
以下示例显示了如何在 v_vmartdb_node0004
上测量存储位置的性能:
=> SELECT MEASURE_LOCATION_PERFORMANCE('/secondVerticaStorageLocation/','v_vmartdb_node0004');
WARNING: measure_location_performance can take a long time. Please check logs for progress
measure_location_performance
--------------------------------------------------
Throughput : 122 MB/sec. Latency : 140 seeks/sec
您可以在设置群集之前测量性能。当要验证磁盘在正常参数范围内是否正常运行时,此方法非常有用。要执行此测量,必须事先安装 Vertica。
要测量磁盘性能,请使用以下命令:
opt/vertica/bin/vertica -m <path to disk mount>
例如:
opt/vertica/bin/vertica -m /secondVerticaStorageLocation/node0004_data
可以使用 MEASURE_LOCATION_PERFORMANCE 函数返回的测量值作为 SET_LOCATION_PERFORMANCE() 函数的输入值。
以下示例显示了如何使用由 MEASURE_LOCATION_PERFORMANCE 函数为 v_vmartdb_node0004
上的某个存储位置返回的值设置该位置的性能。将吞吐量设置为 122
MB/s 并将延迟设置为 140
seeks/s。 MEASURE_LOCATION_PERFORMANCE
=> SELECT SET_LOCATION_PERFORMANCE('/secondVerticaStorageLocation/','node2','122','140');
设置性能数据参数之后,Vertica 每次存储投影列时会自动利用性能数据对存储位置进行分级。
Vertica 将投影排序顺序中包含的列存储在最快的可用存储位置。未包含在投影排序顺序中的列将存储在稍慢的磁盘中。每个投影的列按如下方式排序:
排序顺序中的列具有最高优先级(编号 >;1000)。
排序顺序中最后一列的排序编号为 1001。
排序顺序中倒数第二的列排序编号为 1002,以此类推,直到排序顺序中的第一列,其排序编号为 1000 + 排序列数。
剩余列的排序编号在 1000–1 之间,从 1000 开始,每列减一。
Vertica 随后将列从最高排号到最低排号存储在磁盘中。它将最高排号的列放在最快的磁盘上,将最低排号的列放在最慢的磁盘上。
首先测量位置性能并在 Vertica 数据库中进行设置。之后,您可以利用性能结果来确定要在存储策略中使用的最快的存储。
将具有最高性能的位置设置为重要数据的默认位置。
将速度较慢的位置用作旧数据或不太重要的数据的默认位置。如果您不想指定默认位置,这种速度较慢的位置可能根本不需要策略。
Vertica 会按如下方式确定数据存储,具体取决于是否存在存储策略:
您可以停用存储位置以停止使用它。停用某个存储位置只是禁止 Vertica 将数据或临时文件存储到其中,而不会移除实际位置。先前存储在已停用位置中的所有数据最终将由 Tuple Mover 合并。使用 RETIRE_LOCATION 函数停用位置。
以下示例从单个节点停用位置:
=> SELECT RETIRE_LOCATION('/secondStorageLocation/' , 'v_vmartdb_node0004');
要停用所有节点上的存储位置,请为第二个实参使用空字符串 (''
)。如果位置是 SHARED,则只能在所有节点上停用它。
您可以通过传递可选的第三个实参 enforce 为 true
来加快存储位置的停用和删除操作。该函数使用此指令将数据从存储位置中移出,而不必等待 Tuple Mover 来处理,这样您便能够立即删除该位置。
此外,还可以使用 ENFORCE_OBJECT_STORAGE_POLICY 函数立即触发所有存储位置的移动操作,从而可用于删除这些位置。这种方法等效于使用 enforce 实参。
以下示例显示了如何停用所有节点上的存储位置,以便可以立即删除它:
=> SELECT RETIRE_LOCATION('/secondStorageLocation/' , '', true);
true
。
可将数据文件和临时文件存储在一个存储位置或多个单独的存储位置中。
有关在停用某个位置后删除该位置的详细信息,请参阅删除存储位置。
您可以还原先前停用的存储位置。该位置还原后,Vertica 会对存储位置重新排名并使用还原后的位置来处理查询(由其排名决定)。
使用 RESTORE_LOCATION 函数还原已停用的存储位置。
以下示例显示了如何还原单个节点上的已停用存储位置:
=> SELECT RESTORE_LOCATION('/secondStorageLocation/' , 'v_vmartdb_node0004');
要还原所有节点上的存储位置,请为第二个实参使用空字符串 (''
)。以下示例演示了在所有节点上创建、停用和还原某个位置:
=> CREATE LOCATION '/tmp/ab1' ALL NODES USAGE 'TEMP';
CREATE LOCATION
=> SELECT RETIRE_LOCATION('/tmp/ab1', '');
retire_location
------------------------
/tmp/ab1 retired.
(1 row)
=> SELECT location_id, node_name, location_path, location_usage, is_retired
FROM STORAGE_LOCATIONS WHERE location_path ILIKE '/tmp/ab1';
location_id | node_name | location_path | location_usage | is_retired
------------------+---------------------+---------------+----------------+------------
45035996273736724 | v_vmart_node0001 | /tmp/ab1 | TEMP | t
45035996273736726 | v_vmart_node0002 | /tmp/ab1 | TEMP | t
45035996273736728 | v_vmart_node0003 | /tmp/ab1 | TEMP | t
45035996273736730 | v_vmart_node0004 | /tmp/ab1 | TEMP | t
(4 rows)
=> SELECT RESTORE_LOCATION('/tmp/ab1', '');
restore_location
-------------------------
/tmp/ab1 restored.
(1 row)
=> SELECT location_id, node_name, location_path, location_usage, is_retired
FROM STORAGE_LOCATIONS WHERE location_path ILIKE '/tmp/ab1';
location_id | node_name | location_path | location_usage | is_retired
------------------+---------------------+---------------+----------------+------------
45035996273736724 | v_vmart_node0001 | /tmp/ab1 | TEMP | f
45035996273736726 | v_vmart_node0002 | /tmp/ab1 | TEMP | f
45035996273736728 | v_vmart_node0003 | /tmp/ab1 | TEMP | f
45035996273736730 | v_vmart_node0004 | /tmp/ab1 | TEMP | f
(4 rows)
RESTORE_LOCATION 仅在位置存在且被停用的节点上还原位置。这个元函数不会将存储位置传播到先前没有该位置的节点。
如果已在任何节点上删除位置,则无法在所有节点上执行还原操作。如果您已删除相同节点上的位置,则有两个选择:
如果不再希望使用已在其上删除该位置的节点,请在其他的每个节点上单独还原位置。
或者,可以在删除位置的节点上重新创建位置。为此,请使用 CREATE LOCATION。重新创建位置后,可随后在所有节点上还原该位置。
以下示例演示了如果您尝试在已删除位置的节点上进行还原,操作将失败:
=> SELECT RETIRE_LOCATION('/tmp/ab1', '');
retire_location
------------------------
/tmp/ab1 retired.
(1 row)
=> SELECT DROP_LOCATION('/tmp/ab1', 'v_vmart_node0002');
drop_location
------------------------
/tmp/ab1 dropped.
(1 row)
==> SELECT location_id, node_name, location_path, location_usage, is_retired
FROM STORAGE_LOCATIONS WHERE location_path ILIKE '/tmp/ab1';
location_id | node_name | location_path | location_usage | is_retired
------------------+---------------------+---------------+----------------+------------
45035996273736724 | v_vmart_node0001 | /tmp/ab1 | TEMP | t
45035996273736728 | v_vmart_node0003 | /tmp/ab1 | TEMP | t
45035996273736730 | v_vmart_node0004 | /tmp/ab1 | TEMP | t
(3 rows)
=> SELECT RESTORE_LOCATION('/tmp/ab1', '');
ERROR 2081: [/tmp/ab1] is not a valid storage location on node v_vmart_node0002
要删除存储位置,请使用 DROP_LOCATION 函数。您不能删除 DATA 用途的位置,只能删除 TEMP 或 USER 用途的位置。(请参阅在删除之前更改存储位置)。
由于删除存储位置的操作无法撤消,因此 Vertica 建议先停用存储位置(请参阅停用存储位置)。通过在删除之前停用存储位置,您可以验证这样做不会对任何数据访问产生负面影响。如果您决定不删除它,可以进行还原(请参阅还原已停用的存储位置)。
以下示例显示了如何删除单个节点上的存储位置:
=> SELECT DROP_LOCATION('/secondStorageLocation/' , 'v_vmartdb_node0002');
删除存储位置时,此操作会级联到关联的对象,包括授予存储的任何权限。
您只能删除包含临时文件的存储位置。因此,在可以删除之前,必须将存储位置更改为 TEMP 使用类型。但是,如果存储位置中仍存在数据文件,Vertica 会禁止您删除该存储位置。删除数据文件不会清除存储位置,并且可能会导致数据库崩溃。要处理包含数据文件的存储区域以便能够删除它,请使用以下选项之一:
删除 HDFS 上的存储位置后,清理 HDFS 上的残留文件和快照,如移除 HDFS 存储位置中所述。
使用 USER 使用类型创建的存储位置只能包含数据文件,而非临时文件。但是,您可以删除 USER 位置,而不考虑剩下的任何数据文件。此行为与未指定用于 USER 访问的存储位置的行为不同。
您可以在 STORAGE_LOCATIONS 系统表中检查存储位置的属性,例如是 USER 位置还是仅用于 TEMP 文件。还可以使用此表来确认位置是否已停用。
如果查询性能欠佳,请使用工作负载分析器获取相关的优化建议,以及有关优化数据库对象的提示。工作负载分析器是一个 Vertica 实用程序,可用于分析 Vertica 系统表 中的系统信息。
工作负载分析器将通过智能监控查询的执行情况、工作负载历史记录、资源和配置,来识别查询性能欠佳的根本原因。它将返回一组优化建议,这些建议基于统计信息、系统和 数据收集器事件以及数据库/表/投影设计的组合。使用这些建议可以快速轻松地优化查询性能。
您可以通过两种方式运行工作负载分析器:
调用 Vertica 函数
ANALYZE_WORKLOAD
。
使用管理控制台。
有关工作负载分析器发现的常见问题和相关建议,请参阅工作负载分析器建议。
可以调用
ANALYZE_WORKLOAD
以获取有关查询和数据库对象的优化建议。使用函数实参指定要分析哪些事件以及何时进行分析。
ANALYZE_WORKLOAD
的 scope
实参确定要分析的内容:
可选 since‑time
实参用于指定从自 since‑time
开始的所有范围内事件返回值,并继续保持当前系统状态。如果省略 since_time
,ANALYZE_WORKLOAD
将返回有关自记录的上次调用函数的时间以来的事件的建议。必须将 since‑time
字符串值显式转换为 TIMESTAMP
或 TIMESTAMPTZ
。
以下示例显示了四种用不同格式表达 since‑time
实参的方法。对于自 2012 年 10 月 4 日以来表 t1
的工作负载,所有查询都返回相同的结果:
=> SELECT ANALYZE_WORKLOAD('t1', TIMESTAMP '2012-10-04 11:18:15');
=> SELECT ANALYZE_WORKLOAD('t1', '2012-10-04 11:18:15'::TIMESTAMPTZ);
=> SELECT ANALYZE_WORKLOAD('t1', 'October 4, 2012'::TIMESTAMP);
=> SELECT ANALYZE_WORKLOAD('t1', '10-04-12'::TIMESTAMPTZ);
通过将 ANALYZE_WORKLOAD
的第二个实参设置为 true
,可以保存该函数的结果,而无需分析自特定时间以来的事件。默认值为 false
,且不保存任何结果。保存函数结果之后,ANALYZE_WORKLOAD
的随后调用仅分析自上次保存已返回的数据以来的事件,并忽略所有先前的事件。
例如,以下语句返回架构中所有数据库对象的建议,并记录此分析调用。
=> SELECT ANALYZE_WORKLOAD('', true);
下一次调用 ANALYZE_WORKLOAD
时,将分析自此点以后的事件。
observation_count
列返回一个整数,表示工作负载分析器为此优化建议观察到的事件总数。对于上述每种情况,工作负载分析器都会提出第一项建议。observation_time
中的 Null 结果表示仅该建议来自当前系统状态,而不是来自之前的事件。
tuning_parameter
列返回工作负载分析器建议您对其应用优化操作的对象。上述示例中的 release
参数通知 DBA 为用户版本设置密码。
工作负载分析器的输出将在 tuning_description
列返回您应当考虑的任务的简短描述并在适当时,在 tuning_command column
列返回可以运行的 SQL 命令。在上述记录 1 和 2 中,工作负载分析器建议您在两个表上运行
Database Designer,并建议在记录 3 中设置用户密码。注意,记录 3 还提供可运行的 ALTER USER
命令,因为优化操为 SQL 命令。
tuning_cost
列中的输出指示运行建议的优化命令所需的成本:
LOW
:运行优化命令对资源的影响最小。可以随时执行优化操作,与上面的在 Record 3 中更改用户密码一样。
MEDIUM
:运行优化命令对资源的影响适中。
HIGH
:运行优化命令对资源的影响最大。根据数据库或表大小,考虑在非负载峰值时段执行开销较大的操作。
以下语句要求工作负载分析器分析 locations
表的所有事件:
=> SELECT ANALYZE_WORKLOAD('locations');
工作负载分析器返回建议,建议您在表中运行 Database Designer。根据 locations
的大小,该操作可能会产生较高的成本:
-[ RECORD 1 ]----------+------------------------------------------------
observation_count | 1
first_observation_time |
last_observation_time |
tuning_parameter | public.locations
tuning_description | run database designer on table public.locations
tuning_command |
tuning_cost | HIGH
以下语句将分析今天之前一周以来 VMart 示例数据库中所有表上的工作负载:
=> SELECT ANALYZE_WORKLOAD('', NOW() - INTERVAL '1 week');
工作负载分析器返回以下结果:
-[ RECORD 1 ]----------+------------------------------------------------------
observation_count | 4
first_observation_time | 2012-02-17 13:57:17.799003-04
last_observation_time | 2011-04-22 12:05:26.856456-04
tuning_parameter | store.store_orders_fact.date_ordered
tuning_description | analyze statistics on table column store.store_orders_fact.date_ordered
tuning_command | select analyze_statistics('store.store_orders_fact.date_ordered');
tuning_cost | MEDIUM
-[ RECORD 2 ]---------+------------------------------------------------------
...
-[ RECORD 14 ]---------+-----------------------------------------------------
observation_count | 2
first_observation_time | 2012-02-19 17:52:03.022644-04
last_observation_time | 2012-02-19 17:52:03.02301-04
tuning_parameter | SELECT x FROM t WHERE x > (SELECT SUM(DISTINCT x) FROM
| t GROUP BY y) OR x < 9;
tuning_description | consider incremental design on query
tuning_command |
tuning_cost | HIGH
工作负载分析器发现两个问题:
在记录 1 中,store.store_orders_fact table
中的 date_ordered
列可能具有过时的统计信息,因此工作负载分析器建议对该列运行
ANALYZE_STATISTICS
。函数输出还会返回要运行的查询。例如:
=> SELECT ANALYZE_STATISTICS('store.store_orders_fact.date_ordered');
在记录 14 中,工作负载分析器在 tuning_parameter
列中识别出性能欠佳的查询。建议使用 Database Designer 运行增量设计。工作负载分析器将潜在成本评定为 HIGH
。
还可以通过查询系统表
TUNING_RECOMMENDATIONS
获取优化建议,这样将从上一次 ANALYZE_WORKLOAD
调用返回优化建议结果。
=> SELECT * FROM tuning_recommendations;
工作负载分析器用于其建议的系统信息保留在 SQL 系统表中,因此查询 TUNING_RECOMMENDATIONS
系统表不会运行工作负载分析器。
工作负载分析器将监控数据库活动,并根据需要将建议记录在系统表 TUNING_RECOMMENDATIONS 中。运行工作负载分析器时,该实用程序会返回以下信息:
需要优化的对象的描述
建议操作
用于实施建议的 SQL 命令
在某些情况下,您需要关闭节点才能执行维护任务或升级硬件。可以通过以下方法之一执行此操作:
运行管理工具 (Administration Tools),选择高级菜单 (Advanced Menu),然后单击确定 (OK)。
选择在主机上停止 Vertica (Stop Vertica on Host),然后单击确定 (OK)。
选择要停止的主机,然后单击确定 (OK)。
返回主菜单,选择查看数据库群集状态 (View Database Cluster State),然后单击确定 (OK)。您之前停止的主机应显示为 DOWN。
现在可执行维护工作。
有关在节点上重新启动 Vertica 的详细信息,请参阅在节点上重新启动 Vertica。
您可以使用命令行工具 stop_node 停止一个或多个节点上的 Vertica。stop_node 将一个或多个节点 IP 地址作为实参。例如,以下命令在两个节点上停止 Vertica:
$ admintools -t stop_node -s 192.0.2.1,192.0.2.2
在停止节点以执行维护任务(如升级硬件)之后,需要重新启动该节点才能重新连接数据库群集。
运行管理工具。从“主菜单 (Main Menu)”中选择在主机上重新启动 Vertica (Restart Vertica on Host),然后单击确定 (OK)。
选择数据库并单击确定 (OK)。
选择要重新启动的主机并单击确定 (OK)。
返回主菜单,选择查看数据库群集状态 (View Database Cluster State),然后单击确定 (OK)。您重新启动的主机现在显示为 UP,如下图所示。
创建节点时,Vertica 会自动将其类型设置为 PERMANENT
。这使 Vertica 可以使用此节点来存储数据。您可以使用
ALTER NODE
将节点的类型更改为以下类型之一:
PERMANENT:(默认值):存储数据的节点。
EPHEMERAL:从一种类型转换到另一种类型的节点——通常是从 PERMANENT 到 STANDBY 或 EXECUTE。
STANDBY:保留以在任何节点发生故障时替换该节点的节点。备用节点不存储段或数据,直到它被调用以替换故障节点。当用作替换节点时,Vertica 将其类型更改为 PERMANENT。有关详细信息,请参阅活动备用节点。
EXECUTE:该节点仅为计算目的而保留。执行节点不包含段或数据。
活动备用节点是企业模式数据库中可用于替换任何故障节点的节点。与标准 Vertica 节点不同,备用节点不执行计算或包含数据。如果永久节点出现故障,那么在故障节点超过故障转移时间限制后,活动备用节点可以替换故障节点。替换故障节点后,活动备用节点包含投影并执行它所替换的节点的全部计算。
可在创建数据库期间在企业模式数据库中创建活动备用节点,也可稍后创建此节点。
创建数据库,包括要用作活动备用节点的节点。
使用 vsql 连接到一个并非 您希望用作活动备用节点的节点。
使用 ALTER NODE 将此节点从永久节点转换为活动备用节点。例如:
=> ALTER NODE v_mart_node5 STANDBY;
在发出 ALTER NODE 语句后,受影响的节点会关闭并作为活动备用节点重新启动。
在创建要用作活动备用节点的节点时,应尽快将新节点更改为暂时状态,以防止群集将数据移动到新节点。
使用 vsql 连接到任何其他节点。
使用 ALTER NODE 将新节点从永久节点转换为暂时节点。例如:
=> ALTER NODE v_mart_node5 EPHEMERAL;
重新平衡群集,以便移除暂时节点中的所有数据。
在暂时节点上使用 ALTER NODE 将该节点转换为活动备用节点。例如:
=> ALTER NODE v_mart_node5 STANDBY;
企业模式数据库上的故障节点可以自动或手动替换为活动备用节点。
您可以使用参数 FailoverToStandbyAfter 配置故障节点的自动替换。如果启用了此参数,它将指定活动备用节点等待多长时间后替换故障节点。如果可以,Vertica 将从故障节点所在的容错组中选择备用节点。否则,Vertica 将随机选择一个可用的活动备用节点。
作为管理员,您可以使用 ALTER NODE 手动替换故障节点:
当企业模式数据库中的已关闭节点准备好重新激活时,您可以还原该节点,方法是将其替换节点恢复为备用状态。您可以使用 ALTER NODE 针对单个节点执行此操作,或使用 ALTER DATABASE 针对整个数据库执行此操作:
恢复备用节点。
使用 ALTER NODE 恢复单个节点:
ALTER NODE node‑name RESET;
使用 ALTER DATABASE 恢复整个数据库群集中的节点:
ALTER DATABASE DEFAULT RESET STANDBY;
如果关闭的节点无法恢复操作,Vertica 会忽略重置请求并将备用节点留在原地。
Vertica 使用 Spread 服务在数据库节点之间广播控制消息。此服务可以限制 Vertica 数据库群集的增长。随着群集节点数量的增加,Spread 服务的负载也会随着更多参与者交换消息而增加。负载增加后会降低群集的整体性能。此外,网络寻址将 Spread 服务中参与者的最大数量限制为 120(通常要少得多)。在这种情况下,您可以使用大型群集来克服这些 Spread 限制。
启用大型群集后,名为控制节点的群集节点子集使用 Spread 服务交换消息。群集中的其他节点会分配给这些控制节点之一,并依赖它们进行群集范围的通信。每个控制节点将消息从 Spread 服务传递到它的依赖节点。当依赖节点需要向群集中的其他节点广播消息时,它将消息传递给其控制节点,控制节点又将消息发送到其他依赖节点以及 Spread 服务。
通过设置控制节点和其他节点之间的依赖关系,您可以增加数据库节点的总数,并使节点总数符合 Spread 限制(即 120 个节点)。
大型群集功能的一个缺点是,如果一个控制节点发生故障,它的依赖节点就会与数据库群集的其余部分断开。这些节点无法参与数据库活动,Vertica 也认为它们已关闭。当控制节点恢复时,它会在其依赖节点与数据库之间重新建立通信,因此所有节点都重新加入群集。
当您的数据库启用了大型群集时,Vertica 会决定是将新添加的节点设为控制节点还是依赖节点,如下所示:
在企业模式下,如果为数据库群集配置的控制节点数大于其包含的当前节点数,Vertica 会将新节点设为控制节点。在 Eon 模式下,在子群集级别设置控制节点的数量。如果为包含新节点的子群集设置的控制节点数小于此设置,Vertica 会将新节点设为控制节点。
如果企业模式群集或 Eon 模式子群集已达到其控制节点限制,则新节点将成为现有控制节点的依赖节点。
当新添加的节点为依赖节点时,Vertica 会自动将其分配给控制节点。Vertica 选择哪个控制节点由数据库模式引导:
企业模式数据库:Vertica 将新节点分配给具有最少依赖项的控制节点。如果您在数据库中创建了容错组,Vertica 会在与新节点相同的容错组中选择一个控制节点。此功能允许您使用容错组来组织控制节点及其依赖项,以反映底层主机硬件的物理布局。例如,您可能希望依赖节点与其控制节点位于同一机架中。否则,影响整个机架的故障(如电源故障)不仅会导致机架中的节点关闭,而且还会导致其他架构中其控制节点位于受影响机架中的节点关闭。有关详细信息,请参阅容错组。
Eon 模式数据库:Vertica 始终将新节点添加到子群集。Vertica 将新节点分配给该子群集中具有最少依赖节点的控制节点。启用了大型群集的 Eon 模式数据库中的每个子群集都至少有一个控制节点。将依赖节点与其控制节点保持在同一个子群集中可以保持子群集隔离。
当将子群集添加到 Eon Mode 数据库时,Spread 服务的上限(120 名参与者)可能会导致错误。如果您的数据库群集有 120 个控制节点,则尝试创建子群集会失败并出现错误。每个子群集必须至少有一个控制节点。当您的群集有 120 个控制节点时,Vertica 无法为新的子群集创建控制节点。如果发生此错误,您必须在添加子群集之前减少数据库群集中的控制节点数量。
Vertica 在以下两种情况下自动启用大型群集:
数据库群集包含 120 个或更多节点。对于企业模式和 Eon 模式都是如此。
您创建一个包含 16 个或更多初始节点的 Eon 模式 子群集( 主要子群集或一个 辅助子群集)。
如果您通过向现有子群集添加节点来将其扩展到 16 个或更多节点,Vertica 不会自动启用大型群集。
您可以选择在 Vertica 自动启用大型群集之前手动启用大型群集模式。您的最佳做法是在数据库群集大小达到阈值时启用大型群集:
对于基于云的数据库,当群集包含 16 个或更多节点时启用大型群集。在云环境中,您的数据库使用点对点网络通信。在点对点通信模式下,Spread 服务的缩放性很差。当数据库群集达到 16 个节点时启用大型群集有助于限制因 Spread 服务处于点对点模式而造成的影响。
对于内部部署数据库,当群集包含 50 到 80 个节点时启用大型群集。在内部部署环境中,Spread 服务的缩放性较好。但是,当群集达到 50 到 80 个节点时,Spread 服务可能会开始出现性能问题。
在云或内部部署环境中,如果您开始注意到与 Spread 相关的性能问题,请启用大型群集。Spread 性能问题的症状包括:
Spread 服务上的负载开始导致性能问题。因为 Vertica 将 Spread 服务用于群集范围的控制消息,所以 Spread 性能问题会对数据库性能产生不利影响。对于基于云的数据库尤其如此,由于云基础架构中网络广播的性质,Spread 性能问题很快成为瓶颈。在内部部署数据库中,广播消息通常不太受关注,因为消息通常保留在本地子网中。即便如此,在群集达到 120 个节点时,在 Vertica 自动启用大型群集之前,Spread 问题通常会成为瓶颈。
群集中地址的压缩列表太大,无法放入最大传输单元 (MTU) 数据包(1478 字节)。MTU 数据包必须包含参与 Spread 服务的节点的所有地址。在理想情况下(当节点的 IP 地址为 1.1.1.1、1.1.1.2 等时),此数据包中可以容纳 120 个地址。这就是为什么当数据库群集达到 120 个节点时,Vertica 会自动启用大型群集。实际上,IP 地址的压缩列表将在 50 到 80 个节点处达到 MTU 数据包大小限制。
在计划将数据库群集扩展到需要使用大型群集的程度时,您应该考虑两个因素:
数据库群集应该有多少个控制节点?
这些控制节点应该如何分布?
当您手动启用大型群集或添加足够的节点触发 Vertica 自动启用大型群集时,一部分群集节点将成为控制节点。在少于 16 个节点的子群集中,所有节点都是控制节点。在许多情况下,可以将控制节点数设置为整个企业模式群集中或超过 16 个节点的 Eon 模式子群集中节点总数的平方根。但是,这个计算控制数量的公式不能保证总是能满足您的要求。
在选择数据库群集中控制节点的数量时,您必须平衡两个相互竞争的考虑因素:
如果控制节点发生故障或关闭,所有依赖于它的节点都将从数据库中断开。它们也会一直关闭到控制节点重新加入数据库。您可以通过增加群集中控制节点的数量来减少控制节点故障所带来的影响。
群集中的控制节点越多,spread 服务的负载就越大。在云环境中,网络环境广播越复杂,延迟时间越长。这种延迟可能会导致通过 spread 服务发送的消息需要更长的时间才能到达群集中的所有节点。
在云环境中,经验表明,16 个控制节点可以平衡可靠性和性能的需求。在 Eon 模式数据库中,每个子群集必须至少有一个控制节点。因此,如果您有 16 个以上子群集,则必须有 16 个以上控制节点。
在 Eon 模式数据库中,无论是内部部署数据库还是云中数据库,都要考虑向主子群集中添加比辅助子群集更多的控制节点。在 Eon 模式数据库中,只有主子群集中的节点才负责维护 K-Safety。因此,与辅助子群集中的控制节点故障相比,主子群集中的控制节点故障对数据库的影响可能更大。
在内部部署企业模式数据库中,在选择控制节点的数量时,请考虑运行数据库的主机的物理布局。如果您的主机分布在多个服务器机架上,则需要有足够的控制节点将它们分发到各个机架上。分发控制节点有助于确保在发生涉及整个机架的故障(如电源或网络交换机故障)时实现可靠性。您可以将数据库配置为没有节点依赖某个单独机架中的控制节点。将依赖性限制在机架内可防止在出现会影响整个机架的故障时,由于控制节点丢失而导致在机架外出现额外的节点丢失。
根据物理布局选择控制节点的数量还可以降低跨交换机的网络流量。通过将依赖节点放在与其控制节点相同的机架上,会使它们之间的通信保留在机架中,而不是通过网络交换机进行通信。
您可能需要增加控制节点的数量以将它们均匀地分布在各个机架上。例如,内部部署企业模式数据库共有 64 个节点,分布在三个机架上。对于此群集,对节点数求平方根将得到 8 个控制节点。但是,八个控制节点无法均匀地分布在三个机架中。相反,您可以有 9 个控制节点,并在每个机架上平均分布三个控制节点。
在确定群集的节点数后,需要确定如何在群集节点之间分布它们。Vertica 选择哪些节点成为控制节点。您可以影响 Vertica 如何选择控制节点以及哪些节点成为其依赖项。您使用的确切过程取决于数据库的模式:
企业模式内部部署数据库:定义容错组以影响控制节点的放置。依赖节点始终与其控制节点位于同一容错组中。您通常定义容错组来反映运行数据库的主机的物理布局。例如,您通常为单机架服务器中的节点定义一个或多个容错组。当容错组反映物理布局时,Vertica 会以可限制机架故障所带来影响的方式放置控制节点及其依赖项。有关详细信息,请参阅容错组。
Eon 模式数据库:使用子群集对控制节点的放置进行控制。每个子群集必须至少有一个控制节点。依赖节点始终与其控制节点位于同一个子群集中。您可以设置每个子群集的控制节点数。这样做可以让您将更多控制节点分配给主子群集,在这些子群集中,将控制节点发生故障的影响降至最低至关重要。
Vertica 可以自动选择整个群集(在企业模式下)或子群集(在 Eon 模式下)的控制节点数。它在以下情况下设置默认值:
当您将 default
关键字传递给
install_vertica
脚本的 --large-cluster
选项时(请参阅安装 Vertica 时启用大型群集)。
当数据库群集增长到 120 个或更多节点时,Vertica 会自动启用大型群集。
如果您创建超过 16 个节点的 Eon 模式子群集,Vertica 会自动启用大型群集。请注意,对于通过扩展超过 16 节点限制的子群集,Vertica 不会启用大型群集。它仅在一开始便超过 16 个节点时启用大型群集。
Vertica 选择的控制节点数取决于触发 Vertica 设置该值的原因。
如果您将 --large-cluster default
选项传递给
install_vertica
脚本,Vertica 会将控制节点数设置为初始群集中节点数的平方根。
如果数据库群集达到 120 个节点,Vertica 会通过将任何新添加的节点设为依赖项来启用大型群集。默认的控制节点数限值为 120。当达到此限制时,任何新添加的节点都将添加为依赖项。例如,假设您有一个包含 115 个节点的企业模式数据库群集,但您没有针对该群集手动启用大型群集。如果您向此群集中添加 10 个节点,Vertica 会添加 5 个节点作为控制节点(使您达到 120 个节点的限制),另外 5 个节点作为依赖项。
在 Eon 模式数据库中,每个子群集都有自己的控制节点数设置。Vertica 仅在您最初创建超过 16 个节点的子群集时自动设置控制节点的数量。在发生这种情况时,Vertica 会将子群集的控制节点数设置为子群集中节点数的平方根。
例如,假设您添加一个包含 25 个节点的新子群集。此子群集的节点数一开始便超过 16 个节点数限制,因此 Vertica 将此子群集的控制节点数设置为 5(即 25 的平方根)。其中五个节点被添加为控制节点,其余 20 个被添加为这五个节点的依赖项。
尽管每个子群集都有自己的控制节点数设置,但 Eon 模式数据库群集的控制节点总数仍然有 120 个节点的限制。
在以下情况下,Vertica 自动启用大型群集功能:
数据库群集中的节点总数超过 120。
创建的 Eon 模式子群集包含 16 个以上节点。
在大多数情况下,您应该考虑在群集大小达到这些阈值中的任何一个之前手动启用大型群集。有关何时启用大型群集的指南,请参阅计划大型群集。
您可以针对新 Vertica 数据库或现有数据库启用大型群集。
您可以在将 Vertica 安装到新数据库群集时启用大型群集。如果您从一开始就知道大型群集会对您的数据库带来好处,则这种选择非常有用。
在安装过程中,install_vertica 脚本的
‑‑large‑cluster
实参启用大型群集。它采用 1 到 120 之间的单个整数值,该值指定要在新数据库群集中创建的控制节点的数量。或者,此选项可以采用字面量实参 default
。在这种情况下,Vertica 启用大型群集模式并将控制节点的数量设置为您在
‑‑hosts
实参中提供的节点数的平方根。例如,如果 ‑‑hosts
指定 25 个主机并且 ‑‑large‑cluster
设置为 default
,则安装脚本会创建一个具有 5 个控制节点的数据库群集.
‑‑large‑cluster
实参的作用略有不同,具体取决于您在创建数据库时选择的数据库模式:
企业模式: ‑‑large‑cluster
设置整个数据库群集的控制节点总数。
Eon 模式: ‑‑large‑cluster
设置初始
默认子群集中的控制节点数。此设置对您稍后创建的
子群集不起作用。
您不能使用 ‑‑large‑cluster
将初始数据库中的控制节点数设置为高于您在 ‑‑hosts
实参中传递的主机数。安装程序将控制节点的数量设置为以下两个值中的较小值:传递给 ‑‑large‑cluster
选项的值或 ‑‑hosts
选项中的主机数。
您可以使用元函数 SET_CONTROL_SET_SIZE 将控制节点数设置为高于现有数据库中的当前节点数。在计划未来扩展时,您可以选择设置更大的数量来预分配控制节点。有关详细信息,请参阅更改控制节点的数量并重新对齐。
安装过程完成后,请使用 管理工具 或 管理控制台创建数据库。有关详细信息,请参阅创建空数据库。
对于在企业模式下运行的内部部署数据库,通常需要定义可反映主机物理布局的容错组。它们让您可以定义哪些主机位于相同的服务器机架中,并且依赖于相同的基础架构(例如电源和网络交换机)。掌握了这些情况,Vertica 可以重新对齐控制节点,使您的数据库能够更好地应对硬件故障。有关详细信息,请参阅容错组。
创建数据库后,默认情况下,您添加的任何节点都是依赖节点。可以使用元函数 SET_CONTROL_SET_SIZE 更改数据库中的控制节点数。
您可以在现有数据库中手动启用大型群集。通常,您会选择在数据库达到 Vertica 自动启用大型群集的点之前手动启用大型群集。请参阅何时启用大型群集,了解何时应该考虑启用大型群集。
使用元函数 SET_CONTROL_SET_SIZE 启用大型群集并设置控制节点数。您向此函数传递一个整数值,该值设置整个企业模式群集或 Eon 模式子群集中的控制节点数。
在企业模式下,可以更改整个数据库群集中控制节点的数量,或者在 Eon 模式下可以更改子群集中控制节点的数量。您可以选择更改群集或子群集中控制节点的数量,以减少控制节点丢失对数据库的影响。请参阅计划大型群集了解有关何时应更改数据库中控制节点数量的详细信息。
可以通过调用元函数 SET_CONTROL_SET_SIZE 来更改控制节点的数量。如果在调用 SET_CONTROL_SET_SIZE 之前未启用大型群集,则该函数会在您的数据库中启用大型群集。有关详细信息,请参阅启用大型群集。
在企业模式数据库中调用 SET_CONTROL_SET_SIZE 时,它会设置整个数据库群集中控制节点的数量。在 Eon 模式数据库中,除了提供控制节点的数量之外,还必须为 SET_CONTROL_SET_SIZE 提供一个子群集的名称。该函数设置该子群集的控制节点数。数据库群集中的其他子群集不受此调用的影响。
在更改 Eon 模式子群集中的控制节点数量之前,请验证该子群集是否正在运行。如果在子群集关闭时更改其控制节点的数量,可能会导致配置问题,从而阻止子群集中的节点启动。
您可以将控制节点数设置为高于群集或子群集中当前节点数的值。当控制节点数大于当前节点数时,新增节点成为控制节点,直到群集或子群集中的节点数达到您设置的控制节点数。
您可以选择将控制节点数设置为高于当前节点数,以计划未来的扩展。例如,假设您在 Eon 模式数据库中有一个 4 节点子群集,计划在将来扩展。您确定要将此群集中的控制节点数限制到 8 个,即使您将其扩展至超出该大小也是如此。在这种情况下,您现在可以选择将子群集的控制节点大小设置为 8。当您向该子群集添加新节点时,新节点将成为控制节点,直到子群集的大小达到 8 个。之后,Vertica 将新添加的节点分配为子群集中现有控制节点的依赖项。
调用 SET_CONTROL_SET_SIZE 函数后,必须执行多个其他步骤才能使新设置生效。
调用 REALIGN_CONTROL_NODES 函数。此函数告诉 Vertica 重新评估群集或子群集中控制节点及其依赖项的分配。在 Eon 模式数据库中调用此函数时,必须提供在其中更改控制节点设置的子群集的名称。
调用 RELOAD_SPREAD 函数。此函数更新配置文件中的控制节点分配信息,并触发 Spread 重新加载。
重新启动受控制节点变更影响的节点。在企业模式数据库中,必须重新启动整个数据库,才能确保所有节点都已更新配置信息。在 Eon 模式下,重新启动受变更影响的一个或多个子群集。如果您更改了关键子群集(例如唯一的 主子群集),则必须重新启动整个 Eon 模式数据库。
在企业模式数据库中,调用 START_REBALANCE_CLUSTER() 来重新平衡群集。此过程会转移伙伴实例投影分配以限制控制节点故障所带来的影响,从而提高数据库的容错能力。在 Eon 模式数据库中无需执行此步骤。
以下示例将企业模式数据库中 8 个节点中的 4 个节点设置为控制节点。它查询 LARGE_CLUSTER_CONFIGURATION_STATUS 系统表,该表显示数据库中每个节点的控制节点分配情况。一开始,所有节点都是自己的控制节点。有关与大型群集关联的系统表的详细信息,请参阅监控大型群集。
=> SELECT * FROM V_CATALOG.LARGE_CLUSTER_CONFIGURATION_STATUS;
node_name | spread_host_name | control_node_name
------------------+------------------+-------------------
v_vmart_node0001 | v_vmart_node0001 | v_vmart_node0001
v_vmart_node0002 | v_vmart_node0002 | v_vmart_node0002
v_vmart_node0003 | v_vmart_node0003 | v_vmart_node0003
v_vmart_node0004 | v_vmart_node0004 | v_vmart_node0004
v_vmart_node0005 | v_vmart_node0005 | v_vmart_node0005
v_vmart_node0006 | v_vmart_node0006 | v_vmart_node0006
v_vmart_node0007 | v_vmart_node0007 | v_vmart_node0007
v_vmart_node0008 | v_vmart_node0008 | v_vmart_node0008
(8 rows)
=> SELECT SET_CONTROL_SET_SIZE(4);
SET_CONTROL_SET_SIZE
----------------------
Control size set
(1 row)
=> SELECT REALIGN_CONTROL_NODES();
REALIGN_CONTROL_NODES
---------------------------------------------------------------
The new control node assignments can be viewed in vs_nodes.
Check vs_cluster_layout to see the proposed new layout. Reboot
all the nodes and call rebalance_cluster now
(1 row)
=> SELECT RELOAD_SPREAD(true);
RELOAD_SPREAD
---------------
Reloaded
(1 row)
=> SELECT SHUTDOWN();
重新启动数据库后,最后一步是重新平衡群集并查询 LARGE_CLUSTER_CONFIGURATION_STATUS 表以查看当前的控制节点分配情况:
=> SELECT START_REBALANCE_CLUSTER();
START_REBALANCE_CLUSTER
-------------------------
REBALANCING
(1 row)
=> SELECT * FROM V_CATALOG.LARGE_CLUSTER_CONFIGURATION_STATUS;
node_name | spread_host_name | control_node_name
------------------+------------------+-------------------
v_vmart_node0001 | v_vmart_node0001 | v_vmart_node0001
v_vmart_node0002 | v_vmart_node0002 | v_vmart_node0002
v_vmart_node0003 | v_vmart_node0003 | v_vmart_node0003
v_vmart_node0004 | v_vmart_node0004 | v_vmart_node0004
v_vmart_node0005 | v_vmart_node0001 | v_vmart_node0001
v_vmart_node0006 | v_vmart_node0002 | v_vmart_node0002
v_vmart_node0007 | v_vmart_node0003 | v_vmart_node0003
v_vmart_node0008 | v_vmart_node0004 | v_vmart_node0004
(8 rows)
以下示例在名为 analytics 的 8 节点辅助子群集中配置 4 个控制节点。主子群集未发生更改。此示例与前面的企业模式示例的主要区别在于:调用 SET_CONTROL_SET_SIZE 时需要指定子群集,不必重新启动整个数据库,也不必调用 START_REBALANCE_CLUSTER。
=> SELECT * FROM V_CATALOG.LARGE_CLUSTER_CONFIGURATION_STATUS;
node_name | spread_host_name | control_node_name
----------------------+----------------------+----------------------
v_verticadb_node0001 | v_verticadb_node0001 | v_verticadb_node0001
v_verticadb_node0002 | v_verticadb_node0002 | v_verticadb_node0002
v_verticadb_node0003 | v_verticadb_node0003 | v_verticadb_node0003
v_verticadb_node0004 | v_verticadb_node0004 | v_verticadb_node0004
v_verticadb_node0005 | v_verticadb_node0005 | v_verticadb_node0005
v_verticadb_node0006 | v_verticadb_node0006 | v_verticadb_node0006
v_verticadb_node0007 | v_verticadb_node0007 | v_verticadb_node0007
v_verticadb_node0008 | v_verticadb_node0008 | v_verticadb_node0008
v_verticadb_node0009 | v_verticadb_node0009 | v_verticadb_node0009
v_verticadb_node0010 | v_verticadb_node0010 | v_verticadb_node0010
v_verticadb_node0011 | v_verticadb_node0011 | v_verticadb_node0011
(11 rows)
=> SELECT subcluster_name,node_name,is_primary,control_set_size FROM
V_CATALOG.SUBCLUSTERS;
subcluster_name | node_name | is_primary | control_set_size
--------------------+----------------------+------------+------------------
default_subcluster | v_verticadb_node0001 | t | -1
default_subcluster | v_verticadb_node0002 | t | -1
default_subcluster | v_verticadb_node0003 | t | -1
analytics | v_verticadb_node0004 | f | -1
analytics | v_verticadb_node0005 | f | -1
analytics | v_verticadb_node0006 | f | -1
analytics | v_verticadb_node0007 | f | -1
analytics | v_verticadb_node0008 | f | -1
analytics | v_verticadb_node0009 | f | -1
analytics | v_verticadb_node0010 | f | -1
analytics | v_verticadb_node0011 | f | -1
(11 rows)
=> SELECT SET_CONTROL_SET_SIZE('analytics',4);
SET_CONTROL_SET_SIZE
----------------------
Control size set
(1 row)
=> SELECT REALIGN_CONTROL_NODES('analytics');
REALIGN_CONTROL_NODES
-----------------------------------------------------------------------------
The new control node assignments can be viewed in vs_nodes. Call
reload_spread(true). If the subcluster is critical, restart the database.
Otherwise, restart the subcluster
(1 row)
=> SELECT RELOAD_SPREAD(true);
RELOAD_SPREAD
---------------
Reloaded
(1 row)
此时,需要重新启动 analytics 子群集。可通过几个选项重新启动它。有关详细信息,请参阅启动和停止子群集。以下示例使用 admintools 命令行停止和启动子群集。
$ admintools -t stop_subcluster -d verticadb -c analytics -p password
*** Forcing subcluster shutdown ***
Verifying subcluster 'analytics'
Node 'v_verticadb_node0004' will shutdown
Node 'v_verticadb_node0005' will shutdown
Node 'v_verticadb_node0006' will shutdown
Node 'v_verticadb_node0007' will shutdown
Node 'v_verticadb_node0008' will shutdown
Node 'v_verticadb_node0009' will shutdown
Node 'v_verticadb_node0010' will shutdown
Node 'v_verticadb_node0011' will shutdown
Shutdown subcluster command successfully sent to the database
$ admintools -t restart_subcluster -d verticadb -c analytics -p password
*** Restarting subcluster for database verticadb ***
Restarting host [10.11.12.19] with catalog [v_verticadb_node0004_catalog]
Restarting host [10.11.12.196] with catalog [v_verticadb_node0005_catalog]
Restarting host [10.11.12.51] with catalog [v_verticadb_node0006_catalog]
Restarting host [10.11.12.236] with catalog [v_verticadb_node0007_catalog]
Restarting host [10.11.12.103] with catalog [v_verticadb_node0008_catalog]
Restarting host [10.11.12.185] with catalog [v_verticadb_node0009_catalog]
Restarting host [10.11.12.80] with catalog [v_verticadb_node0010_catalog]
Restarting host [10.11.12.47] with catalog [v_verticadb_node0011_catalog]
Issuing multi-node restart
Starting nodes:
v_verticadb_node0004 (10.11.12.19) [CONTROL]
v_verticadb_node0005 (10.11.12.196) [CONTROL]
v_verticadb_node0006 (10.11.12.51) [CONTROL]
v_verticadb_node0007 (10.11.12.236) [CONTROL]
v_verticadb_node0008 (10.11.12.103)
v_verticadb_node0009 (10.11.12.185)
v_verticadb_node0010 (10.11.12.80)
v_verticadb_node0011 (10.11.12.47)
Starting Vertica on all nodes. Please wait, databases with a large catalog may take a while to initialize.
Node Status: v_verticadb_node0004: (DOWN) v_verticadb_node0005: (DOWN) v_verticadb_node0006: (DOWN)
v_verticadb_node0007: (DOWN) v_verticadb_node0008: (DOWN) v_verticadb_node0009: (DOWN)
v_verticadb_node0010: (DOWN) v_verticadb_node0011: (DOWN)
Node Status: v_verticadb_node0004: (DOWN) v_verticadb_node0005: (DOWN) v_verticadb_node0006: (DOWN)
v_verticadb_node0007: (DOWN) v_verticadb_node0008: (DOWN) v_verticadb_node0009: (DOWN)
v_verticadb_node0010: (DOWN) v_verticadb_node0011: (DOWN)
Node Status: v_verticadb_node0004: (INITIALIZING) v_verticadb_node0005: (INITIALIZING) v_verticadb_node0006:
(INITIALIZING) v_verticadb_node0007: (INITIALIZING) v_verticadb_node0008: (INITIALIZING)
v_verticadb_node0009: (INITIALIZING) v_verticadb_node0010: (INITIALIZING) v_verticadb_node0011: (INITIALIZING)
Node Status: v_verticadb_node0004: (UP) v_verticadb_node0005: (UP) v_verticadb_node0006: (UP)
v_verticadb_node0007: (UP) v_verticadb_node0008: (UP) v_verticadb_node0009: (UP)
v_verticadb_node0010: (UP) v_verticadb_node0011: (UP)
Syncing catalog on verticadb with 2000 attempts.
子群集重新启动后,可以查询系统表以查看控制节点配置:
=> SELECT * FROM V_CATALOG.LARGE_CLUSTER_CONFIGURATION_STATUS;
node_name | spread_host_name | control_node_name
----------------------+----------------------+----------------------
v_verticadb_node0001 | v_verticadb_node0001 | v_verticadb_node0001
v_verticadb_node0002 | v_verticadb_node0002 | v_verticadb_node0002
v_verticadb_node0003 | v_verticadb_node0003 | v_verticadb_node0003
v_verticadb_node0004 | v_verticadb_node0004 | v_verticadb_node0004
v_verticadb_node0005 | v_verticadb_node0005 | v_verticadb_node0005
v_verticadb_node0006 | v_verticadb_node0006 | v_verticadb_node0006
v_verticadb_node0007 | v_verticadb_node0007 | v_verticadb_node0007
v_verticadb_node0008 | v_verticadb_node0004 | v_verticadb_node0004
v_verticadb_node0009 | v_verticadb_node0005 | v_verticadb_node0005
v_verticadb_node0010 | v_verticadb_node0006 | v_verticadb_node0006
v_verticadb_node0011 | v_verticadb_node0007 | v_verticadb_node0007
(11 rows)
=> SELECT subcluster_name,node_name,is_primary,control_set_size FROM subclusters;
subcluster_name | node_name | is_primary | control_set_size
--------------------+----------------------+------------+------------------
default_subcluster | v_verticadb_node0001 | t | -1
default_subcluster | v_verticadb_node0002 | t | -1
default_subcluster | v_verticadb_node0003 | t | -1
analytics | v_verticadb_node0004 | f | 4
analytics | v_verticadb_node0005 | f | 4
analytics | v_verticadb_node0006 | f | 4
analytics | v_verticadb_node0007 | f | 4
analytics | v_verticadb_node0008 | f | 4
analytics | v_verticadb_node0009 | f | 4
analytics | v_verticadb_node0010 | f | 4
analytics | v_verticadb_node0011 | f | 4
(11 rows)
要禁用大型群集,请调用值为 -1 的 SET_CONTROL_SET_SIZE。该值是非大型群集数据库的默认值。它通知 Vertica 将所有节点设为控制节点。
在 Eon 模式数据库中,为了完全禁用大型群集,必须在每个具有设定控制节点数的子群集中将控制节点数设置为 -1。可以通过查询 V_CATALOG.SUBCLUSTERS 系统表的 CONTROL_SET_SIZE 列来查看哪些子群集具有设定数量的控制节点。
以下示例重置在前面的 Eon 模式示例中设置的控制节点数。
=> SELECT subcluster_name,node_name,is_primary,control_set_size FROM subclusters;
subcluster_name | node_name | is_primary | control_set_size
--------------------+----------------------+------------+------------------
default_subcluster | v_verticadb_node0001 | t | -1
default_subcluster | v_verticadb_node0002 | t | -1
default_subcluster | v_verticadb_node0003 | t | -1
analytics | v_verticadb_node0004 | f | 4
analytics | v_verticadb_node0005 | f | 4
analytics | v_verticadb_node0006 | f | 4
analytics | v_verticadb_node0007 | f | 4
analytics | v_verticadb_node0008 | f | 4
analytics | v_verticadb_node0009 | f | 4
analytics | v_verticadb_node0010 | f | 4
analytics | v_verticadb_node0011 | f | 4
(11 rows)
=> SELECT SET_CONTROL_SET_SIZE('analytics',-1);
SET_CONTROL_SET_SIZE
----------------------
Control size set
(1 row)
=> SELECT REALIGN_CONTROL_NODES('analytics');
REALIGN_CONTROL_NODES
---------------------------------------------------------------------------------------
The new control node assignments can be viewed in vs_nodes. Call reload_spread(true).
If the subcluster is critical, restart the database. Otherwise, restart the subcluster
(1 row)
=> SELECT RELOAD_SPREAD(true);
RELOAD_SPREAD
---------------
Reloaded
(1 row)
-- After restarting the analytics subcluster...
=> SELECT * FROM V_CATALOG.LARGE_CLUSTER_CONFIGURATION_STATUS;
node_name | spread_host_name | control_node_name
----------------------+----------------------+----------------------
v_verticadb_node0001 | v_verticadb_node0001 | v_verticadb_node0001
v_verticadb_node0002 | v_verticadb_node0002 | v_verticadb_node0002
v_verticadb_node0003 | v_verticadb_node0003 | v_verticadb_node0003
v_verticadb_node0004 | v_verticadb_node0004 | v_verticadb_node0004
v_verticadb_node0005 | v_verticadb_node0005 | v_verticadb_node0005
v_verticadb_node0006 | v_verticadb_node0006 | v_verticadb_node0006
v_verticadb_node0007 | v_verticadb_node0007 | v_verticadb_node0007
v_verticadb_node0008 | v_verticadb_node0008 | v_verticadb_node0008
v_verticadb_node0009 | v_verticadb_node0009 | v_verticadb_node0009
v_verticadb_node0010 | v_verticadb_node0010 | v_verticadb_node0010
v_verticadb_node0011 | v_verticadb_node0011 | v_verticadb_node0011
(11 rows)
=> SELECT subcluster_name,node_name,is_primary,control_set_size FROM subclusters;
subcluster_name | node_name | is_primary | control_set_size
--------------------+----------------------+------------+------------------
default_subcluster | v_verticadb_node0001 | t | -1
default_subcluster | v_verticadb_node0002 | t | -1
default_subcluster | v_verticadb_node0003 | t | -1
analytics | v_verticadb_node0004 | f | -1
analytics | v_verticadb_node0005 | f | -1
analytics | v_verticadb_node0006 | f | -1
analytics | v_verticadb_node0007 | f | -1
analytics | v_verticadb_node0008 | f | -1
analytics | v_verticadb_node0009 | f | -1
analytics | v_verticadb_node0010 | f | -1
analytics | v_verticadb_node0011 | f | -1
(11 rows)
通过查询以下系统表来监控大型群集的特征:
V_CATALOG.LARGE_CLUSTER_CONFIGURATION_STATUS — 显示编录中的当前 spread 主机和控制目标,以便查看它们是否匹配。
V_MONITOR.CRITICAL_HOSTS — 列出故障可能会导致数据库不安全并强制关机的主机。
在 Eon 模式数据库中,V_CATALOG.SUBCLUSTERS 系统表的 CONTROL_SET_SIZE 列显示为每个子群集设置的控制节点数。
您可能还要查询以下系统表:
V_CATALOG.FAULT_GROUPS — 显示容错组及其在群集中的层次结构。
V_CATALOG.CLUSTER_LAYOUT — 显示参与数据库群集的节点和影响这些节点的容错组的实际排列的相对位置。
容错组允许您为物理群集布局配置企业模式数据库。共享您的群集拓扑让您可以使用 Terrace 路由来减少大型查询的缓冲区要求。它还有助于最大限度地降低环境中固有的相关故障所带来的风险,这些故障通常由共享资源引起。
Vertica 将在大型群集排列中的 控制节点(运行 spread 的服务器)周围自动创建容错组,将共享控制节点的节点放在同一容错组中。自动容错组和用户定义的容错组不包括暂时节点,因为类似的节点不容纳数据。
如果要执行以下操作,请考虑定义您自己的特定于群集物理布局的容错组:
使用 Terrace 路由来减少大型查询的缓冲区要求。
降低相关故障的风险。例如,通过定义机架布局,Vertica 对故障的容忍度更高。
影响控制节点在群集中的放置。
Vertica 支持使用具有不同形状和大小的复杂、层次结构容错组。数据库平台提供容错组脚本(DDL 生成器)、SQL 语句、系统表和其他监控工具。
有关包含群集拓扑示例的容错组的概述,请参阅使用容错组的高可用性。
为了帮助您定义群集的容错组,Vertica 在
/opt/vertica/scripts
目录中提供了一个名为 fault_group_ddl_generator.py
的脚本。您需要利用此脚本生成的 SQL 语句来创建容错组。
fault_group_ddl_generator.py
脚本不会为您创建容错组,但您可以将输出复制到文件中。然后,当您运行帮助脚本时,可以使用 \i
或
vsql–f
命令来将群集拓扑传递到 Vertica。
容错组脚本采用以下实参:
数据库名称
容错组输入文件名
例如:
$ python /opt/vertica/scripts/fault_group_ddl_generator.py VMartdb fault_grp_input.out
使用文本编辑器,为目标群集创建容错组输入文件。
以下示例显示如何为具有 8 个机架且每个机架上有 8 个节点的群集创建容错组输入文件,群集中总共有 64 个节点。
在文件的第一行,列出父(顶级)容错组,以空格分隔。
rack1 rack2 rack3 rack4 rack5 rack6 rack7 rack8
在随后的行中,列出父容错组,后跟等号 (=)。在等号之后,列出以空格分隔的节点或容错组。
<parent> = <child_1> <child_2> <child_n...>
如:
rack1 = v_vmart_node0001 v_vmart_node0002 v_vmart_node0003 v_vmart_node0004
rack2 = v_vmart_node0005 v_vmart_node0006 v_vmart_node0007 v_vmart_node0008
rack3 = v_vmart_node0009 v_vmart_node0010 v_vmart_node0011 v_vmart_node0012
rack4 = v_vmart_node0013 v_vmart_node0014 v_vmart_node0015 v_vmart_node0016
rack5 = v_vmart_node0017 v_vmart_node0018 v_vmart_node0019 v_vmart_node0020
rack6 = v_vmart_node0021 v_vmart_node0022 v_vmart_node0023 v_vmart_node0024
rack7 = v_vmart_node0025 v_vmart_node0026 v_vmart_node0027 v_vmart_node0028
rack8 = v_vmart_node0029 v_vmart_node0030 v_vmart_node0031 v_vmart_node0032
在父容错组的第一行之后,编写容错组描述的顺序并不重要。您在此文件中定义的所有容错组都必须引用父容错组。您可以直接指示父组,也可以通过指定作为父容错组子级的容错组的子级来指示。
如:
rack1 rack2 rack3 rack4 rack5 rack6 rack7 rack8
rack1 = v_vmart_node0001 v_vmart_node0002 v_vmart_node0003 v_vmart_node0004
rack2 = v_vmart_node0005 v_vmart_node0006 v_vmart_node0007 v_vmart_node0008
rack3 = v_vmart_node0009 v_vmart_node0010 v_vmart_node0011 v_vmart_node0012
rack4 = v_vmart_node0013 v_vmart_node0014 v_vmart_node0015 v_vmart_node0016
rack5 = v_vmart_node0017 v_vmart_node0018 v_vmart_node0019 v_vmart_node0020
rack6 = v_vmart_node0021 v_vmart_node0022 v_vmart_node0023 v_vmart_node0024
rack7 = v_vmart_node0025 v_vmart_node0026 v_vmart_node0027 v_vmart_node0028
rack8 = v_vmart_node0029 v_vmart_node0030 v_vmart_node0031 v_vmart_node0032
创建容错组输入文件后,即可运行 fault_group_ddl_generator.py
。您需要利用此脚本生成的 DDL 语句来在 Vertica 中创建容错组。
如果您的 Vertica 数据库共置于一个 Hadoop 群集上,并且该群集使用多个机架,您可以使用容错组来提高性能。请参阅配置机架位置。
在定义容错组时,Vertica 会将数据段分发到整个群集中。这使得群集能够了解群集拓扑,以便它可以容忍环境中固有的相关故障,例如机架故障。有关概览,请参阅容错组的高可用性。
要定义容错组,您必须具有:
超级用户权限
现有数据库
以数据库管理员身份运行 fault_group_ddl_generator.py
脚本:
python /opt/vertica/scripts/fault_group_ddl_generator.py databasename fault-group-inputfile > sql‑filename
例如,以下命令将 Python 脚本输出写入 SQL 文件 fault_group_ddl.sql
中。
$ python /opt/vertica/scripts/fault_group_ddl_generator.py
VMart fault_groups_VMart.out > fault_group_ddl.sql
在脚本返回后,可以运行 SQL 文件,而不是单独运行多个 DDL 语句。
使用
vsql,运行 fault_group_ddl.sql
中的 DDL 语句或使用
vsql 执行该文件中的命令。
=> \i fault_group_ddl.sql
如果启用了大型群集,请使用 REALIGN_CONTROL_NODES 重新对齐控制节点。否则,跳过此步骤。
=> SELECT REALIGN_CONTROL_NODES();
通过调用 RELOAD_SPREAD 将群集更改保存到 Spread 配置文件:
=> SELECT RELOAD_SPREAD(true);
使用 管理工具 (Administration Tools) 重新启动数据库。
通过调用 REBALANCE_CLUSTER 将更改保存到群集的数据布局:
=> SELECT REBALANCE_CLUSTER();
您可以通过查询 Vertica 系统表或登录到管理控制台界面监控容错组。
使用以下系统表查看与容错组和群集缺陷有关的信息,例如在不关闭数据库的情况下群集无法丢失的节点:
V_CATALOG.FAULT_GROUPS:查看群集中所有容错组的层次结构。
V_CATALOG.CLUSTER_LAYOUT:观察参与数据业务的节点的排列以及影响这些节点的容错组。临时节点不显示在群集布局环中,因为它们没有任何数据。
MC 管理员可以通过以下步骤监控和突出显示感兴趣的容错组:
单击要监控的正在运行的数据库,然后在任务栏中单击管理 (Manage)。
打开容错组视图 (Fault Group View) 菜单,然后选择要查看的容错组。
(可选)隐藏不在选定容错组中的节点以关注感兴趣的容错组。
分配给容错组的节点每个都在节点图标左上角有一个彩色气泡。每个容错组都有一个独特的颜色。如果容错组的数量超过可用的颜色数量,MC 将循环使用先前使用的颜色。
由于 Vertica 支持不同形状和大小的复杂分层容错组,因此 MC 将多个容错组的参与显示为一堆不同颜色的气泡。气泡越高,代表容错组的层次越低,这意味着气泡更接近父容错组(而不是子容错组或孙容错组)。
有关容错组层次结构的详细信息,请参阅容错组的高可用性。
从群集中移除容错组时,请注意删除操作会移除指定的容错组及其子容错组。Vertica 将所有节点都放在删除的容错组的父代下面。要查看群集中的当前容错组层次结构,请查询
FAULT_GROUPS
系统表。
使用 DROP FAULT GROUP
语句从群集中移除容错组。以下示例显示了如何删除 group2
容错组:
=> DROP FAULT GROUP group2;
DROP FAULT GROUP
使用 ALTER DATABASE
语句从指定数据库群集中删除所有容错组以及任何子容错组。
以下命令将从当前数据库中删除所有容错组。
=> ALTER DATABASE DEFAULT DROP ALL FAULT GROUP;
ALTER DATABASE
要将节点重新添加到容错组中,必须手动将其重新分配到新容错组或现有容错组。要执行此操作,请使用 CREATE FAULT GROUP
和 ALTER FAULT GROUP..ADD NODE
语句。
Terrace 路由可以显著减少大型群集数据库上的消息缓冲。以下几部分介绍 Vertica 如何在企业模式数据库和 Eon 模式数据库上实现 Terrace 路由。
企业模式数据库上的 Terrace 路由是通过用来定义基于机架的拓扑的容错组实现的。在禁用 Terrace 路由的大型群集中,Vertica 群集中的节点形成一个完全互连的网络,其中每个非依赖(控制)节点通过与所有其他非依赖节点(无论是在其自己的机架/容错组内部还是外部)的连接在数据库群集中发送消息:
在这种情况下,大型 Vertica 群集可能需要每个节点上有许多连接,其中每个连接都会产生自己的网络缓冲要求。每个节点所需的缓冲区总数按如下公式计算:
(numRacks * numRackNodes) - 1
在如上所示每个机架 4 个节点的双机架群集中,求解结果为每个节点 7 个缓冲区。
启用 Terrace 路由后,可以显著减少大型群集网络缓冲。机架/容错组中的每第 n 个节点与所有其他容错组的相应第 n 个节点配对。例如,在启用 Terrace 路由时,同一个双机架群集中的消息传递现在按如下方式实现:
因此,来自机架 A (A2) 上节点 2 的消息将发送到机架 A 上的所有其他节点;然后,每个机架 A 节点将消息传送到机架 B 上的相应节点 — A1 到 B1,A2 到 B2,依此类推。
在启用 Terrace 路由时,给定机架的每个节点都避免了为所有其他节点维护消息缓冲区的开销。相反,每个节点只负责维护与以下节点的连接:
同一机架的所有其他节点 (
numRackNodes - 1
)
每个其他机架上的一个节点 (
numRacks - 1
)
因此,每个节点所需的消息缓冲区总数按如下公式计算:
(numRackNodes-1) + (numRacks-1)
在如上所示具有 4 个节点的双机架群集中,求解结果为每个节点 4 个缓冲区。
Terrace 路由以时间(机架内跃点数)换取空间(网络消息缓冲区数)。通过向群集中添加额外的机架和节点来扩展群集时,支持这种权衡的论点变得越来越有说服力:
在这个每个机架 4 个节点的三机架群集中,如果没有 Terrace 路由,每个节点所需的缓冲区数量为 11。如果有 Terrace 路由,每个节点的缓冲区数量为 5。通过向群集中添加机架和向每个机架中添加节点来扩展群集时,缓冲区要求之间的差异也会扩大。例如,给定一个每个机架 16 个节点的六机架群集,如果没有 Terrace 路由,每个节点所需的缓冲区数量为 95;如果有 Terrace 路由,则为 20。
Terrace 路由取决于容错组定义,这些定义描述围绕机架及其成员节点组织的群集网络拓扑。如前所述,启用 Terrace 路由时,Vertica 首先在机架/容错组内分发数据;然后使用第 n 个节点到第n 个节点的映射将此数据转发到数据库群集中的所有其他机架。
您可以通过 TerraceRoutingFactor 配置参数为任何实现基于机架的容错组的企业模式大型群集启用(或禁用)Terrace 路由。要启用 Terrace 路由,请按如下方式设置此参数:
其中:
numRackNodes:机架中的节点数
numRacks:群集中的机架数
例如:
默认情况下,TerraceRoutingFactor 设置为 2,这通常可以确保为任何实现基于机架的容错组的企业模式大型群集启用 Terrace 路由。当群集包含 64 个或更多节点时,或者如果查询需要过多的缓冲空间,Vertica 建议启用 Terrace 路由。
要禁用 Terrace 路由,请将 TerraceRoutingFactor 设置为一个大整数,例如 1000:
=> ALTER DATABASE DEFAULT SET TerraceRoutingFactor = 1000;
与企业模式一样,Terrace 路由在 Eon 模式数据库上默认处于启用状态,并通过容错组实现。但是,您不会为 Eon 模式数据库创建容错组。相反,Vertica 会自动在大型群集数据库上创建容错组;这些容错组围绕每个子群集的控制节点及其依赖项进行配置。这些容错组由 Vertica 内部管理,无法供用户访问。
您可以根据数据库需要,扩大或缩减群集。最常见的情况是向数据库群集添加节点以容纳更多数据,并提供更好的查询性能。但是,如果发现群集过度配置或需要将硬件用于其他用途,您也可以缩减群集。
您可以通过添加或移除节点来对群集进行扩展或缩减。添加或移除节点时无需关闭或重新启动数据库。添加节点后或移除节点前,Vertica 会开始重新平衡过程,即在群集中移动数据,以填充新节点或将数据从要从数据库移除的节点中移出。在此过程中,数据也可能在未在添加或移除的节点之间交换,以此来维持强大的智能 K-safety。如果 Vertica 确定由于磁盘空间不足,数据不能在一次迭代中重新平衡,则会在多次迭代中完成重新平衡。
为确保在扩展或缩减群集时更高效地实现数据重新平衡,Vertica 会在每个节点本地对数据存储进行分段,以便它能够轻松移动到群集中的其他节点。将新节点添加到群集时,群集中的现有节点会放弃某些数据段以填充新节点并交换数据段,从而将任何一个节点所依赖的节点数量保持为最低。此策略会将节点出现故障时成为关键节点的节点数量保持为最低(请参阅 关键节点/企业模式数据库中的 K-safety)。从群集中移除节点时,该节点的所有存储容器都会移动到群集中的其他节点(这时也会重新迁移数据段来最大限度减少节点出现故障时成为关键节点的节点数量)。这种将数据拆分为便于迁移的数据段的方式称为弹性群集,因为它有利于轻松扩展或缩减群集。
弹性群集的替代方案是对投影中的所有数据进行重新分段,并在添加或移除节点时将这些数据均匀分布到数据库中的所有节点。此方法需要处理的工作量更多,占用的磁盘空间也更多,因为它实质上需要转储并重新加载所有投影中的所有数据。
在新安装中,每个节点都有一个指定本地数据段数量的伸缩系数(请参阅伸缩系数)。重新平衡可以通过重新迁移本地数据段来高效地重新分布数据,前提是在添加或移除节点后,群集中具有足够的本地数据段来均匀地重新分布数据(由 MAXIMUM_SKEW_PERCENT 确定)。例如,如果伸缩系数 = 8,并且最初有 5 个节点,那么整个群集中总共有 40 个本地数据段。
如果添加两个额外的节点(7 个节点),Vertica 会在 2 个节点上重新定位 5 个本地分段,在 5 个节点上重新定位 6 个此类分段,从而导致大约 16.7% 的偏差。只有当得到的偏差率低于允许的阈值(这由 MAXIMUM_SKEW_PERCENT 确定)时,重新平衡机制才重新定位本地分段。否则,重新平衡机制会在这 7 个节点上均匀分布分段空间(进而均匀分布数据,如果数据在此空间中均匀分布的话),并为每个节点划定新的本地数据段边界,确保每个节点再次拥有 8 个本地数据段。
默认情况下,只有当 Vertica 重新平衡数据库时,伸缩系数才会发挥作用。重新平衡时,每个节点都会将它包含的投影分段拆分到存储容器中,然后根据需要将存储容器移动至其他节点。重新平衡后,数据会重新合并到
使用 ENABLE_ELASTIC_CLUSTER 启用弹性群集。查询 ELASTIC_CLUSTER 系统表来验证弹性群集是否已启用:
=> SELECT is_enabled FROM ELASTIC_CLUSTER;
is_enabled
------------
t
(1 row)
为避免 ROS 容器数量增加,请不要启用局部分段,也不要更改伸缩系数。
要查看伸缩系数,请查询 ELASTIC_CLUSTER 表:
=> SELECT scaling_factor FROM ELASTIC_CLUSTER;
scaling_factor
---------------
4
(1 row)
=> SELECT SET_SCALING_FACTOR(6);
SET_SCALING_FACTOR
--------------------
SET
(1 row)
=> SELECT scaling_factor FROM ELASTIC_CLUSTER;
scaling_factor
---------------
6
(1 row)
比例因子决定了在启用局部分段时,Vertica 在重新平衡期间用于跨数据库存储每个投影的存储容器的数量。在设置比例因子时,请遵循以下准则:
存储容器的数量应大于或等于分区数量与本地分段数量的积:
num‑storage‑containers >= ( num‑partitions * num‑local‑segments
)
若将比例因子设置得足够高,以便重新平衡可以传输本地分段以满足偏移阈值,但将值设置得很小,以便存储容器数量将不会产生过多的 ROS 容器,并导致 ROS 推回)。ROS 容器的最大数量(默认为 1024)由配置参数 ContainersPerProjectionLimit 设置。
使用 SET_SCALING_FACTOR 函数可以更改数据库的伸缩系数。伸缩系数可以为 1 到 32 之间的整数。
=> SELECT SET_SCALING_FACTOR(12);
SET_SCALING_FACTOR
--------------------
SET
(1 row)
默认情况下,只有当 Vertica 重新平衡数据库时,伸缩系数才会发挥作用。在重新平衡期间,节点会将其包含的投影分段分解到多个存储容器中,以便它们能够快速移动到其他节点。
与对整个投影进行重新分段相比,此过程更加高效(特别是,它需要更少的可用磁盘空间),但是仍然需要较大开销,因为必须将存储容器分离到多个局部分段中,然后将其中一些局部分段传输到其他节点。如果您很少从数据库中添加或移除节点,此开销并不算是一个问题。
但是,如果您的数据库增长快速并且持续处于繁忙状态,则可能会发现添加节点的过程将变得具有破坏性。这种情况下,您可以启用局部分段,这会告诉 Vertica 始终根据伸缩系数对其数据进行分段,因此始终将数据分解到多个易于移动的容器中。通过这种方式对数据进行分段会显著提高添加或删除节点的速度,因为数据始终处于一种可以快速重新定位到另一个节点的状态。Vertica 在添加或移除节点后执行的重新平衡过程只需要决定要重新定位的存储容器即可,无须首先将数据分解到存储容器中。
局部数据分段会增加存储在每个节点上的存储容器数量。除非表包含许多分区,否则这算不上一个问题。(例如,如果表按天进行分区并包含一个或多个年份。)如果启用局部数据分段,则会将其中每个表分区分解到多个局部存储分段中,这可能会生成大量文件,进而可能导致 ROS“推回”。请先考虑表分区以及启用局部数据分段可能造成的影响,然后再启用此功能。
要启用局部分段,请使用 ENABLE_LOCAL_SEGMENTS 函数。要禁用局部分段,请使用 DISABLE_LOCAL_SEGMENTATION 函数:
=> SELECT ENABLE_LOCAL_SEGMENTS();
ENABLE_LOCAL_SEGMENTS
-----------------------
ENABLED
(1 row)
=> SELECT is_local_segment_enabled FROM elastic_cluster;
is_enabled
------------
t
(1 row)
=> SELECT DISABLE_LOCAL_SEGMENTS();
DISABLE_LOCAL_SEGMENTS
------------------------
DISABLED
(1 row)
=> SELECT is_local_segment_enabled FROM ELASTIC_CLUSTER;
is_enabled
------------
f
(1 row)
下面是一些关于局部分段的最佳实践。
局部数据分段 可以显著加快调整群集大小的过程。在以下情况下,应当启用局部数据分段:
数据库不包含具有数百个分区的表。
数据库群集中的节点数为 2 的幂次方。
计划扩大或缩小群集大小。
局部分段可能会导致具有数百个分区的表或节点数不是 2 的幂次方的群集的存储容器数量过多。如果您的数据库具有这两个特征,在启用局部分段时请小心。
在 Vertica 安装中添加一个或多个节点的原因有许多:
将数据库设置为 K-safe ( K-safety=1) 或将 K-safety 增加至 2。有关详细信息,请参阅故障恢复。
**交换节点进行维护。**使用备用计算机临时接管需要维护的现有节点的活动。需要维护的节点是事先已知的,因此将此节点从服务中暂时删除时,群集并不会轻易受到其他节点故障的影响。
**替换节点。**永久性添加节点,以移除过时或有故障的硬件。
localhost
),则无法扩展群集。必须重新安装 Vertica 并指定 IP 地址或主机名(非 localhost/127.0.0.1
)。
添加节点时,包括以下常规任务:
Vertica 强烈建议在执行这项重大操作前先备份数据库,因为在操作过程中需要创建新投影,刷新投影,然后删除旧投影。有关详细信息,请参阅备份和还原数据库。
迁移投影设计以包括其他节点的过程可能需要一些时间;但在此期间,数据库上的所有用户活动都可以使用旧投影正常进行。
配置要添加至群集的主机。
请参阅在安装 Vertica 之前。此外,还需要编辑群集中所有现有节点上的主机配置文件,以确保这些节点可以解析新的主机。
将已添加至群集的主机添加到数据库中(第 3 步)。
“主机”添加到数据库后即变为一个“节点”。可使用
管理工具或
管理控制台将节点添加到数据库(请参阅使用 MC 进行监控)。
也可以使用 admintools
命令行添加节点,这样可以保留所添加节点的特定顺序。
向数据库中添加节点之后,Vertica 会自动将更新后的配置文件分布到群集中的其余节点,并启动在群集中重新平衡数据的过程。有关详细信息,请参阅在节点之间重新平衡数据。
备份数据库并配置要添加到群集的主机后,此时可以使用
update_vertica
脚本将主机添加到群集。
您不能使用 MC 向内部部署环境中的群集中添加主机。但是,在向群集中添加主机后,MC 确实允许您将主机作为节点添加到数据库中。
如果将 Vertica 安装在一个节点上,但没有指定 IP 地址或主机名(使用了 localhost),则无法扩展群集。您必须重新安装 Vertica,并指定 IP 地址或主机名。
在一个现有群集主机中,运行至少带有
\--add-hosts host(s)
参数(其中 host(s) 是要添加到群集的系统的主机名或 IP 地址)和 --rpm
或 --deb
参数的 update_vertica 脚本。
# /opt/vertica/sbin/update_vertica --add-hosts host(s) --rpm package
update_vertica
* * 脚本使用与
install_vertica
相同的所有选项,并执行以下操作:
在新主机中,安装 Vertica RPM。
执行安装后检查,包括 RPM 版本和 N 向网络连接检查。
将 spread 更改为包含更大的群集。
配置管理工具以处理更大的群集。
重要提示:
节点达到或超过 50 个时,请考虑使用 --large-cluster
。
使用要添加到群集的系统的主机名或 IP 地址可以指定主机。但是,在内部,Vertica 将所有主机地址都存储为 IP 地址。
如果指定了一个以上的主机,请勿在随 --add-hosts
提供的主机名/IP 地址列表中使用空格。
如果使用 --rpm/--deb
指定包,并且此包比当前在现有群集中安装的包要新,则 Vertica 会在安装新添加的主机之前,首先将新包安装在现有群集主机中。
参照最初安装群集时使用的参数,为数据库管理员用户名、密码和目录路径使用相同的命令行参数。或者,您可以创建一个属性文件保存安装期间的参数,然后在后续安装和更新时重复使用该属性文件。请参阅静默安装 Vertica。
如果使用 sudo 安装,数据库管理员用户 (dbadmin) 必须已存在于要添加的主机中,并且必须配置与现有主机相同的密码和主目录路径。如有必要,Vertica 会从现有主机建立与新主机的免密码 SSH。
如果最初使用 --point-to-point
选项将 spread 配置为在子网中节点之间使用直接点对点通信,则无论何时运行 install_vertica
或 update_vertica
,均应使用 --point-to-point
选项。否则,群集的配置将恢复为默认设置 (broadcast),这会影响将来的数据库。
点对点通信和广播流量支持的 spread 守护程序的最大数量为 80 个。使用大型群集模式时,节点数量可能会超过 80 个,这时将无法在每个节点上安装一个 spread 守护程序。
--add-hosts host01 --rpm
--add-hosts 192.168.233.101
--add-hosts host02,host03
向群集中添加一个或多个主机后,可以使用以下选项之一将它们作为节点添加到数据库:
admintools
命令行,以确保按特定顺序添加节点
管理工具
管理控制台
使用 admintools db_add_node 工具,可以控制将节点添加到数据库群集的顺序。它使用 ‑s
或 ‑‑hosts
选项指定新节点的主机,这些选项采用逗号分隔的实参列表。Vertica 按列表指定的顺序添加新节点。例如,以下命令添加三个节点:
$ admintools ‑t db_add_node \
-d VMart \
-p 'password' \
-s 192.0.2.1,192.0.2.2,192.0.2.3
您可以按如下方式,使用管理工具向数据库中添加节点:
打开管理工具。
在主菜单 (Main Menu) 上,选择查看数据库群集状态 (View Database Cluster State) 以验证数据库是否正在运行。如果未运行,请将其启动。
从主菜单 (Main Menu) 中,选择高级菜单 (Advanced Menu),然后单击确定 (OK)。
在高级菜单 (Advanced Menu) 中,选择群集管理 (Cluster Management),然后单击确定 (OK)。
在群集管理 (Cluster Management) 菜单中,选择添加主机 (Add Host(s)),然后单击 确定 (OK)。
选择要将一个或多个主机添加到的数据库,然后选择确定 (OK)。
此时会显示未使用主机列表。
选择要添加到数据库的主机,然后单击确定 (OK)。
显示提示时,单击是 (Yes) 以确认要添加的主机。
显示提示时,输入数据库的密码,然后选择确定 (OK)。
显示主机已成功添加的提示时,选择确定 (OK)。
此时,Vertica 自动启动重新平衡过程,以便为新节点填充数据。显示提示时,输入 Database Designer 可用于重新平衡数据库中数据的临时目录的路径,然后选择确定 (OK)。
按 Enter 接受默认的 K-Safety 值,或为数据库输入更大的值,然后选择确定 (OK)。
选择是立即重新平衡数据库还是稍后重新平衡。在这两种情况下,Vertica 都会创建一个脚本,您可以随时使用它来重新平衡。
查看重新平衡过程的摘要,并选择继续 (Proceed)。
如果选择自动重新平衡,则会运行重新平衡过程。如果选择创建脚本,则会生成和保存脚本。无论哪种情况,您都会看到一个成功屏幕。
选择确定 (OK) 以完成“添加节点”过程。
要使用 MC 向 Eon 模式数据库中添加节点,请参阅将节点添加到云上正在运行的群集。
要使用 MC 向企业模式数据库中添加节点,请参阅 向群集中添加主机
永久删除节点虽然不像添加节点那样常见,但如果主机系统过时或过度配置,它将非常有用。
为了管理群集中节点的运行状况,Vertica 通过发送和接收检测信号来定期执行运行状况检查。在运行状况检查期间,群集中的每个节点都会验证对其编录、编录磁盘和本地存储位置('TEMP、DATA'、TEMP、DATA 和 DEPOT)的读写访问权限。验证后,节点发送检测信号。如果一个节点在五个间隔后未能发送检测信号(五次运行状况检查失败),则会将该节点从群集中逐出。
您可以使用 DatabaseHeartBeatInterval 参数控制相邻两次运行状况检查之间的时间。默认情况下,DatabaseHeartBeatInterval 设置为 120,即允许在无检测信号的情况下经过五个 120 秒的间隔。
逐出前允许的时间长度为:
TOT = DHBI * 5
其中,TOT 是在逐出之前所允许的无检测信号总时间(以秒为单位),DHBI 等于 DatabaseHeartBeatInterval 的值。
如果您设置的 DatabaseHeartBeatInterval 值过低,则可能导致节点在出现短暂的运行状况问题时被逐出。有时,此类过早逐出会降低 Vertica 数据库的可用性和性能。
常规参数中的 DatabaseHeartbeatInterval 常规参数
K-safety 级别为 1 的数据库至少需要三个节点才能运行,K-safety 级别为 2 的数据库至少需要 5 个节点才能运行。您可以按照如下方式检查群集的当前 K-safety 级别:
=> SELECT current_fault_tolerance FROM system;
current_fault_tolerance
-------------------------
1
(1 row)
要从具有 K-safety 所要求的最少节点数的群集中移除节点,请首先使用
MARK_DESIGN_KSAFE
降低 K-safety 级别。
只要有足够的剩余节点来满足 K-Safety 要求,便可以从数据库中移除节点。不能删除对 K-safety 至关重要的节点。请参阅降低 K‑Safety 以启用节点移除。
可使用以下方法之一从数据库中移除节点:
管理控制台界面
管理工具
先决条件
在从数据库中移除节点之前,请验证数据库是否符合以下要求:
它正在运行。
它已备份。
该数据库具有符合 K-safety 所需的最少节点数。如有必要,请暂时降低数据库的 K‑safety 级别。
数据库中的所有节点都必须处于正常运行状态或处于活动备用状态。如果您在数据库节点关闭时尝试移除节点,Vertica 会报告错误“所有节点都必须处于 UP 或 STANDBY 才能删除节点 (All nodes must be UP or STANDBY before dropping a node)”。即使尝试移除处于关闭状态的节点,也会收到此错误。
使用管理控制台将节点从其管理页面中移除:
按照如下方式移除数据库节点:
选择要移除的节点。
在节点列表中单击移除节点 (Remove node)。
存在以下限制:
只能移除属于数据库群集的节点。
不能移除处于 DOWN 状态的节点。
移除节点时,它的状态会更改为 STANDBY。可稍后将处于 STANDBY 状态的节点添加回数据库。
要使用管理工具从数据库中移除未使用的主机:
打开管理工具。有关访问管理工具的信息,请参阅使用管理工具。
在“主菜单 (Main Menu)”上,选择“查看数据库群集状态 (View Database Cluster State)”以验证数据库是否正在运行。如果数据库未在运行,请启动该数据库。
从“主菜单 (Main Menu)”中,选择“高级菜单 (Advanced Menu)”,然后选择“确定 (OK)”。
在“高级菜单 (Advanced Menu)”中,选择“群集管理 (Cluster Management)”,然后选择“确定 (OK)”。
在“群集管理 (Cluster Management)”菜单中,选择“从数据库中移除主机 (Remove Host(s) from Database)”,然后选择“确定 (OK)”。
当出现警告,提示您必须重新设计数据库并创建不包括待删除主机的投影时,选择“是 (Yes)”。
选择要从中移除主机的数据库,然后选择“确定 (OK)”。
此时将出现当前处于活动状态的主机列表。
选择要从数据库中移除的主机,然后选择“确定 (OK)”。
出现提示时,选择“确定 (OK)”以确认您希望移除这些主机。
收到主机已成功移除的通知时,选择“确定 (OK)”。
如果从大型群集 (Large Cluster) 配置中移除了某个主机,则打开 vsql 会话并运行 realign_control_nodes:
SELECT realign_control_nodes();
有关更多详细信息,请参阅 REALIGN_CONTROL_NODES。
如果该主机未被群集中的任何其他数据库使用,您可以从群集中移除该主机。请参阅从群集中移除主机。
如果从数据库中移除的某个主机未被任何其他数据库使用,您可以使用
update_vertica
将该主机从群集中移除。在此操作期间,可让数据库一直运行。
当您使用 update_vertica 减小群集的大小时,它还会执行以下任务:
将 spread 修改为匹配小型群集。
将 管理工具配置为处理小型群集。
在其中一台 Vertica 群集主机上,运行带 –-remove-hosts
开关的
update_vertica
。此开关采用要从群集中移除的主机的逗号分隔列表。您可以按名称或 IP 地址引用主机。例如,可以按如下方式移除主机 host01
、host02
和 host03
:
# /opt/vertica/sbin/update_vertica --remove-hosts host01,host02,host03 \
--rpm /tmp/vertica-10.1.1-0.x86_64.RHEL6.rpm \
--dba-user mydba
如果 --rpm
指定新的 RPM,则 Vertica 会先将其安装在现有群集主机上,然后再继续。
update_vertica
使用与
install_vertica
.相同的选项。有关所有选项,请参阅使用安装脚本安装 Vertica。
如果 -remove-host
s 指定了包含多个主机的列表,则在该列表中的主机之间不能嵌入任何空格。
使用与原始安装相同的命令行选项。如果您针对数据库管理员用户名、密码或目录路径使用非默认值,请在移除主机时提供相同的信息;否则,此过程将失败。考虑将原始安装选项保存在属性文件中,以便在后续安装和更新操作中重新使用这些选项。请参阅静默安装 Vertica。
如果您具有 K-safe 数据库,则可根据需要替换节点,而且不会降低系统性能。例如,您可以希望在以下情况下替换现有节点:
需要修复不再正常运行的现有主机系统并将其恢复为群集
要将现有主机系统换成更加强大的另一系统
用于替换节点的过程取决于您是否正在将节点替换为:
使用相同名称和 IP 地址的主机
使用不同名称和 IP 地址的主机
活动的备用节点
配置 Vertica 的替换主机。请参阅在安装 Vertica 之前。
确保新主机上存在数据库管理员用户且其配置方式与现有主机相同。Vertica 将根据需要设置免密码 ssh。
确保将编录路径、数据路径和任何存储位置的目录在创建数据库时已添加到数据库中,并且/或者这些目录已正确安装到新主机上,而且数据库管理员用户具有读写访问权限。另请确保有足够的磁盘空间。
按照下面的最佳实践步骤将故障硬件重新引入群集中,从而避免虚假完整节点重建。
按照下述过程进行操作可防止 Vertica 将磁盘缺失或挂载错误的情况错误地诊断为数据损坏,进而避免发生耗时的全节点恢复。
如果服务器因硬件问题(例如磁盘错误或控制器故障)而发生故障,请在修复硬件时:
将计算机重启至运行级别 1,这是仅适用于控制台的 root 模式。
运行级别 1 可禁止网络连接并阻止 Vertica 尝试重新连接到群集。
在运行级别 1 中,验证硬件是否已得到修复,控制器是否处于联机状态,以及是否可以继续执行任何 RAID 恢复操作。
只有在确认硬件一致之后才能重启至运行级别 3 或更高级别。
此时将激活网络,同时 Vertica 会重新加入群集并自动恢复任何缺失的数据。请注意,在单节点数据库中,如果与投影关联的任何文件被删除或损坏,Vertica 将删除与该投影关联的所有文件,这可能会导致数据丢失。
如果已移除现有 Vertica 数据库的主机,您可以在该数据库正在运行时替换此主机。
您可以将此主机替换为与旧主机具有以下相同特征的新主机:
名称
IP 地址
操作系统
操作系统管理员用户
目录位置
在数据库正在运行时替换主机可防止系统停机。在替换主机之前,请首先备份您的数据库。有关详细信息,请参阅备份和还原数据库。
按照如下方式替换为具有以下相同特征的主机:
使用 --rpm 或 --deb 参数从正常运行的主机运行 install_vertica:
$ /opt/vertica/sbin/install_vertica --rpm rpm_package
有关详细信息,请参阅使用命令行安装。
使用现有节点中的管理工具重新启动新主机。请参阅在节点上重新启动 Vertica。
节点即会自动加入数据库,并通过查询数据库中的其他节点恢复其数据。随后它会过渡为 UP 状态。
使用 IP 地址与原始地址不同的主机系统替换故障节点包含以下步骤:
Vertica 建议您在执行这项重大操作前先备份数据库,因为在操作过程中需要执行创建新投影、删除旧投影和重新加载数据的操作。
将新主机添加到群集中。请参阅向群集中添加主机。
如果 Vertica 仍在将被替换节点中运行,请使用管理工具在将被替换的主机上停止主机上的 Vertica。
使用向新主机分发配置文件将元数据传输到新主机。
使用管理工具在主机上重新启动 Vertica。在主菜单 (Main Menu) 上,选择在主机上重新启动 Vertica (Restart Vertica on Host),然后单击确定 (OK)。有关详细信息,请参阅启动数据库。
完成此过程后,替换节点将查询数据库内的其他节点,从而自动恢复存储在原始节点中的数据。
使用 IP 地址和主机名与原始值不同的主机系统替换节点包含以下常规步骤:
Vertica 建议您在执行这项重大操作前先备份数据库,因为在操作过程中需要执行创建新投影、删除旧投影和重新加载数据的操作。
此时,您要删除的原始主机和新替换主机均为群集成员。
使用管理工具在将被替换的主机上停止主机上的 Vertica。
重新启动主机上的 Vertica。
完成此过程后,替换节点将查询数据库内的其他节点,从而自动恢复存储在原始节点中的数据。随后它会过渡为 UP 状态。
如果要将一个节点替换为一个具有不同名称和 IP 地址的节点,可使用管理工具将原始主机替换为新主机。或者,也可以使用管理控制台替换节点。
要使用管理工具将原始主机替换为新主机:
备份数据库。请参阅备份和还原数据库。
在一个正常运行且不会被替换的节点上打开 管理工具。
在主菜单 (Main Menu) 上,选择查看数据库群集状态 (View Database Cluster State) 以验证数据库是否正在运行。如果数据库未在运行,请使用“主菜单 (Main Menu)”上的“启动数据库 (Start Database)”命令重新启动数据库。
在主菜单 (Main Menu) 上,选择高级菜单 (Advanced Menu)。
在高级菜单 (Advanced Menu) 上,选择在主机上停止 Vertica (Stop Vertica on Host)。
选择要替换的主机,然后单击确定 (OK) 停止该节点。
当系统提示您是否要停止主机时,选择是 (Yes)。
在高级菜单 (Advanced Menu) 上,选择群集管理 (Cluster Management),然后单击确定 (OK)。
在群集管理 (Cluster Management) 菜单上,选择替换主机 (Replace Host),然后单击确定 (OK)。
选择包含要替换的主机的数据库,然后单击确定 (OK)。
随即将显示当前正在使用的所有主机的列表。
选择要替换的主机,然后单击确定 (OK)。
选择要作为替换主机的主机,然后单击确定 (OK)。
出现提示时,输入数据库的密码,然后单击确定 (OK)。
出现提示时,单击是 (Yes) 确认您想要替换主机。
当系统提示主机已成功替换时,单击确定 (OK)。
在主菜单 (Main Menu) 上,选择查看数据库群集状态 (View Database Cluster State) 验证是否所有主机都在运行。您可能需要在刚刚替换的主机上启动 Vertica。使用在主机上重新启动 Vertica (Restart Vertica on Host)。
节点进入“正在恢复 (RECOVERING)”状态。
如果您正在使用
K-Safe 数据库,请牢记:恢复节点相当于关闭一个节点,尽管该节点可能尚不包含完整的数据副本。这表示如果您具有一个 K safety=1 的数据库,则数据库的当前容错能力处于关键级别。如果您又失去一个节点,则数据库将关闭。确保您未停止任何其他节点。
当您添加或移除节点时,Vertica 可以重新平衡您的数据库。作为超级用户,您可以使用管理工具 (Administration Tools)、SQL 函数或管理控制台手动触发重新平衡。
重新平衡操作可能需要一些时间,具体取决于群集大小、投影数量以及投影中包含的数据量。您不得中断此过程,而是允许其正常完成。如果必须取消此操作,请调用
CANCEL_REBALANCE_CLUSTER
函数。
在执行以下操作之一后,重新平衡很有用甚至是必要的:
通过添加或移除节点来更改群集的大小。
在准备从群集移除一个或多个节点时,将其标记为临时节点。
更改弹性群集的比例因子,该比例因子可确定用于跨数据库存储投影的存储容器的数目。
设置控制节点大小或重新对齐大型群集布局上的控制节点。
在初始 Vertica 群集配置中指定 120 个以上的节点。
通过添加或移除节点来修改容错组。
当您重新平衡数据库群集时,Vertica 对所有投影(无论是分段的还是未分段的)执行以下任务:
基于下列内容分配数据:
忽略投影定义中特定于节点的分布规范。在重新平衡节点时始终会将数据分布在所有节点上。
重新平衡完成后,将 Ancient History Mark 设置为允许的最大时期(现在)。
Vertica 对于分段和未分段的投影采用不同的重新平衡方式,如下所述。
对于每个分段投影,Vertica 执行以下任务:
复制和重命名投影同伴并将它们均匀地分布在所有节点上。重命名的投影共享同一投影基本名。
刷新为新的投影。
删除原始投影。
对于每个未分段的投影,Vertica 执行以下任务:
如果添加节点:
会在节点上创建投影同伴。
会将新投影映射到它们在数据库编录中的共享名称。
如果删除节点:会从节点中删除其投影同伴。
在重新平衡完成之前,Vertica 会使用现有 K-safe 值进行操作。重新平衡完成后,Vertica 将使用在重新平衡操作期间指定的 K-safe 值进行操作。新的 K-safe 值必须等于或高于当前的 K-safety。Vertica 不支持 K-safety 降级,如果您尝试减小其当前值,则会返回一条警告。有关详细信息,请参阅降低 K‑Safety 以启用节点移除。
如果在重新平衡数据库时发生故障,您可以再次执行重新平衡操作。解决故障原因后,重新平衡操作会从发生故障之处继续进行。但是,数据重新平衡故障会导致投影变为过时。
要查找过期的投影,请查询系统表
PROJECTIONS
,如下所示:
=> SELECT projection_name, anchor_table_name, is_up_to_date FROM projections
WHERE is_up_to_date = false;
要移除过时的投影,请使用
DROP PROJECTION
。
节点重新平衡对临时表的投影没有影响。
有关重新平衡的详细信息
请参阅知识库文章:
要重新平衡数据库中的数据:
打开管理工具。(请参阅使用管理工具。)
在主菜单 (Main Menu) 上,选择查看数据库群集状态 (View Database Cluster State) 以验证数据库是否正在运行。如果未运行,请将其启动。
从主菜单 (Main Menu) 中,选择高级菜单 (Advanced Menu),然后单击确定 (OK)。
在高级菜单 (Advanced Menu) 中,选择群集管理 (Cluster Management),然后单击确定 (OK)。
在群集管理 (Cluster Management) 菜单中,选择重新平衡数据 (Re-balance Data),然后单击确定 (OK)。
选择要重新平衡的数据库,然后选择确定 (OK)。
输入 Database Designer 输出目录(例如 /tmp)
),然后单击确定 (OK)。
接受建议的 K-safety 值或提供新值。有效值为 0 到 2。
查看消息,然后单击继续 (Proceed) 开始重新平衡数据。
Database Designer 将修改现有投影,以在具有您提供的 K-safety 的所有数据库节点之间重新平衡数据。此外,还会生成一个用于重新平衡数据的脚本,该脚本位于指定的路径中(例如 /tmp/extend_catalog_rebalance.sql
),您可在以后手动运行它。
当重新平衡操作完成时,终端窗口会通知您。
按 Enter 返回管理工具。
Vertica 具有三个用于启动和停止群集重新平衡的 SQL 函数。您可以从在非高峰时段运行的脚本调用这些函数,而不是通过管理工具手动触发重新平衡。
REBALANCE_CLUSTER
作为会话前台任务同步重新平衡数据库群集。
START_REBALANCE_CLUSTER()
作为后台任务异步重新平衡数据库群集。
CANCEL_REBALANCE_CLUSTER
停止任何正在进行中或正在等待执行的重新平衡任务。
添加和移除节点过程会自动重新分发 Vertica 配置文件。您很少需要重新分发配置文件来帮助解决配置问题。
要向主机分发配置文件:
登录到包含这些文件的主机,然后启动管理工具。
在“管理工具 (Administration Tools)”的主菜单 (Main Menu) 中,单击配置菜单 (Configuration Menu),然后单击确定 (OK)。
在配置菜单 (Configuration Menu) 上,选择分发配置文件 (Distribute Config Files),然后单击确定 (OK)。
选择数据库配置 (Database Configuration)。
选择要分发文件的数据库,然后单击确定 (OK)。
Vertica 配置文件会分发到所有其他数据库主机上。如果这些文件已经存在于某个主机上,则会被覆盖。
在配置菜单 (Configuration Menu) 上,选择分发配置文件 (Distribute Config Files),然后单击确定 (OK)。
选择 SSL 密钥 (SSL Keys)。
配置和密钥会分发到所有其他数据库主机上。如果它们已经存在于某个主机上,则会被覆盖。
在配置菜单 (Configuration Menu) 上,选择分发配置文件 (Distribute Config Files),然后单击确定 (OK)。
选择 AdminTools 元数据 (AdminTools Meta-Data)。
管理工具元数据会分发到群集中的每个主机上。
要通过命令行或脚本分发配置文件 admintools.conf
,请使用 admintools 选项 distribute_config_files
:
$ admintools -t distribute_config_files
您可以在管理 (Manage) 页面单击特定节点以选择它,然后单击“节点列表 (Node List)”中的“启动 (Start)”或“停止 (Stop)”按钮,以此来启动或停止一个或多个数据库节点。
在数据库和群集 (Databases and Clusters) 页面,您必须首先单击选择数据库。要在该数据库中停止或启动节点,请单击查看 (View) 按钮。您将转到“概览 (Overview)”页面。单击页面底部小程序面板中的管理 (Manage),随后您将转到数据库节点视图。
“启动 (Start)”和“停止 (Stop)”数据库按钮始终处于活动状态,但是只有当选择的一个或多个节点处于相同状态时,节点“启动 (Start)”或“停止 (Stop)”按钮才会处于活动状态;例如,所有节点都是 UP 或 DOWN。
单击“启动 (Start)”或“停止 (Stop)”按钮后,管理控制台会为您正在启动或停止的节点或数据库更新状态和消息图标。
有时,运行的现有 Vertica 数据库群集的节点需要采用新 IP 地址。群集节点可能还需要基于不同的 IP 协议运行,例如,将协议从广播更改为点对点时。
要更改数据库群集中主机的 IP 地址,请使用 re_ip
函数更改 IP 地址并将旧地址映射到新地址:
$ admintools -t re_ip -f mapfile
mapfile 引用您创建的文件,其中包含新旧 IP 地址。
在下列任一情况下,可以使用此函数更改 IP 地址:
如果您的 Vertica 数据库群集具有相同的数据和控制消息地址,请执行以下操作之一:
更改所有数据库群集节点的 IP 地址。
仅更改一个或部分数据库群集节点的 IP 地址。
$ admintools -t re_ip -f mapfile
将 Vertica 数据库群集的 IP 地址从广播模式更改为点对点(单播)模式:
$ admintools -t re_ip -d dbname -T
将 Vertica 数据库群集的 IP 地址从点对点(单播)模式更改为广播模式:
$ admintools -t re_ip -d dbname -U
更改数据库群集的控制 IP 地址。在这种情况下,映射文件必须包含控制消息 IP 地址和相关的广播地址。
$ admintools -t re_ip -f mapfile
仅更改一个数据库的 IP 地址,而不更改 admintools 配置。请参阅仅映射数据库上的 IP 地址。
SELECT * from vs_nodes order by name
显示节点信息。
有关上述命令中所用选项的详细信息,请参阅Re-IP 命令行选项。
默认情况下,节点 IP 地址和导出 IP 地址配置为相同的 IP 地址。导出地址是网络上可以访问其他 DBMS 系统的节点的 IP 地址。使用导出地址可以从 DBMS 系统导入和导出数据。您可以使用此处的说明手动更改导出地址。
如果更改导出地址并运行 re-ip 命令,则导出地址保持不变。
示例
运行以下命令:
=> SELECT node_name, node_address, export_address FROM nodes;
node_name | node_address | export_address
------------------------------------------------------
v_VMartDB_node0001 | 192.168.100.101 | 192.168.100.101
v_VMartDB_node0002 | 192.168.100.102 | 192.168.100.101
v_VMartDB_node0003 | 192.168.100.103 | 192.168.100.101
v_VMartDB_node0004 | 192.168.100.104 | 192.168.100.101
(4 rows)
在上面的示例中,export_address 是默认值。在这种情况下,当您运行 re-ip 命令时,export_address 会更改为新的 node_address。
如果您手动将导出地址更改为此处描述的地址,可能会出现以下情况:
=> SELECT node_name, node_address, export_address FROM nodes;
node_name | node_address | export_address
------------------------------------------------------
v_VMartDB_node0001 | 192.168.100.101 | 10.10.10.1
v_VMartDB_node0002 | 192.168.100.102 | 10.10.10.2
v_VMartDB_node0003 | 192.168.100.103 | 10.10.10.3
v_VMartDB_node0004 | 192.168.100.104 | 10.10.10.4
(4 rows)
在这种情况下,当您运行 re-ip 命令时,export_address 不会发生更改。
主机和节点的 IP 地址存储在 opt/vertica/config/admintools.conf
中:
[Cluster]
hosts = 203.0.113.111, 203.0.113.112, 203.0.113.113
[Nodes]
node0001 = 203.0.113.111/home/dbadmin,/home/dbadmin
node0002 = 203.0.113.112/home/dbadmin,/home/dbadmin
node0003 = 203.0.113.113/home/dbadmin,/home/dbadmin
还可以使用以下命令显示 IP 地址列表:
$ admintools -t list_allnodes
Node | Host | State | Version | DB
-----------------+---------------+-------+----------------+-----------
v_vmart_node0001 | 203.0.113.111 | UP | vertica-10.1.1 | VMart
v_vmart_node0002 | 203.0.113.112 | UP | vertica-10.1.1 | VMart
v_vmart_node0003 | 203.0.113.113 | UP | vertica-10.1.1 | VMart
映射新 IP 地址包括:
创建将旧 IP 地址映射到新 IP 地址的映射文件。
使用映射文件更新配置文件和数据库编录。
如果使用控制消息在主机之间进行通信,您可能还需要使用新的 controlAddress 和 controlBroadcast IP 地址更新 IP 地址。
re_ip
之前,请检查是否需要更新子网、网络接口、负载均衡组和负载均衡规则,使其与新的 IP 相匹配。如果不匹配,可能会导致负载均衡或导入/导出失败。
按如下方式创建映射文件:
获取新 IP 地址并将它们保存在一个文本文件中。
获取旧 IP 地址,使用 list_allnodes:
$ admintools -t list_allnodes
Node | Host | State | Version | DB
-----------------+---------------+-------+----------------+-----------
v_vmart_node0001 | 192.0.2.254 | UP | vertica-10.1.1 | VMart
v_vmart_node0002 | 192.0.2.255 | UP | vertica-10.1.1 | VMart
v_vmart_node0003 | 192.0.2.256 | UP | vertica-10.1.1 | VMart
按以下格式将 Host
列的内容复制到新 IP 地址所保存到的文本文件中:
oldIPaddress newIPaddress
例如:
192.0.2.254 198.51.100.255
192.0.2.255 198.51.100.256
192.0.2.256 198.51.100.257
使用该文本文件创建可用于执行下列任务之一的映射文件:
创建采用如下格式的映射文件:
oldIPaddress newIPaddress[, controlAddress, controlBroadcast]
例如:
192.0.2.254 198.51.100.255, 198.51.100.255, 203.0.113.255
192.0.2.255 198.51.100.256, 198.51.100.256, 203.0.113.255
192.0.2.256 198.51.100.257, 198.51.100.257, 203.0.113.255
controlAddress 和 controlBroadcast 是可选项。如果省略:
controlAddress 将默认为 newIPaddress。
controlBroadcast 将默认为 newIPaddress 的广播 IP 地址的主机。
按如下方式运行 re_ip
:
$ admintools -t re_ip -f mapfile
将 IP 地址从旧 IP 地址更改为新 IP 地址并更改控制消息模式
创建采用如下格式的映射文件:
oldIPaddress newIPaddress, controlAddress, controlBroadcast
例如:
192.0.2.254 198.51.100.255, 203.0.113.255, 203.0.113.258
192.0.2.255 198.51.100.256, 203.0.113.256, 203.0.113.258
192.0.2.256 198.51.100.257, 203.0.113.257, 203.0.113.258
运行 re_ip
并按如下方式更改控制消息传递模式:
将控制消息传递模式更改为点对点:
$ admintools -t re_ip -d db name -T
将控制消息传递模式更改为广播:
$ admintools -t re_ip -d db name -U
当您使用 -U 选项时,嵌入式消息子系统基于 controlAddress 和 controlBroadcast IP 运行。
创建采用如下格式的映射文件:
nodeName nodeIPaddress, controlAddress, controlBroadcast
例如:
v_vmart_node0001 192.0.2.254, 203.0.113.255, 203.0.113.258
v_vmart_node0002 192.0.2.255, 203.0.113.256, 203.0.113.258
v_vmart_node0003 192.0.2.256, 203.0.113.257, 203.0.113.258
按如下方式运行 re_ip
:
$ admintools -t re_ip -f mapfile -O -d database
有关详细信息,请参阅下面的映射数据库上的 IP 地址。
创建映射文件后,可以更改新 IP 地址。更改 IP 地址过程会自动备份 admintools.conf
,以便在必要时恢复原始设置。
停止数据库。
运行 re-ip
以将旧 IP 地址映射到新 IP 地址:
$ admintools -t re_ip -f mapfile
如果出现以下情况,则会出现警告:
任何 IP 地址的格式不正确
文件中的旧 IP 地址或新 IP 地址重复 — 例如,192.0.2.256
在旧 IP 地址集中出现了两次。
如果语法正确,则在开始映射时,re_ip
将执行以下任务:
对映射文件中列出的 IP 地址进行重新映射。
除非使用 -i
选项,否则会提示您确认对数据库的更新。
使用新 IP 地址更新要求的本地配置文件。
将更新后的配置文件分发到使用新 IP 地址的主机。
使用以下提示跟踪这些步骤:
Parsing mapfile...
New settings for Host 192.0.2.254 are:
address: 198.51.100.255
New settings for Host 192.0.2.255 are:
address: 198.51.100.256
New settings for Host 192.0.2.254 are:
address: 198.51.100.257
The following databases would be affected by this tool: Vmart
Checking DB status ...
Enter "yes" to write new settings or "no" to exit > yes
Backing up local admintools.conf ...
Writing new settings to local admintools.conf ...
Writing new settings to the catalogs of database Vmart ...
The change was applied to all nodes.
Success. Change committed on a quorum of nodes.
Initiating admintools.conf distribution ...
Success. Local admintools.conf sent to all hosts in the cluster.
重新启动数据库。
您只能映射数据库的 IP 地址。此任务涉及将数据库中的节点名称映射到新 IP 地址。这对于错误恢复很有用,因为 admintools.conf 不会更新。Vertica 仅更新 spread.conf
和包含更改的编录。
还可以仅映射数据库上的 IP 地址,以便在单个数据库上设置 controlAddress 和 controlBroadcast。此任务允许同一主机上的节点拥有不同的数据和 controlAddress。
停止数据库。
创建具有如下格式的映射文件:
nodeName IPaddress, controlAddres, controlBroadcast
例如:
vertica_node001 192.0.2.254, 203.0.113.255, 203.0.113.258
vertica_node002 192.0.2.255, 203.0.113.256, 203.0.113.258
vertica_node003 192.0.2.256, 203.0.113.257, 203.0.113.258
运行以下命令以映射新 IP 地址:
$ admintools -t re_ip -f mapfile -O -d database
重新启动数据库。
re_ip
支持以下选项:
-h \--help
re_ip
的联机帮助。-f mapfile \--file=mapfile
-O \--dba-only
NodeName AssociatedNodeIPAddress, new ControlAddress, new ControlBroadcast
NodeName 和 AssociatedNodeIPAddress 必须与 admintools.conf
中相同。
此选项一次只更新一个数据库,因此它需要使用 -d 选项:
$ admintools -t re_ip -f mapfile -O -d database
-i \--noprompts
-T \--point-to-point
-U \--broadcast
-d database name \--database=database name
-O
-T
-U
仅限 Kubernetes
Kubernetes 上 Eon 模式数据库的节点 IP 地址偶尔必定会更新 — 例如,pod 失败、添加到群集中或进行重新调度。发生这种情况时,必须使用受影响节点的新 IP 地址更新 Vertica 编录并重新启动节点。
Vertica 的 restart_node
工具通过其 --new-host-ips
选项(该选项允许您更改在 Kubernetes 上运行的 Eon 模式数据库的节点 IP 地址)满足这些要求,并重新启动已更新的节点。与在其他(非 Kubernetes)数据库上重新映射节点 IP 地址不同,您可以在正运行的数据库中的各个节点上执行此任务:
admintools -t restart_node \
{-d db-name |--database=db-name} [-p password | --password=password] \
{{-s nodes-list | --hosts=nodes-list} --new-host-ips=ip-address-list}
nodes-list 是要重新启动的节点的逗号分隔列表。列表中的所有节点都必须处于关闭状态,否则 admintools 会返回错误。
ip-address-list 是分配给指定节点的新 IP 地址或主机名的逗号分隔列表。
以下要求适用于 nodes-list 和 ip-address-list:
节点主机数和 IP 地址或主机名必须相同。
列表不得包含任何嵌入空格。
例如,可以使用新 IP 地址重新启动节点 v_k8s_node0003
:
$ admintools -t list_allnodes
Node | Host | State | Version | DB
----------------+------------+----------+----------------+-----
v_k8s_node0001 | 172.28.1.4 | UP | vertica-10.1.1 | K8s
v_k8s_node0002 | 172.28.1.5 | UP | vertica-10.1.1 | K8s
v_k8s_node0003 | 172.28.1.6 | DOWN | vertica-10.1.1 | K8s
$ admintools -t restart_node -s v_k8s_node0003 --new-host-ips 172.28.1.7 -d K8s
Info: no password specified, using none
*** Updating IP addresses for nodes of database K8s ***
Start update IP addresses for nodes
Updating node IP addresses
Generating new configuration information and reloading spread
*** Restarting nodes for database K8s ***
Restarting host [172.28.1.7] with catalog [v_k8s_node0003_catalog]
Issuing multi-node restart
Starting nodes:
v_k8s_node0003 (172.28.1.7)
Starting Vertica on all nodes. Please wait, databases with a large catalog may take a while to initialize.
Node Status: v_k8s_node0003: (DOWN)
Node Status: v_k8s_node0003: (DOWN)
Node Status: v_k8s_node0003: (DOWN)
Node Status: v_k8s_node0003: (DOWN)
Node Status: v_k8s_node0003: (RECOVERING)
Node Status: v_k8s_node0003: (UP)
$ admintools -t list_allnodes
Node | Host | State | Version | DB
----------------+------------+-------+----------------+-----
v_k8s_node0001 | 172.28.1.4 | UP | vertica-10.1.1 | K8s
v_k8s_node0002 | 172.28.1.5 | UP | vertica-10.1.1 | K8s
v_k8s_node0003 | 172.28.1.7 | UP | vertica-10.1.1 | K8s
Vertica 检测并在日志文件中报告磁盘空间不足的情况,这样,您就可以先解决该问题,以防发生严重问题。它还会通过 SNMP 陷阱(如果已启用)检测和报告磁盘空间不足的情况。
关键磁盘空间问题的报告速度比其他问题快。例如,编录空间不足属于严重情况;因此,Vertica 报告该情况将早于不那么关键的情况。要在磁盘空间低于特定阈值时避免数据库发生损坏,Vertica 将开始拒绝更新编录或数据的事务。
当 Vertica 报告磁盘空间不足的情况时,请使用 DISK_RESOURCE_REJECTIONS 系统表确定将拒绝的磁盘空间请求的类型和将拒绝请求的主机。
要添加磁盘空间,请参阅为节点添加磁盘空间。要更换故障磁盘,请参阅更换故障磁盘。
可以使用这些系统表来监控群集中的磁盘空间使用情况。
此过程介绍如何为 Vertica 群集中的节点添加磁盘空间。
要为节点添加磁盘空间:
如果您必须关闭正向其中添加磁盘空间的硬件,则首先在添加磁盘空间的主机上关闭 Vertica。
根据硬件环境的要求为系统添加新磁盘。启动硬件(如果硬件已关闭)
根据硬件环境的需求对新磁盘进行分区、格式化和挂载。
在新卷上创建数据目录路径。
例如:
mkdir –p /myNewPath/myDB/host01_data2/
如果您关闭硬件,请在主机上重新启动 Vertica。
打开与 Vertica 的数据库连接,然后添加一个 存储位置,以添加新数据目录路径。在 CREATE LOCATION 中指定节点,否则 Vertica 会认为您在所有节点上创建存储位置。
请参阅本指南中的创建存储位置,以及《SQL 参考手册》中的 CREATE LOCATION 语句。
如果数据或编录目录所在的磁盘出现故障,导致完全或部分磁盘数据丢失,请执行以下步骤:
更换磁盘并重新创建数据或编录目录。
将配置文件 (
vertica.conf
) 分配到新主机。有关详细信息,请参阅将配置文件分配到新主机。
如在主机上重新启动 Vertica 中所述,在主机上重新启动 Vertica。
有关查找 DATABASE_HOME_DIR 的信息,请参阅编录和数据文件。
为使恢复过程成功完成,编录和数据文件必须位于正确的目录中。
在 Vertica 中, 编录是一个文件集,其中包含数据库中的对象(例如,节点、表、约束和投影)的相关信息(元数据)。编录文件会在群集中的所有节点上进行复制,而数据文件对于每个节点都是唯一的。这些文件默认安装在以下目录中:
/DATABASE_HOME_DIR/DATABASE_NAME/v_db_nodexxxx_catalog/ /DATABASE_HOME_DIR/DATABASE_NAME/v_db_nodexxxx_catalog/
要查看数据库路径:
运行 管理工具。
$ /opt/vertica/bin/admintools
从主菜单中,选择配置菜单 (Configuration Menu),然后单击确定 (OK)。
选择查看数据库 (View Database),然后单击确定 (OK)。
选择要查看的数据库,然后单击确定 (OK) 以查看数据库配置文件。
有关编录目录的内容解释,请参阅了解编录目录。
编录目录用于存储数据库的元数据和支持文件。此目录中的一些文件可帮助您解决数据加载问题或其他数据库问题。有关查找数据库编录目录的说明,请参阅编录和数据文件。默认情况下,编录目录位于数据库目录中。例如,如果您在数据库管理员帐户中创建了 Vmart 数据库,则编录目录的路径为:
/home/dbadmin/VMart/v_vmart_nodennnn_catalog
其中 node
nnnn 是您所登录的节点的名称。每个节点的编录目录名称是唯一的,但每个节点的大部分编录目录内容都是相同的。
下表说明了编录目录中可能显示的文件和目录。
Vertica 定期轮询自己的内存使用情况,以确定它是否低于由配置参数
MemoryPollerReportThreshold
设置的阈值。轮询按照配置参数
MemoryPollerIntervalSec
的设置定期发生(默认情况下每 2 秒轮询一次)。
内存轮询器将 MemoryPollerReportThreshold
与以下表达式进行比较:
RSS / available‑memory
当此表达式的计算结果大于 MemoryPollerReportThreshold
(默认设置为 0.93)时,内存轮询器会将报告写入 Vertica 工作目录中的 MemoryReport.log
。此报告包括有关 Vertica 内存池、单个查询和会话消耗的内存量等信息。内存轮询器还将报告作为事件记录在系统表
MEMORY_EVENTS
中,并设置 EVENT_TYPE
为 MEMORY_REPORT
。
内存轮询器还会检查由 glibc 分配的可用内存是否过多(glibc 内存膨胀)。有关详细信息,请参阅内存修剪。
在某些工作负载下,glibc 会累积其分配区域中的大量可用内存。该内存消耗由常驻集大小 (RSS) 指示的物理内存,glibc 并不总是将物理内存返回给操作系统。glibc 对物理内存的高保留(glibc 内存膨胀)会对其他进程产生不利影响,并且在高工作负载下,有时会导致 Vertica 内存不足。
Vertica 提供了两个配置参数,让您可以控制 Vertica 检测和合并由 glibc 分配的大量可用内存的频率,然后将其返回给操作系统:
MemoryPollerTrimThreshold
:设置内存轮询器开始检查是否修剪 glibc
所分配内存的阈值。
内存轮询器将 MemoryPollerTrimThreshold
(默认设置为 0.83)与以下表达式进行比较:
RSS / available‑memory
如果此表达式的计算结果大于 MemoryPollerTrimThreshold
,则内存轮询器开始检查下一个阈值(在 MemoryPollerMallocBloatThreshold
中设置)是否存在 glibc 内存膨胀。
MemoryPollerTrimThreshold
设置更高的设置。要关闭自动修剪,请将此参数设置为 0。
MemoryPollerMallocBloatThreshold
:设置 glibc 内存膨胀的阈值。
内存轮询器将调用 glibc 函数 malloc_info()
,以获取 malloc 中的空闲内存量。然后它将 MemoryPollerMallocBloatThreshold
(默认设置为 0.3)与以下表达式进行比较:
free‑memory‑in‑malloc / RSS
如果此表达式的计算结果大于 MemoryPollerMallocBloatThreshold
,内存轮询器将调用 glibc 函数
malloc_trim()
。此函数从 malloc 中回收空闲内存,并将其返回给操作系统。调用 malloc_trim()
的详细信息将写入系统表
MEMORY_EVENTS
。
例如,当满足以下条件时,内存轮询器调用 malloc_trim()
:
MemoryPollerMallocBloatThreshold
设置为 0.5。
malloc_info()
返回 malloc 中的 15GB 可用内存。
RSS 为 30GB。
MemoryPollerTrimThreshold
设置为 0(禁用),则忽略此参数。
如果禁用自动修剪,您可以通过调用 Vertica 函数
MEMORY_TRIM
手动减少由 glibc 分配的内存。此函数调用 malloc_trim()
。
Tuple Mover 管理 ROS 数据存储。在合并时,它会将较小的 ROS 容器合并到较大的 ROS 容器中并清除已删除的数据。Tuple Mover 在后台自动执行这些任务。
数据库模式会影响哪些节点执行 Tuple Mover 操作:
在企业模式数据库中,所有节点都运行 Tuple Mover 以对它们存储的数据执行合并操作。
在 Eon 模式下,每个 分片的 主订户都针对该分片中的 ROS 容器计划 Tuple Mover 合并操作。它可以将此计划的执行权委托给群集中的另一个节点。
Tuple Mover 操作通常不需要干预。但是,Vertica 提供了多种调整 Tuple Mover 行为的方法。有关详细信息,请参阅管理 Tuple Mover。
在 Eon 模式中,Tuple Mover 的操作分为两部分:合并计划和合并执行。合并计划总是由参与合并的分片的 主订户执行。这些主订户是同一主子群集的一部分。作为合并计划的一部分,主订户选择一个节点来执行合并计划。它使用两个标准来决定哪个节点应该执行合并:
只有为其 TM 资源池分配了内存的节点才有资格执行合并。主订户忽略其 TM 池的 MEMORYSIZE 和 MAXMEMORYSIZE 的设置为 0 的子群集中的所有节点。
从能够执行合并的节点组中,主订户选择其存储库中包含最多 ROS 容器的节点,这些容器参与合并。
可以通过将辅助子群集的 TM 池的 MEMORYSIZE 和 MAXMEMORYSIZE 设置更改为 0 来禁止为辅助子群集分配合并任务。这些设置阻止主订户将合并任务分配给子群集中的节点。
例如,此语句禁止名为 dashboard 的子群集运行合并任务。
=> ALTER RESOURCE POOL TM FOR SUBCLUSTER dashboard MEMORYSIZE '0%'
MAXMEMORYSIZE '0%';
“合并”是用于合并 ROS 容器并清除已删除记录的Tuple Mover 进程。 DML 活动(例如 COPY 和数据分区)会生成新的 ROS 容器,这些容器通常需要整合,而删除数据和对数据重新分区则需要重组现有容器。Tuple Mover 持续监控这些活动,并根据需要执行合并以整合和重组容器。这样做,Tuple Mover 为的是避免两个问题:
当列数据分散在多个 ROS 容器中时,性能会下降。
当给定投影的 ROS 容器的增长速度超过 Tuple Mover 的处理速度时,存在 ROS 推回的风险。一个投影最多可以有 1024 个 ROS 容器;当达到该限制时,Vertica 开始在所有的投影查询尝试中返回 ROS 推回错误。
Tuple Mover 持续监控所有生成新 ROS 容器的活动。这样做时,它会创建合并请求并根据类型对它们进行排队。这些类型如下所示,按优先级降序排列:
RECOMPUTE_LIMITS:设置 Tuple Mover 用于确定何时对投影的新合并请求进行排队的标准。此请求类型在以下两种情况下排队:
创建投影时。
当现有投影发生更改时(例如添加或删除列),或者影响该投影的 ROS 存储的配置参数(如 ActivePartitionCount)发生更改时。
MERGEOUT:整合新容器。这些容器通常包含来自最近加载活动或表分区的数据。
DVMERGEOUT:整合具有删除标记(或删除向量)的数据。
PURGE:从容器中清除过期的删除向量。
Tuple Mover 还监控为每个投影创建容器的频率,以确定哪些投影可能面临 ROS 推回的风险。投影上的密集 DML 活动通常会导致容器创建速率很高。Tuple Mover 监控 MERGEOUT 和 DVMERGEOUT 请求,并在每个集合中根据请求的投影活动级别确定请求的优先级。对具有最高容器创建速率的投影的合并请求将获得立即执行的优先级。
Tuple Mover 定期检查合并队列中是否有待定请求,检查间隔由配置参数
MergeOutInterval
设置:
如果队列包含合并请求,则 Tuple Mover 不执行任何操作并重新进入睡眠状态。
如果队列为空,Tuple Mover:
处理待处理的存储位置移动请求。
检查新的未排队清除请求并将它们添加到队列中。
然后它重新进入睡眠状态。
默认情况下,此参数设置为 600(秒)。
您可以随时通过调用 Vertica 元函数
DO_TM_TASK
来对一个或多个投影调用合并操作:
DO_TM_TASK('mergeout'[, '[[database.]schema.]{table | projection} ]')
该函数扫描指定范围内的数据库编录以确定待定的合并任务。如果未指定表或投影,则 DO_TM_TASK
扫描整个编录。与在 TM 资源池中运行的连续 TM 服务不同,DO_TM_TASK
在 GENERAL 池中运行。如果 DO_TM_TASK
执行合并请求队列中待定的合并任务,TM 服务会从队列中移除这些任务而不采取任何操作。
Vertica 将来自不同表分区或分区组的数据在磁盘上分开保存。Tuple Mover 在整合 ROS 容器时遵循此分离策略。首次创建某个分区时,它通常会频繁加载数据,而且需要 Tuple Mover 的定期活动。随着分区的老化,它通常会转变为一个基本上只读的工作负载,并且需要的活动要少得多。
Tuple Mover 有两个不同的策略,用于管理这些不同的分区工作负载:
活动分区是最近创建的分区。Tuple Mover 使用基于层的算法,旨在最大限度减少各个元组进行合并的次数。表的活动分区计数确定该表有多少个处于活动状态的分区。
非活动分区是指那些不是最近创建的分区。Tuple Mover 将 ROS 容器整合至极小集,同时避免合并大小超过 MaxMrgOutROSSizeMB
的容器。
有关 Tuple Mover 如何确定活动分区的详细信息,请参阅活动和非活动分区。
TM 资源池使用其 MAXCONCURRENCY 参数设置可用于合并的线程数。默认情况下,此参数设置为 7。Vertica 将一半线程分配给活动分区,其余一半分配给活动和非活动分区。如果 MAXCONCURRENCY 设置为奇整数,Vertica 会向上舍入以支持处于活动状态的分区。
例如,如果 MAXCONCURRENCY 设置为 7,则 Vertica 会将四个线程专门分配给活动分区,并根据需要将其余的三个线程分配给活动和非活动分区。如果需要额外的线程来避免 ROS 推回,请使用 ALTER RESOURCE POOL 增加 MAXCONCURRENCY。
当您从数据库中删除数据时,Vertica 并未移除该数据,而是将其标记为已删除。如果使用许多
DELETE
语句标记少数几行(相对于表大小而言),则会导致创建许多小型容器(删除向量)来保存具有删除标记的数据。每一个删除向量容器都会消耗资源,因此这样的容器如果很多,则会对性能产生不利影响,尤其是在恢复期间。
在 Tuple Mover 执行合并后,它会查找包含少量条目的删除标记容器。如果存在这样的容器,Tuple Mover 会将它们合并到一个更大的容器中。此过程会释放多个容器所使用的资源,从而有助于降低跟踪已删除数据所需的开销。Tuple Mover 并不清除已删除的数据,也不对已删除的数据产生任何影响,只是整合删除向量以提高效率。
DELETE_VECTORS
可以查看用来存储已删除数据的容器的数量和大小。
默认情况下,对所有表及其投影启用合并。可以使用 ALTER TABLE 在表上禁用合并。例如:
=> ALTER TABLE public.store_orders_temp SET MERGEOUT 0;
ALTER TABLE
通常,对于为临时目的(例如,用于对旧分区数据进行存档或在表之间交换分区的临时表)而创建的表禁用合并很有用,在任务完成后,很快就会删除这些表。这样做,便可以避免与合并相关的表开销。
您可以查询系统表 TABLES 以确定已禁用合并的表:
=> SELECT table_schema, table_name, is_mergeout_enabled FROM v_catalog.tables WHERE is_mergeout_enabled= 0;
table_schema | table_name | is_mergeout_enabled
--------------+-------------------+---------------------
public | store_orders_temp | f
(1 row)
Vertica 定期检查 ROS 存储容器以确定删除向量是否符合清除条件,如下所示:
计算每个容器中过期删除向量的个数,即等于或早于 Ancient History Mark (AHM) 时期的删除向量。
计算过期删除向量相对于同一 ROS 容器中记录总数的百分比。
如果此百分比超过由配置参数 PurgeMergeoutPercent 设置的阈值(默认为 20%),Vertica 会自动对 ROS 容器执行合并,从而永久移除所有过期的删除向量。Vertica 使用 TM 资源池的 MAXCONCURRENCY 设置来确定可用于合并操作的线程数。
还可以使用两个 Vertica 元函数从 ROS 容器中手动清除所有过期的删除向量:
这两个函数都从 ROS 容器中移除所有过期的删除向量,而与给定容器中有多少删除向量无关。
合并操作使用基于层的算法来验证每个元组是否都经历少量恒定次数的合并操作,而不考虑用来加载数据的过程。合并操作使用此算法来选择要为非分区表和分区表中处于活动状态的分区合并哪些 ROS 容器。
Vertica 为每个处于活动状态的分区和锚定到非分区表的投影构建层。层数、每个层的大小和层中的最大 ROS 容器数根据磁盘大小、内存和投影中的列数计算得出。
先合并小 ROS 容器后合并大 ROS 容器可以在合并过程中获得最大的好处。该算法从第 0 层开始并向上移动。它检查层中的 ROS 容器数是否已达到等于或大于每个层允许的最大 ROS 容器数的值。默认值为 32。如果算法发现一个层已满,它会将投影和该层标记为满足合并条件。
Tuple Mover 已预先配置为处理典型的工作负载。但是,某些情况可能需要您调整 Tuple Mover 行为。您可以通过多种方式执行此操作:
[配置 TM 资源池](#Configur)。
Tuple Mover 使用内置的 TM 资源池来处理其工作负载。可以调整此资源池的几个设置以方便处理大量负载:
指定为每个节点的 TM 池保留多少内存。TM 池可以通过从 GENERAL 池中借用内存而超过这个下限。默认情况下,此参数设置为可用内存的 5%。如果 GENERAL 资源池的 MEMORYSIZE 也设置为一定的百分比,则 TM 池会与 GENERAL 池争用内存。此值必须始终小于或等于 MAXMEMORYSIZE 设置。
设置可分配给 TM 池的内存上限。TM 池可以通过从 GENERAL 池中借用内存而超过由 MEMORYSIZE 设置的值。此值必须始终等于或大于 MEMORYSIZE 设置。
在 Eon 模式数据库中,如果在子群集级别将此值设置为 0,则 Tuple Mover 在子群集上处于禁用状态。
在所有节点上设置 TM 池可用的最大并发执行槽数。在 Vertica ≥9.3 版本中创建的数据库中,默认值为 7。在早期版本中创建的数据库中,默认值为 3。此设置指定可以在多个线程上同时发生的最大合并数。
指定要在资源池中跨所有节点并发执行的查询的首选数量,默认设置为 6。资源管理器使用 PLANNEDCONCURRENCY 来计算对于给定查询可用的目标内存:
TM-memory-size / PLANNEDCONCURRENCY
对于 TM 池,PLANNEDCONCURRENCY 设置必须与 RAM、CPU 和存储子系统的大小成比例。根据存储类型的不同,如果增加 Tuple Mover 线程的 PLANNEDCONCURRENCY,可能会产生存储 I/O 瓶颈。监控存储子系统;如果其充满长 I/O 队列、多于 2 个 I/O 队列以及读写延迟时间长,请调整 PLANNEDCONCURRENCY 参数以使存储子系统资源保持在饱和级别以下。
Tuple Mover 假定分区表的所有加载和更新都是针对一个或多个标识为活动的分区。通常,具有最大分区键的分区(通常是最近创建的分区)均视为活动分区。随着分区的老化,其工作负载通常会缩小,并大多变为只读状态。
您可以按优先级的升序指定两个级别的分区表的活动分区数:
配置参数 ActivePartitionCount 将确定数据库中分区表的活动分区数。默认情况下,ActivePartitionCount 设置为 1。Tuple Mover 会将此设置应用于自身未设置活动分区计数的所有表。
通过使用 CREATE TABLE 和 ALTER TABLE 设置各个表自身的活动分区计数,可以取代各个表的 ActivePartitionCount。
有关详细信息,请参阅活动和非活动分区。
Vertica 的资源管理架构支持在数据库中高效地运行不同的并发工作负载。对于基本操作,Vertica 会根据 RAM 和计算机内核数预配置内置 GENERAL 池。可以自定义 GENERAL 池,以便处理特定的并发要求。
也可以定义新的资源池并对其进行配置,以限制内存使用量、并发和查询优先级。之后,您可以视情况指定每个数据库用户使用特定资源池,以便控制其请求所占用的内存资源。
如果不同类别的工作负载之间存在资源争用需求,则用户定义的池很有用。示例场景如下:
一个大型批处理作业占用了所有服务器资源,导致更新网页的小型作业资源不足。这会降低用户体验。
在此场景中,可创建一个资源池来处理网页请求并确保用户获得所需的资源。也可以为批处理作业创建一个受限的资源池,以使该作业不能用尽所有系统资源。
某个应用程序的优先级低于其他应用程序,且您要限制低优先级应用程序的内存使用量和并发用户数。
在此场景中,可创建一个设有查询内存上限的资源池并将该池与低优先级应用程序的用户关联起来。
还可以使用资源池管理分配给正在运行的查询的资源。可以为资源池分配运行时优先级以及一个阈值,为持续时间不同的查询分配不同的优先级。有关详细信息,请参阅在查询运行时管理资源。
在企业模式下,整个数据库有一组全局资源池。在 Eon 模式下,您可以在全局或按子群集分配资源。有关详细信息,请参阅管理 Eon 模式数据库中的工作负载资源。
在单用户环境中,系统可以将所有资源用于单个查询,使该查询得到最有效率的执行。您的环境更有可能需要同时运行几个查询,因此难以做到为每个查询提供最大数量的资源(运行时间最快),同时又能以合理运行时间同时为多个查询提供服务。
Vertica 资源管理器可以解决此矛盾,同时确保每个查询最终得到服务,实际系统限制始终被顾及。
例如,在系统面临资源压力时,资源管理器可能会对查询进行排队,直到资源可用或达到超时值。另外,在配置各个资源管理器设置时,您可以根据针对系统运行的并发查询的预期数量来优化每个查询的目标内存。
资源管理器以各种方式影响单个查询的执行。将查询提交到数据库时,会发生以下一系列事件:
对查询进行解析、通过优化查询确定执行计划,然后将查询分发到参与节点。
在每个节点上调用资源管理器,以评估运行查询所需的资源并与当前使用的资源进行比较。将发生下列情况之一:
如果查询本身所需的内存超过计算机的物理内存,则会拒绝查询,即查询可能无法运行。除了配置明显不足的节点外,这种情况几乎不会出现。
如果当前无法满足资源要求,查询则会排队等候。查询将保留在队列中,直到有足够的资源释放出来后才会运行;若超时,查询则会被拒绝。
其他情况下允许查询运行。
当所有参与节点都允许查询运行时,查询即会开始运行。
特定查询的资源分配和允许运行的最大查询数量取决于资源池配置。请参阅资源池架构。
在每个节点上,不会为处于队列中的查询保留或保存任何资源。但是,在某些节点上排队的多节点查询将持有其他节点上的资源。在这种情况下,Vertica 会竭力避免发生死锁。
资源管理器将资源处理为一个或多个资源池,这些池是使用关联队列预先分配的系统资源子集。
在企业模式下,有一组全局资源池适用于整个数据库中的所有子群集。在 Eon 模式下,您可以在全局或按子群集分配资源。全局级资源池适用于所有子群集。子群集级别的资源池允许您针对子群集执行的工作负载类型微调资源。如果您同时具有全局和子群集级别的资源池设置,则可以覆盖该子群集的任何与内存相关的全局设置。全局设置适用于没有子群集级别资源池设置的子群集。有关微调每个子群集的资源池的详细信息,请参阅管理 Eon 模式数据库中的工作负载资源。
Vertica 预配置了一组内置池,这些池将资源分配给不同请求类型,其中的 GENERAL 池根据计算机中的 RAM 和内核允许某种并发级别。
可以根据实际的并发要求和性能要求配置内置的 GENERAL 池,如内置池中所述。也可以创建自定义池以处理各种类别的工作负载并可选择将用户请求限制在自定义池范围内。
您可以使用
CREATE RESOURCE POOL
创建用户定义的资源池,使用
ALTER RESOURCE POOL
修改用户定义的资源池。您可以针对内存使用、并发性和队列优先级配置这些资源池。您还可以将数据库用户或用户会话限制为使用特定的资源池。这样做可以让您控制内存、CPU 和其他资源的分配方式。
下图说明了在哪个资源池中执行了哪些数据库操作。仅显示了三个内置池。
您可以定义正在运行的查询在超出初始资源池的 RUNTIMECAP
时可以级联到的辅助资源池。
通过定义辅助资源池,您可以指定一个位置,超出正在运行查询的资源池的 RUNTIMECAP
的查询可以在该位置执行。这样,如果某个查询超出池的 RUNTIMECAP
,该查询可级联至具有更大 RUNTIMECAP
的池,而不会导致错误。当查询级联到另一个池时,原始池将重新获得此查询使用的内存。
因为不考虑在辅助池上授予权限,所以您可以使用此功能为用户查询指定辅助资源池而无需为用户提供在该池上运行查询的明确权限。
您还可使用辅助池来存储长时间运行的查询,以便日后使用。使用 PRIORITY HOLD
选项,您可以指定一个对队列重新排队的辅助池,直至达到 QUEUETIMEOUT
或者池的优先级更改为非保留值。
在 Eon 模式下,为特定于子群集的资源池定义辅助资源池时,需遵循以下限制:
全局资源池只能级联到其他全局资源池。
特定于子群集的资源池可以级联到全局资源池,或级联到属于同一子群集的另一个特定于子群集的资源池。如果子群集特定资源池级联到全局和子群集级别中均具有的用户定义资源池,则优先子群集级别资源池。例如:
=> CREATE RESOURCE POOL billing1;
=> CREATE RESOURCE POOL billing1 FOR CURRENT SUBCLUSTER;
=> CREATE RESOURCE POOL billing2 FOR CURRENT SUBCLUSTER CASCADE TO billing1;
WARNING 9613: Resource pool billing1 both exists at both subcluster level and global level, assuming subcluster level
CREATE RESOURCE POOL
当达到初始池上的 RUNTIMECAP
时,Vertica 会将查询路由到辅助池。然后,Vertica 检查辅助池的 RUNTIMECAP
值。如果辅助池的 RUNTIMECAP
大于初始池的值,查询将在辅助池上执行。如果辅助池的 RUNTIMECAP
小于或等于初始池的值,Vertica 将在链中的下一个池上重新尝试查询,直至找到一个 RUNTIMECAP
大于初始池值的池。如果此时辅助池没有足够的可用资源来执行查询,SELECT
查询可能会在该池上重新排队、重新计划和中止。其他类型的查询会因为资源不足而失败。如果查询不存在合适的辅助池,查询将出现错误。
下图演示了执行查询时采用的路径。
在 Vertica 找到用于运行查询的相应池之后,它会继续不间断地执行该查询。现在,查询在用于完成查询的两个池的 RUNTIMECAP
限制方面存在一些差异:
query execution time allocation = rp2 RUNTIMECAP - rp1 RUNTIMECAP
作为
超级用户,您可以使用
CREATE RESOURCE POOL
或
ALTER RESOURCE POOL
语句中的 CASCADE TO
参数来识别辅助池。辅助池必须已作为用户定义的池或 GENERAL
池存在。使用 CASCADE TO
时,不能创建资源池循环。
此示例说明了一个场景,其中管理员希望 user1
的查询在 user_0
资源池上启动,但在查询过长时级联到 userOverflow
池。
=> CREATE RESOURCE POOL userOverflow RUNTIMECAP '5 minutes';
=> CREATE RESOURCE POOL user_0 RUNTIMECAP '1 minutes' CASCADE TO userOverflow;
=> CREATE USER "user1" RESOURCE POOL user_0;
在此场景中,user1
无法在 userOverflow
资源池上启动他或她的查询,但由于不考虑为辅助池授予权限,因此如果辅助池超出 user_0
池的 RUNTIMECAP
,user1
的查询可以级联到 userOverflow
池。使用辅助池会释放主要池中的空间,因此可以运行简短的查询。
此示例显示了一个场景,即管理员想要长时间运行的查询在辅助池上保持排队状态。
=> CREATE RESOURCE POOL rp2 PRIORITY HOLD;
=> CREATE RESOURCE POOL rp1 RUNTIMECAP '2 minutes' CASCADE TO rp2;
=> SET SESSION RESOURCE_POOL = rp1;
在此场景中,在 rp1
上运行超过 2 分钟的查询将在 rp2
上排队,直至达到 QUEUETIMEOUT
,此时查询将被拒绝。
如果您尝试删除的资源池是另一个资源池的辅助池,则 Vertica 会返回一个错误。该错误会列出与您尝试删除的辅助池有关的资源池。要删除辅助资源池,首先在主要资源池上将 CASCADE TO 参数设置为 DEFAULT
,然后再删除辅助池。
例如,可以删除资源池 rp2
,它是 rp1
的辅助池,如下:
=> ALTER RESOURCE POOL rp1 CASCADE TO DEFAULT;
=> DROP RESOURCE POOL rp2;
当查询进入辅助池时,该池的 CPUAFFINITYSET
和 CPUAFFINITYMODE
会应用于该查询。
查询会根据以下情况在不同时间采用辅助池的 RUNTIMEPRIORITY
:
如果当查询在主要池中运行时未启动 RUNTIMEPRIORITYTHRESHOLD
计时器,查询会在级联时采用辅助池的 RUNTIMEPRIORITY
。当未设置主要池的 RUNTIMEPRIORITYTHRESHOLD
时或者将主要池的 RUNTIMEPRIORITY
设置为 HIGH 时,即会发生上述情况。
如果达到主要池的 RUNTIMEPRIORITYTHRESHOLD
,查询会在级联时采用辅助池的 RUNTIMEPRIORITY
。
如果未达到主要池的 RUNTIMEPRIORITYTHRESHOLD
并且辅助池没有阈值,查询会在级联时采用新池的 RUNTIMEPRIORITY
。
如果未达到主要池的 RUNTIMEPRIORITYTHRESHOLD
并且辅助池设置了一个阈值:
如果主要池的RUNTIMEPRIORITYTHRESHOLD
大于或等于辅助池的 RUNTIMEPRIORITYTHRESHOLD
,查询在达到主要池的 RUNTIMEPRIORITYTHRESHOLD
之后会采用辅助池的 RUNTIMEPRIORITY
。
例如:
RUNTIMECAP
(主要池)为 5 秒RUNTIMEPRIORITYTHRESHOLD
(主要池)为 8 秒RUNTIMTPRIORITYTHRESHOLD
(辅助池)为 7 秒
在这种情况下,查询会在主要池上运行 5 秒,然后级联到次要池。再过去 3 秒(总共 8 秒)后,查询会采用次要池的 RUNTIMEPRIORITY
。
如果主要池的 RUNTIMEPRIORITYTHRESHOLD
小于辅助池的 RUNTIMEPRIORITYTHRESHOLD
,查询在达到辅助池的 RUNTIMEPRIORITYTHRESHOLD
之后将采用辅助池的 RUNTIMEPRIORITY
。
例如,主要池的
RUNTIMECAP
= 5 秒RUNTIMEPRIORITYTHRESHOLD
(主要池)为 8 秒RUNTIMTPRIORITYTHRESHOLD
(辅助池)为 12 秒
在这种情况下,查询会在主要池上运行 5 秒,然后级联到次要池。再过去 7 秒(总共 12 秒)后,查询会采用次要池的 RUNTIMEPRIORITY
。
您可以使用以下内容来获取有关资源池的信息:
RESOURCE_POOLS 从 Vertica 数据库编录中返回由 CREATE RESOURCE POOL 和 ALTER RESOURCE POOL 设置的资源池设置。
SUBCLUSTER_RESOURCE_POOL_OVERRIDES 显示全局资源池设置的所有子群集级别覆盖。
有关资源池的运行时信息,请参阅监控资源池。
以下示例查询两个内部资源池 GENERAL 和 TM 的各种设置:
=> SELECT name, subcluster_oid, subcluster_name, maxmemorysize, memorysize, runtimepriority, runtimeprioritythreshold, queuetimeout
FROM RESOURCE_POOLS WHERE name IN('general', 'tm');
name | subcluster_oid | subcluster_name | maxmemorysize | memorysize | runtimepriority | runtimeprioritythreshold | queuetimeout
---------+----------------+-----------------+---------------+------------+-----------------+--------------------------+--------------
general | 0 | | Special: 95% | | MEDIUM | 2 | 00:05
tm | 0 | | | 3G | MEDIUM | 60 | 00:05
(2 rows)
在 Eon 模式下,您可以在系统表中查询 SUBCLUSTER_RESOURCE_POOL_OVERRIDES 以查看对各个子群集的全局资源池的任何覆盖。以下查询显示了一个覆盖在 analytics_1 子群集中将内置资源池 TM 的 MEMORYSIZE 和 MAXMEMORYSIZE 设置为 0% 的覆盖。这些设置阻止子群集执行 Tuple Mover 合并任务。
=> SELECT * FROM SUBCLUSTER_RESOURCE_POOL_OVERRIDES;
pool_oid | name | subcluster_oid | subcluster_name | memorysize | maxmemorysize | maxquerymemorysize
-------------------+------+-------------------+-----------------+------------+---------------+--------------------
45035996273705058 | tm | 45035996273843504 | analytics_1 | 0% | 0% |
(1 row)
用户配置文件是与某个用户相关联的属性,用于控制该用户对多个系统资源的访问权限。这些资源包括:
分配给用户的资源池 (RESOURCE POOL)
用户会话可以使用的最大内存量 (MEMORYCAP)
用户会话可以使用的最大临时文件存储量 (TEMPSPACECAP)
用户查询可以运行的最长时间 (RUNTIMECAP)
可使用 CREATE USER 语句设置这些属性,之后可以使用 ALTER USER 修改这些属性。
可使用两种策略来限制用户对资源的访问权限:直接为用户设置属性来控制资源使用,或将用户分配到一个资源池。第一种方法允许您对单个用户进行微调,而第二种方法可以轻松将许多用户分到一组,然后设置他们的共同资源使用量。
如果要通过资源池分配来统一限制用户资源,请考虑以下事项:
除非用户拥有 GENERAL 池的权限或者已分配给默认资源池,否则他们无法登录 Vertica。
如果删除了用户的默认资源池,则用户的查询将使用 GENERAL 池。
如果用户没有 GENERAL 池的权限,则在您尝试删除为他们分配的资源池时,DROP 操作将失败。
在 Eon 模式数据库中,您可以将用户的默认资源池设置为特定于子群集的资源池。如果是这种情况,Vertica 将使用以下方法之一来确定在用户连接到子群集时使用哪个资源池进行查询:
如果子群集使用为用户分配的默认资源池,那么用户的查询将使用为用户分配的资源池。
如果子群集不使用用户分配的默认资源池,但用户有权访问 GENERAL 池,则用户的查询使用 GENERAL 池。
如果子群集不使用为用户分配的资源池,并且用户对于 GENERAL 池没有权限,则用户无法从该子群集的任何节点进行查询。
以下示例说明了何设置用户的资源池属性。有关其他示例,请参阅将用户定义的池和用户配置文件用于工作负载管理中描述的场景。
设置用户的 RESOURCE POOL 属性以将该用户分配到一个资源池。若要创建一个名为 user1
的用户,并且该用户具有对资源池 my_pool
的访问权限,请使用以下命令:
=> CREATE USER user1 RESOURCE POOL my_pool;
若要不通过指定池的方式来限制用户的内存量,可将该用户的 MEMORYCAP
设置为一个特定单位或占总可用内存的百分比。例如,若要创建一个名为 user2
的用户,并且将其会话限制为每个会话使用 200 MB 内存,请使用以下命令:
=> CREATE USER user2 MEMORYCAP '200M';
若要限制允许用户查询运行的时间,可设置 RUNTIMECAP
属性。若要阻止 user2
的查询运行超过五分钟,可使用以下命令:
=> ALTER USER user2 RUNTIMECAP '5 minutes';
若要限制用户会话可以使用的临时磁盘空间量,可将 TEMPSPACECAP
设置为一个特定大小或占可用临时磁盘空间的百分比。例如,下一个语句将创建 user3
,并将其限制为使用 1 GB 的临时空间:
=> CREATE USER user3 TEMPSPACECAP '1G';
可以将不同属性组合到一个命令中:例如,若要为 user3
限制 MEMORYCAP
和 RUNTIMECAP
,可将这两个属性包含在一个 ALTER USER 语句中:
=> ALTER USER user3 MEMORYCAP '750M' RUNTIMECAP '10 minutes';
ALTER USER
=> \x
Expanded display is on.
=> SELECT * FROM USERS;
-[ RECORD 1 ]-----+------------------
user_id | 45035996273704962
user_name | release
is_super_user | t
resource_pool | general
memory_cap_kb | unlimited
temp_space_cap_kb | unlimited
run_time_cap | unlimited
-[ RECORD 2 ]-----+------------------
user_id | 45035996273964824
user_name | user1
is_super_user | f
resource_pool | my_pool
memory_cap_kb | unlimited
temp_space_cap_kb | unlimited
run_time_cap | unlimited
-[ RECORD 3 ]-----+------------------
user_id | 45035996273964832
user_name | user2
is_super_user | f
resource_pool | general
memory_cap_kb | 204800
temp_space_cap_kb | unlimited
run_time_cap | 00:05
-[ RECORD 4 ]-----+------------------
user_id | 45035996273970230
user_name | user3
is_super_user | f
resource_pool | general
memory_cap_kb | 768000
temp_space_cap_kb | 1048576
run_time_cap | 00:10
在执行查询之前,Vertica 会设计一个查询计划,并将其发送到将参与执行查询的每个节点。资源管理器评估每个节点上的计划,并估算节点执行其查询部分需要的内存量和并发程度。这是查询预算,Vertica 将其存储在系统表
V_MONITOR.RESOURCE_POOL_STATUS
的 query_budget_kb
列中。
查询预算基于查询将执行的资源池的几个参数设置:
MEMORYSIZE
MAXMEMORYSIZE
PLANNEDCONCURRENCY
您可以使用
ALTER RESOURCE POOL
修改 GENERAL 资源池的 MAXMEMORYSIZE
和 PLANNEDCONCURRENCY
。此资源池通常执行未分配给用户定义资源池的查询。您可以在使用
CREATE RESOURCE POOL
或者稍后使用
ALTER RESOURCE POOL
创建任何由用户定义的资源池时设置所有三个参数。
Vertica 使用以下公式计算 GENERAL 池中的查询预算:
queryBudget = queuingThresholdPool / PLANNEDCONCURRENCY
MAXMEMORYSIZE
设置的 95%。
对于用户定义的资源池,Vertica 使用以下算法:
如果 MEMORYSIZE
设置为 0 并且 MAXMEMORYSIZE
未设置:
queryBudget = queuingThresholdGeneralPool / PLANNEDCONCURRENCY
如果 MEMORYSIZE
设置为 0 并且 MAXMEMORYSIZE
设置为非默认值:
query-budget = queuingThreshold / PLANNEDCONCURRENCY
MAXMEMORYSIZE
设置的 95%。
如果 MEMORYSIZE
设置为非默认值:
queryBudget = MEMORYSIZE / PLANNEDCONCURRENCY
通过仔细调整资源池的 MEMORYSIZE
和 PLANNEDCONCURRENCY
参数,可以控制能够为查询预算多少内存。
查询预算通常不需要调整,但是,如果由于其他目的需要内存而减少 MAXMEMORYSIZE
,请注意这样做也会减少查询预算。减少查询预算会对查询性能产生负面影响,尤其是在查询很复杂的情况下。
为了保持资源池的原始查询预算,一定要一起减小参数 MAXMEMORYSIZE
和 PLANNEDCONCURRENCY
。
资源管理器估算运行查询所需的资源,然后确定查询的优先级。您可以通过多种方式控制资源管理器如何确定查询执行的优先级:
对于每个资源池,可以管理分配给正在运行的查询的资源。您可以为每个资源池分配 HIGH、MEDIUM 或 LOW 运行时优先级。这些设置确定了运行查询时资源池中为其分配的运行时资源量(如 CPU 数量和 I/O 带宽)。优先级为 HIGH 的资源池中的查询获得的运行时资源比运行时优先级为 MEDIUM 或 LOW 的资源池中的查询要多。
虽然运行时优先级有助于管理资源池的资源,但有些情况下,您可能希望资源池内具有一定灵活性。例如,您可能希望确保每个短小查询都以高优先级运行,同时确保所有其他查询以中等或低优先级运行。
资源管理器可以为资源池设置运行时优先级阈值,从而提供这种灵活性。使用此阈值,您可以指定一个时间限制(以秒为单位),查询必须在该时间限制内完成,否则会被分配资源池的运行时优先级。所有查询在开始运行时优先级均为 HIGH;当查询的持续时间超过运行时优先级阈值中指定的时间限制后,将为其分配资源池的运行时优先级。
通过使用
CREATE RESOURCE POOL
或
ALTER RESOURCE POOL
设置两个资源池参数,指定运行时优先级和运行时优先级阈值:
RUNTIMEPRIORITY
RUNTIMEPRIORITYTHRESHOLD
CHANGE_CURRENT_STATEMENT_RUNTIME_PRIORITY
允许您更改查询的运行时优先级。您可以更改已在执行的查询的运行时优先级。
此函数具有两个实参:
查询的事务 ID(从系统表中获取)
SESSIONS
所需的优先级,以下字符串值之一:HIGH
、MEDIUM
或 LOW
超级用户可以将任何查询的运行时优先级更改为任何优先级。对于其他用户,以下限制适用:
他们只能更改其各自查询的运行时优先级。
他们不能将查询的运行时优先级提高至高于资源池的优先级。
更改查询的运行时优先级涉及到一个两步过程:
通过查询系统表
SESSIONS
来获取查询的事务 ID。例如,以下语句返回有关所有正在运行的查询的信息:
=> SELECT transaction_id, runtime_priority, transaction_description from SESSIONS;
运行 `CHANGE_CURRENT_STATEMENT_RUNTIME_PRIORITY```,指定查询的事务 ID 和所需的运行时优先级:
=> SELECT CHANGE_CURRENT_STATEMENT_RUNTIME_PRIORITY(45035996273705748, 'low')
数据库管理员可以使用 MOVE_STATEMENT_TO_RESOURCE_POOL 元函数将查询移至另一个执行当中的资源池。
如果某一个查询正在使用大量资源,您可能希望利用这一功能,以防止执行较小的查询。
将查询从一个资源池移至另一个资源池时,如果目标池具有足以容纳传入查询的资源,该查询便会继续执行。如果在目标池中无法至少在一个节点上分配足够的资源,则 Vertica 会取消该查询并尝试重新计划查询。如果 Vertica 无法重新计划查询,则该查询将被无限期地取消。
当您成功将查询移至目标资源池后,该查询的资源将由目标池提供,而在第一个池中占用的资源将被释放。
如果将查询移至具有 PRIORITY HOLD 的资源池,Vertica 会取消该查询并将其排入目标池的队列中。在您更改 PRIORITY 或将查询移至另一个没有 PRIORITY HOLD 的资源池之前,该取消状态将一直有效。如果要存储长时间运行的查询以便日后使用,可使用此选项。
若要确定目标池能否容纳要移动的查询,可查看 RESOURCE_ACQUISITIONS 或 RESOURCE_POOL_STATUS 系统表。请注意,当您查询系统表时以及调用 MOVE_STATEMENT_TO_RESOURCE_POOL 元函数时,这些表可能发生变化。
当查询从一个资源池成功移至另一个执行当中的资源池时,该查询会一直执行,直至达到现有 RUNTIMECAP 和新 RUNTIMECAP
之间的较大者为止。例如,如果初始池的 RUNTIMECAP
大于目标池,则查询可在达到初始 RUNTIMECAP
之前一直执行。
当查询从一个资源池成功移至另一个执行当中的资源池时,CPU 关联性将改变。
若要手动将查询从其当前资源池移至另一资源池,请使用 MOVE_STATEMENT_TO_RESOURCE_POOL 元函数。如下所示提供会话 ID、事务 ID、语句 ID 和目标资源池名称:
=> SELECT MOVE_STATEMENT_TO_RESOURCE_POOL ('v_vmart_node0001.example.-31427:0x82fbm', 45035996273711993, 1, 'my_target_pool');
系统表 RESOURCE_POOL_DEFAULTS 存储所有内置和用户定义的资源池的所有参数的默认值。
如果更改了任何资源池中任何参数的值并想要将其还原为默认值,只需更改表并将参数设置为 DEFAULT 即可。例如,以下语句可将资源池 sysquery 的 RUNTIMEPRIORITY 重新设置为默认值:
=> ALTER RESOURCE POOL sysquery RUNTIMEPRIORITY DEFAULT;
本部分提供如何针对各种常见情景设置和优化资源池的一般指导原则和最佳实践。
Vertica 数据库在一组商用硬件上运行。对数据库运行的所有加载和查询均需占用 CPU、内存、磁盘 I/O 带宽、文件句柄等系统资源。给定查询的性能(运行时间)取决于为其分配的资源量。
在系统中同时运行多个查询时,这些查询会共享资源;因此,每个查询的运行时间可能比其单独运行时要长。在可扩展的高效系统中,如果某个查询占用计算机的所有资源且运行时间为 X,则运行两个此类查询可能会使每个查询的运行时间增加一倍。如果查询的运行时间超过 2X,则说明系统并非可线性扩展;如果查询的运行时间小于 2X,则说明单个查询在资源使用方面存在浪费情况。请注意,只要查询获得其运行所需的最少资源且受 CPU 周期限制,就会发生上述情况。相反,如果因系统瓶颈导致查询无法获得运行所需的足够特定资源,则说明系统已达到极限。在这种情况下,为了提高并发性,必须通过另外添加此资源来扩展系统。
实际上,Vertica 应在达到系统资源限制之前,通过提高并发性而在运行时间方面实现近乎线性的可扩展性。如果达到足够的并发性且未引发任何瓶颈,便可认为系统大小非常适合工作负载。
可以为允许查询运行的时间长度设置限制。您可以将此限制设置为三个级别,按优先级降序排列:
要将用户分配到的资源池。
具有 RUNTIMECAP
(由
CREATE USER
/ALTER USER 配置)的用户配置文件
ALTER USER
会话查询(由 SET SESSION RUNTIMECAP 设置)
SET SESSION RUNTIMECAP
在所有情况下,您都可以使用不超过一年的 interval 值设置运行时限制。在多个级别设置运行时限制时,Vertica 始终使用最短的值。如果为非超级用户设置了运行时限制,则该用户无法将任何会话设置为更长的运行时限制。超级用户可以将其他用户和他们自己的会话的运行时限制设置为不超过一年(包含)的任意值。
user1
被分配至 ad_hoc_queries
资源库:
=> CREATE USER user1 RESOURCE POOL ad_hoc_queries;
user1
的 RUNTIMECAP
设置为 1 小时。
=> ALTER USER user1 RUNTIMECAP '60 minutes';
ad_hoc_queries
资源池的 RUNTIMECAP
设置为 30 分钟。
=> ALTER RESOURCE POOL ad_hoc_queries RUNTIMECAP '30 minutes';
在本例中,如果 user1
的查询超过 30 分钟,Vertica 会终止这些查询。尽管 user1
的运行时间限制设置为一小时,但运行查询的池(具有 30 分钟的运行时间限制)具有优先权。
CASCADE TO
函数为 ad_hoc_queries
池指定了一个辅助池,则当查询超过 ad_hoc_queries
池上的 RUNTIMECAP
时,就会在该辅助池上执行查询。
针对给定查询等待客户端输入或输出时,会话套接字可能会被阻止。会话套接字通常因多种原因而被阻止 — 例如,当 Vertica 执行引擎将数据传输到客户端时,或
COPY LOCAL
操作等待从客户端加载数据时。
在极少数情况下,会话套接字可能会无限期地保持被阻止状态。例如,客户端上的查询超时,这会尝试强制取消查询或者依赖会话 RUNTIMECAP
设置来终止查询。在任何一种情况下,如果查询在等待消息或数据时结束,则套接字可能会保持阻止状态,会话将挂起,直到它被强制关闭。
您可以为系统配置一个宽限期,在此期间滞后的客户端或服务器可以跟上并传递待定的响应。如果套接字保持被阻止状态的连续期间超过宽限期设置,服务器将关闭套接字并引发致命错误。随后,会话终止。如果没有设置宽限期,查询会使套接字无限期地保持被阻止状态。
您应该将会话宽限期设置为高到足以覆盖可接受的延迟范围并避免会话过早关闭 — 例如,为了对服务器进行响应而设置正常的客户端延迟。超大负载操作可能会要求您根据需要调整会话宽限期。
您可以将宽限期设置为四个级别,按优先级降序排列:
会话(最高)
用户
节点
数据库
在数据库和节点级别,可以通过 BlockedSocketGracePeriod
配置参数将宽限期设置为不超过 20 天的任意间隔:
ALTER DATABASE db-name SET BlockedSocketGracePeriod = 'interval';
ALTER NODE node-name SET BlockedSocketGracePeriod = 'interval';
默认情况下,这两个级别的宽限期都设置为空字符串,允许无限期阻止。
您可以为单个用户和给定会话设置宽限期,如下所示:
{ CREATE | ALTER USER } user-name GRACEPERIOD {'interval' | NONE };
SET SESSION GRACEPERIOD { 'interval' | = DEFAULT | NONE };
用户可以将会话设置为等于或小于为该用户设置的宽限期的任意间隔。超级用户可以将其他用户和他们自己的会话的宽限期设置为不超过 20 天(包含)的任意值。
超级用户 dbadmin
将数据库宽限期设置为 6 小时。此限制仅适用于非超级用户。 dbadmin
可以将自己的会话宽限期设置为不超过 20 天的任意值 — 在本例中为 10 小时:
=> ALTER DATABASE VMart SET BlockedSocketGracePeriod = '6 hours';
ALTER DATABASE
=> SHOW CURRENT BlockedSocketGracePeriod;
level | name | setting
----------+--------------------------+---------
DATABASE | BlockedSocketGracePeriod | 6 hours
(1 row)
=> SET SESSION GRACEPERIOD '10 hours';
SET
=> SHOW GRACEPERIOD;
name | setting
-------------+---------
graceperiod | 10:00
(1 row)
dbadmin
创建未设置宽限期的用户 user777
。因此,user777
的有效宽限期派生自数据库的 BlockedSocketGracePeriod
设置,即 6 小时。user777
将会话宽限期设置为大于 6 小时的任何尝试都会返回错误:
=> CREATE USER user777;
=> \c - user777
You are now connected as user "user777".
=> SHOW GRACEPERIOD;
name | setting
-------------+---------
graceperiod | 06:00
(1 row)
=> SET SESSION GRACEPERIOD '7 hours';
ERROR 8175: The new period 07:00 would exceed the database limit of 06:00
dbadmin
为 user777
设置 5 分钟的宽限期。现在,user777
可以将会话宽限期设置为等于或小于用户级别设置的任何值:
=> \c
You are now connected as user "dbadmin".
=> ALTER USER user777 GRACEPERIOD '5 minutes';
ALTER USER
=> \c - user777
You are now connected as user "user777".
=> SET SESSION GRACEPERIOD '6 minutes';
ERROR 8175: The new period 00:06 would exceed the user limit of 00:05
=> SET SESSION GRACEPERIOD '4 minutes';
SET
本部分中的场景介绍常见的工作负载管理问题,并提供带示例的解决方案。
您每天晚上都执行批量加载,偶尔(很少)在白天执行批量加载。当加载正在运行时,减少查询的资源使用量是可接受的,但在其他所有时间,您希望所有资源都可用于查询。
为加载创建一个单独的资源池,使其优先级高于内置 GENERAL 池上的预配置设置。
在此场景中,在从 GENERAL 池中借用内存时,每天晚上的加载具有优先权。在没有运行加载时,所有内存将可自动用于查询。
创建一个优先级高于 GENERAL 池的资源池:
创建 PRIORITY 设置为 10 的资源池 load_pool
:
=> CREATE RESOURCE POOL load_pool PRIORITY 10;
修改用户 load_user
以使用新的资源池:
=> ALTER USER load_user RESOURCE POOL load_pool;
CEO 在每周一的上午 9 点运行报告,您想要确保该报告始终运行。
若要确保某个查询或某类查询始终获得资源,可以为其创建专用池,如下所示:
使用 PROFILE 命令,运行 CEO 每周运行的查询,确定应当分配的内存量:
=> PROFILE SELECT DISTINCT s.product_key, p.product_description
-> FROM store.store_sales_fact s, public.product_dimension p
-> WHERE s.product_key = p.product_key AND s.product_version = p.product_version
-> AND s.store_key IN (
-> SELECT store_key FROM store.store_dimension
-> WHERE store_state = 'MA')
-> ORDER BY s.product_key;
在查询结束时,系统返回一条含有资源使用量的通知:
NOTICE: Statement is being profiled.HINT: select * from v_monitor.execution_engine_profiles where
transaction_id=45035996273751349 and statement_id=6;
NOTICE: Initiator memory estimate for query: [on pool general: 1723648 KB,
minimum: 355920 KB]
创建具有上述提示所报告的 MEMORYSIZE 的资源池,确保至少为 CEO 查询保留此内存大小:
=> CREATE RESOURCE POOL ceo_pool MEMORYSIZE '1800M' PRIORITY 10;
CREATE RESOURCE POOL
=> \x
Expanded display is on.
=> SELECT * FROM resource_pools WHERE name = 'ceo_pool';
-[ RECORD 1 ]-------+-------------
name | ceo_pool
is_internal | f
memorysize | 1800M
maxmemorysize |
priority | 10
queuetimeout | 300
plannedconcurrency | 4
maxconcurrency |
singleinitiator | f
假设 CEO 报告用户已存在,使用 ALTER USER 语句将此用户与上述资源池相关联。
=> ALTER USER ceo_user RESOURCE POOL ceo_pool;
发出以下命令确认 ceo_user 与 ceo_pool 相关联:
=> SELECT * FROM users WHERE user_name ='ceo_user';
-[ RECORD 1 ]-+------------------
user_id | 45035996273713548
user_name | ceo_user
is_super_user | f
resource_pool | ceo_pool
memory_cap_kb | unlimited
如果 CEO 查询内存使用量过大,可以让资源管理器将其降至符合特定预算的水平。请参阅查询预算。
Joe 是一位经常在中午运行大型报告的业务分析员,这些报告占用了整个机器的资源。您想要防止 Joe 使用超过 100MB 的内存,并且想要将 Joe 的查询运行时间限制在 2 小时以内。
用户配置文件 为此场景提供了一个解决方案。若要限制 Joe 一次可以使用的内存量,使用 ALTER USER 命令将 Joe 的 MEMORYCAP 设置为 100MB。若要限制 Joe 的查询可以运行的时间长度,使用相同的命令将 RUNTIMECAP 设置为 2 小时。如果 Joe 运行的查询超过其最高限值,Vertica 会拒绝该查询。
如果您有一批需要限制查询的用户,也可以为他们创建一个资源池并为该资源池设置 RUNTIMECAP。将这些用户移至此资源池后,Vertica 会将这些用户的所有查询限制为您为该资源池指定的 RUNTIMECAP。
=> ALTER USER analyst_user MEMORYCAP '100M' RUNTIMECAP '2 hours';
如果 Joe 尝试运行超过 100MB 的查询,系统会返回一个错误,说明请求超出内存会话限制,如以下示例所示:
\i vmart_query_04.sqlvsql:vmart_query_04.sql:12: ERROR: Insufficient resources to initiate plan
on pool general [Request exceeds memory session limit: 137669KB > 102400KB]
只有系统数据库管理员 (dbadmin) 可以提高 MEMORYCAP 设置。用户不能提高自己的 MEMORYCAP 设置,如果他们尝试编辑其 MEMORYCAP 或 RUNTIMECAP 设置,则将看到类似于以下内容的错误:
ALTER USER analyst_user MEMORYCAP '135M';
ROLLBACK: permission denied
您最近将数据仓库提供给一大群用户,而他们并不熟悉 SQL。一些用户对大量的行运行报告,耗尽了系统资源。您想要制约这些用户的系统使用量。
为 MAXMEMORYSIZE 等于 MEMORYSIZE 的临时应用程序创建资源池。这可以防止该资源池中的查询从 GENERAL 池中借用资源。另外,设置 RUNTIMECAP 以限制临时查询的最大持续时间。
=> CREATE RESOURCE POOL adhoc_pool
MEMORYSIZE '200M'
MAXMEMORYSIZE '200M'
RUNTIMECAP '20 seconds'
PRIORITY 0
QUEUETIMEOUT 300
PLANNEDCONCURRENCY 4;
=> SELECT pool_name, memory_size_kb, queueing_threshold_kb
FROM V_MONITOR.RESOURCE_POOL_STATUS WHERE pool_name='adhoc_pool';
pool_name | memory_size_kb | queueing_threshold_kb
------------+----------------+-----------------------
adhoc_pool | 204800 | 153600
(1 row)
将此资源池与应用程序用于连接到数据库的数据库用户相关联。
=> ALTER USER app1_user RESOURCE POOL adhoc_pool;
为了便于开票,分析员 Jane 希望对此应用程序的并发性实施硬性限制。如何做到这一点?
最简单的解决方案是为该应用程序的用户创建一个单独的资源池,并将其 MAXCONCURRENCY 设置为所需的并发级别。任何超过 MAXCONCURRENCY 的查询都会进入队列。
在本示例中,存在四位与开票池相关联的开票用户。目的是对资源池设置硬性限制,使一次最多可以执行三个并发查询。所有其他查询将排队并在资源释放后完成。
=> CREATE RESOURCE POOL billing_pool MAXCONCURRENCY 3 QUEUETIMEOUT 2;
=> CREATE USER bill1_user RESOURCE POOL billing_pool;
=> CREATE USER bill2_user RESOURCE POOL billing_pool;
=> CREATE USER bill3_user RESOURCE POOL billing_pool;
=> CREATE USER bill4_user RESOURCE POOL billing_pool;
=> \x
Expanded display is on.
=> select maxconcurrency,queuetimeout from resource_pools where name = 'billing_pool';
maxconcurrency | queuetimeout
----------------+--------------
3 | 2
(1 row)
> SELECT reason, resource_type, rejection_count FROM RESOURCE_REJECTIONS
WHERE pool_name = 'billing_pool' AND node_name ilike '%node0001';
reason | resource_type | rejection_count
---------------------------------------+---------------+-----------------
Timedout waiting for resource request | Queries | 16
(1 row)
如果查询正在运行并且没有在规定时间内完成(默认超时设置为 5 分钟),请求的下一查询将收到一条类似于以下内容的错误:
ERROR: Insufficient resources to initiate plan on pool billing_pool [Timedout waiting for resource request: Request exceeds limits:
Queries Exceeded: Requested = 1, Free = 0 (Limit = 3, Used = 3)]
下表显示在开票池中存在三个活动查询。
=> SELECT pool_name, thread_count, open_file_handle_count, memory_inuse_kb FROM RESOURCE_ACQUISITIONS
WHERE pool_name = 'billing_pool';
pool_name | thread_count | open_file_handle_count | memory_inuse_kb
--------------+--------------+------------------------+-----------------
billing_pool | 4 | 5 | 132870
billing_pool | 4 | 5 | 132870
billing_pool | 4 | 5 | 132870
(3 rows)
您具有一个带交互门户的 Web 应用程序。有时,当 IT 人员运行批处理报告时,Web 页面需要很长时间才能刷新,引起用户抱怨,因此您想要为网站用户提供更好的体验。
可以应用先前场景中学习到的原则解决此问题。基本思想是将查询分离成与不同资源池相关联的两组。先决条件是存在两个不同的数据库用户,他们发出不同类型的查询。如果情况与此不同,请将此视为应用程序设计的最佳方法。
方法 1
: 创建专门用于 Web 页面刷新查询的资源池,同时:
根据查询的平均资源需求以及门户中发出的并发查询的预期数量来设定资源池大小。
将此资源池与运行网站查询的数据库用户相关联。有关创建专用池的信息,请参阅CEO 查询。
这可确保网站查询始终运行并且从不排列在大型批处理作业之后。让处理作业不在 GENERAL 池上运行。
例如,以下池是根据从 Web 运行的查询所需的平均资源量以及并发查询的预期数量来确定的。另外,它给予 Web 查询的优先级要高于任何正在运行的批处理作业,并假定将查询调整为每个查询占用 250M:
=> CREATE RESOURCE POOL web_pool
MEMORYSIZE '250M'
MAXMEMORYSIZE NONE
PRIORITY 10
MAXCONCURRENCY 5
PLANNEDCONCURRENCY 1;
方法 2
创建具有固定内存大小的资源池。这将限制批处理报告可用的内存量,使内存始终留作其他用途。有关详细信息,请参阅限制临时查询应用程序的资源使用率。
例如:
=> CREATE RESOURCE POOL batch_pool
MEMORYSIZE '4G'
MAXMEMORYSIZE '4G'
MAXCONCURRENCY 10;
如果您有三个或更多不同类别的工作负载,此原则同样适用。
您希望一个部门的用户查询比另一个部门的查询具有更高的优先级。
解决方案类似于混合工作负载场景。在此场景中,您没有限制资源使用量;您设置了不同的优先级。若要这样做,可创建两个不同的池,每个池都具有 MEMORYSIZE=0% 以及一个不同的 PRIORITY 参数。这两个池都从 GENERAL 池中借用资源,但在争夺资源时,优先级决定了每个池的请求获得批准的顺序。例如:
=> CREATE RESOURCE POOL dept1_pool PRIORITY 5;
=> CREATE RESOURCE POOL dept2_pool PRIORITY 8;
如果您发现此解决方案不足以满足需要,或者一个部门的查询一直使另一个部门的用户难以执行查询,则可以通过设置 MEMORYSIZE 为每个池添加预留,这样会保证每个部门都可以使用一些内存。
例如,两个资源都使用 GENERAL 池获取内存,所以您可以通过使用 ALTER RESOURCE POOL 更改每个池的 MEMORYSIZE,为每个资源池分配一些内存。
=> ALTER RESOURCE POOL dept1_pool MEMORYSIZE '100M';
=> ALTER RESOURCE POOL dept2_pool MEMORYSIZE '150M';
您希望应用程序运行连续加载流,但许多应用程序运行的是并行查询流。您希望确保性能是可预计的。
此场景的解决方案取决于您的查询组合。在所有情况下,以下方法均适用:
确定所需的连续加载流数量。如果单个流没有提供足够的吐吞量,则这可能与所需加载速率有关,或者与要加载的数据源数量有更直接的关系。为加载创建一个专用资源池,并将其与要执行加载的数据库用户相关联。有关详细信息,请参阅CREATE RESOURCE POOL。
通常,加载池的并行设置应少于每个节点的内核数。除非源进程较慢,否则会更高效地为每个加载贡献更多内存,并且具有更多的加载队列。如果预计有队列,请调整加载池的 QUEUETIMEOUT 设置。
运行加载工作负载一会儿,观察加载性能是否符合预期。如果 Tuple Mover 没有经过充分优化以涵盖加载行为,请参阅管理 Tuple Mover。
如果系统中有多种类型的查询(例如,对于某些查询,必须为交互用户快速提供答复,而其他查询属于批量报告处理的一部分),请遵循处理混合工作负载:批处理与交互式处理中的准则。
运行查询并观察性能。如果某些类别的查询没有按预期执行,那么您可能需要按照限制临时查询应用程序的资源使用率中所述调整 GENERAL 池,或者为这些查询创建更多专用资源池。有关详细信息,请参阅CEO 查询和处理混合工作负载:批处理与交互式处理。
有关在混合工作负载环境中获得可预测结果的信息,请参阅关于管理工作负载和CREATE RESOURCE POOL的部分。
您最近为没有 SQL 方面经验并且经常运行临时报表的用户创建了一个资源池。到目前为止,您通过创建 MEMORYSIZE 和 MAXMEMORYSIZE 相等的资源池来管理资源分配。这可以防止该资源池中的查询从 GENERAL 池中借用资源。现在,您希望在运行时管理资源并优先考虑短查询,使其不会因为运行时资源有限而排队。
将资源池的 RUNTIMEPRIORITY
设置为 MEDIUM 或 LOW。
将资源池的 RUNTIMEPRIORITYTHRESHOLD
设置为您希望确保始终以较高优先级运行的查询的持续时间。
例如:
=> ALTER RESOURCE POOL ad_hoc_pool RUNTIMEPRIORITY medium RUNTIMEPRIORITYTHRESHOLD 5;
由于 RUNTIMEPRIORITYTHRESHOLD
设置为 5,因此资源池 ad_hoc_pool
中在 5 秒内完成的所有查询都以高优先级运行。超过 5 秒的查询将下降到分配给该资源池的 RUNTIMEPRIORITY
MEDIUM。
您希望资源池中的大部分查询以 HIGH 运行时优先级运行,但能够将长于 1 小时的作业调整为更低的优先级。
将资源池的 RUNTIMEPRIORITY 设置为 LOW,将 RUNTIMEPRIORITYTHRESHOLD 设置为一个仅截断最长作业的数值。
要确保向所有持续时间长于 3600 秒(1 小时)的查询分配低运行时优先级,请按如下所示修改资源池:
将 RUNTIMEPRIORITY 设置为 LOW。
将 RUNTIMETHRESHOLD 设置为 3600
=> ALTER RESOURCE POOL ad_hoc_pool RUNTIMEPRIORITY low RUNTIMEPRIORITYTHRESHOLD 3600;
本部分中的场景介绍如何优化内置池。
您有一个嵌入 Vertica 的单节点应用程序,部分 RAM 需要专用于该应用程序进程。在此场景中,您想要将 Vertica 限制为仅使用可用 RAM 的 60%。
将 GENERAL 池的 MAXMEMORYSIZE 参数设置为所需的内存大小。有关资源限制的讨论,请参阅资源池架构。
您有一个大型数据库,其中包含一个具有两个投影以及默认设置的大型表,恢复操作所需的时间过长。您想要为恢复操作提供更多内存以提高速度。
将 RECOVERY 池的 PLANNEDCONCURRENCY 和 MAXCONCURRENCY 设为 1,使恢复操作可以从 GENERAL 池中获得尽可能多的内存并且每次仅运行一个线程。
当 刷新操作正在运行时,系统性能会受到影响,用户查询会被拒绝。您想要降低刷新作业的内存用量。
将刷新池的 MEMORYSIZE 设置为固定值。然后,资源管理器调整刷新查询,使其仅使用此数量的内存。
在重负载操作期间,您偶尔会注意到 ROS 容器的数量激增。您希望 Tuple Mover 更主动地执行合并以合并 ROS 容器,并避免 ROS 推回。
使用 ALTER RESOURCE POOL 增加 TM 资源池 中 MAXCONCURRENCY 的设置。此设置确定有多少线程可用于合并。默认情况下,此参数设置为 7。Vertica 将一半线程分配给处于活动状态的分区,其余一半根据需要分配给处于活动状态和非活动状态的分区。如果 MAXCONCURRENCY 设置为奇整数,Vertica 会向上舍入以支持处于活动状态的分区。
例如,如果您将 MAXCONCURRENCY 增加到 9,则 Vertica 会将五个线程专门分配给处于活动状态的分区,并将剩余的四个线程分配给处于活动状态和非活动状态的分区。
您有一个专用于时间敏感型分析查询的辅助子群集。您希望限制此子群集上可能干扰其处理查询的任何其他工作负载,并同时释放内存以执行查询。
默认情况下,每个子群集都有一个用于 Tuple Mover 操作的内置 TM 资源池,该资源池可用于执行 Tuple Mover 合并操作。TM 池消耗可用于查询的内存。此外,合并操作可能会给子群集的处理稍微增加一点开销。您希望重新分配由 TM 池消耗的内存,并阻止子群集运行合并操作。
使用 ALTER RESOURCE POOL 覆盖辅助子群集的全局 TM 资源池,并将其 MAXMEMORYSIZE 和 MEMORYSIZE 都设置为 0。这允许您使用由全局 TM 池消耗的内存来运行分析查询,并防止为子群集分配要执行的 TM 合并操作。
大量机器学习功能正在运行,您希望为它们提供更多内存以提高性能。
Vertica 在 BLOBDATA 资源池中执行机器学习功能。为了提高机器学习功能的性能并避免将查询溢出到磁盘,请使用 ALTER RESOURCE POOL 增加池的 MAXMEMORYSIZE 设置。
有关调整查询预算的更多信息,请参阅查询预算。
查询的运行时间取决于查询的复杂性、计划中的运算符数、数据量和投影设计。I/O 或 CPU 瓶颈可能导致查询运行速度低于预期。您通常可以使用更好的投影设计来纠正 CPU 使用率过高的问题。高 I/O 通常可以追溯到由溢出到磁盘的联接和排序引起的争用。但是,没有单一的解决方案可以解决所有导致高 CPU 或 I/O 使用率的查询。您必须单独分析和优化每个查询。
可通过两种方式评估运行缓慢的查询:
使用
EXPLAIN
预先修复查询以查看优化器的查询计划。
通过查询系统表
QUERY_CONSUMPTION
或
执行_引擎_配置文件
检查执行配置文件。
检查查询计划会揭露以下一个或多个问题:
投影排序顺序不理想
对未排序或未编码的列进行谓词评估
使用 GROUPBY HASH
而非 GROUPBY PIPE
Vertica 提供的分析机制可帮助您评估不同级别的数据库性能。例如,可为单个语句、单个会话或所有节点上的所有会话收集分析数据。有关详细信息,请参阅对数据库性能执行分析。
您主要使用子群集来控制 Eon 模式数据库中的工作负载。例如,您可以为特定用例(如 ETL 或查询工作负载)创建子群集,也可以为不同的用户组创建子群集以隔离工作负载。在每个子群集中,您可以创建单独的资源池以根据工作负载优化资源分配。有关 Vertica 如何使用子群集的详细信息,请参阅管理子群集。
您可以定义影响数据库中所有节点的全局资源池分配。您还可以在子群集级别创建资源池分配。如果您同时创建这两种分配,则子群集级别的设置会覆盖全局设置。
您可以使用此功能移除子群集不需要的全局资源池。此外,您可以使用适合大多数子群集的设置创建资源池,然后根据需要为特定子群集定制设置。
覆盖子群集级别的资源池设置将允许您隔离内置和用户定义的资源池并按工作负载优化它们。您通常为不同的子群集分配特定的角色:
专用于 ETL 工作负载和 DDL 语句(这些语句用来更改数据库)的子群集。
专用于运行长时间运行的深入分析查询的子群集。为了获得最佳性能,需要为这些查询分配更多的资源。
用于运行许多短时间运行的“仪表板”查询(您希望快速完成且并行运行这些查询)的子群集。
定义由每个子群集执行的查询类型后,您可以创建一个特定于子群集的资源池,并对该资源池进行优化以提高此工作负载的效率。
以下方案按工作负载优化 3 个子群集:
ETL:用于执行 ETL 的子群集,您希望针对 Tuple Mover 操作优化该 ETL。
仪表板:您要为短时间运行的查询指定的子群集,这些查询由大量用户执行,可用于刷新网页。
分析:要为长时间运行的查询指定的子群集。
有关资源池调整的其他方案,请参阅工作负载资源管理最佳实践。
Vertica 选择在存储库中的合并操作中涉及 ROS 容器最多的子群集来执行合并(请参阅 Eon 模式数据库中的 Tuple Mover)。通常,执行 ETL 的子群集将是执行合并的最佳候选者,因为该子群集加载的数据参与合并。您可以选择通过更改 TM 池的 MAXCONCURRENCY 设置来提高子群集上合并操作的性能,以增加可用于合并操作的线程数。您无法在子群集级别更改此设置,因此您必须在全局设置它:
=> ALTER RESOURCE POOL TM MAXCONCURRENCY 10;
有关 Tuple Mover 资源的更多信息,请参阅调整 Tuple Mover 池设置。
默认情况下,辅助子群集将内存分配给 Tuple Mover 资源池。此池设置允许 Vertica 将合并操作分配给辅助子群集,这会增加少量开销。如果您将辅助子群集主要用于查询,则最佳做法是回收 TM 池使用的内存并防止将合并操作分配给辅助子群集。
要优化仪表板查询辅助子群集,请设置其 TM 池的 MEMORYSIZE 和 MAXMEMORYSIZE 设置为 0:
=> ALTER RESOURCE POOL TM FOR SUBCLUSTER dashboard MEMORYSIZE '0%'
MAXMEMORYSIZE '0%';
若要确认覆盖,请查询 SUBCLUSTER_RESOURCE_POOL_OVERRIDES 表:
=> SELECT pool_oid, name, subcluster_name, memorysize, maxmemorysize
FROM SUBCLUSTER_RESOURCE_POOL_OVERRIDES;
pool_oid | name | subcluster_name | memorysize | maxmemorysize
-------------------+------+-----------------+------------+---------------
45035996273705046 | tm | dashboard | 0% | 0%
(1 row)
若要针对网页上短时间运行的查询优化仪表板子群集,请创建一个名为 dash_pool 的子群集级资源池,该池使用子群集 70% 的内存。此外,增加 PLANNEDCONCURRENCY 以使用机器的所有逻辑核心,并将 EXECUTIONPARALLELISM 限制为不超过机器可用核心的一半:
=> CREATE RESOURCE POOL dash_pool FOR SUBCLUSTER dashboard
MEMORYSIZE '70%'
PLANNEDCONCURRENCY 16
EXECUTIONPARALLELISM 8;
若要针对网页上长时间运行的查询优化分析子群集,请创建一个名为 analytics_pool 的子群集级资源池,该池使用子群集 60% 的内存。在这种情况下,您无法为该池分配更多内存,因为该子群集中的节点仍将内存分配给其 TM 池。此外,将 EXECUTIONPARALLELISM 设置为 AUTO 以使用节点上的所有可用核心来处理查询,并将 PLANNEDCONCURRENCY 限制为不超过 8 个并发查询:
=> CREATE RESOURCE POOL analytics_pool FOR SUBCLUSTER analytics
MEMORYSIZE '60%'
EXECUTIONPARALLELISM AUTO
PLANNEDCONCURRENCY 8;
您可以使用 使用系统表 来跟踪群集上的总体资源使用情况。Vertica 系统表中介绍了这些系统表和其他系统表。
如果您的查询因资源不可用而遇到错误,则可使用以下系统表获取更多详细信息:
当某种类型的资源请求被拒绝时,请执行以下操作之一:
RESOURCE_REJECTIONS 中的 LAST_REJECTED_VALUE
字段指示问题原因。例如:
消息 Usage of a single requests exceeds high limit
表示系统没有可用于单个请求的足够资源。当文件处理限制设置得过低且您正在加载包含大量列的表时,通常会出现上述情况。
消息“等待资源预留超时或已取消等待资源预留 (Timed out or Canceled waiting for resource reservation)”通常表示资源争夺过于激烈,因为硬件平台无法支持使用它的并发用户数。
Vertica 为数据库管理员提供了用于查看和控制会话的强大方法。具体方法因会话类型而异:
外部(用户)会话通过 vsql 或编程式(ODBC 或 JDBC)连接启动,并且具有关联的客户端状态。
内部(系统)会话由 Vertica 启动,但没有客户端状态。
每个节点的最大用户会话数由配置参数 MaxClientSessions
设置,默认值为 50。您可以将 MaxClientSessions
参数设置为 0 到 1000 之间的任何值。除了这个最大值外,Vertica 还允许每个节点最多五个管理会话。
例如:
=> ALTER DATABASE DEFAULT SET MaxClientSessions = 100;
MaxClientSessions
值的情况下的预期相比,这些情况可以产生更多成功的“连接到数据库 (Connect to Database)”命令。
系统表
SESSIONS
包含有关用户会话的详细信息,每个会话将返回一行。超级用户可以无限制地访问所有数据库元数据。其他用户的访问权限因他们的权限而异。
您可以使用 Vertica 函数
INTERRUPT_STATEMENT
中断正在运行的语句。中断正在运行的语句会将会话返回到空闲状态:
没有语句或事务正在运行。
没有保留任何锁。
数据库不代表会话做任何工作。
关闭用户会话将中断该会话并处置与其相关的所有状态,包括用于目标会话的客户端套接字连接。以下 Vertica 函数关闭一个或多个用户会话:
SELECT
用来调用这些函数的 SELECT 语句在中断或关闭消息传递到所有节点后返回。该函数可能会在 Vertica 完成中断或关闭操作的执行之前返回。因此,在语句返回之后以及在中断或关闭操作在整个群集中生效过程中可能存在一个延迟。若要确定会话或事务是否结束,请查询 SESSIONS
系统表。
为了关闭数据库,您必须首先关闭所有用户会话。有关数据库关闭的更多信息,请参阅停止数据库。
您可以使用系统表 LOAD_STREAMS 在数据加载到群集时对数据进行监控。此表中的几列显示每个节点上每个加载流的指标,包括:
取决于数据大小,在 PARSE_COMPLETE_PERCENT
达到 100% 的时间与 SORT_COMPLETE_PERCENT
开始增长的时间之间可能会出现明显的滞后。
当 Vertica 数据库正在运行时,
群集中的每个
节点都将消息写入名为 vertica.log
的文件中。例如,
Tuple Mover 和事务管理器以特定间隔将 INFO 消息写入 vertica.log
,即使没有任何合并活动也是如此。
您配置 vertica.log
文件的位置。默认情况下,日志文件位于:
catalog-path/database-name/node-name_catalog/vertica.log
catalog-path 是 NODES 系统表中显示的路径去掉末尾的 Catalog 目录。
database-name 是您的数据库的名称。
node-name
是在 NODES 系统表中显示的节点的名称。
要实时监控正在运行的数据库中的某个节点:
在群集中的任意节点上登录到数据库管理员帐户。
在终端窗口中,输入:
$ tail -f catalog-path/database-name/node-name_catalog/vertica.log
系统启动期间,在 Vertica 日志已初始化为写入消息之前,群集中的每个节点都将消息写入名为 dbLog
的文件中。此日志可用于诊断数据库无法启动、进而无法将消息写入 vertica.log
的情况。dblog
位于以下路径中,使用的是上文所述的 catalog-path
和 database-name
:
catalog-path/database-name/dbLog
大部分 Linux 分发版都包含 logrotate
实用程序。使用此实用程序可简化日志文件管理。通过设置 logrotate
配置文件,您可以使用此实用程序自动完成以下一项或多项任务:
压缩和轮转日志文件
自动删除日志文件
通过电子邮件将日志文件发送给指定的收件人
可将 logrotate
配置为定期或在日志文件达到特定大小时完成这些任务。
如果安装 Vertica 时存在 logrotate
,则 Vertica 会自动将此实用程序设置为查找配置文件。因此,logrotate 会在每个节点的
/opt/vertica/config/logrotate
目录中搜索配置文件。
当您创建数据库时,Vertica 会在群集中的每个节点上创建特定于数据库的 logrotate
配置,以供 logrotate
实用程序使用。之后,它会为每个单独的数据库创建一个路径为
/opt/vertica/config/logrotate/
的文件。
有关其他设置的信息,请使用 man logrotate
命令。
在 Vertica 安装期间,安装程序会为 dbadmin
用户配置 cron 作业。此 cron 作业被配置为执行运行 logrotate
实用程序的 Python 脚本。您可以通过查看 dbadmin.cron
文件(位于 /opt/vertica/config
目录中)查看此 cron 作业的详细信息。
如果要自定义 cron 作业,以便为 Vertica 数据库配置 logrotate,您必须在 dbadmin
用户下创建 cron 作业。
可借助 admintools logrotate
选项为数据库配置 logrotate
脚本并在整个群集上分发这些脚本。使用 logrotate
选项可以指定:
轮转日志的频率
可轮转的日志大小
日志的保留时长
例如:
以下示例显示如何将日志设置为每周轮转一次并保留三个月(12 个日志)。
$ admintools -t logrotate -d <dbname> -r weekly -k 12
有关详细使用信息,请参阅编写管理工具脚本。
管理控制台日志文件是:
/opt/vconsole/log/mc/mconsole.log
要为 MC 配置 logrotate
,请配置以下文件:
/opt/vconsole/temp/webapp/WEB-INF/classes/log4j.xml
编辑 log4j.xml
文件并按如下方式设置相应的参数:
限制日志的大小:
<param name="MaxFileSize" value="1MB"/>
限制日志的文件备份数量:
<param name="MaxBackupIndex" value="1"/>
以 root 用户身份重新启动 MC:
# etc/init.d/vertica-consoled restart
要实施自定义日志轮转过程,请执行以下步骤:
对现有的 vertica.log 文件进行重命名或存档。例如:
$ mv vertica.log vertica.log.1
使用以下任一方法向 Vertica 进程发送 USR1 信号:
$ killall -USR1 vertica
或
$ ps -ef | grep -i vertica
$ kill -USR1 process-id
可以使用 ps
监控数据库和在群集中的每个节点上运行的 Spread 进程。例如:
$ ps aux | grep /opt/vertica/bin/vertica
$ ps aux | grep /opt/vertica/spread/sbin/spread
对于常见配置,您应在每个节点上看到一个 Vertica 进程和一个 Spread 进程。要监控管理工具 (Administration Tools) 和连接器进程:
$ ps aux | grep vertica
连接进程会有许多,但管理工具 (Administration Tools) 进程只有一个。
您应对群集中任何节点或所有节点上的系统资源使用情况进行监控。可以使用系统活动报告 (SAR) 监控资源使用情况。
pstack
和 sysstat
以帮助监控 Linux 资源。SYSSTAT 包中包含用于监控系统性能和使用活动的实用程序(例如 sar
),以及可通过 cron
调度以收集性能和活动数据的工具。有关详细信息,请参阅 SYSSTAT 网页。pstack
实用程序用于打印正在运行的进程的堆栈跟踪。有关详细信息,请参阅 PSTACK 手册页。
在任意节点上登录到数据库管理员帐户。
运行 top
实用程序
$ top
如果 top
中的 CPU 百分比很高,则表示 Vertica 为计算密集型。例如:
top - 11:44:28 up 53 days, 23:47, 9 users, load average: 0.91, 0.97, 0.81
Tasks: 123 total, 1 running, 122 sleeping, 0 stopped, 0 zombie
Cpu(s): 26.9%us, 1.3%sy, 0.0%ni, 71.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 4053136 total, 3882020k used, 171116 free, 407688 buffers
Swap: 4192956 total, 176k used, 4192780 free, 1526436 cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
13703 dbadmin 1 0 1374m 678m 55m S 99.9 17.1 6:21.70 vertica
2606 root 16 0 32152 11m 2508 S 1.0 0.3 0:16.97 X
1 root 16 0 4748 552 456 S 0.0 0.0 0:01.51 init
2 root RT -5 0 0 0 S 0.0 0.0 0:04.92 migration/0
3 root 34 19 0 0 0 S 0.0 0.0 0:11.75 ksoftirqd/0
...
下面是一些可能造成高 CPU 使用率的原因:
即使未连接至数据库, Tuple Mover 也会自动运行,因而消耗了 CPU 时间。
swappiness 内核参数不能设置为 0。从 Linux 命令行执行以下命令,查看以下参数的值:
$ cat /proc/sys/vm/swappiness
如果此参数的值不为 0,请按照检查 swappiness中的步骤进行更改。
一些信息源:
运行 iostat
实用程序。如果在 iostat
中的块读取速率很高的情况下,top
中出现高空闲时间,则表示 Vertica 为磁盘密集型。例如:
$ /usr/bin/iostat
Linux 2.6.18-164.el5 (qa01) 02/05/2011
avg-cpu: %user %nice %system %iowait %steal %idle
0.77 2.32 0.76 0.68 0.00 95.47
Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
hda 0.37 3.40 10.37 2117723 6464640
sda 0.46 1.94 18.96 1208130 11816472
sdb 0.26 1.79 15.69 1114792 9781840
sdc 0.24 1.80 16.06 1119304 10010328
sdd 0.22 1.79 15.52 1117472 9676200
md0 8.37 7.31 66.23 4554834 41284840
可以使用这些系统表来监控群集中的磁盘空间使用情况。
Vertica 包括一些系统表,可用于监控弹性群集的重新平衡状态并获得节点上的弹性群集状态的常规信息。
REBALANCE_TABLE_STATUS 提供有关重新平衡的常规信息。该表显示了每个表的已分隔数据量、当前正在分隔的数据量以及要分隔的数据量。该表还显示了已传输的数据量、当前正在传输的数据量以及要传输的剩余数据量(或者是估计值,如果未分隔存储空间)。
REBALANCE_PROJECTION_STATUS 可用于获取与正在重新平衡的特定投影有关的更多详细信息。它提供的信息类型与上述信息类型相同,但这些信息与投影有关而非与表有关。
在每个表中,SEPARATED_PERCENT
和 TRANSFERRED_PERCENT
列可用于确定总体进度。
有关已完成工作的历史信息会保留下来,因此使用表列 IS_LATEST 查询可将输出限制为仅包含最近或当前的重新平衡活动。历史数据可能包括与已删除的投影或表有关的信息。如果表或投影已删除并且有关锚表的信息不可用,则将表 ID 显示为 NULL,将表名显示为 <unknown>
。与已删除的表有关的信息仍然很有用,例如,可用于在执行任务期间提供理由。
为了帮助您监控数据库系统,Vertica 捕获并记录如果根本原因未得到解决便会影响数据库性能和功能的重要事件。此部分介绍事件的记录位置、Vertica 记录的事件类型、如何对这些事件作出响应、Vertica 为这些事件提供的信息以及如何配置事件监控。
Vertica 将事件发布至以下机制:
下表列出了 Vertica 记录到事件系统表的事件代码。
为了帮助您解释并解决触发某一事件的问题,每个事件都提供各种各样的数据,具体视所用的事件日志记录机制而定。
下表介绍事件数据并指出其使用位置。
系统为
vertica.log
自动配置事件报告,并将当前事件自动发布至 ACTIVE_EVENTS 系统表。您也可以将 Vertica 配置为将事件发布至 syslog 和 SNMP。
Syslog 是一个网络日志记录实用程序,用于发出、存储和处理日志消息。这是一种将异构数据放入单个数据存储库的有用方法。
要将事件记录到 syslog 中,请为希望记录的每个单独事件启用事件报告。默认情况下,消息会记录到 /var/log/messages
中。
配置报告给 syslog 的事件包括以下步骤:
允许 Vertica 为 syslog 捕获事件。
定义 Vertica 为 syslog 捕获哪些事件。
Vertica 强烈建议您捕获“陈旧检查点 (Stale Checkpoint)”事件。
定义要使用的 syslog 程序模块。
要为 syslog 启用事件捕获功能,请发出以下 SQL 命令:
=> ALTER DATABASE DEFAULT SET SyslogEnabled = 1;
要为 syslog 禁用事件捕获功能,请发出以下 SQL 命令:
=> ALTER DATABASE DEFAULT SET SyslogEnabled = 0;
要定义用来生成 syslog 条目的事件,请发出以下 SQL 命令,命令下方的列表中所述的一个事件:
=> ALTER DATABASE DEFAULT SET SyslogEvents = 'events-list';
其中 events-list 是以逗号分隔的事件列表,其中包含下面的一项或多项:
磁盘空间不足
只读文件系统
K-Safety 丢失
当前容错能力处于临界水平
ROS 容器过多
节点状态更改
恢复故障
恢复错误
恢复锁定错误
恢复投影检索错误
刷新错误
刷新锁定错误
Tuple Mover 错误
计时器服务任务错误
陈旧检查点
以下示例会为低磁盘空间和恢复故障生成一个 syslog 条目:
=> ALTER DATABASE DEFAULT SET SyslogEvents = 'Low Disk Space, Recovery Failure';
Syslog 机制可以对日志记录消息进行多种不同的常规分类,这称为程序模块。通常,所有与身份验证相关的消息都使用 auth
(或 authpriv
)程序模块记录。这些消息预期是安全的并且未经授权的人员无法查看。普通的操作消息使用 daemon
程序模块记录,该程序模块可接收并视情况存储消息。
SyslogFacility 指令可将所有日志记录消息定向到默认设置以外的其他不同程序模块。使用此指令时,所有日志记录操作都将使用指定的程序模块(身份验证(安全)等等)来完成。
要定义 Vertica 使用哪个 SyslogFacility,请发出以下 SQL 命令:
=> ALTER DATABASE DEFAULT SET SyslogFacility = 'Facility_Name';
其中程序模块级别实参 <Facility_Name>
为以下项目之一:
auth
authpriv(仅限 Linux)
cron
uucp(UUCP 子系统)
daemon
ftp(仅限 Linux)
lpr(行式打印机子系统)
mail(邮件系统)
news(网络新闻子系统)
user(默认系统)
Local0(本地使用 0)
local1(本地使用 1)
local2(本地使用 2)
local3(本地使用 3)
local4(本地使用 4)
local5(本地使用 5)
local6(本地使用 6)
local7(本地使用 7)
要捕获除上面列出的事件之外的事件,请创建一个 syslog 通知程序并允许它使用 SET_DATA_COLLECTOR_NOTIFY_POLICY 捕获所需的事件。
此通知程序类型监视的事件不会记录到
MONITORING_EVENTS 或 vertica.log
。
以下示例创建了一个通知程序,当
数据收集器 (DC) 组件 LoginFailures
更新时,它会向 syslog 写入一条消息:
为当前数据库启用 syslog 通知程序:
=> ALTER DATABASE DEFAULT SET SyslogEnabled = 1;
创建并启用系统日志通知程序 v_syslog_notifier
:
=> CREATE NOTIFIER v_syslog_notifier ACTION 'syslog'
ENABLE
MAXMEMORYSIZE '10M'
IDENTIFIED BY 'f8b0278a-3282-4e1a-9c86-e0f3f042a971'
PARAMETERS 'eventSeverity = 5';
配置 syslog 通知程序 v_syslog_notifier
以更新具有 SET_DATA_COLLECTOR_NOTIFY_POLICY 的 LoginFailures
DC 组件:
=> SELECT SET_DATA_COLLECTOR_NOTIFY_POLICY('LoginFailures','v_syslog_notifier', 'Login failed!', true);
当用户未能以用户 Bob 身份进行身份验证时,此通知程序将以下消息写入 syslog(默认位置:/var/log/messages):Apr 25 16:04:58 vertica_host_01 vertica: Event Posted: Event Code:21 Event Id:0 Event Severity: Notice [5] PostedTimestamp: 2022-04-25 16:04:58.083063 ExpirationTimestamp: 2022-04-25 16:04:58.083063 EventCodeDescription: Notifier ProblemDescription: (Login failed!) { "_db":"VMart", "_schema":"v_internal", "_table":"dc_login_failures", "_uuid":"f8b0278a-3282-4e1a-9c86-e0f3f042a971", "authentication_method":"Reject", "client_authentication_name":"default: Reject", "client_hostname":"::1", "client_label":"", "client_os_user_name":"dbadmin", "client_pid":523418, "client_version":"", "database_name":"dbadmin", "effective_protocol":"3.8", "node_name":"v_vmart_node0001", "reason":"REJECT", "requested_protocol":"3.8", "ssl_client_fingerprint":"", "ssl_client_subject":"", "time":"2022-04-25 16:04:58.082568-05", "user_name":"Bob" }#012 DatabaseName: VMart Hostname: vertica_host_01
为 SNMP 配置事件报告包括以下步骤:
将 Vertica 配置为启用 SNMP 事件陷阱,如下所述。
将 Vertica 管理信息库 (MIB) 文件导入到 SNMP 监控设备中。
利用 Vertica MIB 文件,SNMP 陷阱接收器可以了解其从 Vertica 接收的陷阱,进而使您能够配置其在接收陷阱时采取的操作。
Vertica 支持 SNMP V1 陷阱协议,该协议位于 /opt/vertica/sbin/VERTICA-MIB 中。有关导入 MIB 文件的详细信息,请参阅 SNMP 监控设备文档。
配置 SNMP 陷阱接收器处理来自 Vertica 的陷阱。
不同供应商的 SNMP 陷阱接收器配置显著不同。因而,此处介绍的有关配置 SNMP 陷阱接收器处理来自 Vertica 的陷阱的指南为通用指南。
Vertica 陷阱是包含若干信息标识字段的单一通用陷阱。这些字段相当于监控事件中所述的事件数据。但是,字段名称所使用的格式略有不同。在 SNMP 下,字段名称不包含空格。此外,字段名称会预先加上“vert”。例如,事件严重级别变为 vertEventSeverity。
配置陷阱接收器时,请确保使用用于在 Vertica 中配置事件陷阱的相同主机名、端口和社区字符串。
网络管理提供程序示例:
IBM Tivoli
AdventNet
Net-SNMP(开源)
Nagios(开源)
Open NMS(开源)
将 Vertica 配置为捕获 SNMP 事件时,默认会捕获以下事件:
磁盘空间不足
只读文件系统
K-Safety 丢失
当前容错能力处于临界水平
ROS 容器过多
节点状态更改
恢复故障
陈旧检查点
CRC 不匹配
启用 Vertica 以捕获 SNMP 事件。
定义 Vertica 发送陷阱的位置。
(可选)重新定义 Vertica 捕获哪些 SNMP 事件。
Stale Checkpoint
事件,即使您决定减少 Vertica 捕获的 SNMP 事件数也是如此。您所定义的特定设置不会影响已发送到日志的陷阱。所有事件均被捕获到日志中。
使用以下 SQL 命令:
=> ALTER DATABASE DEFAULT SET SnmpTrapsEnabled = 1;
使用以下 SQL 命令,其中 Host_name 和 port 标识 SNMP 所在的计算机,CommunityString 相当于密码,用于控制 Vertica 对服务器的访问:
=> ALTER DATABASE DEFAULT SET SnmpTrapDestinationsList = 'host_name port CommunityString';
例如:
=> ALTER DATABASE DEFAULT SET SnmpTrapDestinationsList = 'localhost 162 public';
也可以指定多个目标,方法是指定以逗号分隔的目标列表:
=> ALTER DATABASE DEFAULT SET SnmpTrapDestinationsList = 'host_name1 port1 CommunityString1, hostname2 port2 CommunityString2';
使用以下 SQL 命令,其中 Event_Name
是命令下方列表中的一个事件:
=> ALTER DATABASE DEFAULT SET SnmpTrapEvents = 'Event_Name1, Even_Name2';
磁盘空间不足
只读文件系统
K-Safety 丢失
当前容错能力处于临界水平
ROS 容器过多
节点状态更改
恢复故障
恢复错误
恢复锁定错误
恢复投影检索错误
刷新错误
Tuple Mover 错误
陈旧检查点
CRC 不匹配
以下示例指定两个事件名称:
=> ALTER DATABASE DEFAULT SET SnmpTrapEvents = 'Low Disk Space, Recovery Failure';
要创建一组检查 SNMP 配置的测试事件:
将 SNMP 陷阱处理程序设置为捕获 Vertica 事件。
使用以下命令测试您的设置:
SELECT SNMP_TRAP_TEST();
SNMP_TRAP_TEST
--------------------------
Completed SNMP Trap Test
(1 row)
以下示例展示了在 vertica.log 内发布并清除的 Too Many ROS Containers 事件:
08/14/15 15:07:59 thr:nameless:0x45a08940 [INFO] Event Posted: Event Code:4 Event Id:0 Event Severity: Warning [4] PostedTimestamp:
2015-08-14 15:07:59.253729 ExpirationTimestamp: 2015-08-14 15:08:29.253729
EventCodeDescription: Too Many ROS Containers ProblemDescription:
Too many ROS containers exist on this node. DatabaseName: TESTDB
Hostname: fc6-1.example.com
08/14/15 15:08:54 thr:Ageout Events:0x2aaab0015e70 [INFO] Event Cleared:
Event Code:4 Event Id:0 Event Severity: Warning [4] PostedTimestamp:
2015-08-14 15:07:59.253729 ExpirationTimestamp: 2015-08-14 15:08:53.012669
EventCodeDescription: Too Many ROS Containers ProblemDescription:
Too many ROS containers exist on this node. DatabaseName: TESTDB
Hostname: fc6-1.example.com
以下示例展示了发布到 SNMP 的 Too Many ROS Containers 事件:
Version: 1, type: TRAPREQUESTEnterprise OID: .1.3.6.1.4.1.31207.2.0.1
Trap agent: 72.0.0.0
Generic trap: ENTERPRISESPECIFIC (6)
Specific trap: 0
.1.3.6.1.4.1.31207.1.1 ---> 4
.1.3.6.1.4.1.31207.1.2 ---> 0
.1.3.6.1.4.1.31207.1.3 ---> 2008-08-14 11:30:26.121292
.1.3.6.1.4.1.31207.1.4 ---> 4
.1.3.6.1.4.1.31207.1.5 ---> 1
.1.3.6.1.4.1.31207.1.6 ---> site01
.1.3.6.1.4.1.31207.1.7 ---> suse10-1
.1.3.6.1.4.1.31207.1.8 ---> Too many ROS containers exist on this node.
.1.3.6.1.4.1.31207.1.9 ---> QATESTDB
.1.3.6.1.4.1.31207.1.10 ---> Too Many ROS Containers
以下示例展示了在系统日志内发布并清除的 Too Many ROS Containers 事件:
Aug 14 15:07:59 fc6-1 vertica: Event Posted: Event Code:4 Event Id:0 Event Severity: Warning [4] PostedTimestamp: 2015-08-14 15:07:59.253729 ExpirationTimestamp:
2015-08-14 15:08:29.253729 EventCodeDescription: Too Many ROS Containers ProblemDescription:
Too many ROS containers exist on this node. DatabaseName: TESTDB Hostname: fc6-1.example.com
Aug 14 15:08:54 fc6-1 vertica: Event Cleared: Event Code:4 Event Id:0 Event Severity:
Warning [4] PostedTimestamp: 2015-08-14 15:07:59.253729 ExpirationTimestamp:
2015-08-14 15:08:53.012669 EventCodeDescription: Too Many ROS Containers ProblemDescription:
Too many ROS containers exist on this node. DatabaseName: TESTDB Hostname: fc6-1.example.com
Vertica 系统表提供有关系统资源、后台进程、工作负载和性能的信息,例如加载流、查询配置文件和 Tuple Mover 操作。Vertica 自动收集并刷新此信息。
可使用表达式、谓词、聚合、分析、子查询和联接来查询系统表。也可以将系统表查询结果保存到用户表中,以供将来进行分析。例如,以下查询将创建一个名为 mynode
的表,并从 NODES 系统表中选择三个与节点相关的列:
=> CREATE TABLE mynode AS SELECT node_name, node_state, node_address FROM nodes;
CREATE TABLE
=> SELECT * FROM mynode;
node_name | node_state | node_address
------------------+------------+----------------
v_vmart_node0001 | UP | 192.168.223.11
(1 row)
系统表可分为以下两个架构:
V_CATALOG 架构:提供有关编录中永久对象的信息
V_MONITOR 架构:提供有关瞬时系统状态的信息
这些架构驻留在默认搜索路径中。除非您更改搜索路径以排除 V_MONITOR
和/或 V_CATALOG
,否则查询可以指定不包含表架构的系统表名。
您可以在 SYSTEM_TABLES 表中查询所有 Vertica 系统表及其架构。例如:
SELECT * FROM system_tables ORDER BY table_schema, table_name;
Vertica 系统表可分为以下区域:
系统信息
系统资源
后台进程
工作负载和性能
Vertica 会保留一些内存以帮助监控繁忙的系统。使用简单的系统表查询可以更轻松地排除问题。另请参阅 SYSQUERY。
可使用外部监控工具或脚本来查询系统表并根据需要对信息执行操作。例如,当主机故障导致
K-safety 级别低于所需级别时,工具或脚本通常会将此变化以电子邮件形式通知数据库管理员和/或相应的 IT 人员。
您可以授予和撤销对系统表的权限,但有以下限制:
您不能向 SYSMONITOR 或 PSEUDOSUPERUSER 角色授予对系统表的权限。
您不能授予对系统架构的权限。
一些系统表数据可能以大小写混合的形式存储。例如,Vertica 会按照您在 CREATE 语句中所指定的那样来存储混合大小写的标识符名称,即使在查询中引用它们时忽略大小写。当这些对象名称作为数据出现在系统表中时,如果使用等于 (=
) 运算符查询它们将会遇到错误,因为大小写必须与存储的标识符完全匹配。特别是,系统表 TABLES 中的列 TABLE_SCHEMA
和 TABLE_NAME
中的数据区分大小写。
如果您不知道标识符的存储方式,可使用不区分大小写的运算符
ILIKE
。例如,给定以下架构:
=> CREATE SCHEMA SS;
=> CREATE TABLE SS.TT (c1 int);
=> CREATE PROJECTION SS.TTP1 AS SELECT * FROM ss.tt UNSEGMENTED ALL NODES;
=> INSERT INTO ss.tt VALUES (1);
使用 =
运算符的查询返回 0 行:
=> SELECT table_schema, table_name FROM v_catalog.tables WHERE table_schema ='ss';
table_schema | table_name
--------------+------------
(0 rows)
使用不区分大小写的 ILIKE
的查询返回预期结果:
=> SELECT table_schema, table_name FROM v_catalog.tables WHERE table_schema ILIKE 'ss';
table_schema | table_name
--------------+------------
SS | TT
(1 row)
以下示例将展示在查询中使用系统表的简单方法。
=> SELECT current_epoch, designed_fault_tolerance, current_fault_tolerance FROM SYSTEM;
current_epoch | designed_fault_tolerance | current_fault_tolerance
---------------+--------------------------+-------------------------
492 | 1 | 1
(1 row)
=> SELECT node_name, total_user_session_count, executed_query_count FROM query_metrics;
node_name | total_user_session_count | executed_query_count
------------------+--------------------------+----------------------
v_vmart_node0001 | 115 | 353
v_vmart_node0002 | 114 | 35
v_vmart_node0003 | 116 | 34
(3 rows)
=> SELECT DISTINCT(schema_name), schema_owner FROM schemata;
schema_name | schema_owner
--------------+--------------
v_catalog | dbadmin
v_txtindex | dbadmin
v_func | dbadmin
TOPSCHEMA | dbadmin
online_sales | dbadmin
v_internal | dbadmin
v_monitor | dbadmin
structs | dbadmin
public | dbadmin
store | dbadmin
(10 rows)
数据收集器收集并保留重要系统活动的历史记录,并记录基本性能和资源利用率计数器。
数据收集器以最小的开销扩展系统表功能,它执行以下任务:
提供一个用于记录事件的框架
将信息传播到系统表。
您可以通过查询数据收集器信息来获取系统表的过去状态并提取聚合信息。它还可以帮助您:
查看用户已采取哪些操作。
找出性能瓶颈。
确定 Vertica 配置的潜在改进领域。
数据收集器与工作负载分析器搭配使用,后者是一款工具,可智能监控 SQL 查询和工作负载的性能,并通过观察实际工作负载的历史记录来建议优化操作。
数据收集器根据可配置的保留策略保留它收集的数据。数据收集器在默认情况下处于启用状态;您可以禁用数据收集器,方法是使用 ALTER DATABASE 和 ALTER NODE 分别在数据库级别和节点级别将配置参数 EnableDataCollector 设置为 0。
您可以通过系统表 DATA_COLLECTOR 访问有关所有组件的收集数据的元数据。此表包含有关该组件的当前收集策略以及在内存和磁盘中保留了多少数据的信息。
收集的数据记录在磁盘 Vertica /catalog 路径下的 DataCollector
目录中。您可以从特定于组件的数据收集器表中查询所记录的数据。您还可以使用 Vertica 元函数管理所记录的数据;有关详细信息,请参阅管理数据收集日志。
数据收集器为其监控的每个 Vertica 组件(例如 TupleMoverEvents 或 DepotEvictions)维护保留策略。您可以通过查询 DATA_COLLECTOR 系统表来识别受监控的组件。例如,以下查询返回分区活动组件:
=> SELECT DISTINCT component FROM data_collector WHERE component ILIKE '%partition%';
component
----------------------
HiveCustomPartitions
CopyPartitions
MovePartitions
SwapPartitions
(4 rows)
每个组件都有自己的保留策略,保留策略由几个属性组成:
MEMORY_BUFFER_SIZE_KB 指定数据收集器在将收集的数据移至磁盘之前,在内存中缓冲的最大数据量(以千字节为单位)。
DISK_SIZE_KB 指定为该组件的数据收集器表分配的最大磁盘空间(以千字节为单位)。
INTERVAL_TIME 是一种 INTERVAL 数据类型,用于指定给定组件的数据在该组件的数据收集器表中保留多长时间。
Vertica 为所有属性设置默认值,您可以使用元函数 SET_DATA_COLLECTOR_POLICY 和 SET_DATA_COLLECTOR_TIME_POLICY 更改默认值。
您可以通过调用 GET_DATA_COLLECTOR_POLICY 来查看保留策略设置。例如,以下语句返回 TupleMoverEvents 组件的保留策略:
=> SELECT get_data_collector_policy('TupleMoverEvents');
get_data_collector_policy
-----------------------------------------------------------------------------
1000KB kept in memory, 15000KB kept on disk. Time based retention disabled.
(1 row)
通过结合使用保留策略属性 MEMORY_BUFFER_SIZE_KB 和 DISK_SIZE_KB 来确定在任何给定时间可用的收集数据量。这两个属性具有以下依赖关系:如果 MEMORY_BUFFER_SIZE_KB 设置为 0,则数据收集器不会在内存或磁盘上保留此组件的任何数据;如果 DISK_SIZE_KB 设置为 0,则数据收集器仅保留它可以缓冲的最大组件数据量(由 MEMORY_BUFFER_SIZE_KB 设置)。
例如,以下语句将 ResourceAcquisitions 组件的内存和磁盘设置从 1,000 KB 内存和 10,000 KB 磁盘空间的当前设置分别更改为 1500 KB 和 25000 KB:
=> SELECT set_data_collector_policy('ResourceAcquisitions', '1500', '25000');
set_data_collector_policy
---------------------------
SET
(1 row)
在以下情况下,您应该考虑将 MEMORY_BUFFER_SIZE_KB 设置为较高的值:
数据收集量异常高。如果 MEMORY_BUFFER_SIZE_KB 设置得太低,数据收集器将缓冲的数据刷新到磁盘的速度可能不够快,无法跟上活动级别,这可能导致内存中的数据丢失。
数据收集器记录非常大 — 例如,记录中包含非常长的查询字符串。数据收集器使用双重缓冲,因此它在内存中不能保留比 MEMORY_BUFFER_SIZE_KB 大 50% 以上的记录。
默认情况下,给定组件的所有收集数据都保留在磁盘上,并且可以在组件的数据收集器表中进行访问,直至达到该组件的保留策略的磁盘存储限值(由其 DISK_SIZE_KB 属性设置)。您可以调用 SET_DATA_COLLECTOR_POLICY 来限制数据在组件的数据收集器表中保留的时间。在以下示例中,对组件 TupleMoverEvents 调用 SET_DATA_COLLECTOR_POLICY 并将其 INTERVAL_TIME 属性设置为 30 分钟间隔:
SELECT set_data_collector_policy('TupleMoverEvents ', '30 minutes'::interval);
set_data_collector_time_policy
--------------------------------
SET
(1 row)
在此调用之后,数据收集器表 dc_tuple_mover_events
仅保留过去 30 分钟内发生的 Tuple Mover 活动的记录。旧 Tuple Mover 数据会自动从该表中删除。例如,在上一次调用 SET_DATA_COLLECTOR_POLICY 之后,查询 dc_tuple_mover_events
会返回过去 30 分钟(在本例中,是从 07:58:21 开始)内收集的 Tuple Mover 活动的数据:
=> SELECT current_timestamp(0) - '30 minutes'::interval AS '30 minutes ago';
30 minutes ago
---------------------
2020-08-13 07:58:21
(1 row)
=> SELECT time, node_name, session_id, user_name, transaction_id, operation FROM dc_tuple_mover_events WHERE node_name='v_vmart_node0001' ORDER BY transaction_id;
time | node_name | session_id | user_name | transaction_id | operation
-------------------------------+------------------+---------------------------------+-----------+-------------------+-----------
2020-08-13 08:16:54.360597-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807826 | Mergeout
2020-08-13 08:16:54.397346-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807826 | Mergeout
2020-08-13 08:16:54.424002-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807826 | Mergeout
2020-08-13 08:16:54.425989-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807829 | Mergeout
2020-08-13 08:16:54.456829-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807829 | Mergeout
2020-08-13 08:16:54.485097-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807829 | Mergeout
2020-08-13 08:19:45.8045-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x37b08 | dbadmin | 45035996273807855 | Mergeout
2020-08-13 08:19:45.742-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x37b08 | dbadmin | 45035996273807855 | Mergeout
2020-08-13 08:19:45.684764-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x37b08 | dbadmin | 45035996273807855 | Mergeout
2020-08-13 08:19:45.799796-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807865 | Mergeout
2020-08-13 08:19:45.768856-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807865 | Mergeout
2020-08-13 08:19:45.715424-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807865 | Mergeout
2020-08-13 08:25:20.465604-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807890 | Mergeout
2020-08-13 08:25:20.497266-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807890 | Mergeout
2020-08-13 08:25:20.518839-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807890 | Mergeout
2020-08-13 08:25:20.52099-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807893 | Mergeout
2020-08-13 08:25:20.549075-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807893 | Mergeout
2020-08-13 08:25:20.569072-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807893 | Mergeout
(18 rows)
25 分钟过去后,这些记录中有 12 条超出了为 TupleMoverEvents 设置的 30 分钟间隔,将从 dc_tuple_mover_events
中删除:
=> SELECT current_timestamp(0) - '30 minutes'::interval AS '30 minutes ago';
30 minutes ago
---------------------
2020-08-13 08:23:33
(1 row)
=> SELECT time, node_name, session_id, user_name, transaction_id, operation FROM dc_tuple_mover_events WHERE node_name='v_vmart_node0001' ORDER BY transaction_id;
time | node_name | session_id | user_name | transaction_id | operation
-------------------------------+------------------+---------------------------------+-----------+-------------------+-----------
2020-08-13 08:25:20.465604-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807890 | Mergeout
2020-08-13 08:25:20.497266-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807890 | Mergeout
2020-08-13 08:25:20.518839-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807890 | Mergeout
2020-08-13 08:25:20.52099-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807893 | Mergeout
2020-08-13 08:25:20.549075-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807893 | Mergeout
2020-08-13 08:25:20.569072-04 | v_vmart_node0001 | v_vmart_node0001-190508:0x375db | dbadmin | 45035996273807893 | Mergeout
(6 rows)
元函数 SET_DATA_COLLECTOR_TIME_POLICY 也设置保留策略的 INTERVAL_TIME 属性。与 SET_DATA_COLLECTOR_POLICY 不同,此元函数仅设置 INTERVAL_TIME 属性。不同之处还在于,您可以使用此元函数通过省略组件实参来更新所有组件上的 INTERVAL_TIME。例如:
SELECT set_data_collector_time_policy('1 day'::interval);
set_data_collector_time_policy
--------------------------------
SET
(1 row)
=> SELECT DISTINCT component, INTERVAL_SET, INTERVAL_TIME FROM DATA_COLLECTOR WHERE component ILIKE '%partition%';
component | INTERVAL_SET | INTERVAL_TIME
----------------------+--------------+---------------
HiveCustomPartitions | t | 1
MovePartitions | t | 1
CopyPartitions | t | 1
SwapPartitions | t | 1
(4 rows)
要清除 INTERVAL_TIME 策略属性,请调用带有负整数实参的 SET_DATA_COLLECTOR_TIME_POLICY。例如:
=> SELECT set_data_collector_time_policy('-1');
set_data_collector_time_policy
--------------------------------
SET
(1 row)
=> SELECT DISTINCT component, INTERVAL_SET, INTERVAL_TIME FROM DATA_COLLECTOR WHERE component ILIKE '%partition%';
component | INTERVAL_SET | INTERVAL_TIME
----------------------+--------------+---------------
MovePartitions | f | 0
SwapPartitions | f | 0
HiveCustomPartitions | f | 0
CopyPartitions | f | 0
(4 rows)
dc_
)位于 V_INTERNAL
架构中。如果您在脚本或监控工具中使用数据收集器表,请注意,任何 Vertica 升级都可能会移除或更改它们,而不另行通知。
您可以从数据收集器表中获取特定于组件的数据。数据收集器将其日志文件中的组件数据编译为可以通过标准 SQL 查询进行查询的表格式。您可以通过数据收集器系统表识别特定组件的数据收集器表名。例如:
=> SELECT distinct component, table_name FROM data_collector where component ILIKE 'lock%';
component | table_name
--------------+------------------
LockRequests | dc_lock_requests
LockReleases | dc_lock_releases
LockAttempts | dc_lock_attempts
(3 rows)
然后,您可以查询所需的数据收集器表 — 例如,检查 dc_lock_attempts
中的锁定延迟:
=> SELECT * from dc_lock_attempts WHERE description != 'Granted immediately';
-[ RECORD 1 ]------+------------------------------
time | 2020-08-17 00:14:07.187607-04
node_name | v_vmart_node0001
session_id | v_vmart_node0001-319647:0x1d
user_id | 45035996273704962
user_name | dbadmin
transaction_id | 45035996273819050
object | 0
object_name | Global Catalog
mode | X
promoted_mode | X
scope | TRANSACTION
start_time | 2020-08-17 00:14:07.184663-04
timeout_in_seconds | 300
result | granted
description | Granted after waiting
启动时,Vertica 在每个节点的数据库编录目录下创建一个 DataCollector
目录。对于各个组件,此目录包含一个或多个日志。例如:
[dbadmin@doch01 DataCollector]$ pwd
/home/dbadmin/VMart/v_vmart_node0001_catalog/DataCollector
[dbadmin@doch01 DataCollector]$ ls -1 -g Lock*
-rw------- 1 verticadba 2559879 Aug 17 00:14 LockAttempts_650572441057355.log
-rw------- 1 verticadba 614579 Aug 17 05:28 LockAttempts_650952885486175.log
-rw------- 1 verticadba 2559895 Aug 14 18:31 LockReleases_650306482037650.log
-rw------- 1 verticadba 1411127 Aug 17 05:28 LockReleases_650759468041873.log
对于每个组件,DataCollector 目录还包含一对 SQL 模板文件:
CREATE_component_TABLE.sql
提供用于创建表的 DDL,您可以在其中加载给定组件的数据收集器日志 — 例如,LockAttempts:
[dbadmin@doch01 DataCollector]$ cat CREATE_LockAttempts_TABLE.sql
\set dcschema 'echo ${DCSCHEMA:-dc}'
CREATE TABLE :dcschema.dc_lock_attempts(
"time" TIMESTAMP WITH TIME ZONE,
"node_name" VARCHAR(128),
"session_id" VARCHAR(128),
"user_id" INTEGER,
"user_name" VARCHAR(128),
"transaction_id" INTEGER,
"object" INTEGER,
"object_name" VARCHAR(128),
"mode" VARCHAR(128),
"promoted_mode" VARCHAR(128),
"scope" VARCHAR(128),
"start_time" TIMESTAMP WITH TIME ZONE,
"timeout_in_seconds" INTEGER,
"result" VARCHAR(128),
"description" VARCHAR(64000)
);
COPY_component_TABLE.sql
包含用于(通过 COPY)将数据日志文件加载到 CREATE 脚本所创建的表中的 SQL。例如:
[dbadmin@doch01 DataCollector]$ cat COPY_LockAttempts_TABLE.sql
\set dcpath 'echo ${DCPATH:-$PWD}'
\set dcschema 'echo ${DCSCHEMA:-dc}'
\set logfiles '''':dcpath'/LockAttempts_*.log'''
COPY :dcschema.dc_lock_attempts(
LockAttempts_start_filler FILLER VARCHAR(64) DELIMITER E'\n',
"time_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"time" FORMAT '_internal' DELIMITER E'\n',
"node_name_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"node_name" ESCAPE E'\001' DELIMITER E'\n',
"session_id_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"session_id" ESCAPE E'\001' DELIMITER E'\n',
"user_id_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"user_id" FORMAT 'd' DELIMITER E'\n',
"user_name_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"user_name" ESCAPE E'\001' DELIMITER E'\n',
"transaction_id_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"transaction_id" FORMAT 'd' DELIMITER E'\n',
"object_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"object" FORMAT 'd' DELIMITER E'\n',
"object_name_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"object_name" ESCAPE E'\001' DELIMITER E'\n',
"mode_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"mode" ESCAPE E'\001' DELIMITER E'\n',
"promoted_mode_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"promoted_mode" ESCAPE E'\001' DELIMITER E'\n',
"scope_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"scope" ESCAPE E'\001' DELIMITER E'\n',
"start_time_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"start_time" FORMAT '_internal' DELIMITER E'\n',
"timeout_in_seconds_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"timeout_in_seconds" FORMAT 'd' DELIMITER E'\n',
"result_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"result" ESCAPE E'\001' DELIMITER E'\n',
"description_nfiller" FILLER VARCHAR(32) DELIMITER ':',
"description" ESCAPE E'\001'
) FROM :logfiles RECORD TERMINATOR E'\n.\n' DELIMITER E'\n';
您可以使用 Vertica 元函数 FLUSH_DATA_COLLECTOR 和 CLEAR_DATA_COLLECTOR 管理数据收集器日志。这两个函数都可以指定单个组件,也可以对所有组件执行:
FLUSH_DATA_COLLECTOR 等待内存日志移至磁盘后,刷新数据收集器,同时将日志与磁盘存储同步。例如,对所有组件执行以下语句:
=> SELECT flush_data_collector();
flush_data_collector
----------------------
FLUSH
(1 row)
CLEAR_DATA_COLLECTOR 清除数据收集器表和日志中的所有内存及磁盘记录,并重置 DATA_COLLECTOR 系统表中的收集统计信息。例如,对为 ResourceAcquisitions 组件收集的数据执行以下语句:
=> SELECT clear_data_collector('ResourceAcquisitions');
clear_data_collector
----------------------
CLEAR
(1 row)
在使用
ALTER TABLE...REORGANIZE
时,操作会在后台重组数据。
可以通过轮询以下系统表来监控重组进程的详细信息:
V_MONITOR.PARTITION_STATUS
显示每个已正确分区的表的碎片。
V_MONITOR.PARTITION_REORGANIZE_ERRORS
记录重组过程发出的错误。
V_MONITOR.PARTITIONS
对于任何未重组的 ROS,在 partition_key
列中显示 NULL
。
您可以使用以下内容查找有关资源池的信息:
RESOURCE_POOL_STATUS 从资源池返回当前数据 — 例如,当前的内存使用情况、通过各种请求所要求和获取的资源以及队列的状态。
RESOURCE_ACQUISITIONS 显示了授予当前正在运行的查询的所有资源。
SHOW SESSION 与其他会话级别参数一起显示 当前会话的资源池。
您还可以使用管理控制台获取有关资源池使用情况的运行时数据。
以下示例在 RESOURCE_POOL_STATUS 中查询内存大小数据:
=> SELECT pool_name poolName,
node_name nodeName,
max_query_memory_size_kb maxQueryMemSizeKb,
max_memory_size_kb maxMemSizeKb,
memory_size_actual_kb memSizeActualKb
FROM resource_pool_status WHERE pool_name='ceo_pool';
poolName | nodeName | maxQueryMemSizeKb | maxMemSizeKb | memSizeActualKb
----------+------------------+-------------------+--------------+-----------------
ceo_pool | v_vmart_node0001 | 12179388 | 13532654 | 1843200
ceo_pool | v_vmart_node0002 | 12191191 | 13545768 | 1843200
ceo_pool | v_vmart_node0003 | 12191170 | 13545745 | 1843200
(3 rows)
以下示例显示了授予当前正在运行的查询的所有资源。这里显示的信息存储在 RESOURCE_ACQUISITIONS 系统表中。您可以看到查询执行使用了 GENERAL 池中 708504 KB 的内存。
=> SELECT pool_name, thread_count, open_file_handle_count, memory_inuse_kb,
queue_entry_timestamp, acquisition_timestamp
FROM V_MONITOR.RESOURCE_ACQUISITIONS WHERE node_name ILIKE '%node0001';
-[ RECORD 1 ]----------+------------------------------
pool_name | sysquery
thread_count | 4
open_file_handle_count | 0
memory_inuse_kb | 4103
queue_entry_timestamp | 2013-12-05 07:07:08.815362-05
acquisition_timestamp | 2013-12-05 07:07:08.815367-05
-[ RECORD 2 ]----------+------------------------------
...
-[ RECORD 8 ]----------+------------------------------
pool_name | general
thread_count | 12
open_file_handle_count | 18
memory_inuse_kb | 708504
queue_entry_timestamp | 2013-12-04 12:55:38.566614-05
acquisition_timestamp | 2013-12-04 12:55:38.566623-05
-[ RECORD 9 ]----------+------------------------------
...
您可以确定查询运行之前在队列中的等待时间。为此,您应使用以下示例中所示的查询,获取 acquisition_timestamp
和 queue_entry_timestamp
之差:
=> SELECT pool_name, queue_entry_timestamp, acquisition_timestamp,
(acquisition_timestamp-queue_entry_timestamp) AS 'queue wait'
FROM V_MONITOR.RESOURCE_ACQUISITIONS WHERE node_name ILIKE '%node0001';
-[ RECORD 1 ]---------+------------------------------
pool_name | sysquery
queue_entry_timestamp | 2013-12-05 07:07:08.815362-05
acquisition_timestamp | 2013-12-05 07:07:08.815367-05
queue wait | 00:00:00.000005
-[ RECORD 2 ]---------+------------------------------
pool_name | sysquery
queue_entry_timestamp | 2013-12-05 07:07:14.714412-05
acquisition_timestamp | 2013-12-05 07:07:14.714417-05
queue wait | 00:00:00.000005
-[ RECORD 3 ]---------+------------------------------
pool_name | sysquery
queue_entry_timestamp | 2013-12-05 07:09:57.238521-05
acquisition_timestamp | 2013-12-05 07:09:57.281708-05
queue wait | 00:00:00.043187
-[ RECORD 4 ]---------+------------------------------
...
系统表 RESOURCE_POOLS 和 RESOURCE_POOL_STATUS 中的布尔列 IS_INTERNAL 允许您仅获取有关用户定义的资源池的数据。例如:
SELECT name, subcluster_oid, subcluster_name, memorysize, maxmemorysize, priority, maxconcurrency
dbadmin-> FROM V_CATALOG.RESOURCE_POOLS where is_internal ='f';
name | subcluster_oid | subcluster_name | memorysize | maxmemorysize | priority | maxconcurrency
--------------+-------------------+-----------------+------------+---------------+----------+----------------
load_pool | 72947297254957395 | default | 0% | | 10 |
ceo_pool | 63570532589529860 | c_subcluster | 250M | | 10 |
ad hoc_pool | 0 | | 200M | 200M | 0 |
billing_pool | 45579723408647896 | ar_subcluster | 0% | | 0 | 3
web_pool | 0 | analytics_1 | 25M | | 10 | 5
batch_pool | 47479274633682648 | default | 150M | 150M | 0 | 10
dept1_pool | 0 | | 0% | | 5 |
dept2_pool | 0 | | 0% | | 8 |
dashboard | 45035996273843504 | analytics_1 | 0% | | 0 |
(9 rows)
当您的 Vertica 数据库从故障中恢复时,监控恢复过程非常重要。有多种方法可以监控数据库恢复:
在数据库恢复期间,Vertica 会向每个主机上的
vertica.log
添加日志记录消息。每个消息均标有字符串 [Recover]
。
使用 tail
命令查看相关状态消息以监控恢复进度,如下所示。
$ tail -f catalog-path/database-name/node-name_catalog/vertica.log
01/23/08 10:35:31 thr:Recover:0x2a98700970 [Recover] <INFO> Changing host v_vmart_node0001 startup state from INITIALIZING to RECOVERING
01/23/08 10:35:31 thr:CatchUp:0x1724b80 [Recover] <INFO> Recovering to specified epoch 0x120b6
01/23/08 10:35:31 thr:CatchUp:0x1724b80 [Recover] <INFO> Running 1 split queries
01/23/08 10:35:31 thr:CatchUp:0x1724b80 [Recover] <INFO> Running query: ALTER PROJECTION proj_tradesquotes_0 SPLIT v_vmart_node0001 FROM 73911;
使用以下系统表来监控恢复:
具体来讲,recovery_status
系统表包括有关正在恢复的节点、要恢复的时期、当前恢复阶段以及运行状态的信息:
=>select node_name, recover_epoch, recovery_phase, current_completed, is_running from recovery_status;
node_name | recover_epoch | recovery_phase | current_completed | is_running
---------------------+---------------+-------------------+-------------------+--------------
v_vmart_node0001 | | | 0 | f
v_vmart_node0002 | 0 | historical pass 1 | 0 | t
v_vmart_node0003 | 1 | current | 0 | f
projection_recoveries
系统表维护投影恢复的历史记录。若要检查恢复状态,可汇总正在恢复的节点的数据,并多次运行同一查询以查看计数是否发生变化。计数不同表示正在执行恢复工作,所有丢失的数据正在恢复中。
=> select node_name, status , progress from projection_recoveries;
node_name | status | progress
-----------------------+-------------+---------
v_vmart_node0001 | running | 61
若要查看 projection_recoveries
系统表中的单个记录,请向查询中添加限值 1。
恢复完成后,Vertica 继续存储这些表中的最近恢复的信息。
使用 admintools view_cluster
工具从命令行查看群集状态:
$ /opt/vertica/bin/admintools -t view_cluster
DB | Host | State
---------+--------------+------------
<data_base> | 112.17.31.10 | RECOVERING
<data_base> | 112.17.31.11 | UP
<data_base> | 112.17.31.12 | UP
<data_base> | 112.17.31.17 | UP
________________________________
恢复完成时:
启动管理工具。
从“主菜单 (Main Menu)”中选择查看数据库群集状态 (View Database Cluster State),然后单击确定 (OK)。
实用程序将节点状态报告为 UP
。
系统表 PROJECTION_REFRESHES 记录刷新操作成功和失败的相关信息。PROJECTION_REFRESHES 将保留投影刷新数据,直到出现以下事件之一为止:
给定的投影中开始出现另一个刷新操作。
CLEAR_PROJECTION_REFRESHES PROJECTION_REFRESHES 被调用并清除所有投影的数据。
已超出表的存储配额。
要立即清除此信息,请调用 CLEAR_PROJECTION_REFRESHES
:
=> SELECT clear_projection_refreshes();
clear_projection_refreshes
----------------------------
CLEAR
(1 row)
PROJECTION_REFRESHES
检查 PROJECTION_REFRESHES
中的布尔列 IS_EXECUTING
以确定刷新操作是仍在运行还是已完成。该函数仅移除已完成的刷新操作的信息。
PROJECTION_REFRESHES
Vertica 通知程序是一种基于推送的机制,用于将消息从 Vertica 发送到 Apache Kafka 或 syslog 等端点。例如,您可以将长时间运行的脚本配置为在各个阶段以及任务完成时发送通知。
要使用通知程序:
创建 syslog 或 Kafka 通知程序。
使用以下任何内容向 NOTIFIER 端点发送通知:
NOTIFY:手动向 NOTIFIER 端点发送消息。
SET_DATA_COLLECTOR_NOTIFY_POLICY:创建一个通知策略,当指定的事件发生时,该策略会自动向 NOTIFIER 端点发送消息。
定期备份数据库是基本维护任务的重要组成部分。为此,Vertica 提供了一个全面的实用程序:vbr
。 vbr
它支持您执行以下操作。除非另有说明,Enterprise 模式和 Eon 模式均支持以下操作:
备份数据库。
备份数据库中的特定对象(架构或表)。
从备份中还原数据库或单个对象。
将数据库复制到其他群集。例如,将测试群集提升为生产环境(仅限 Enterprise 模式)。
将单个对象(架构或表)复制到另一个群集。
列出可用的备份。
运行 vbr
时,您需要指定配置 (.ini) 文件。在此文件中,您可以指定操作的所有配置参数:备份内容、备份位置、保留的备份数量、是否加密传输等等。Vertica 提供了多个示例 vbr 配置文件,您可将其用作模板。
您可以使用 vbr
还原由 vbr
创建的备份。通常,可以针对这两个操作使用相同的配置文件。 常见用例 引入了最常见的 vbr
操作。
执行备份时,可将数据保存到以下位置之一:
每个节点上的本地目录
远程文件系统
其他 Vertica 群集(有助于高效克隆数据库)
云存储
您无法先备份 Enterprise 模式数据库,然后在 Eon 模式下还原它,反之亦然。
Vertica 支持在以下云存储位置进行备份和还原操作:
Amazon Web Services (AWS) S3
与 S3 兼容的私有云存储,例如 Pure Storage 或 Minio
Google Cloud Storage (GCS)
Azure Blob 存储
如果您要备份 Eon 模式数据库,则必须使用受支持的云存储位置。
您无法在不同的云提供商之间执行备份或还原操作。例如,您无法从 GCS 备份或还原到 S3 位置。
如果您的数据库在 HDFS 上有任何存储位置,则需额外进行配置,才能启用这些存储位置来执行备份操作。请参阅备份和还原 HDFS 存储位置的要求。
您可以使用 vbr
执行许多与备份和还原相关的任务。vbr 引用详细介绍了所有任务。此部分总结了常见用例。但是,此处并未列出每个用例的额外要求。请务必阅读链接的主题以获取详细信息。
这并非备份/还原功能的完整列表。
完整备份将您的数据副本存储在另一个位置。理想情况下,该位置与数据库位置分隔开(例如,不同的硬件或云中)。您需为备份命名(快照名称),这样可以拥有不同的备份和备份类型,且二者互不干扰。在配置文件中,您可以将数据库节点映射到备份位置,并设置其他参数。
在第一次备份之前,运行 vbr init 任务。
使用 vbr backup 任务执行完整备份。外部完整备份/还原示例为您的配置提供了起点。有关完整备份的完整文档,请参阅创建完整备份。
在大多数情况下,Eon 模式下的备份与 Enterprise 模式下的备份方法相同。Eon 模式有一些额外要求(如Eon 模式数据库的要求中所述),且对于备份到云存储,一些配置参数有所不同。您可以使用受支持的云存储位置来备份或还原在云中或内部部署中运行的 Eon 模式数据库。
使用 vbr backup 任务执行完整备份。备份/还原到云存储示例为您的配置提供了起点。有关完整备份的完整文档,请参阅创建完整备份。
在执行删除表等破坏性操作或将 Vertica 升级到新版本等主要操作之前,最好先对数据库进行备份。
为此,您可以执行定期完整备份,但更快捷的方法是创建硬链接本地备份。这种备份将复制编录,并将数据文件链接到每个节点的本地文件系统中的另一个位置。(您也可以对特定对象(而不是整个数据库)进行硬链接备份。)硬链接本地备份提供的保护与外部存储的备份不同。例如,它不能保护您免受本地系统故障的影响。不过,如果您仅暂时需要备份,则硬链接本地备份不失为权宜之计。请勿使用硬链接本地备份代替在其他节点上进行的定期备份。
硬链接备份使用与其他备份相同的 vbr backup 任务,但配置不同。硬链接的完整备份/还原示例为您的配置提供了起点。有关详细信息,请参阅创建硬链接本地备份。
有时您需要还原特定对象(例如,您删除的表),而不是整个数据库。您可以从包含这些对象的任何备份(无论是完整备份还是对象备份)中还原单个表或架构。
使用 vbr restore 任务和 --restore-objects
参数指定要还原的内容。通常,您使用的配置文件与创建备份所用的配置文件相同。有关详细信息,请参阅还原单个对象。
您可以通过完整备份还原 Enterprise 模式和 Eon 模式数据库。您无法通过还原来更改数据库的模式。在 Eon 模式下,您可以还原到主子群集,而无需考虑辅助子群集。
使用 vbr restore 任务还原数据库。与还原选定对象一样,您使用的配置文件通常与创建备份所用的配置文件相同。有关详细信息,请参阅从完整备份中还原数据库和还原硬链接本地备份。
您可能需要将数据库复制到另一个计算机群集,例如,当您将数据库从临时环境升级到生产环境时便是如此。从本质上来说,将数据库复制到另一个群集是同时在进行备份和还原操作。数据将从源数据库群集中备份,然后通过单个操作还原到目标群集。
使用 vbr copycluster 任务复制群集。将数据库复制到备用群集示例为您的配置提供了起点。有关详细信息,请参阅将数据库复制到其他群集。
您可能需要将特定的表或架构从一个数据库复制到另一个数据库。例如,您需要将数据从生产数据库复制到测试数据库,以单独调查问题。另外,当您在单个数据库中加载大量数据之后,复制到另一个数据库可能比在其他数据库中重复执行加载操作更高效。
使用 vbr replicate 任务复制对象。您可以在配置文件中指定要复制的对象。将对象复制到备用数据库示例为您的配置提供了起点。有关详细信息,请参阅将对象复制到备用群集。
vbr 实用程序使用配置文件提供所需的必要信息,以备份和还原完整备份或对象级别备份或者复制群集。不存在默认配置文件。您必须始终使用 vbr 命令指定配置文件。
Vertica 包含示例配置文件,您可以为各种 vbr 任务复制、编辑和部署这些文件。Vertica 会自动将这些文件安装在:
/opt/vertica/share/vbr/example_configs
外部(分布式)备份将每个数据库节点备份到不同的备份主机。节点映射到 [Mapping] 部分中的主机。
要还原,请使用与创建备份时相同的配置文件。
; This sample vbr configuration file shows full or object backup and restore to a separate remote backup-host for each respective database host.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; An equal sign separates options and values.
; Specify arguments marked '!!Mandatory!!' explicitly.
; All commented parameters are set to their default value.
; ------------------------------------------- ;
;;; BASIC PARAMETERS ;;;
; ------------------------------------------- ;
[Mapping]
; !!Mandatory!! This section defines what host and directory will store the backup for each node.
; node_name = backup_host:backup_dir
; In this "parallel backup" configuration, each node backs up to a distinct external host.
; To backup all database nodes to a single external host, use that single hostname/IP address in each entry below.
v_exampledb_node0001 = 10.20.100.156:/home/dbadmin/backups
v_exampledb_node0002 = 10.20.100.157:/home/dbadmin/backups
v_exampledb_node0003 = 10.20.100.158:/home/dbadmin/backups
v_exampledb_node0004 = 10.20.100.159:/home/dbadmin/backups
[Misc]
; !!Recommended!! Snapshot name. Object and full backups should always have different snapshot names.
; Backups with the same snapshotName form a time sequence limited by restorePointLimit.
; SnapshotName is used for naming archives in the backup directory, and for monitoring and troubleshooting.
; Valid characters: a-z A-Z 0-9 - _
; snapshotName = backup_snapshot
[Database]
; !!Recommended!! If you have more than one database defined on this Vertica cluster, use this parameter to specify which database to backup/restore.
; dbName = current_database
; If this parameter is True, vbr prompts the user for the database password every time.
; If False, specify the location of password config file in 'passwordFile' parameter in [Misc] section.
; dbPromptForPassword = True
; If true, vbr attempts to connect to the database using a local connection.
; dbUseLocalConnection = False
; ------------------------------------------- ;
;;; ADVANCED PARAMETERS ;;;
; ------------------------------------------- ;
[Misc]
; The temp directory location on all database hosts.
; The directory must be readable and writeable by the dbadmin, and must implement POSIX style fcntl lockf locking.
; tempDir = /tmp/vbr
; Specifies the number of historical backups to retain in addition to the most recent backup.
; 1 current + n historical backups
; restorePointLimit = 1
; Full path to the password configuration file
; Store this file in directory readable only by the dbadmin
; (no default)
; passwordFile = /path/to/vbr/pw.txt
; When enabled, Vertica confirms that the specified backup locations contain
; sufficient free space and inodes to allow a successful backup. If a backup
; location has insufficient resources, Vertica displays an error message explaining the shortage and
; cancels the backup. If Vertica cannot determine the amount of available space
; or number of inodes in the backupDir, it displays a warning and continues
; with the backup.
; enableFreeSpaceCheck = True
[Transmission]
; Specifies the default port number for the rsync protocol.
; port_rsync = 50000
; Total bandwidth limit for all backup connections in KBPS, 0 for unlimited. Vertica distributes
; this bandwidth evenly among the number of connections set in concurrency_backup.
; total_bwlimit_backup = 0
; The maximum number of backup TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_backup = 1
; The total bandwidth limit for all restore connections in KBPS, 0 for unlimited
; total_bwlimit_restore = 0
; The maximum number of restore TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_restore = 1
; The maximum number of delete TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_delete = 16
[Database]
; Vertica user name for vbr to connect to the database.
; This setting is rarely needed since dbUser is normally identical to the database administrator
; dbUser = current_username
您可以将 Enterprise 模式和 Eon 模式数据库备份和还原到云存储位置。您必须将 Eon 模式数据库备份到受支持的云存储位置。对于 Enterprise 模式和 Eon 模式,[CloudStorage] 部分中的配置设置完全相同。
第一次备份到新的云存储位置之前,您必须完成一些一次性配置。有关详细信息,请参阅云存储的其他注意事项。
备份到内部部署云存储目标时,还需要对 Enterprise 模式和 Eon 数据库额外进行配置。有关额外要求的详细信息,请参阅配置与云存储之间的备份。
要还原,请使用与创建备份时相同的配置文件。要还原选定对象(而不是整个数据库),请使用 --restore-objects
在 vbr
命令行上指定要还原的对象。
; This sample vbr configuration file shows backup to Cloud Storage e.g AWS S3, GCS, HDFS or on-premises (e.g. Pure Storage)
; This can be used for Vertica databases in Enterprise or Eon mode.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; Option and values are separated by an equal sign.
; Only arguments marked as '!!Mandatory!!' must be specified explicitly.
; All commented parameters are set to their default value.
; ------------------------------------------- ;
;;; BASIC PARAMETERS ;;;
; ------------------------------------------- ;
[CloudStorage]
; This section replaces the [Mapping] section and is required to back up to cloud storage.
; !!Mandatory!! Backup location on Cloud or HDFS (no default).
cloud_storage_backup_path = gs://backup_bucket/database_backup_path/
; cloud_storage_backup_path = s3://backup_bucket/database_backup_path/
; cloud_storage_backup_path = webhdfs://backup_nameservice/database_backup_path/
; cloud_storage_backup_path = azb://backup_account/backup_container/
; !!Mandatory!! directory used to manage locking during a backup (no default). If the directory is mounted on the initiator host, you
; should use "[]" instead of the local host name. The file system must support POSIX fcntl flock.
cloud_storage_backup_file_system_path = []:/home/dbadmin/backup_locks_dir/
[Misc]
; !!Recommended!! Snapshot name
; Backups with the same snapshotName form a time sequence limited by restorePointLimit.
; SnapshotName is used for naming archives in the backup directory, and for monitoring and troubleshooting.
; Valid values: a-z A-Z 0-9 - _
; snapshotName = backup_snapshot
; Specifies how Vertica handles objects of the same name when restoring schema or table backups.
; objectRestoreMode = createOrReplace
; Specifies which tables and/or schemas to copy. For tables, the containing schema defaults to public.
; Note: 'objects' is incompatible with 'includeObjects' and 'excludeObjects'.
; (no default)
; objects = mytable, myschema, myothertable
; Specifies the set of objects to backup/restore; wildcards may be used.
; Note: 'includeObjects' is incompatible with 'objects'.
; includeObjects = public.mytable, customer*, s?
; Subtracts from the set of objects to backup/restore; wildcards may be used
; Note: 'excludeObjects' is incompatible with 'objects'.
; excludeObjects = public.*temp, etl.phase?
[Database]
; !!Recommended!! If you have more than one database defined on this Vertica cluster, use this parameter to specify which database to backup/restore.
; dbName = current_database
; If this parameter is True, vbr prompts the user for the database password every time.
; If False, specify the location of password config file in 'passwordFile' parameter in [Misc] section.
; dbPromptForPassword = True
; If true, vbr attempts to connect to the database using a local connection.
; dbUseLocalConnection = False
; ------------------------------------------- ;
;;; ADVANCED PARAMETERS ;;;
; ------------------------------------------- ;
[CloudStorage]
; Specifies encryption-at-rest on S3
; cloud_storage_encrypt_at_rest = sse
; cloud_storage_sse_kms_key_id = <key_id>
; Specifies SSL encrypted transfer.
; cloud_storage_encrypt_transport = True
; Specifies the number of threads for upload/download - backup
; cloud_storage_concurrency_backup = 10
; Specifies the number of threads for upload/download - restore
; cloud_storage_concurrency_restore = 10
; Specifies the number of threads for deleting objects from the backup location
; cloud_storage_concurrency_delete = 10
; Specifies the path to a custom SSL server certificate bundle
; cloud_storage_ca_bundle = /home/user/ssl_folder/ca_bundle.pem
[Misc]
; The temp directory location on all database hosts.
; The directory must be readable and writeable by the dbadmin, and must implement POSIX style fcntl lockf locking.
; tempDir = /tmp/vbr
; Specifies the number of historical backups to retain in addition to the most recent backup.
; 1 current + n historical backups
; restorePointLimit = 1
; Full path to the password configuration file
; Store this file in directory readable only by the dbadmin.
; (no default)
; passwordFile = /path/to/vbr/pw.txt
; Specifies the service name of the Vertica Kerberos principal. This only applies to HDFS.
; kerberos_service_name = vertica
; Specifies the realm (authentication domain) of the Vertica Kerberos principal. This only applies to HDFS.
; kerberos_realm = your_auth_domain
; Specifies the location of the keytab file which contains the credentials for the Vertica Kerberos principal. This only applies to HDFS.
; kerberos_keytab_file = /path/to/keytab_file
; Specifies the location of the Hadoop XML configuration files of the HDFS clusters. Only set this when your cluster is on HA. This only applies to HDFS.
; If you have multiple conf directories, please separate them with ':'.
; hadoop_conf_dir = /path/to/conf or /path/to/conf1:/path/to/conf2
[Database]
; Vertica user name for vbr to connect to the database.
; This setting is rarely needed since dbUser is normally identical to the database administrator
; dbUser = current_username
配置硬链接本地备份时,需满足以下要求:
在 [Transmission]
部分下,添加参数 hardLinkLocal:
hardLinkLocal = True
备份目录必须与数据库数据目录位于相同的文件系统中。
省略加密参数。如果配置文件将加密参数和 hardLinkLocal 参数都设置为 true,则 vbr 会发出警告并忽略加密参数。
; This sample vbr configuration file shows backup and restore using hard-links to data files on each database host for that host's backup.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; An equal sign separates options and values.
; Specify arguments marked '!!Mandatory!!' explicitly.
; All commented parameters are set to their default value.
; ------------------------------------------- ;
;;; BASIC PARAMETERS ;;;
; ------------------------------------------- ;
[Mapping]
; For each database node there must be one [Mapping] entry to indicate the directory to store the backup.
; !!Mandatory!! Backup host name (no default) and Backup directory (no default).
; node_name = backup_host:backup_dir
; Must use [] for hardlink backups
v_exampledb_node0001 = []:/home/dbadmin/backups
v_exampledb_node0002 = []:/home/dbadmin/backups
v_exampledb_node0003 = []:/home/dbadmin/backups
v_exampledb_node0004 = []:/home/dbadmin/backups
[Misc]
; !!Recommended!! Snapshot name. Object and full backups should always have different snapshot names.
; Backups with the same snapshotName form a time sequence limited by restorePointLimit.
; Valid characters: a-z A-Z 0-9 - _
; snapshotName = backup_snapshot
[Transmission]
; !!Mandatory!! Identifies the backup as a hardlink style backup.
hardLinkLocal = True
; If copyOnHardLinkFailure is True, when a hard-link local backup cannot create links the data is copied instead.
copyOnHardLinkFailure = False
; ------------------------------------------- ;
;;; ADVANCED PARAMETERS ;;;
; ------------------------------------------- ;
[Database]
; !!Recommended!! If you have more than one database defined on this Vertica cluster, use this parameter to specify which database to backup/restore.
; dbName = current_database
; If this parameter is True, vbr prompts the user for the database password every time.
; If False, specify the location of password config file in 'passwordFile' parameter in [Misc] section.
; dbPromptForPassword = True
[Misc]
; The temp directory location on all database hosts.
; The directory must be readable and writeable by the dbadmin, and must implement POSIX style fcntl lockf locking.
; tempDir = /tmp/vbr
; Full path to the password configuration file
; Store this file in directory readable only by the dbadmin.
; (no default)
; passwordFile =
; Specifies the number of historical backups to retain in addition to the most recent backup.
; 1 current + n historical backups
; restorePointLimit = 1
; When enabled, Vertica confirms that the specified backup locations contain
; sufficient free space and inodes to allow a successful backup. If a backup
; location has insufficient resources, Vertica displays an error message explaining the shortage and
; cancels the backup. If Vertica cannot determine the amount of available space
; or number of inodes in the backupDir, it displays a warning and continues
; with the backup.
; enableFreeSpaceCheck = True
[Database]
; Vertica user name for vbr to connect to the database.
; This setting is rarely needed since dbUser is normally identical to the database administrator.
; dbUser = current_username
; This is a sample vbr configuration file for backup and restore using a file system on each database host for that host's backup.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; An equal sign separates options and values.
; Specify arguments marked '!!Mandatory!!' explicitly.
; All commented parameters are set to their default value.
; ------------------------------------------- ;
;;; BASIC PARAMETERS ;;;
; ------------------------------------------- ;
[Mapping]
; !!Mandatory!! For each database node there must be one [Mapping] entry to indicate the directory to store the backup.
; node_name = backup_host:backup_dir
; [] indicates backup to localhost
v_exampledb_node0001 = []:/home/dbadmin/backups
v_exampledb_node0002 = []:/home/dbadmin/backups
v_exampledb_node0003 = []:/home/dbadmin/backups
v_exampledb_node0004 = []:/home/dbadmin/backups
[Misc]
; !!Recommended!! Snapshot name
; Backups with the same snapshotName form a time sequence limited by restorePointLimit.
; SnapshotName is used for naming archives in the backup directory, and for monitoring and troubleshooting.
; Valid values: a-z A-Z 0-9 - _
; snapshotName = backup_snapshot
[Database]
; !!Recommended!! If you have more than one database defined on this Vertica cluster, use this parameter to specify which database to backup/restore.
; dbName = current_database
; If this parameter is True, vbr prompts the user for the database password every time.
; If False, specify the location of password config file in 'passwordFile' parameter in [Misc] section.
; dbPromptForPassword = True
; ------------------------------------------- ;
;;; ADVANCED PARAMETERS ;;;
; ------------------------------------------- ;
[Misc]
; The temp directory location on all database hosts.
; The directory must be readable and writeable by the dbadmin, and must implement POSIX style fcntl lockf locking.
; tempDir = /tmp/vbr
; Specifies the number of historical backups to retain in addition to the most recent backup.
; 1 current + n historical backups
; restorePointLimit = 1
; Full path to the password configuration file
; Store this file in directory readable only by the dbadmin.
; (no default)
; passwordFile = /path/to/vbr/pw.txt
; When enabled, Vertica confirms that the specified backup locations contain
; sufficient free space and inodes to allow a successful backup. If a backup
; location has insufficient resources, Vertica displays an error message explaining the shortage and
; cancels the backup. If Vertica cannot determine the amount of available space
; or number of inodes in the backupDir, it displays a warning and continues
; with the backup.
; enableFreeSpaceCheck = True
[Transmission]
; The total bandwidth limit for all restore connections in KBPS, 0 for unlimited
; total_bwlimit_restore = 0
; The maximum number of restore TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_restore = 1
; Total bandwidth limit for all backup connections in KBPS, 0 for unlimited. Vertica distributes
; this bandwidth evenly among the number of connections set in concurrency_backup.
; total_bwlimit_backup = 0
; The maximum number of backup TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_backup = 1
; The maximum number of delete TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_delete = 16
[Database]
; Vertica user name for vbr to connect to the database.
; This setting is rarely needed since dbUser is normally identical to the database administrator
; dbUser = current_username
对象备份仅备份由参数对象或参数 includeObjects 和 excludeObjects 在 [Misc]
部分中指定的架构或表。
对于对象还原,请使用与创建备份相同的配置文件,并使用 vbr 命令行参数指定要还原的对象
--restore-objects
。
; This sample vbr configuration file shows object-level backup and restore
; using a file system on each database host for that host's backup.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; Option and values are separated by an equal sign.
; Only arguments marked as '!!Mandatory!!' must be specified explicitly.
; All commented parameters are set to their default value.
; ------------------------------------------- ;
;;; BASIC PARAMETERS ;;;
; ------------------------------------------- ;
[Mapping]
; There must be one [Mapping] section for all of the nodes in your database cluster.
; !!Mandatory!! Backup host name (no default) and Backup directory (no default)
; node_name = backup_host:backup_dir
; [] indicates backup to localhost
v_exampledb_node0001 = []:/home/dbadmin/backups
v_exampledb_node0002 = []:/home/dbadmin/backups
v_exampledb_node0003 = []:/home/dbadmin/backups
v_exampledb_node0004 = []:/home/dbadmin/backups
[Misc]
; !!Recommended!! Snapshot name. Object and full backups should always have different snapshot names.
; Backups with the same snapshotName form a time sequence limited by restorePointLimit.
; SnapshotName is used for naming archives in the backup directory, and for monitoring and troubleshooting.
; Valid values: a-z A-Z 0-9 - _
; snapshotName = backup_snapshot
; Specifies how Vertica handles objects of the same name when restoring schema or table backups.
; objectRestoreMode = createOrReplace
; Specifies which tables and/or schemas to copy. For tables, the containing schema defaults to public.
; Note: 'objects' is incompatible with 'includeObjects' and 'excludeObjects'.
; (no default)
objects = mytable, myschema, myothertable
; Specifies the set of objects to backup/restore; wildcards may be used.
; Note: 'includeObjects' is incompatible with 'objects'.
; includeObjects = public.mytable, customer*, s?
; Subtracts from the set of objects to backup/restore; wildcards may be used
; Note: 'excludeObjects' is incompatible with 'objects'.
; excludeObjects = public.*temp, etl.phase?
[Database]
; !!Recommended!! If you have more than one database defined on this Vertica cluster, use this parameter to specify which database to backup/restore.
; dbName = current_database
; If this parameter is True, vbr will prompt user for database password every time.
; If set to False, specify location of password config file in 'passwordFile' parameter in [Misc] section.
; dbPromptForPassword = True
; ------------------------------------------- ;
;;; ADVANCED PARAMETERS ;;;
; ------------------------------------------- ;
[Misc]
; The temp directory location on all database hosts.
; The directory must be readable and writeable by the dbadmin, and must implement POSIX style fcntl lockf locking.
; tempDir = /tmp/vbr
; Specifies the number of historical backups to retain in addition to the most recent backup.
; 1 current + n historical backups
; restorePointLimit = 1
; Full path to the password configuration file
; Store this file in directory readable only by the dbadmin.
; (no default)
; passwordFile = /path/to/vbr/pw.txt
; When enabled, Vertica confirms that the specified backup locations contain
; sufficient free space and inodes to allow a successful backup. If a backup
; location has insufficient resources, Vertica displays an error message explaining the shortage and
; cancels the backup. If Vertica cannot determine the amount of available space
; or number of inodes in the backupDir, it displays a warning and continues
; with the backup.
; enableFreeSpaceCheck = True
[Transmission]
; The total bandwidth limit for all restore connections in KBPS, 0 for unlimited
; total_bwlimit_restore = 0
; The maximum number of restore TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_restore = 1
; Total bandwidth limit for all backup connections in KBPS, 0 for unlimited. Vertica distributes
; this bandwidth evenly among the number of connections set in concurrency_backup.
; total_bwlimit_backup = 0
; The maximum number of backup TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_backup = 1
; The maximum number of delete TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_delete = 16
[Database]
; Vertica user name for vbr to connect to the database.
; This setting is rarely needed since dbUser is normally identical to the database administrator.
; dbUser = current_username
; This sample vbr configuration file shows object restore to another cluster from an existing full or object backup.
; To restore objects from an existing backup(object or full), you must use the "--restore-objects" vbr command line option.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; An equal sign separates options and values.
; Specify arguments marked '!!Mandatory!!' explicitly.
; All commented parameters are set to their default value.
; ------------------------------------------- ;
;;; BASIC PARAMETERS ;;;
; ------------------------------------------- ;
[Mapping]
; There must be one [Mapping] section for all of the nodes in your database cluster.
; !!Mandatory!! Backup host name (no default) and Backup directory (no default)
; node_name = backup_host:backup_dir
v_exampledb_node0001 = backup_host0001:/home/dbadmin/backups
v_exampledb_node0002 = backup_host0002:/home/dbadmin/backups
v_exampledb_node0003 = backup_host0003:/home/dbadmin/backups
v_exampledb_node0004 = backup_host0004:/home/dbadmin/backups
[NodeMapping]
; !!Recommended!! This section is required when performing an object restore from a full/object backup to a different cluster and node names are different between source (backup) and destination (restoring) databases.
v_sourcedb_node0001 = v_exampledb_node0001
v_sourcedb_node0002 = v_exampledb_node0002
v_sourcedb_node0003 = v_exampledb_node0003
v_sourcedb_node0004 = v_exampledb_node0004
[Database]
; !!Recommended!! If you have more than one database defined on this Vertica cluster, use this parameter to specify which database to backup/restore.
; dbName = current_database
; If this parameter is True, vbr prompts the user for database password every time.
; If False, specify location of password config file in 'passwordFile' parameter in [Misc] section.
; dbPromptForPassword = True
; ------------------------------------------- ;
;;; ADVANCED PARAMETERS ;;;
; ------------------------------------------- ;
[Misc]
; !!Recommended!! Snapshot name.
; SnapshotName is useful for monitoring and troubleshooting.
; Valid characters: a-z A-Z 0-9 - _
; snapshotName = backup_snapshot
; Specifies how Vertica handles objects of the same name when restoring schema or table backups. Options are coexist, createOrReplace or create.
; objectRestoreMode = createOrReplace
; The temp directory location on all database hosts.
; The directory must be readable and writeable by the dbadmin, and must implement POSIX style fcntl lockf locking.
; tempDir = /tmp/vbr
; Full path to the password configuration file.
; Store this file in a directory only readable by the dbadmin.
; (no default)
; passwordFile = /path/to/vbr/pw.txt
; When enabled, Vertica confirms that the specified backup locations contain
; sufficient free space and inodes to allow a successful backup. If a backup
; location has insufficient resources, Vertica displays an error message and
; cancels the backup. If Vertica cannot determine the amount of available space
; or number of inodes in the backupDir, it displays a warning and continues
; with the backup.
; enableFreeSpaceCheck = True
[Transmission]
; Sets options for transmitting the data when using backup hosts.
; Specifies the default port number for the rsync protocol.
; port_rsync = 50000
; The total bandwidth limit for all restore connections in KBPS, 0 for unlimited
; total_bwlimit_restore = 0
; The maximum number of backup TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_restore = 1
[Database]
; Vertica user name for vbr to connect to the database.
; This setting is rarely needed since dbUser is normally identical to the database administrator.
; dbUser = current_username
; This sample vbr configuration file shows the replicate vbr task.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; An equal sign separates options and values.
; Specify arguments marked '!!Mandatory!!' explicitly.
; All commented parameters are set to their default value.
; ------------------------------------------- ;
;;; BASIC PARAMETERS ;;;
; ------------------------------------------- ;
[Mapping]
; There must be one [Mapping] section for all of the nodes in your database cluster.
; !!Mandatory!! Target host name (no default)
; node_name = new_host
v_exampledb_node0001 = destination_host0001
v_exampledb_node0002 = destination_host0002
v_exampledb_node0003 = destination_host0003
v_exampledb_node0004 = destination_host0004
[Misc]
; !!Recommended!! Snapshot name.
; SnapshotName is useful for monitoring and troubleshooting.
; Valid characters: a-z A-Z 0-9 - _
; snapshotName = backup_snapshot
; Specifies which tables and/or schemas to copy. For tables, the containing schema defaults to public.
; objects for replication. You must specify only one of either objects or includeObjects.
; Use comma-separated list for multiple objects
; (no default)
objects = mytable, myschema, myothertable
; Specifies the set of objects to replicate; wildcards may be used.
; Note: 'includeObjects' is incompatible with 'objects'.
; includeObjects = public.mytable, customer*, s?
; Subtracts from the set of objects to replicate; wildcards may be used
; Note: 'excludeObjects' is incompatible with 'objects'.
; excludeObjects = public.*temp, etl.phase?
; Specifies how Vertica handles objects of the same name when copying schema or tables.
; objectRestoreMode = createOrReplace
[Database]
; !!Recommended!! If you have more than one database defined on this Vertica cluster, use this parameter to specify which database to replicate.
; dbName = current_database
; If this parameter is True, vbr prompts the user for the database password every time.
; If False, specify the location of password config file in 'passwordFile' parameter in [Misc] section.
; dbPromptForPassword = True
; !!Mandatory!! These settings are all mandatory for replication. None of which have defaults.
dest_dbName = target_db
dest_dbUser = dbadmin
dest_dbPromptForPassword = True
; ------------------------------------------- ;
;;; ADVANCED PARAMETERS ;;;
; ------------------------------------------- ;
[Misc]
; The temp directory location on all database hosts.
; The directory must be readable and writeable by the dbadmin, and must implement POSIX style fcntl lockf locking.
; tempDir = /tmp/vbr
; Full path to the password configuration file containing database password credentials
; Store this file in directory readable only by the dbadmin.
; (no default)
; passwordFile = /path/to/vbr/pw.txt
; Specifies the service name of the Vertica Kerberos principal. This only applies to HDFS.
; kerberos_service_name = vertica
; Specifies the realm (authentication domain) of the Vertica Kerberos principal. This only applies to HDFS.
; kerberos_realm = your_auth_domain
; Specifies the location of the keytab file which contains the credentials for the Vertica Kerberos principal. This only applies to HDFS.
; kerberos_keytab_file = /path/to/keytab_file
; Specifies the location of the Hadoop XML configuration files of the HDFS clusters. Only set this when your cluster is on HA. This only applies to HDFS.
; If you have multiple conf directories, please separate them with ':'.
; hadoop_conf_dir = /path/to/conf or /path/to/conf1:/path/to/conf2
[Transmission]
; Specifies the default port number for the rsync protocol.
; port_rsync = 50000
; Total bandwidth limit for all backup connections in KBPS, 0 for unlimited. Vertica distributes
; this bandwidth evenly among the number of connections set in concurrency_backup.
; total_bwlimit_backup = 0
; The maximum number of replication TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_backup = 1
; The maximum number of restore TCP rsync connection threads per node.
; Results vary depending on environment, but values between 2 and 16 are sometimes quite helpful.
; concurrency_restore = 1
; The maximum number of delete TCP rsync connection threads per node.
; Results vary depending on environment, but values between 2 and 16 are sometimes quite helpful.
; concurrency_delete = 16
[Database]
; Vertica user name for vbr to connect to the database.
; This is very rarely be needed since dbUser is normally identical to the database administrator.
; dbUser = current_username
; This sample vbr configuration file is configured for the copycluster vbr task.
; Copycluster supports full database copies only, not specific objects.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; An equal sign separates options and values.
; Specify arguments marked '!!Mandatory!!' explicitly.
; All commented parameters are set to their default value.
; ------------------------------------------- ;
;;; BASIC PARAMETERS ;;;
; ------------------------------------------- ;
[Mapping]
; For each node of the source database, there must be a [Mapping] entry specifying the corresponding hostname of the destination database node.
; !!Mandatory!! node_name = new_host/ip (no defaults)
v_exampledb_node0001 = destination_host1.example
v_exampledb_node0002 = destination_host2.example
v_exampledb_node0003 = destination_host3.example
v_exampledb_node0004 = destination_host4.example
; v_exampledb_node0001 = 10.0.90.17
; v_exampledb_node0002 = 10.0.90.18
; v_exampledb_node0003 = 10.0.90.19
; v_exampledb_node0004 = 10.0.90.20
[Database]
; !!Recommended!! If you have more than one database defined on this Vertica cluster, use this parameter to specify which database to copy.
; dbName = current_database
; If this parameter is True, vbr prompts the user for the database password every time.
; If False, specify the location of password config file in 'passwordFile' parameter in [Misc] section.
; dbPromptForPassword = True
; ------------------------------------------- ;
;;; ADVANCED PARAMETERS ;;;
; ------------------------------------------- ;
[Misc]
; !!Recommended!! Snapshot name.
; SnapshotName is used for monitoring and troubleshooting.
; Valid characters: a-z A-Z 0-9 - _
; snapshotName = backup_snapshot
; The temp directory location on all database hosts.
; The directory must be readable and writeable by the dbadmin, and must implement POSIX style fcntl lockf locking.
; tempDir = /tmp/vbr
; Full path to the password configuration file containing database password credentials
; Store this file in directory readable only by the dbadmin.
; (no default)
; passwordFile = /path/to/vbr/pw.txt
[Transmission]
; Specifies the default port number for the rsync protocol.
; port_rsync = 50000
; Total bandwidth limit for all copycluster connections in KBPS, 0 for unlimited. Vertica distributes
; this bandwidth evenly among the number of connections set in concurrency_backup.
; total_bwlimit_backup = 0
; The maximum number of backup TCP rsync connection threads per node.
; Optimum settings depend on your particular environment.
; For best performance, experiment with values between 2 and 16.
; concurrency_backup = 1
; The maximum number of restore TCP rsync connection threads per node.
; Results vary depending on environment, but values between 2 and 16 are sometimes quite helpful.
; concurrency_restore = 1
; The maximum number of delete TCP rsync connection threads per node.
; Results vary depending on environment, but values between 2 and 16 are sometimes quite helpful.
; concurrency_delete = 16
[Database]
; Vertica user name for vbr to connect to the database.
; This setting is rarely needed since dbUser is normally identical to the database administrator
; dbUser = current_username
与其他配置(.ini
)文件不同,密码配置文件必须通过其密码文件参数由另一个配置文件引用。
; This is a sample password configuration file.
; Point to this file in the 'passwordFile' parameter of the [Misc] section.
; Section headings are enclosed by square brackets.
; Comments have leading semicolons (;) or pound signs (#).
; Option and values are separated by an equal sign.
[Passwords]
; The database administrator's password, and used if dbPromptForPassword is False.
; dbPassword=myDBsecret
; The password for the rsync user account.
; serviceAccessPass=myrsyncpw
; The password for the dest_dbuser Vertica account, for replication tasks only.
; dest_dbPassword=destDBsecret
Eon 模式数据库执行的备份和还原操作与 Enterprise 模式数据库相同。Eon 模式还有一些额外的要求,因为它使用不同的架构。
您必须将 Eon 模式数据库备份到受支持的云存储位置。Vertica 要求您在 vbr
配置文件中设置以下值:
cloud_storage_backup_path
cloud_storage_backup_file_system_path
一个备份路径仅对一个数据库有效。您不能使用相同的路径来存储多个数据库的备份。有关这些配置参数的详细信息,请参阅 [CloudStorage]。
使用与 S3 兼容的内部部署云存储的 Eon 模式数据库可以备份到 Amazon Web Services (AWS) S3。
除了访问用于数据库公共存储的云存储桶外,您还必须访问云存储备份位置。验证您用于访问公共存储的凭据是否也有权访问备份位置。有关为 Vertica 配置云存储访问的详细信息,请参阅配置与云存储之间的备份。
对于 AWS,请注意,尽管您的备份位置可能位于不同的区域,但跨不同 S3 区域的备份和还原操作与虚拟私有云 (VPC) 端点不兼容。
如果数据库在内部部署中运行,则您的公共存储不在 AWS 上,而是在另一个使用 S3 协议的存储平台上。这意味着可能有两个端点和两组凭据,具体取决于您备份的位置。这一额外信息存储在环境变量中,而不是配置文件的参数中。有关详细信息,请参阅配置与云存储之间的备份。
Eon 模式的内部部署数据库的备份不支持 AWS IAM 配置文件。
Eon 模式的内部部署数据库支持以下 vbr
任务:
备份
还原
列表备份
复制
快速检查
完整检查
快速修复
回收垃圾
移除
要备份使用 HDFS 内部部署存储的 Eon 模式数据库,公共存储和备份位置必须使用相同的 HDFS 凭据和域。支持所有 vbr 操作(除了 copycluster 以外)。
Vertica 支持用于 vbr 操作的 Kerberos 身份验证、高可用性名称节点和线路加密。Vertica 不支持 Hadoop 存储的静态加密。
有关详细信息,请参阅配置与 HDFS 之间的备份。
还原 Eon 模式数据库的备份时,还原数据库必须满足以下要求:
与备份数据库同名。
至少拥有与备份数据库中的主子群集同样多的节点。
与备份数据库的节点具有相同的节点名称。
使用与备份数据库相同的编录目录位置。
使用与备份数据库相同的端口编号。
对于对象还原,具有相同的分片订阅。如果分片订阅已更改,则无法进行对象还原,但可以进行完整还原。当您添加或移除节点或重新平衡群集时,分片订阅可能会发生变化。
您可以将从具有主子群集和辅助子群集的数据库中获取的完整备份或对象备份还原到目标数据库中的主子群集。数据库可以只有主子群集,也可以有任意数量的辅助子群集。辅助子群集不需要匹配备份数据库。复制数据库也是如此;只要求是主子群集即可。这些要求与恢复 Eon 模式数据库群集的要求类似。
使用配置文件中的 [Mapping]
部分指定主子群集的映射。
备份和还原 HDFS 存储位置时,有一些注意事项:
存储位置的 HDFS 目录必须已启用快照。您可以直接自行配置此功能,或者让数据库管理员的 Hadoop 帐户自动为您执行此操作。有关详细信息,请参阅用于备份和还原的 Hadoop 配置。
如果 Hadoop 群集使用 Kerberos,则 Vertica 节点必须有权访问某些 Hadoop 配置文件。请参阅下面的配置 Kerberos。
要还原 HDFS 存储位置,Vertica 群集必须能够运行 Hadoop distcp
命令。请参阅下文的在 Vertica 群集上配置 distcp。
HDFS 存储位置不支持对象级别备份。您必须执行完整数据库备份,才能备份 HDFS 存储位置中的数据。
HDFS 存储位置中的数据将备份至 HDFS。此备份可防止数据的意外删除或损坏。但如果整个 Hadoop 群集发生灾难性故障,它并不能防止数据丢失。为了防止数据丢失,您必须为 Hadoop 群集制定备份和灾难恢复计划。
存储在 Linux 本机文件系统上的数据仍将备份到您在备份配置文件中指定的位置。该数据和 HDFS 存储位置中的数据将由 vbr
备份脚本分别处理。
如果 HDFS 使用 Kerberos,则要备份 HDFS 存储位置,需额外执行以下步骤:
向每个 Vertica 节点的 Kerberos 主体授予 Hadoop 超级用户权限。
按照访问 Hadoop 配置文件中所述,将 Hadoop 配置文件复制到您的数据库节点。Vertica 需要访问 core-site.xml
、hdfs-site.xml
和 yarn-site.xml
以进行备份和还原。如果 Vertica 节点共置于 HDFS 节点上,则这些文件已经存在。
将 HadoopConfDir 参数设置为包含这些文件的目录的位置。如果文件位于多个目录中,则该值可以是路径。例如:
=> ALTER DATABASE exampledb SET HadoopConfDir = '/etc/hadoop/conf:/etc/hadoop/test';
这三个配置文件必须全部存在于每个数据库节点的此路径上。
如果您的 Vertica 节点共置于 HDFS 节点上,而且您使用 Kerberos,则还必须更改某些 Hadoop 配置参数。要想从备份还原为工作状态,就需要执行这些更改。在每个 Vertica 节点上的 yarn-site.xml
中,设置以下参数:
无需在不属于 Vertica 节点的 HDFS 节点上做任何更改。
Vertica 群集必须能够运行 Hadoop distcp
命令,才能还原 HDFS 存储位置的备份。要使群集能够运行此命令,最简单的方法是在每个节点上安装几个 Hadoop 程序包。这些程序包必须来自同一个发行版,而且其 Hadoop 版本必须与 Hadoop 群集上运行的版本相同。
您需要遵循的步骤取决于:
包含 HDFS 存储位置的 Hadoop 群集上运行的 Hadoop 发行版和版本。
Vertica 群集上运行的 Linux 发行版。
distcp
所必需的 Hadoop 程序包不会将 Vertica 数据库变成 Hadoop 群集。这个过程仅在您的群集上安装足以运行 distcp
命令的 Hadoop 支持文件。除 Hadoop 支持文件消耗少量额外的磁盘空间以外,Vertica 群集上没有任何额外开销。
配置 Vertica 群集以还原 HDFS 存储位置备份的步骤如下:
如有必要,在 Vertica 群集中的主机上安装并配置 Java 运行时。
找到 Hadoop 发行版包存储库的位置。
在群集中的所有主机上,将 Hadoop 发行版的包存储库添加到 Linux 程序包管理器中。
在 Vertica 主机上安装必要的 Hadoop 程序包。
在 Vertica 数据库中设置两个与 Java 和 Hadoop 有关的配置参数。
确认 Hadoop distcp
命令可在 Vertica 主机上运行。
以下各节将更加详尽地介绍上述步骤。
Vertica 群集必须安装 Java 虚拟机 (JVM) 才能运行 Hadoop distcp
命令。如果您已出于以下目的配置群集,则其已经安装了 JVM:
执行用 Java 开发的用户定义扩展。有关详细信息,请参阅开发用户定义的扩展 (UDx)。
使用 HCatalog 连接器访问 Hadoop 数据。有关详细信息,请参阅使用 HCatalog 连接器。
如果 Vertica 数据库安装了 JVM,则验证您的 Hadoop 发行版是否支持它。请参阅 Hadoop 发行版的文档,以确定其所支持的 JVM。
如果您的 Hadoop 发行版不支持 Vertica 群集上安装的 JVM,您必须卸载该 JVM。然后,您必须安装 Vertica 和 Hadoop 发行版均支持的 JVM。有关与 Vertica 兼容的 JVM 列表,请参阅 Vertica SDK。
如果您的 Vertica 群集没有 JVM(或其现有 JVM 与您的 Hadoop 发行版不兼容),请按照在 Vertica 群集上安装 Java 运行时中的说明进行操作。
许多 Hadoop 发行版均具有自己的安装系统,如 Cloudera Manager 或 Ambari。但它们同样支持使用本机 Linux 程序包(如 RPM 和 .deb
文件)执行手动安装。这些程序包文件均保存在存储库中。您可以配置 Vertica 主机以访问此存储库,以便下载并安装 Hadoop 程序包。
请查阅 Hadoop 发行版的文档,以找到其 Linux 包存储库的位置。这一信息通常可在文档中介绍手动安装技巧的部分找到。
每个 Hadoop 发行版均针对各个主要的 Linux 程序包管理系统维护不同的存储库。请为 Vertica 群集上运行的 Linux 发行版找到特定的存储库。请确保您选择的包存储库与 Hadoop 群集上使用的版本相匹配。
配置 Vertica 群集中的节点,使其可以访问 Hadoop 发行版的包存储库。Hadoop 发行版的文档应该说明如何将存储库添加到 Linux 平台。如果该文档没有说明如何将存储库添加到打包系统,请参阅 Linux 发行版的文档。
您需要遵循的步骤取决于 Linux 平台使用的程序包管理系统。通常而言,此过程包括:
下载配置文件。
将配置文件添加到程序包管理系统的配置目录。
对于基于 Debian 的 Linux 发行版,将 Hadoop 存储库加密密钥添加到根帐户 keyring。
更新程序包管理系统的索引,让其发现新的程序包。
您必须将 Hadoop 存储库添加到 Vertica 群集中的所有主机。
存储库配置完毕后,您就可以安装 Hadoop 程序包。您需要安装的程序包有:
hadoop
hadoop-hdfs
hadoop-client
通常情况下,这些程序包的名称在所有 Hadoop 和 Linux 发行版中都是相同的。这些程序包往往具有其他相关性。请务必接受 Linux 程序包管理器要求安装的所有额外的程序包。
要安装这些程序包,请针对您的 Linux 发行版使用程序包管理器命令。您需要使用的程序包管理器命令取决于您的 Linux 发行版:
在 Red Hat 和 CentOS 上,程序包管理器命令为 yum
。
在 Debian 和 Ubuntu 上,程序包管理器命令为 apt-get
。
在 SUSE 上,程序包管理器命令为 zypper
。
有关安装程序包的说明,请查阅 Linux 发行版的文档。
您必须设置两个 Hadoop 配置参数,才能使 Vertica 还原 HDFS 数据:
JavaBinaryForUDx 是指到 Java 可执行文件的路径。您可能已经设置此值,以使用 Java UDx 或 HCatalog 连接器。您可以使用以下命令,从 Bash 命令外壳中找到默认 Java 可执行文件的路径:
$ which java
HadoopHome 是包含 bin/hadoop
的目录(包含 Hadoop 可执行文件的 bin 目录)。此参数的默认值为 /usr
。如果您的 Hadoop 可执行文件位于 /usr/bin/hadoop
,则默认值是正确的。
以下示例展示了如何设置并随后查看这些参数的值:
=> ALTER DATABASE DEFAULT SET PARAMETER JavaBinaryForUDx = '/usr/bin/java';
=> SELECT current_value FROM configuration_parameters WHERE parameter_name = 'JavaBinaryForUDx';
current_value
---------------
/usr/bin/java
(1 row)
=> ALTER DATABASE DEFAULT SET HadoopHome = '/usr';
=> SELECT current_value FROM configuration_parameters WHERE parameter_name = 'HadoopHome';
current_value
---------------
/usr
(1 row)
您还可以设置以下参数:
HadoopFSReadRetryTimeout 和 HadoopFSWriteRetryTimeout 指定失败前的等待时长。二者的默认值均为 180 秒。如果确信您的文件系统失败速度更快,降低这些值有助于您提高性能。
HadoopFSReplication 指定 HDFS 生成的副本数量。Hadoop 客户端默认选择此参数;Vertica 将相同的值用于所有节点。
HadoopFSBlockSizeBytes 是要写入到 HDFS 的数据块大小;较大的文件将被划分成这般大小的数据块。默认值为 64MB。
程序包在群集中的所有主机上安装完毕后,数据库应该能够运行 Hadoop distcp
命令。要进行测试:
以 数据库超级用户身份登录到群集中的任何主机。
在 Bash 外壳,输入以下命令:
$ hadoop distcp
该命令应打印出与下面类似的消息:
usage: distcp OPTIONS [source_path...] <target_path>
OPTIONS
-async Should distcp execution be blocking
-atomic Commit all changes or none
-bandwidth <arg> Specify bandwidth per map in MB
-delete Delete from target, files missing in source
-f <arg> List of files that need to be copied
-filelimit <arg> (Deprecated!) Limit number of files copied to <= n
-i Ignore failures during copy
-log <arg> Folder on DFS where distcp execution logs are
saved
-m <arg> Max number of concurrent maps to use for copy
-mapredSslConf <arg> Configuration for ssl config file, to use with
hftps://
-overwrite Choose to overwrite target files unconditionally,
even if they exist.
-p <arg> preserve status (rbugpc)(replication, block-size,
user, group, permission, checksum-type)
-sizelimit <arg> (Deprecated!) Limit number of files copied to <= n
bytes
-skipcrccheck Whether to skip CRC checks between source and
target paths.
-strategy <arg> Copy strategy to use. Default is dividing work
based on file sizes
-tmp <arg> Intermediate work path to be used for atomic
commit
-update Update target, copying only missingfiles or
directories
在数据库中的其他主机上重复上述步骤,以验证所有主机均可运行 distcp
。
如果您无法运行 distcp
命令,请尝试执行以下步骤:
如果 Bash 找不到 hadoop
命令,您可能需要将 Hadoop 的 bin
目录手动添加到系统搜索路径。另一种方法是,在到 hadoop
二进制文件的搜索路径(如 /usr/bin
)的现有目录中创建符号链接。
请确保 Vertica 群集上安装的 Java 版本与您的 Hadoop 发行版兼容。
审查 Linux 程序包安装工具的日志中是否存在错误。在某些情况下,程序包可能没有完全安装,或者由于网络问题而没有被下载。
请确保数据库管理员帐户具有执行 hadoop
命令的权限。您可能必须将该帐户添加到特定的组,才能使其运行必要的命令。
完整备份和对象级别备份都驻留在备份主机上,即存储备份和存档的计算机系统。在备份主机上,Vertica 将备份保存在特定的备份位置(目录)中。
您必须先设置备份主机,然后才能创建备份。
备份位置中的存储格式类型必须支持 fcntl lockf (POSIX) 文件锁定。
您可以使用 vbr
将数据库备份到一个或多个主机(称为备份主机),这些主机可位于数据库群集之外。
您可以使用一个或多个备份主机或单个云存储桶来备份数据库。使用 vbr
配置文件可指定群集中的每个节点应使用哪个备份主机。
在备份到本地群集之外的主机之前,先配置 vbr
要使用的目标备份位置。所用的备份主机必须满足以下条件:
可通过 SSH 从数据库群集访问。
数据库管理员帐户可执行免密码 SSH 访问。
已安装 Vertica rpm 或 Python 3.7 及 rsync 3.0.5 或更高版本。
如果使用状态防火墙,请将 tcp_keepalive_time
和 tcp_keepalive_intvl sysctl
设置配置为使用小于防火墙超时值的值。
vbr
依赖 TCP 转发将连接从数据库主机转发到备份主机。对于 copycluster 和复制任务,必须在两组主机上均启用 TCP 转发。与备份主机的 SSH 连接不需要 SSH 转发。
如果默认情况下尚未设置,请在 /etc/ssh/sshd_config 中设置 AllowTcpForwarding = Yes
,然后向每个主机上的 sshd 发送 SIGHUP 信号。有关更多信息,请参阅 Linux sshd 文档。
如果未启用 TCP 转发,则需要该转发的任务将会失败,并显示以下消息:“连接到远程主机时出错: 请检查 SSH 设置,且所有节点上均已安装相同的 Vertica 版本。(Errors connecting to remote hosts: Check SSH settings, and that the same Vertica version is installed on all nodes.)”
在单节点群集上,vbr
使用随机的大数字端口创建本地 ssh 隧道。如果将 PermitOpen
设置为限制该端口,则上述操作将会失败。注释掉 sshd_config 中的 PermitOpen
行。
为完整备份或对象级别备份创建单独的配置文件,每个配置文件采用不同的名称。此外,使用相同的节点、备份主机和目录位置对。为每个数据库指定不同的备份目录位置。
您必须先准备目标备份目录,然后 vbr
才能备份数据库。运行任务类型为 init
的 vbr
,为备份过程创建必需清单。您只需执行一次 init 进程。之后,Vertica 将自动维护清单。
无论计划在何处保存数据备份,均需考虑站点对历史备份的磁盘要求。此外,如果使用多个存档,则多个存档可能需要更多的磁盘空间。Vertica 建议每个备份主机的空间至少是数据库节点占用空间大小的两倍。无论站点备份计划和保留要求的具体情况如何,均应遵循此建议。
要估计数据库大小,请使用 storage_containers
系统表的 used_bytes
列,如下例所示:
=> SELECT SUM(used_bytes) FROM storage_containers WHERE node_name='v_mydb_node0001';
total_size
------------
302135743
(1 row)
您必须验证源数据库节点和目标备份主机之间的任何防火墙均允许连接,以便在端口 50000 上执行 SSH 和 rsync。
备份主机运行的 rsync 和 Python 版本必须与 Vertica 安装包中提供的版本相同。
要允许 vbr
访问备份主机,
数据库超级用户必须满足两个要求:
在每台备份主机上都有一个帐户,且对备份目录具有写入权限。
可执行从每个数据库群集主机到相应备份主机的免密码 SSH 访问。
如何满足这些要求取决于您的平台和基础设施。
备份主机之间的 SSH 访问以及从备份主机到数据库节点的访问并不是必需的。
如果您的站点未使用集中登录系统(例如 LDAP),则通常可使用 useradd
命令或通过 GUI 管理工具添加用户。有关详细信息,请参阅您的 Linux 分发版文档。
若平台支持,可使用 ssh-copy-id
命令将数据库管理员的 SSH 标识文件从其中一个数据库节点复制到备份位置,从而启用免密码 SSH 登录。例如,若要将 SSH 标识文件从某个节点复制到名为 backup01
的备份主机:
$ ssh-copy-id -i dbadmin@backup01|
Password:
尝试使用 "ssh dbadmin@backup01"
登录到计算机。然后,检查 ~/.ssh/authorized_keysfile
的内容,确认未添加您不想包括的额外键。
$ ssh backup01
Last login: Mon May 23 11:44:23 2011 from host01
重复上述步骤,将数据库管理员的 SSH 标识复制到用于备份数据库的所有备份主机中。
复制数据库管理员的 SSH 标识后,您应能够从群集中的任意节点登录到备份主机,而不收到输入密码的提示。
如果配置要求将多个节点备份到一个备份主机 (n:1),请增加 SSH 后台程序 (sshd
) 的并发 SSH 连接数。默认情况下,每个主机上的并发 SSH 连接数为 10
,该值是在 sshd_config
文件中通过 MaxStartups
关键字设置的。每个备份主机的 MaxStartups
值应大于要备份到此备份主机的主机总数。有关配置 MaxStartups
的详细信息,请参考该参数的手册页。
为硬链接本地配置文件指定 backupHost
参数时,请使用 Admintools 所知的数据库主机名(或 IP 地址)。不要使用节点名称。设置群集时使用的是主机名(或 IP 地址)。backupHost
参数请勿使用 localhost
。
要查询节点名称和主机名:
=> SELECT node_name, host_name FROM node_resources;
node_name | host_name
------------------+----------------
v_vmart_node0001 | 192.168.223.11
v_vmart_node0002 | 192.168.223.22
v_vmart_node0003 | 192.168.223.33
(3 rows)
由于您要创建本地备份,请使用方括号 [] 将主机映射到本地主机。有关详细信息,请参考 [mapping]。
[Mapping]
v_vmart_node0001 = []:/home/dbadmin/data/backups
v_vmart_node0002 = []:/home/dbadmin/data/backups
v_vmart_node0003 = []:/home/dbadmin/data/backups
要将 Enterprise 模式或 Eon 模式数据库备份到受支持的云存储位置,需将参数添加到备份配置文件中。可以从本地群集或云提供商的虚拟服务器中创建这些备份。但还需要一些额外的配置。
有关配置身份验证和加密的信息,请参阅云存储的其他注意事项。
要将任何 Eon 模式或 Enterprise 模式群集备份到云存储目标,备份配置文件必须包含 [CloudStorage] 部分。Vertica 提供了一个示例云存储配置文件,您可以复制和编辑该文件。
环境变量支持采用安全方式传递备份位置的凭据。在以下备份场景中,Eon 和 Enterprise 模式数据库需要环境变量:
在 Google Cloud Platform (GCP) 上,从 Vertica 备份到 Google Cloud Storage (GCS)。
如果要备份到 GCS,则需具有基于哈希的消息身份验证代码 (HMAC) 密钥,其中包含访问 ID 和秘密访问密钥。有关如何创建 HMAC 密钥的说明,请参阅 GCP 上 Eon 模式的先决条件。
从内部部署数据库备份到以下任何存储位置:
Amazon Web Services (AWS)
任何与 S3 兼容的存储
Azure Blob 存储(仅限 Enterprise 模式)
内部部署数据库备份要求您使用环境变量传递凭据。您不能对跨端点备份使用其他认证方法。
任何不使用 Azure 托管标识管理资源的 Azure 用户环境。
vbr 日志将记录您何时发送环境变量。出于安全目的,不会记录环境变量所代表的值。有关查看 vbr 日志的详细信息,请参阅备份和还原故障排除。
所有 Enterprise 模式和 Eon 模式数据库都需要下表中所述的环境变量:
Eon 模式数据库需要下表中描述的环境变量:
如果用户环境不使用 Azure 托管标识管理资源,则必须使用环境变量提供凭据。如果在使用 Azure 托管标识的环境中设置环境变量,则使用环境变量设置的凭据优先于 Azure 托管标识凭据。
您可以在两个单独的 Azure 帐户之间进行备份和还原。跨帐户操作需要每个帐户的凭据配置 JSON 对象和端点配置 JSON 对象。每个环境变量都接受一个或多个以逗号分隔的 JSON 对象的集合。
跨帐户和跨区域的备份和还原操作可能会导致性能下降。有关性能和成本的详细信息,请参阅 Azure 文档。
Azure Blob 存储环境变量如下表所述:
以下命令将 Azure Blob 存储环境变量导出到当前 shell 会话:
$ export VbrCredentialConfig=[{"accountName": "account1","blobEndpoint": "host[:port]","accountKey": "account-key1","sharedAccessSignature": "sas-token1"}]
$ export VbrEndpointConfig=[{"accountName": "account1", "blobEndpoint": "host[:port]", "protocol": "http"}]
如果您要备份到受支持的云存储位置,则需额外执行一些一次性配置。如果备份的群集正在云中的实例上运行,则还需额外执行一些步骤。对于 Amazon Web Services (AWS),您可能会选择加密备份。这需要额外执行一些步骤。
默认情况下,存储桶访问仅限于公共存储桶。对于其他存储桶的一次性操作(例如备份和还原数据库),请使用适当的凭据。有关其他信息,请参阅 Google Cloud Storage 参数和 S3 参数。
与任何存储位置一样,您必须使用 vbr
init
任务对云存储位置进行初始化。
由于云存储不支持文件锁定,因此 Vertica 使用本地文件系统或云存储文件系统处理备份期间的文件锁定。您可以使用 vbr 配置文件中的
cloud_storage_backup_file_system_path
参数来识别此位置。在备份期间,Vertica 将在您的本地或云实例上创建一个锁定的标识文件,并在云存储备份位置创建一个重复文件。只要文件匹配,Vertica 就会继续备份,并在备份完成时释放锁定文件。只要文件始终保持相同,您就可以使用云存储位置来执行备份和还原任务。
如果锁定位置中的文件与备份位置中的文件不同步,则备份和还原任务将会失败并显示错误消息。您可以使用 --cloud-force-init
参数重新运行 init
任务,从而解决锁定不一致问题:
$ /opt/vertica/bin/vbr --task init --cloud-force-init -c filename.ini
如果您要从基于 Google Cloud Platform 的群集备份到 Google Cloud Storage (GCS),则必须向 GCS 公共存储位置提供身份验证。按照配置与云存储之间的备份中的详细说明设置环境变量,以对 GCS 存储进行身份验证。
有关其他身份验证信息(包括如何创建基于哈希的消息身份验证代码 (HMAC) 密钥),请参阅 GCP 上 Eon 模式的先决条件。
如果从基于 EC2 的群集备份到 S3,则必须向 S3 主机提供身份验证。无论选择何种身份验证类型,您的凭据均不会离开 EC2 群集。Vertica 支持以下身份验证类型:
AWS 凭据文件
环境变量
IAM 角色
AWS 凭据文件 - 您可以通过 ~/.aws/credentials 在 EC2 启动程序主机上手动创建配置文件。
[default]
aws_access_key_id = YOUR_ACCESS_KEY
aws_secret_access_key = YOUR_SECRET_KEY
有关凭据文件的更多信息,请参考 Amazon Web Services 文档。
环境变量 - Amazon Web Services 提供以下环境变量:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
在启动程序上使用这些变量为您的 S3 主机提供身份验证。当会话结束时,AWS 会删除这些变量。有关更多信息,请参考 AWS 文档。
IAM 角色 - 创建 AWS IAM 角色,并授予该角色访问您的 EC2 群集和 S3 资源的权限。建议使用此方法管理长期访问权限。有关更多信息,请参考 Amazon Web Services 文档。
使用本机服务器端 S3 加密功能,可以对 Amazon S3 的备份进行加密。有关 Amazon S3 加密的更多信息,请参考 Amazon 文档。
Vertica 支持以下形式的 S3 加密:
使用 Amazon S3 管理密钥 (SSE-S3) 的服务器端加密
使用 AES-256 加密备份
Amazon 管理加密密钥
使用 AWS KMS 管理密钥 (SSE-KMS) 的服务器端加密
使用 AES-256 加密备份
需要 Amazon 密钥管理服务提供加密密钥
S3 存储桶必须与加密密钥来自同一区域
允许审核用户活动
启用备份加密之后,Vertica 将在创建备份时对其进行加密。如果在创建初始备份后启用加密,则仅对启用加密后添加的增量备份进行加密。为了确保备份完全加密,请在启用加密之后创建新备份。
要启用加密,请将以下设置添加到配置文件中:
cloud_storage_encrypt_transport:在传输期间将备份加密。如果使用 SSE-KMS 加密,则必须启用此参数。
cloud_storage_encrypt_at_rest:启用备份加密。如果启用加密但不提供 KMS 密钥,则 Vertica 将使用 SSE-S3 加密。
cloud_storage_sse_kms_key_id:如果使用 KMS 加密,请使用此参数提供密钥 ID。
有关这些设置的更多信息,请参阅 [CloudStorage]。
以下示例显示了备份的 KMS 加密的典型配置。
[CloudStorage]
cloud_storage_encrypt_transport = True
cloud_storage_encrypt_at_rest = sse
cloud_storage_sse_kms_key_id = 6785f412-1234-4321-8888-6a774ba2aaaa
仅限 Eon 模式
要备份使用 HDFS 内部部署存储的 Eon 模式数据库,公共存储和备份位置必须使用相同的 HDFS 凭据和域。支持所有 vbr 操作(除了 copycluster 以外)。
Vertica 支持用于 vbr 操作的 Kerberos 身份验证、高可用性名称节点和 TLS(线路加密)。
要使用 HDFS 上的公共存储在内部部署备份 Eon 模式,则需提供备份配置文件。在 [CloudStorage] 部分中,提供 cloud_storage_backup_path 和 cloud_storage_backup_file_system_path 值。
如果您在 Hadoop 群集中使用 Kerberos 身份验证或高可用性 NameNode,则 vbr 实用程序要求访问您在引导文件(此文件在数据库安装期间创建)中设置的相同值。在备份文件的 [misc] 部分中包含这些值。
下表将 vbr 配置选项映射到其关联的引导文件参数:
例如,如果 KerberosServiceName 在引导文件中设置为 principal-name,则在 [Misc] 中将 kerberos_service_name 设置为 principal-name 配置文件的部分。
Vertica 支持在公共存储和备份位置之间使用线路加密的 vbr 操作。使用备份配置文件的[CloudStorage]部分中的 cloud_storage_encrypt_transport 参数来配置加密。
要启用加密:
将 cloud_storage_encrypt_transport 设置为 true
。
对 cloud_storage_backup_path 使用 swebhdfs://
协议。
如果不使用加密:
将 cloud_storage_encrypt_transport 设置为 false
。
对 cloud_storage_backup_path 使用 webhdfs://
协议。
Vertica 不支持 Hadoop 存储的静态加密。
您应定期对数据库执行完整备份。此外,在以下情况下,您也应执行完整备份:
进行以下操作之前:
将 Vertica 升级到另一个版本。
删除分区。
添加、移除或替换数据库群集中的节点。
进行以下操作之后:
加载大量数据。
添加、移除或替换数据库群集中的节点。在这种情况下,始终创建新的完整备份。
恢复崩溃的群集。
如果:
最好计划持续备份来备份数据。可以从 cron
作业或其他任务调度程序运行 Vertica vbr
。
您还可以备份选定对象。使用对象备份来补充(而不是替换)完整备份。备份类型中介绍了备份类型。
运行 vbr
不会影响活动的数据库应用程序。 vbr
支持在创建备份的同时,并发运行执行 DML 语句(包括 COPY、INSERT、UPDATE、DELETE 和 SELECT)的应用程序。
完整备份和对象级别备份都驻留在备份主机上,即存储备份和存档的计算机系统。
Vertica 将备份保存在特定的备份位置中,即备份主机的目录。此位置可以包含多个备份(包括完整备份和对象级别备份),其中包括关联的存档。备份也可兼容,允许您从完整的数据库备份中还原任何对象。Eon 模式数据库的备份位置必须位于 S3 上。
在开始备份之前,您必须使用 vbr init 任务准备备份位置,如下例所示:
$ vbr -t init -c full_backup.ini
有关备份位置的详细信息,请参阅设置备份位置。
备份包含备份开始时备份对象的所有已提交数据。备份不包含未提交数据或备份期间提交的数据。备份不会延迟合并或加载活动。
如果 Vertica 群集使用 HDFS 存储位置,则必须先进行一些额外配置,然后才能执行备份。请参阅备份和还原 HDFS 存储位置的要求。
HDFS 存储位置仅支持完整备份和还原。您无法在使用 HDFS 存储位置的群集上执行对象备份或还原。
进行备份时,备份过程可能会占用额外的存储空间。占用的空间量取决于编录的大小以及备份期间删除的任何对象。备份完成后,备份过程将释放此存储。
创建备份配置文件时:
创建单独的配置文件以创建完整备份或对象级别备份。
在每个配置文件中使用不同的快照名称。
对两种类型的备份使用同一备份主机目录位置:
由于备份共享磁盘空间,因此执行还原时它们彼此兼容。
每个群集节点还必须在其指定的备份主机上使用相同的目录位置。
为获得最佳网络性能,每个群集节点使用一个备份主机。
对每个备份节点使用一个目录,以存储后续备份。
为了供今后参考,将主要的 Vertica 版本号附加到配置文件名 (mybackup
9x)。
选定的备份对象可以包括一个或多个架构、表或这两者。例如,您可以将架构 S1
、表 T1
和 T2
包括在对象级备份中。可将多个备份合并成一个备份。架构级备份可与数据库备份集成(而表备份可与架构级备份集成,以此类推)。
vbr
支持以下类型的备份:
vbr
配置文件包含 snapshotName
参数。对于不同类型的备份(包括对象级别备份中对象的不同组合),使用不同的快照名称。具有相同快照名称的备份形成了一个由 restorePointLimit
限制的时序,因此如果您为所有备份指定相同的快照名称,最终它们相互之间会出现干扰。
完整备份是数据库编录及其架构、表和其他对象的完整副本。此类型的备份提供数据库在备份时点的一致映像。您可以将完整备份用于灾难恢复,以便还原损坏的或不完整的数据库。您还可以从完整备份中还原单个对象。
当完整备份已经存在时,vbr
将备份自上次完整备份以来新增数据或更改的数据,而不是生成另一个完整的副本。您可以指定要保留的历史备份数量。
存档包括相同名称的备份的集合。每个存档可以有不同的保留策略。例如,假设 TBak
是表 T 的对象级别备份名称,并且您在每周都创建了每日备份。那么 TBak
存档包括七个备份。保留备份存档有助于您恢复到存档中任何一个已保存的备份。
对象级别备份包含一个或多个架构或表,或一组此类对象。将对象级备份联合起来并不会构成整个数据库。如果存在对象级别备份,您可以还原其中的所有内容或单个对象。
对象级别备份包含以下对象类型:
您可以直接在数据库节点上,更快速地对完整数据库或特定对象进行本地备份。通常,您将在执行破坏性操作之前临时使用此类备份。不要长期依赖这种备份。它无法使您免受节点故障的影响,因为数据和备份位于同一节点上。
检查点备份称为硬链接本地备份。它包括数据库编录的完整副本,以及指向相应数据文件的一组硬文件链接。您必须将硬链接本地备份保存在编录和数据库文件使用的文件系统中。
仅 Enterprise 模式支持硬链接本地备份。
创建数据库备份之前,请确认满足以下条件:
已使用 vbr init 任务准备了备份目录:
$ vbr -t init -c full_backup.ini
您的数据库正在运行。在 K-safe 数据库中,所有节点无需都处于正常运行状态。但是,出现故障的节点不会被备份。
所有备份主机可正常运行且可供使用。
备份主机(位于数据库群集上或其他位置)具有足够的磁盘空间来存储备份。
启动 vbr
用户的用户帐户对主机备份位置上的目标目录具有写入访问权限。此用户可以是 dbadmin
或其他分配的角色。但是,您无法以 root 身份运行 vbr
。
每个备份都具有唯一的文件名。
如果要保留较早的备份,请在配置文件中将 restorePointLimit
设置为大于 1 的数字。
如果您正在备份 Eon 模式数据库,您可能已满足Eon 模式数据库的要求。
从终端运行 vbr
。使用数据库群集启动程序节点中的数据库管理员帐户。该命令仅需要 --task backup
和 --config-file
实参(或其缩写形式,-t
和 -c
)。
如果您的配置文件不包含数据库管理员密码,则 vbr
会提示您输入该密码。系统不会显示您键入的内容。
vbr
在调用后无需进一步的交互操作。
以下示例显示完整备份:
$ vbr -t backup -c full_backup.ini
Starting backup of database VTDB.
Participating nodes: v_vmart_node0001, v_vmart_node0002, v_vmart_node0003, v_vmart_node0004.
Snapshotting database.
Snapshot complete.
Approximate bytes to copy: 2315056043 of 2356089422 total.
[==================================================] 100%
Copying backup metadata.
Finalizing backup.
Backup complete!
默认情况下,屏幕中除了进度条之外不显示任何输出。要包含附加的进度信息,请使用 --debug
选项,其值为 1、2 或 3。
使用对象级别备份可备份单个架构或表。对象级备份特别适用于多租户数据库站点。例如,一个国际机场可以使用多租户数据库来表示其架构中的不同航线。然后,可以使用表来维护不同类型的航线信息,其中包括“抵达 (ARRIVALS)”、“出港 (DEPARTURES)”以及“乘客 (PASSENGER)”信息。对于此类组织,通过创建特定架构的对象级备份,您可以按航线租户或任何其他重要数据分段进行还原。
若要创建一个或多个对象级备份,请创建一个配置文件,该文件用于指定备份位置、对象级备份名称以及要包括的对象的列表(一个或多个架构和表)。您可以将 includeObjects
和 excludeObjects
参数与通配符一起使用,以指定所需对象。有关指定要包含对象的详细信息,请参阅包括和排除对象。
有关完整备份或对象级别备份的配置文件的详细信息,请参阅示例 vbr 配置文件和配置文件参考。
尽管并非必须,但 Vertica 建议您首先创建一个完整备份,然后再创建任何对象级别备份。
在您可以创建备份之前,必须先使用 vbr -init 任务准备备份目录。您还必须创建一个配置文件来指定要备份的对象。
使用数据库群集中某个节点的数据库管理员帐户,从终端运行 vbr
。您无法以 root 身份运行 vbr
。
您可以创建对象级别备份,如下例所示。
$ vbr --task backup --config-file objectbak.ini
Preparing...
Found Database port: 5433
Copying...
[==================================================] 100%
All child processes terminated successfully.
Committing changes on all backup sites...
backup done!
为每个对象级备份配置文件提供一个独特的描述性名称。例如,在机场航站楼,基于架构的备份配置文件使用的命名约定为:带有航线前缀,后跟进一步的描述。例如:
AIR1_daily_arrivals_backup
AIR2_hourly_arrivals_backup
AIR2_hourly_departures_backup
AIR3_daily_departures_backup
当存在数据库和对象级别备份时,您就可以对所选的备份进行恢复。
对象级别备份仅包含还原架构或表所需的元素,其中包括选定对象、依赖对象和主体对象。对象级别备份包括以下内容:
存储:属于任何指定对象的数据文件
元数据:包括群集拓扑、时间戳、时期、AHM 等
编录片段:序列化到主体对象和依赖对象中的永久编录对象
例如,AIR2 包含的某些元素是其父架构、表、命名序列、主键和外键约束等。要创建此类备份,vbr
将保存与该表直接关联的对象。它还保存任何依赖项(如外键 (FK) 表),并创建要从中还原备份的对象映射。
vbr
会存储表的定义。
请注意对象级别备份后所做的更改如何影响后续备份。假设您创建一个对象级别备份,然后从数据库中删除架构和表。在这种情况下,您删除的对象也会从后续备份中删除。如果您未保存对象备份的存档,则此类对象可能会永久丢失。
如果在创建表备份之后更改表名,还原备份之后,对表名的更改将失效。假设您在创建一个备份后删除了一位用户,而该用户拥有该备份中的任何选定对象或依赖对象。在这种情况下,还原备份会重新创建对象并将所有权分配给执行还原操作的用户。如果某个已还原对象的所有者仍然存在,则该用户会保留这个已还原对象的所有权。
要从备份中还原已删除的表:
将新创建的表从 t1 重命名为 t2。
还原包含 t1 的备份。
还原 t1。表 t1 和 t2 现在同时存在。
有关 Vertica 如何处理对象覆盖的信息,请参考 [misc] 中的 objectRestoreMode
参数。
完成对象备份后,K-safety 可能会提高。如果同时出现以下两种情况,则备份还原失败:
K-safety 提高。
备份中的任何表都没有足够的投影。
如果您先创建一个备份,然后删除一个主体对象,则还原备份会还原该主体对象。如果已还原对象的所有者也被删除,则 Vertica 会将已还原对象分配给当前 dbadmin。
您可以在 vbr
配置文件中指定 Vertica 如何处理对象覆盖。有关详细信息,请参考 [misc] 中的 objectRestoreMode 参数。
标识和自动增量序列是依赖对象,因为它们必须与其表同时存在。对象级别备份包括此类对象以及它们所依赖的表。
命名序列不是依赖对象,因为它们自主存在。在您删除使用某个命名序列的表之后,该命名序列仍然存在。在这种情况下,命名序列是一个主体对象。因此,您必须备份命名序列和表。如果在您还原表时命名序列尚不存在,则您可以重新生成序列。如果该序列存在,则 vbr
会使用它,而不会进行修改。如果您还原完整数据库,然后将表备份还原到较新的时期,则序列值可能会重复。
当数据库对象通过约束进行关联时,您必须将它们一起备份。例如,如果某架构所含表的约束仅引用该架构中的表,则可以对此架构进行备份。但是,如果某架构包含的表具有另一个架构中某个表的 FK/PK 约束,则无法对此架构进行备份。要备份第二个表,您必须将另一个架构包括在选定对象的列表中。
vbr
会自动将具有不同备份名称但备份位置相同的配置进行关联。
始终创建一个群集范围内的配置文件以及一个或多个指向同一备份位置的对象级配置文件。由于各个备份之间的存储是共享的,因此相同数据不会存在多个副本。对于对象级别备份,使用相同的备份位置会使 vbr
遇到较少的 OID 冲突预防技巧。避免 OID 冲突预防可以减少还原备份时出现的问题。
使用具有相同备份位置的群集和对象配置文件时,vbr
会包括附加配置来确保在完成完整群集还原后可以使用对象级别备份。还原完整群集的一种方法是使用完整数据库备份来启动群集。群集再次处于运行状态后,您即可还原架构和表的最近对象级别备份。
尝试使用对象级别配置文件还原完整数据库失败,导致以下错误:
VMart=> /tmp/vbr --config-file=Table2.ini -t restore
Preparing...
Invalid metadata file. Cannot restore.
restore failed!
有关详细信息,请参阅从对象级别备份中还原所有对象。
每个备份都包括可以将其内容还原到的时期。当 vbr
还原数据时,Vertica 会更新到当前时期。
vbr
会在错误出现和备份失败之前尝试创建对象级别备份五次。
您可以使用 hardLinkLocal
选项创建具有本地数据库主机上的硬文件链接的完整备份或对象级别备份。
创建硬链接本地备份可以为远程主机备份提供以下优势:
速度更快:硬链接本地备份要比远程主机备份的速度快得多。备份时,如果备份目录与数据库目录位于同一文件系统上,则 vbr
不会复制文件。
网络活动更少:硬链接本地备份可最大限度减少网络负载,因为它不需要通过重新同步操作将文件复制到远程备份主机。
占用的磁盘空间更少:备份包括编录副本和硬文件链接。因此,本地备份使用的磁盘空间明显低于具有数据库数据文件副本的备份。但是,每次运行 vbr
时,硬链接本地备份都会保存编录的完整副本。因此,一段时间后磁盘空间将随着编录大小的增加而增加。
硬链接本地备份在实验设计和开发周期期间很有用。数据库设计和开发人员可以在设计和开发阶段定期创建架构和表的硬链接本地对象备份。如果新的开发工作失败,开发人员可以从备份还原一个或多个对象。
如果您计划将硬链接本地备份用作标准站点过程,请对数据库和硬件配置进行相应的设计。考虑将每个节点的所有数据文件存储到一个文件系统中。此类配置具有自动为硬链接本地备份做好设置准备的优势。
配置文件的 backupDir
参数会指定顶级备份目录的位置。硬链接本地备份要求备份目录要位于与数据库数据相同的 Linux 文件系统中。Linux 操作系统无法创建指向其他文件系统的硬文件链接。
请勿在数据库数据存储位置创建硬链接本地备份目录。例如,最佳做法是,数据库数据目录不应位于文件系统的顶级,如以下示例所示:
/home/dbadmin/data/VMart/v_vmart_node0001
相反,Vertica 建议在数据库级别之上为数据添加其他子目录,例如以下示例:
/home/dbadmin/data/dbdata/VMart/v_vmart_node0001
然后,您可以创建硬链接本地备份子目录,将其与您刚刚创建的数据目录配对,例如以下示例:
/home/dbadmin/data/backups
/home/dbadmin/data/dbdata
指定硬链接备份位置时,请确保在将 hardLinkLocal=True
参数添加到配置文件时避免以下常见错误:
在创建 Enterprise 模式数据库的完整硬链接本地数据库备份之前,请验证以下内容:
您的数据库正在运行。所有节点无需在 K-safe 数据库中处于正常运行状态,vbr
即可运行。但请注意,出现故障的节点不会被备份。
启动 vbr
的用户帐户(dbadmin
或其他)具有目标备份目录的写入权限。
Eon 模式不支持硬链接备份。
当您创建完整或对象级别硬链接本地备份时,备份包含以内容:
使用数据库群集中某个节点的数据库管理员帐户,从终端运行 vbr
脚本。您无法以 root 身份运行 vbr
。
硬链接备份使用与其他备份相同的 vbr
实参。将备份配置为硬链接备份的操作完全在配置文件中执行。以下示例显示语法:
$ vbr --task backup --config fullbak.ini
您可以将硬链接本地备份用作临时机制来备份到磁带或其他形式的存储介质。以下步骤介绍了如何通过简化的方法来保存和还原磁带存储中的硬链接本地备份:
复制现有配置文件或示例 vbr 配置文件中所述的示例之一,以此创建配置文件。
编辑配置文件(本例中为 localbak.ini
),使其在 [Transmission]
部分中包含 hardLinkLocal=True
参数。
使用配置文件运行 vbr
:
$ vbr --task backup --config-file localbak.ini
使用一个单独的进程(而非 vbr
)将硬链接本地备份目录复制到磁带或其他外部介质。
如果数据库损坏,将磁带中的备份文件传输到原始备份目录,并按照还原硬链接本地备份中的说明进行还原。
还原硬链接本地备份需要一些额外的(手动)步骤。请勿使用其来代替定期完整备份(创建完整备份)。
硬链接本地备份的可靠性完全取决于存储这些备份的磁盘。如果本地磁盘损坏,硬链接本地备份也会损坏。在这种情况下,您就无法从硬链接本地备份中还原数据库,因为它也会损坏。
所有站点都应该在外部维护完整备份以便用于灾难恢复,因为硬链接本地备份并未实际复制任何数据库文件。
作为最佳做法,Vertica 建议您在数据库内容出现明显差异时经常备份。在发生了显著修改数据库的任何事件(如执行重新平衡)之后,始终执行备份。将具有明显差异的多个备份混合在一起,将会降低数据的 K-safety。例如,如果多个备份均属于同一存档,建议不要在重新平衡之前和之后进行备份。
每次使用同一配置文件备份数据库时,vbr
均会创建一个额外备份,并且可能会移除最旧的备份。备份操作将复制新的存储容器。这些容器可能包括:
上次执行数据库备份时存在的数据
自上次完整备份以来的新增数据和更改的数据
使用配置文件中的 restorePointLimit
参数,可增加存储的备份数。如果备份任务可能导致超出此限制,vbr
会在成功备份后删除最旧的备份。
当您运行备份任务时,vbr
会首先在指定位置创建新的备份,这可能会暂时超出限制。然后,它检查备份数是否超过 restorePointLimit
的值,在必要时删除最旧的备份,直到剩下的数量达到 restorePointLimit
为止。如果请求的备份失败或中断,vbr
不会删除任何备份。
还原数据库时,您可以选择从任何保留的备份(而不是最近的备份)进行还原。因此,如果您希望需要访问较旧的备份,请提高限制。
您可以使用 vbr 还原任务从 vbr
创建的备份中还原完整的数据库或选定的对象。通常,您可以为这两个操作使用相同的配置文件。最小的还原命令为:
$ vbr --task restore --config-file config-file.ini
您必须使用数据库管理员的帐户(而不是 root)登录。
对于完整还原,数据库必须为 DOWN 状态。对于对象还原,数据库必须为 UP 状态。
通常,您将还原到备份的群集。但是,如果原始群集不再可用,也可还原到备用群集。
还原操作必须在与您从中还原的备份相同的架构上完成。您无法先备份 Enterprise 模式数据库,然后在 Eon 模式下还原它,反之亦然。
您可以对 Permanent 节点类型执行还原任务。您无法在 Ephemeral、Execute 或 Standby 节点上还原数据。若要还原或复制到这些节点,必须先将目标节点类型更改为 PERMANENT。有关详细信息,请参考 设置节点类型。
Vertica 支持将对象复制和还原到目标数据库,但最多支持比当前数据库版本晚一个次要版本。例如,您可以将对象从 11.0.x 数据库复制或还原到 11.1.x 数据库。不同版本的还原或复制过程与相同版本的过程相同。
如果还原或复制的对象需要数据库中不存在的更高版本的 UDx 库,Vertica 将显示以下错误:
ERROR 2858: Could not find function definition
通过在目标数据库中安装兼容库,您可以解决此问题。
如果 Vertica 群集使用 HDFS 存储位置,则必须先进行一些额外配置,然后才能还原。请参阅备份和还原 HDFS 存储位置的要求。
HDFS 存储位置仅支持完整备份和还原。您无法在使用 HDFS 存储位置的群集上执行对象备份或还原。
您可以将完整的数据库备份还原到已备份的数据库,或者具有相同架构的备用群集。例如,若要设置测试群集来调查生产群集中的问题,则需要还原到备用群集。
要还原完整数据库备份,必须验证:
数据库为 DOWN 状态。数据库运行期间不能还原完整备份。
所有备份主机可供使用。
备份目录中存在要还原数据的备份。
备份要还原到的群集需具有:
与创建备份所用的群集具有相同的节点数(Enterprise 模式),或至少与主子群集具有相同的节点数(Eon 模式)
与创建备份使用的架构相同(Enterprise 模式或 Eon 模式)
相同的节点名称
目标数据库必须存在于要将数据还原到的群集。
数据库可以完全为空,不含任何数据或架构。
数据库名称必须与备份中的名称匹配
数据库中的所有节点名称均必须与配置文件中的节点名称匹配。
执行还原操作的用户是数据库管理员。
如果您正在还原 Eon 模式数据库,您可能已满足Eon 模式数据库的要求。
您只能使用完整数据库备份还原完整数据库。如果保存了多个备份存档,您可以从最新备份或特定存档进行还原。
从完整数据库备份中还原时,会将每个备份的 OID 注入到完整数据库备份的还原编录中。该编录还接收所有存档。此外,OID 生成器以及当前时期都会设置为当前时期。
此外,您可以将完整备份还原到与您备份的数据库不同的数据库。请参阅将数据库还原到备用群集。
还原 Eon 模式数据库时,即使您还原到新数据库,公共存储也会保留在原始位置。
请勿重新启动原始数据库。使用相同的公共存储运行两个数据库可能会导致数据损坏。
通常,当节点或群集为 DOWN 状态时,您希望将群集返回到其最近状态。此操作需要还原完整的数据库备份。您可以通过在配置文件中标识名称,从存档还原任何完整数据库备份。
要从最近的备份中还原,请将 vbr 还原任务与配置文件一起使用。如果您的 密码配置文件不包含数据库超级用户密码,vbr
会提示您输入该密码。
以下示例显示如何使用 db.ini
配置文件进行还原:
> vbr --task restore --config-file db.ini
Copying...
1871652633 out of 1871652633, 100%
All child processes terminated successfully.
restore done!
如果保存了多个备份,您可以指定要还原的特定存档。要列出已有存档,以便从中选择一个存档进行还原,请使用 vbr \--listbackup
任务并为其指定特定配置文件。请参阅查看备份。
要从存档中还原,请将 --archive
参数添加到命令行。该值是目录名称的 * date_timestamp * 后缀,用于识别要还原的存档。例如:
$ vbr --task restore --config-file fullbak.ini --archive=20121111_205841
--archive
参数识别了在 11-11-2012 (_archive20121111
) 这一天的 205841
(20:58:41) 创建的存档。您只需指定 _archive
后缀,因为配置文件确定了子目录的备份名称,并且 OID 标识符表明了备份是存档。
当还原操作失败时,vbr
可能会在公共存储位置留下额外的文件。如果您使用云中的公共存储,这些额外的文件将耗费您的成本。要移除它们,请重新启动数据库并使用实参 true 调用 CLEAN_COMMUNAL_STORAGE。
Vertica 支持将完整备份还原到备用群集。
此过程类似于从完整备份中还原数据库过程,但具有下列额外的要求:
目标数据库必须:
处于 DOWN 状态。
与源数据库同名。
具有与源群集相同数量的节点。
具有与源群集相同的节点名称。
使用与源数据库相同的编录目录位置。
使用与源数据库相同的端口号。
要将对象级别备份中的所有对象还原到其最初提取时的数据库,请使用 vbr 还原任务以及创建备份所使用的配置文件,如以下示例所示:
$ vbr --task restore --config-file MySchema.ini
Copying...
1871652633 out of 1871652633, 100%
All child processes terminated successfully.
restore done!
数据库必须处于开启状态。
通过配置配置文件中的 objectRestoreMode
参数,您可以指定 Vertica 如何对重复的对象作出反应。
HDFS 存储位置不支持对象级别备份和还原。
与从完整数据库备份进行还原不同,vbr
支持在向群集添加节点后还原对象级别备份。创建对象级别备份时不在群集中的任何节点都不参与还原。您可以在还原后重新平衡群集,从而在新节点之间分配数据。
删除节点、更改节点名称或更改 IP 地址后,不能还原对象级别备份。若尝试在执行此类更改后还原对象级别备份,将导致 vbr
失败并显示以下消息:
Preparing...
Topology changed after backup; cannot restore.
restore failed!
所有对象级别备份和还原事件都被视为 DDL 事件。如果某个表不参与对象级别备份(可能由于节点处于关闭状态),还原备份会对投影产生以下影响:
投影时期重置为 0。
投影必须还原其不具有的任何数据(通过比较时期和其他恢复过程)。
与其他数据库一样,Vertica 事务将遵循严格的锁定协议,以便保持数据完整性。
将对象级别备份还原到处于 UP 状态的群集时,vbr
会首先复制数据并管理存储容器。如有必要,vbr
会拆分容器。此进程不需要任何数据库锁。
完成数据复制任务后,vbr
首先需要一个表对象锁 (O-lock),然后需要一个全局编录锁 (GCLX)。
在某些情况下,当该进程尝试获取表中的 O 锁时,其他数据库操作(例如 DML 语句)会保持正在进行。在这种情况下,vbr
进程会被阻止,直到 DML 语句完成并释放锁为止。依次获得 O-lock 和 GCLX 锁之后,vbr
会阻止其他在同一个表上需要锁的操作。
当 vbr
持有其锁时,将阻止同时修改表。为了允许完成对象级别的还原,将取消数据库系统操作(例如将数据从内存传输到磁盘的 Tuple Mover (TM))。
每个对象级别备份都包含一个数据库编录部分,称为片段。片段包含选定对象、其依赖对象和主体对象。编录片段在结构上类似于数据库编录,但包含一个表示对象信息的子集。可以从编录片段中读取所还原的对象,并使用此类对象来更新全局和局部编录。
已还原备份中的每个对象将在编录中进行更新。如果对象不再存在,vbr
会将该对象从编录中删除。不在备份中的任何依赖对象也将从编录中删除。
vbr
实用程序将使用现有的依赖项验证方法来检查编录,并将还原事件添加到每个已还原表的编录中。该事件还包括发生该事件的时期。如果某个节点缺少还原表事件,则该节点将还原锚定在给定表上的投影。
Vertica 支持将对象复制和还原到目标数据库,但最多支持比当前数据库版本晚一个次要版本。例如,您可以将对象从 11.0.x 数据库复制或还原到 11.1.x 数据库。不同版本的还原或复制过程与相同版本的过程相同。
如果还原或复制的对象需要数据库中不存在的更高版本的 UDx 库,Vertica 将显示以下错误:
ERROR 2858: Could not find function definition
通过在目标数据库中安装兼容库,您可以解决此问题。
执行还原时,如果编录大小大于节点中可用总内存的 5%,则对象级还原可能会失败。在这种情况下,Vertica 建议从备份中还原单个对象。有关详细信息,请参考 还原单个对象。
您可以使用 vbr
从完整或对象级别备份中还原单个表和架构:使用 ‑‑restore-objects
限定 restore
任务,并将要还原的对象指定为以逗号分隔的列表:
$ vbr --task=restore --config-file=filename --restore-objects='objectname[,...]' [--archive=archive-id]
具有以下要求和限制:
数据库必须正在运行,且节点必须处于 UP 状态。
表必须包含其架构名称。
不要在 ‑‑restore-objects
列表的逗号分隔符之前或之后嵌入空格;否则,vbr
将空格解释为对象名称的一部分。
HDFS 存储位置不支持对象级别还原。要还原 HDFS 存储位置,必须执行完整还原。
如果架构具有磁盘配额且还原表将超过配额,则该操作将会失败。
默认情况下,‑‑restore-objects
将从最近的备份中还原指定的对象。您可以使用
‑‑archive
参数从较早的备份中还原。
以下示例使用 db.ini
配置文件,该文件包含数据库管理员的密码:
> vbr --task restore --config-file=db.ini --restore-objects=salesschema,public.sales_table,public.customer_info
Preparing...
Found Database port: 5433
Copying...
[==================================================] 100%
All child processes terminated successfully.
All extract object child processes terminated successfully.
Copying...
[==================================================] 100%
All child processes terminated successfully.
restore done!
当您还原某个对象时,Vertica 不会自动还原任何依赖对象。例如,如果您还原一个包含视图的架构,则 Vertica 不会自动还原这些视图的表。有一个例外:如果数据库表通过外键链接,则必须将其一起还原,但配置文件 vbr
中的
drop_foreign_constraints
设置为 true 时则除外。
通过配置
objectRestoreMode
,您可以指定还原操作如何处理重复对象。默认情况下,它已设置为 createOrReplace
。因此,如果存在重复对象,还原操作将使用存档版本覆盖它。
将对象还原到 Eon 模式数据库时,可能会将不需要的文件留在云存储中。这些文件并不会影响数据库的性能或数据的完整性。但是,它们可能会产生额外的云存储费用。要移除这些文件,请重新启动数据库并使用实参 true 调用 CLEAN_COMMUNAL_STORAGE。
您可以使用还原任务将对象从一个数据库复制到另一个数据库。例如,您可以将表从开发环境“提升”到生产环境。还原到备用群集时,还原单个对象 中所述的所有限制均适用。
要还原到备用数据库,您必须更改用于创建备份的配置文件副本。更改位于 [Mapping] 和 [NodeMapping] 部分。从本质上来说,您为还原操作创建了一个配置文件。vbr
看起来就像目标数据库的备份,但它实际上描述了源数据库的备份。有关示例配置文件,请参阅将对象从备份还原到备用群集。
以下示例使用两个数据库,名为源数据库和目标数据库。源数据库包含一个名为 sales 的表。以下 source_snapshot.ini 配置文件用于备份源数据库:
[Misc]
snapshotName = source_snapshot
restorePointLimit = 2
objectRestoreMode = createOrReplace
[Database]
dbName = source
dbUser = dbadmin
dbPromptForPassword = True
[Transmission]
[Mapping]
v_source_node0001 = 192.168.50.168:/home/dbadmin/backups/
target_snapshot.ini 文件开始为 source_snapshot.ini 的副本。由于 [Mapping] 部分描述了 vbr
运行所在的数据库,因此我们必须更改节点名称以指向目标节点。此外,我们还必须添加 [NodeMapping] 部分并更改数据库名称:
[Misc]
snapshotName = source_snapshot
restorePointLimit = 2
objectRestoreMode = createOrReplace
[Database]
dbName = target
dbUser = dbadmin
dbPromptForPassword = True
[Transmission]
[Mapping]
v_target_node0001 = 192.168.50.151:/home/dbadmin/backups/
[NodeMapping]
v_source_node0001 = v_target_node0001
就 vbr
而言,我们正在从目标数据库的备份中还原对象。而事实上,我们是从源数据库中还原的。
以下命令将 sales 表从源数据库备份还原到目标数据库:
$ vbr --task restore --config-file target_snapshot.ini --restore-objects sales
Starting object restore of database target.
Participating nodes: v_target_node0001.
Objects to restore: sales.
Enter vertica password:
Restoring from restore point: source_snapshot_20160204_191920
Loading snapshot catalog from backup.
Extracting objects from catalog.
Syncing data from backup to cluster nodes.
[==================================================] 100%
Finalizing restore.
Restore complete!
使用还原任务从硬链接本地备份中还原时,其方式与从完整备份中还原的方式相同。如果您使用硬链接本地备份来备份到外部介质,则还需额外执行一些步骤。
当存在完整的硬链接本地备份时,您可以将备份传输到其他存储介质,例如磁带或本地安装的 NFS 目录。将硬链接本地备份传输到其他存储介质可能会复制与硬文件链接关联的数据文件。
将备份文件返回到硬链接本地备份主机时,可以使用其他目录。但在还原备份之前,还必须更改配置文件中的 backupDir
参数值。
完成以下步骤,可从外部介质还原硬链接本地备份:
如果原始备份目录不再存在,在一个或多个本地备份主机节点上,重新创建该目录。
用于还原硬链接备份文件的目录结构必须与创建备份时存在的目录结构完全相同。例如,如果在以下备份目录中创建硬链接本地备份,您可以重新创建该目录结构:
/home/dbadmin/backups/localbak
将备份文件复制到原始备份目录,如配置文件中为每个节点所指定的一样。有关详细信息,请参考 [Mapping](/zh-cn/admin/backup-restore/config-file-reference/mapping/)。
使用以下三个选项之一还原备份:
要还原备份的最新版本,请将备份文件移动到以下目录:
/home/dbadmin/backups/localbak/node_name/snapshotname
若要还原其他备份版本,请将备份文件移动到此目录:
/home/dbadmin/backups/localbak/node_name/snapshotname_archivedate_timestamp
备份文件返回到原始备份目录后,请使用原始配置文件调用 vbr
。验证配置文件是否指定 hardLinkLocal = true
。然后,按如下方式还原备份:
$ vbr --task restore --config-file localbak.ini
对于完整还原,对象拥有备份数据库中的所有者。
执行还原时,Vertica 将数据插入现有数据库对象中。还原默认不会影响被还原对象的所有权或权限。但是,如果还原的对象尚不存在,Vertica 将重新创建该对象。这种情况下,被还原对象由执行还原的用户所有。Vertica 不会还原与被还原对象相关的授予、角色或客户端身份验证。
如果还原对象的存储策略无效,vbr
将应用默认存储策略。由于 HDFS 存储位置、表不兼容以及还原时最小-最大值不可用等原因,还原的存储策略可能会变得无效。
有时,Vertica 会遇到不需要还原的编录对象。当这种情况发生时,Vertica 会为该对象生成一条警告消息,然后继续还原。
假设您具有归用户 Alice 所有的完全备份,包括 Schema1。Schema1 包含 Table1(归 Bob 所有),他最终会将所有权传递给 Chris。用户 dbadmin 会执行还原。以下场景可能会影响这些对象的所有权。
场景 1:
Schema1.Table1 已在自从创建备份后的某个点移除。dbadmin 执行还原时,Vertica 将重新创建 Schema1.Table1。作为执行还原的用户,dbadmin 将取得 Schema1.Table1 的所有权。由于 Schema1 仍存在,Alice 将保留该架构的所有权。
场景 2:
Schema1 及包含的所有对象已移除。当 dbadmin 执行还原时,Vertica 会重新创建架构和所有包含的对象。dbadmin 获得 Schema1 和 Schema1.Table1 的所有权。
场景 3:
Schema1 和 Schema1.Table1 都存在于当前数据库中。当 dbadmin 回退到较早的备份时,对象的所有权仍保持不变。Alice 拥有 Schema1,Bob 拥有 Schema1.Table1。
场景 4:
Schema1.Table1 存在,且 dbadmin 要回退到较早的版本。在执行备份之后,Schema1.Table1 的所有权已更改为 Chris。当 dbadmin 还原 Schema1.Table1 时,Alice 仍为 Schema1 的所有者,Chris 仍为 Schema1.Table1 的所有者。还原不会将 Schema1.Table1 的所有权从 Chris 还原为 Bob。
可使用 vbr
将整个数据库复制到其他 Vertica 群集。此功能可帮助您执行诸如在开发和生产环境之间复制数据库之类的任务。从本质上来说,将数据库复制到另一个群集,是同时在进行备份和还原操作。数据将从源数据库群集中备份,然后通过单个操作还原到目标群集。
rsync
工具将文件从源群集复制到目标群集。HDFS 存储备份和还原以使用快照为基础。HDFS 存储位置中的数据备份至 HDFS 本身。Vertica 不能将数据以与传输至 Linux 群集相同的方式传输至远程 HDFS 群集。
Vertica 编录、数据和临时目录的目录位置在源和目标数据库上必须相同。可使用以下 vsql 查询查看源数据库目录位置。此示例出于演示目的设置了扩展显示,并列出最受关注的各列:node_name
、storage_path
和 storage_usage
。
=> \x
Expanded display is on.
=> select node_name,storage_path, storage_usage from disk_storage;
-[ RECORD 1 ]-+-----------------------------------------------------
node_name | v_vmart_node0001
storage_path | /home/dbadmin/VMart/v_vmart_node0001_catalog/Catalog
storage_usage | CATALOG
-[ RECORD 2 ]-+-----------------------------------------------------
node_name | v_vmart_node0001
storage_path | /home/dbadmin/VMart/v_vmart_node0001_data
storage_usage | DATA,TEMP
-[ RECORD 3 ]-+-----------------------------------------------------
node_name | v_vmart_node0001
storage_path | home/dbadmin/SSD/schemas
storage_usage | DATA
-[ RECORD 4 ]-+-----------------------------------------------------
node_name | v_vmart_node0001
storage_path | /home/dbadmin/SSD/tables
storage_usage | DATA
-[ RECORD 5 ]-+-----------------------------------------------------
node_name | v_vmart_node0001
storage_path | /home/dbadmin/SSD/schemas
storage_usage | DATA
-[ RECORD 6 ]-+-----------------------------------------------------
node_name | v_vmart_node0002
storage_path | /home/dbadmin/VMart/v_vmart_node0002_catalog/Catalog
storage_usage | CATALOG
-[ RECORD 7 ]-+-----------------------------------------------------
node_name | v_vmart_node0002
storage_path | /home/dbadmin/VMart/v_vmart_node0002_data
storage_usage | DATA,TEMP
-[ RECORD 8 ]-+-----------------------------------------------------
node_name | v_vmart_node0002
storage_path | /home/dbadmin/SSD/tables
storage_usage | DATA
.
.
.
注意编录、数据和临时存储的目录路径。这些路径在源数据库中的所有节点上均相同,在目标数据库中也必须相同。
vbr
将覆盖所有现有数据。若要保留目标群集上的现有数据,请在调用 copycluster``vbr
任务之前为目标创建完整数据库备份。
在配置目标群集之前,您需要知道 admintools
为源数据库中所有节点提供的确切名称。
要查看节点名称,请运行如下查询:
=> select node_name from nodes;
node_name
------------------
v_vmart_node0001
v_vmart_node0002
v_vmart_node0003
(3 rows)
您还可以通过从命令行运行 admintools
来查找节点名称。例如,对于 VMart 数据库,您可以输入如下命令:
$ /opt/vertica/bin/admintools -t node_map -d VMART
DATABASE | NODENAME | HOSTNAME
-----------------------------------------------
VMART | v_vmart_node0001 | 192.168.223.xx
VMART | v_vmart_node0002 | 192.168.223.yy
VMART | v_vmart_node0003 | 192.168.223.zz
配置目标以允许源数据库与之连接,然后还原该数据库。目标群集必须满足以下条件:
运行具有与源群集相同版本的热修复程序。例如,如果源群集和目标群集都运行版本 9.1.1-1,您可以还原数据库。
具有与源群集相同数量的节点。
具有与源数据库同名的数据库。目标数据库可以完全为空。
具有与源群集相同的节点名称。NODES 系统表中列出的节点名称在两个群集上必须匹配。
可从源群集访问。
具有相同的数据库管理员帐户,并且所有节点必须允许源群集的数据库管理员通过不需要密码的 SSH 登录。
具有足够的磁盘空间来完成 vbr \--task copycluster
命令。
要将数据库复制到其他群集,您必须专门创建一个配置文件。在该配置文件中,将目标群集中节点的主机名称指定为备份主机。您必须定义 backupHost
;但是,vbr
会忽略 backupDir
选项,并始终将数据存储在目标数据库的编录和数据目录中。
您无法使用 copycluster
命令执行对象级别备份。相反,您必须执行完整数据库备份。
以下示例显示了如何设置 vbr
,从而将 3 节点群集 v_vmart 上的数据库复制到另一个群集 test-host。
[Misc]
snapshotName = CopyVmart
tempDir = /tmp/vbr
[Database]
dbName = vmart
dbUser = dbadmin
dbPassword = password
dbPromptForPassword = False
[Transmission]
encrypt = False
port_rsync = 50000
[Mapping]
; backupDir is not used for cluster copy
v_vmart_node0001= test-host01
v_vmart_node0002= test-host02
v_vmart_node0003= test-host03
您必须在调用 copycluster
之前停止目标群集。
要复制群集,请使用数据库管理员帐户从源数据库中的节点运行 vbr
:
$ vbr -t copycluster -c copycluster.ini
Starting copy of database VMART.
Participating nodes: vmart_node0001, vmart_node0002, vmart_node0003, vmart_node0004.
Enter vertica password:
Snapshotting database.
Snapshot complete.
Determining what data to copy.
[==================================================] 100%
Approximate bytes to copy: 987394852 of 987394852 total.
Syncing data to destination cluster.
[==================================================] 100%
Reinitializing destination catalog.
Copycluster complete!
如果 copycluster 任务被中断,目标群集则会保留已传输的任何数据文件。如果您再次尝试该操作。Vertica 不需要重新发送这些文件。
您可以在组织中将表和架构从一个数据库复制到备用数据库。例如,要在测试、临时和生产群集之间复制表和架构,您可能需要使用对象复制。此外,在发生重要更改(例如加载数据)之后,您可立即复制某些对象,而无需等待下一个计划作业。
您使用 vbr replicate
任务来执行对象复制。
该复制操作不会复制所有对象类型。复制期间未复制的编录对象包括:
用户和角色
授权
访问策略(列和行)
客户端身份验证和配置文件
要复制整个数据库,请使用
copycluster
任务,然后使用 replicate
任务来更新表和架构,而不是执行另一次完整复制。
复制对象通常比导出和导入对象的速度更快。第一次复制对象时会复制整个对象。后续复制操作仅复制自上次复制之后发生更改的数据。Vertica 会复制截至目标数据库当前时期的数据。与 cron 作业一起使用,您可以复制关键对象以创建备份数据库。
在目标数据库出现故障或您计划复制整个数据库的情况下,Vertica 建议您尝试将数据库复制到其他群集。
如果源数据库或目标数据库中的某些节点已关闭,只要这些节点本身仍然可用,您就可以复制对象。此处的 DOWN 节点是指节点上正在关闭、但网络上仍可见的 Vertica 进程。
DOWN 节点对复制任务的影响取决于这些节点处于源数据库还是目标数据库中。
要将对象复制到备用数据库,请从源数据库开始操作,并完成以下步骤:
验证复制要求
编辑 vbr 配置文件
复制对象
监控对象复制
以下要求适用于源数据库和目标数据库:
它们具有与 dbadmin 帐户关联的相同 Linux 用户。
两者中的所有节点都必须为 UP 状态。
在 Enterprise 模式中,它们具有相同数量的节点。
在 Eon 模式中,两个数据库的主子群集具有相同的节点子订阅数。目标数据库与源数据库的主子群集具有相同数量(或更多)的节点。
源群集数据库管理员无需密码,即可通过 SSH 登录所有节点。
将以下参数添加到用于复制对象的配置文件中:
在 [misc] 部分中,添加以下参数:
; Identify the objects that you want to replicate
objects = schema.objectName
在 [misc] 部分中,设置唯一的快照名称。复制任务可以与其他某些 vbr 任务同时运行,但前提是快照名称必须不同。
snapshotName = name
在 [database] 部分中,设置以下参数:
; parameters used to replicate objects between databases
dest_dbName =
dest_dbUser =
dest_dbPromptForPassword =
如果使用的是存储密码,请确保在密码配置文件中配置 dest_dbPassword
参数。
在 [mapping] 部分中,将源节点映射到目标主机:
[Mapping]
v_source_node0001 = targethost01
v_source_node0002 = targethost02
v_source_node0003 = targethost03
要复制对象,请使用 vbr replicate
任务:
vbr -t replicate -c configfile.ini
replicate
任务可以在任一方向上与备份和对象复制任务同时运行。复制不能与要求关闭数据库的任务(完整还原和 copycluster)同时运行。每个并发任务必须具有唯一的快照名称。因此,作为最佳实践,请为每个对象复制创建一个单独的配置文件。如果需要许多表,请考虑复制架构,而不是单个表。
Vertica 支持将对象复制和还原到目标数据库,但最多支持比当前数据库版本晚一个次要版本。例如,您可以将对象从 11.0.x 数据库复制或还原到 11.1.x 数据库。不同版本的还原或复制过程与相同版本的过程相同。
如果还原或复制的对象需要数据库中不存在的更高版本的 UDx 库,Vertica 将显示以下错误:
ERROR 2858: Could not find function definition
通过在目标数据库中安装兼容库,您可以解决此问题。
您可以使用以下方式监控对象复制:
查看源数据库中的 vbr 日志
检查源数据库和目标数据库的数据库日志
在源数据库中查询 REMOTE_REPLICATION_STATUS
您可以使用 vbr
配置和命令行参数 includeObject
s 和 ‑‑include-objects
,分别指定备份、还原和复制操作中要包括的对象。指定操作中要包括的对象之后,您可以选择使用 vbr
配置和命令行参数 excludeObjects
和 ‑‑exclude-objects
,分别指定要从同一操作中排除的一组对象。在这两种情况下,您都可以使用通配符表达式来包括和排除对象组。
vbr.ini
文件和 vbr
命令行参数中均支持通配符。
例如,您可以备份架构 store
中的所有表,然后从备份中排除表 store.orders
以及同一架构中所有名称包含字符串 account
的表:
vbr --task=backup --config-file=db.ini --include-objects 'store.*' --exclude-objects 'store.orders,store.*account*'
任何不带句点 (.
) 字符的字符串模式都表示架构。例如,以下 includeObjects
列表可以匹配任何以字符串 customer
开头的架构名称,以及任何以字母 s
开头的双字符架构名称:
includeObjects = customer*,s?
当 vbr
操作包括架构且架构引用省略任何表引用时,该操作包括该架构的所有表。在这种情况下,您不能从同一架构中排除单个表。例如,以下 vbr.ini
条目无效:
; invalid:
includeObjects = VMart
excludeObjects = VMart.?table?
您可以通过使用模式 schemaname.* 识别架构,以从包含的架构中排除表。在这种情况下,该模式使用通配符 * 明确指定包括该架构中的所有表。在以下示例中,include‑objects
参数包括 VMart 架构中的所有表,然后排除特定表,具体地说,是指排除表 VMart.sales
以及所有包含字符串 account
的 VMart 表:
--include-objects 'VMart.*'
--exclude-objects 'VMart.sales,VMart.*account*'
任何包含句点 (.
) 的模式都代表一个表。例如,在配置文件中,以下 includeObjects
列表匹配表名称 sales.newclients
和同一架构中的任何双字符表名称:
includeObjects = sales.newclients,sales.??
您还可以使用模式 . 来匹配数据库或备份中的所有架构和表。例如,您可以使用以下命令来还原备份中的所有表和架构:
--include-Objects '*.*'
由于 vbr
参数是在命令行中计算,因此您必须将通配符括在单引号中,防止 Linux 误读它们。
您可以将 --dry-run
参数与备份或还原命令一起使用,以测试任何模式的结果。包含 --dry-run
的命令不会影响您的数据库。相反,vbr
会在不执行命令的情况下,显示命令的结果。有关 --dry-run
的详细信息,请参考 vbr 引用。
您可以使用配置文件中的 includeObjects
和 excludeObjects
参数来识别对象备份任务中要包括的对象。典型的配置文件可能包含以下内容:
[Misc]
snapshotName = dbobjects
restorePointLimit = 1
enableFreeSpaceCheck = True
includeObjects = VMart.*,online_sales.*
excludeObjects = *.*temp*
在此示例中,备份将包括来自 VMart 和 online_sales
架构的所有表,同时排除属于任何架构且名称中包含字符串“temp”的任何表。
在评估包括的对象之后,vbr
会评估排除对象并从包括的集中移除它们。例如,如果您包括 schema1.table1,然后排除了 schema1.table1,则该对象将被排除。如果任务中没有包括其他对象,则任务将失败。通配符也是如此。如果排除模式移除了所有包括的对象,则任务失败。
您可以使用 ‑‑include-objects
和 ‑‑exclude-objects
参数来识别还原任务中要包括的对象。
‑‑dry-run``vbr
参数测试通配符还原的影响。
备份时,vbr
会在评估包括的对象之后评估排除对象,并从包括的集中移除排除对象。如果没有剩余对象,则任务失败。
典型的还原命令可能包含此内容。(为了便于阅读,文档中包含换行。但事实上,这是一条完整的命令。)
$ vbr -t restore -c verticaconfig --include-objects 'customers.*,sales??'
--exclude-objects 'customers.199?,customers.200?'
此示例包括架构客户,减去名称匹配 199 和 200 加一个字符的任何表,以及所有匹配“sales”加两个字符的任何架构。
另一个典型的还原命令可能包含此内容。
$ vbr -t restore -c replicateconfig --include-objects '*.transactions,flights.*'
--exclude-objects 'flights.DTW*,flights.LAS*,flights.LAX*'
此示例包括任何名为 transactions 的表(无论架构如何),以及任何以 DTW、LAS 或 LAX 开头且属于架构 flights 的表。尽管这些三个字母的机场代码在示例中大写,但 vbr
不区分大小写。
vbr
提供了几个与管理备份相关的任务:列出备份、检查备份的完整性、选择性删除备份等。此外,vbr
还提供了一些参数,允许您限制其使用系统资源。
您可以通过以下三种方式查看备份:
使用 vbr
,列出驻留在本地或远程备份主机上的备份(需要配置文件)。
使用 DATABASE_BACKUPS 系统表,查看有关备份的历史信息。因为 database_backups
系统表包含历史信息,因此当您删除备份时不会更新此系统表
打开 vbr 日志文件检查备份的状态。该日志文件存在于您从中运行 vbr 的节点上,且位于 vbr 配置参数 tempDir 指定的目录/tmp/vbr
。
若要列出备份主机上的备份,请将 vbr \--task listbackup
与特定配置文件组合使用。以下示例显示了如何使用完整备份配置文件 bak.ini
列出备份:
$ vbr --task listbackup --config-file /home/dbadmin/bak.ini
下表包含有关从 vbr
listbackup 任务返回的每个输出列的信息:
以下示例显示了一个从三节点群集到单个备份主机 bkhost 的完整备份列表。
backup backup_type epoch objects include_patterns exclude_patterns nodes (hosts) version file_system_type
bak_20160414_134452 full 749 v_vmart_node0001(bkhost), v_vmart_node0002(bkhost), v_vmart_node0003(bkhost) v10.0.0 [Linux]
bak_20160413_174544 full 659 v_vmart_node0001(bkhost), v_vmart_node0002(bkhost), v_vmart_node0003(bkhost) v10.0.0 [Linux]
将 --list-all
参数与 listbackup
任务一起使用,查看指定配置文件中列出的主机和路径上存储的所有快照的列表。
$ vbr --task listbackup --list-all --config-file /home/dbadmin/Nightly.ini
以下示例显示了使用配置文件 Nightly.ini 的 --list-all
任务。该配置文件引用主机 doca01、doca02 和 doca03 以及路径 /vertica/backup
。输出显示这些位置不仅包含使用 Nightly 创建的备份,而且还包含使用名为 Weekly.ini 的配置文件创建的备份。
backup backup_type epoch objects include_patterns exclude_patterns nodes(hosts) version file_system_type
Weekly_20170508_183249 full 3449 vmart_1(doca01), vmart_2(doca01), vmart_3(doca01) v10.0.0 [Linux]
Weekly_20170508_182816 full 2901 vmart_1(doca01), vmart_2(doca02), vmart_3(doca03) v10.0.0 [Linux]
Weekly_20170508_182754 full 2649 vmart_1(doca01), vmart_2(doca02), vmart_3(doca03) v10.0.0 [Linux]
Nightly_20170508_183034 object 1794 sales_schema vmart_1(doca01), vmart_2(doca02), vmart_3(doca03) v10.0.0 [Linux]
Nightly_20170508_181808 object 1469 sales_schema vmart_1(doca01), vmart_2(doca02), vmart_3(doca03) v10.0.0 [Linux]
Nightly_20171117_193906 object 173 sales_schema vmart_1(doca01), vmart_2(doca02), vmart_3(doca03) v10.0.0 [Linux]
您还可以将 --json
和 --list-output-file
参数与 listbackup
任务一起使用,以 JSON 分隔格式将相同的内容输出到显示器或输出文件。有关详细信息,请参考 vbr 引用。
可使用以下查询列出有关备份的历史信息。objects
列列出了在对象级别备份中包括了哪些对象。请不要在还原存档时使用 backup_timestamp
值。相反,应在还原存档时使用 vbr \--task listbackup
提供的值。
=> SELECT * FROM v_monitor.database_backups;
-[ RECORD 1 ]----+------------------------------
backup_timestamp | 2013-05-10 14:41:12.673381-04
node_name | v_vmart_node0003
snapshot_name | schemabak
backup_epoch | 174
node_count | 3
file_system_type | [Linux]
objects | public, store, online_sales
-[ RECORD 2 ]----+------------------------------
backup_timestamp | 2013-05-13 11:17:30.913176-04
node_name | v_vmart_node0003
snapshot_name | kantibak
backup_epoch | 175
node_count | 3
file_system_type | [Linux]
objects |
-[ RECORD 13 ]---+------------------------------
backup_timestamp | 2013-05-16 07:02:23.721657-04
node_name | v_vmart_node0003
snapshot_name | objectbak
backup_epoch | 180
node_count | 3
file_system_type | [Linux]
objects | test, test2
-[ RECORD 14 ]---+------------------------------
backup_timestamp | 2013-05-16 07:19:44.952884-04
node_name | v_vmart_node0003
snapshot_name | table1bak
backup_epoch | 180
node_count | 3
file_system_type | [Linux]
objects | test
-[ RECORD 15 ]---+------------------------------
backup_timestamp | 2013-05-16 07:20:18.585076-04
node_name | v_vmart_node0003
snapshot_name | table2bak
backup_epoch | 180
node_count | 3
file_system_type | [Linux]
objects | test2
Vertica 可以确认备份文件以及识别这些文件的清单的完整性。备份完整性检查默认将结果输出到命令行。
quick-check
任务会从配置文件指定的备份位置收集所有备份元数据,并将该元数据与备份清单进行比较。快速检查不会验证对象本身。此任务会输出备份位置的对象与备份清单中列出的对象之间的任何差异的例外列表。
使用以下格式执行快速检查任务:
$ vbr -t quick-check -c configfile.ini
例如:
$ vbr -t quick-check -c backupconfig.ini
full-check
任务会对照文件系统元数据验证备份清单中列出的所有对象。全面检查包含与快速检查相同的步骤。您可以包括可选的 --report-file
参数,以便将结果输出到分隔的 JSON 文件中。此任务将输出标识以下不一致性的例外列表:
不完整的还原点
损坏的还原点
缺少的备份文件
未引用的文件
使用以下模板执行完整检查任务:
$ vbr -t full-check -c configfile.ini --report-file=path/filename
例如:
$ vbr -t full-check -c backupconfig.ini --report-file=logging/fullintegritycheck.json
Vertica 可以重建备份清单,并移除不需要的备份对象。
quick-repair
任务将基于备份位置中包含的清单来重新构建备份清单。
使用以下模板执行快速修复任务:
$ vbr -t quick-repair -c configfile.ini
collect-garbage
任务将重新构建备份清单,并删除清单中没有的备份对象。您可以包括可选的 --report-file
参数,以便将结果输出到分隔的 JSON 文件中。
使用以下模板执行垃圾回收任务:
$ vbr -t collect-garbage -c configfile.ini --report-file=path/filename
您可以使用 vbr
移除现有的备份和还原点。使用 remove
任务时,vbr
将更新受移除影响的清单,并维持其完整性。如果备份存档包含多个还原点,移除一个还原点不会影响其他还原点。移除最后一个还原点时,vbr
会完全移除备份。
使用以下表单执行移除任务:
$ vbr -t remove -c configfile.ini --archive timestamp
可以使用存档参数移除多个还原点。要获取特定还原点的时间戳,请使用 listbackup 任务。
要移除多个还原点,请使用逗号分隔符:
--archive="restore_point1, restore_point2"
要移除包含范围的还原点,请使用冒号:
--archive="restore_point1: restore_point2"
要移除所有还原点,请指定 all
的存档值:
--archive="all"
以下示例展示了如何从现有备份中移除还原点的过程:
$ vbr -t remove -c backup.ini --archive 20160414_134452
Removing restore points: 20160414_134452
Remove complete!
vbr
配置参数之一是 tempDir。此参数指定 vbr
写入日志文件和其他某些临时文件(大小可忽略不计)的数据库主机位置。默认位置为每个数据库主机上的 /tmp/vbr
目录。可通过在配置文件中指定其他路径来更改此默认位置。
临时存储目录还包含用于描述进度、吞吐量和每个节点遇到的任何错误的本地日志文件。每次运行 vbr
时,脚本均会创建一个单独的日志文件,每个文件均以时间戳命名。使用默认设置时,日志文件通常在每个备份的每个节点占用大约 4 KB 的空间。
vbr
日志文件不会自动移除,因此您必须根据需要手动移除较旧的日志文件。
默认情况下,vbr
允许单个 rsync 连接(用于 Linux 文件系统)、10 个并发线程(用于云存储连接)以及无限带宽(用于任何备份或还原操作)。您可以在配置文件中更改这些值。有关这些参数的详细信息,请参阅配置文件参考。
您可能需要增加并发连接数。如果有很多 Vertica 文件,更多连接可以显著提升性能,因为每个连接可以增加并发文件传输数。
有关详细信息,请参考 [transmission] 中的以下参数:
total_bwlimit_backup
total_bwlimit_restore
concurrency_backup
concurrency_restore
另请参考 [CloudStorage] 中的以下参数:
cloud_storage_concurrency_backup
cloud_storage_concurrency_restore
您可以通过 total_bwlimit_backup
和 total_bwlimit_restore
数据传输参数限制网络带宽使用。有关详细信息,请参考 [transmission]。
这些提示有助于您避免与使用 Vertica 进行备份和还原相关问题,并对产生的任何问题进行故障排除。
vbr
日志与 Vertica 日志彼此分开。默认位置为 /tmp/vbr,但您可以通过设置 vbr 配置参数 tempDir 来更改此位置。 vbr
日志不包含在 Scrutinize 报告中。
如果在日志中找不到错误或意外结果的解释,请尝试提高日志记录级别。您可以使用 vbr
命令行上的 --debug
选项设置级别。指定一个从 0(默认值)到 3(最详细)的整数值。例如:
$ vbr -t backup -c full_backup.ini --debug 3
随着您增加日志记录级别,日志的文件大小也会增加。
如果备份主机上的磁盘空间不足,或者 vbr
无法访问所有备份主机,则备份将失败。检查每个备份主机上是否具有足够的空间,能否通过 ssh 访问每个主机。
有时,vbr
会将 rsync 进程留在数据库或备份节点上运行。这些进程可能会干扰新进程。如果您在控制台中收到 rsync 错误,请查找失控进程并将其终止。
确认对象复制中已排除所有 DOWN 节点。
如果不排除 DOWN 节点,复制会失败并出现以下错误:
Error connecting to a destination database node on the host <hostname> : <error> ...
还原存档时,您可能会看到以下错误:
$ vbr --task restore --archive prd_db_20190131_183111 --config-file /home/dbadmin/backup.ini
IOError: [Errno 2] No such file or directory: '/tmp/vbr/vbr_20190131_183111_s0rpYR/prd_db.info'
问题在于:存档名称的格式不正确。按照还原存档中所述,仅指定识别待还原存档目录名称的日期/时间戳后缀。例如:
$ vbr --task restore --archive 20190131_183111 --config-file /home/dbadmin/backup.ini
对含有 HDFS 存储位置的群集进行备份时,您可能会看到以下类似错误:
ERROR 5127: Unable to create snapshot No such file /usr/bin/hadoop:
check the HadoopHome configuration parameter
导致此错误的原因是,备份脚本不能备份 HDFS 存储位置。您必须配置 Vertica 和 Hadoop,使备份脚本能够备份这些位置。请参阅备份和还原 HDFS 存储位置的要求。
HDFS 存储位置不支持对象级别备份和还原。您必须使用完整备份和还原。
执行跨端点操作时,如果无法为公共存储 (VBR_COMMUNAL_STORAGE_ENDPOINT_URL
) 指定端点 URL,则会看到连接错误。如果端点丢失,但您已为公共存储指定凭据,则 vbr
会尝试使用这些凭据来访问 AWS。此访问失败,因为这些凭据是用于您的内部部署存储,而不是 AWS。执行跨端点操作时,请验证是否已正确设置 Eon 模式下的跨端点备份 中所述的所有环境变量。
vbr
让您能够备份和还原完整数据库,或者一个或多个相关的架构和表对象。您还可以复制一个群集以及列出之前创建的备份。
大多数任务不能同时运行;但是,复制任务既可彼此同时运行,也可以与备份任务同时运行。并发任务不能使用相同的快照名称。
vbr
位于 Vertica 二进制目录中(在大多数安装中,为
/opt/vertica/bin/vbr
)。
/opt/vertica/bin/vbr { command }
[ --archive timestamp ]
[ --config-file file ]
[ --debug level]
[ --nodes node[,...] ]
[ --showconfig ]
command 是以下命令之一:
‑‑help | -h
‑‑showconfig
‑‑task | -t { backup | collect-garbage | copycluster | full-check | init | listbackup | quick-check | quick-repair | remove | replicate | restore }
backup
根据配置文件规范创建完整数据库或对象级别备份。
collect-garbage
重建备份清单并删除备份位置中的任何未引用对象。
copycluster
(Eon 模式数据库不支持)将数据库复制到另一个 Vertica 群集。
full-check
根据文件系统元数据验证备份清单中列出的所有对象,然后输出缺失的和未引用的对象。
init
创建新的备份目录,或者准备现有的备份目录以供使用,并创建必要的备份清单。您必须在第一次创建备份目录之前执行此任务。
listbackup
显示与您提供的配置文件关联的现有备份。查看此显示以获取要还原的备份的名称。
quick-check
确认所有备份对象均显示在备份清单中,然后输出备份位置中的对象与备份清单中列出的对象之间的差异。
quick-repair
根据存储位置和对象构建替换备份清单。
remove
移除指定备份或还原点。
replicate
将对象从一个群集复制到备用群集。此任务可以与备份和其他复制任务同时运行。
restore
还原完整数据库备份或对象级别数据库备份;需要创建备份的配置文件。
‑‑archive timestamp
‑‑task restore
和 ‑‑task remove
命令一起使用,待还原或移除的备份的时间戳:
> vbr ‑‑task restore ‑‑config-file myconfig.ini ‑‑archive=20160115_182640
-c file ‑‑config-file file
vbr
的位置的绝对或相对路径的配置文件。如果文件不存在,则会发生错误且 vbr
无法继续。‑‑nodes node[,...]
vbr
任务的节点的逗号分隔列表。列出的节点与配置文件的映射部分中的名称匹配。
‑‑debug level
vbr
提供的调试消息的级别(从 0 到 3)。级别 3 表示详细,而级别 0 为默认值,表示没有消息。‑‑report-file path/filename
‑‑restore-objects objects
‑‑exclude-objects
。‑‑s3-force-init
‑‑task init
命令一起使用,当标识/锁定文件不匹配时,强制 init
任务在 S3 存储目标上成功运行。‑‑showconfig
vbr
开始之前以原始 JSON 格式显示。‑‑list-all
‑‑task listbackup
命令一起使用,显示指定配置文件中列出的主机和路径上存储的所有备份的列表。‑‑json
‑‑task listbackup
命令一起使用,显示指定配置文件中列出的主机和路径上存储的所有备份的 JSON 分隔列表。‑‑list-output-file path/filename
‑‑task listbackup
命令一起使用,输出一个文件,其中包含指定配置文件中列出的主机和路径上存储的所有备份的 JSON 分隔列表。‑‑dry-run
‑‑task
命令一起使用,用于备份、还原和复制任务,且对指定的命令进行测试运行,而不实际执行该任务。您可以使用此命令来评估特定 vbr 命令的影响,而不实际执行该命令。例如,您可以查看潜在备份的大小,或该备份中包含的对象。使用 dry-run
参数执行的任何任务均不会影响您的数据库。‑‑include-objects include-list
不能将 ‑‑restore-objects
参数与此参数一起使用。
‑‑exclude-objects exclude-list
您可以使用 ‑‑include-objects
指定一组对象,然后使用 ‑‑exclude-objects
从该集合中移除对象。使用逗号分隔多个对象和通配符模式。
不能将 ‑‑restore-objects
参数与此参数一起使用。
‑‑restore‑objects='restore‑list'
‑‑include-objects
和 ‑‑exclude-objects
结合使用时则无效。
有关用法详细信息,请参阅还原单个对象。
要取消备份,请使用 Ctrl+C 或向运行 vbr
的 Python 进程发送 SIGINT。 vbr
在复制完数据后停止备份进程。使用 Ctrl+C 取消 vbr
备份时,将立即关闭会话。
中断的备份进程生成的文件仍在目标备份位置目录中。下一个备份进程会从中断的进程停止的位置开始。
备份操作是原子的,因此中断备份操作不会影响上一个备份。Vertica 仅将上一个备份替换为备份数据库的最后一个步骤。
restore
或 copycluster
操作会覆盖数据库编录目录。中断其中任一进程会使数据库不可用,直至您重新启动该进程并允许其完成。
vbr 配置文件将备份设置分为多个部分,列在对应部分的标题下。例如 [Database]
和 [CloudStorage]
,它们分别包含数据库访问设置和云存储位置设置。各部分可以按任何顺序显示且可重复。例如,多个 [Database]
部分。
[CloudStorage] 部分替换了早期版本中的 [S3] 部分(现已弃用)。同样,云存储特定的配置变量替换了等效的 S3 配置变量。
请勿在同一个配置文件中包含 [S3] 和 [CloudStorage] 部分;否则,vbr 将使用 [S3] 配置设置且忽略 [CloudStorage] 设置,这可能会产生意外结果。
设置在受支持的云存储位置中备份数据存储所需的选项。
[CloudStorage] 和 [Mapping] 配置部分相互排斥。如果两者均包含,则备份将会失败并显示以下错误消息:
Config has conflicting sections (Mapping, CloudStorage), specify only one of them.
要使用本地 NFS 文件系统,请指定以下值:
cloud_storage_backup_file_system_path = []:path
要使用主机,请指定以下值:
cloud_storage_backup_file_system_path = [host-name]:path
备份到云存储时,所有节点均会备份到相同的云存储桶。执行备份之前,您必须在云存储中创建备份位置。以下示例指定 S3 存储的备份路径:
cloud_storage_backup_path = s3://backup-bucket/database-backup-path/
备份到 HDFS 位置时,如果使用线路加密,请使用 swebhdfs
协议。如果不使用线路加密,请使用 webhdfs
协议。以下示例使用加密:
cloud_storage_backup_path = swebhdfs://backup-nameservice/database-backup-path/
*pem
) 文件必须位于数据库群集所有节点中的同一路径上。
例如:
cloud_storage_ca_bundle = /home/user/ssl-folder/ca-bundle
默认值: 10
默认值: 10
默认值: 10
sse
值。有关详细信息,请参阅对 Amazon S3 上的备份进行加密。
此值采用以下形式:
cloud_storage_encrypt_at_rest = sse
如果从以下位置备份或还原,则必须将此参数设置为 true:
Amazon EC2 群集
Google Cloud Storage (GCS)
Eon 模式下的内部部署数据库(使用 HDFS 公共存储和线路加密)。
默认值: true
此值采用以下形式:
cloud_storage_sse_kms_key_id = key-id
设置用于访问数据库的选项。
Micro Focus 建议您提供数据库名称。
最佳做法:如果 dbUseLocalConnection 设置为 true,则将 dbPromptForPassword 设置为 false。
默认值: true
默认值: 当前用户名
如果启用 dbUseLocalConnection,则必须向 vbr 用户(通常是 dbadmin)授予身份验证方法,其中方法类型设置为 trust,访问权限设置为“本地 (local)”:
=> CREATE AUTHENTICATION h1 method 'trust' local;
=> GRANT AUTHENTICATION h1 to dbadmin;
默认值:false
仅当复制备用群集中的对象时,才需设置目标数据库参数:
vbr 配置文件有一个 [Mapping] 部分,用于指定备份中包含的所有数据库节点。它还包括每个节点的备份主机和目录。如果正在将对象复制到备用数据库,[Mapping] 部分会将目标数据库节点映射到源数据库备份位置。
如果您编辑现有配置文件,并以当前样式添加一个映射,新部分必须组合所有现有映射的信息。
与其他配置文件部分不同,[Mapping] 部分不使用命名参数。相反,它使用以下格式的条目:
dbNode = backupHost:backupDir
下表介绍了这三个元素。
v_dbname_node000int
要在群集中查找数据库节点名称,请查询 NODES 系统表的 node_name
列。
backupHost
名称与 dbNode
不同。copycluster
命令使用此值来确定目标数据库节点主机名称。
运行具有 --task backup
选项的 vbr 时,目录已经存在
可由用于运行 vbr 的用户帐户写入。
专用于正在备份的数据库。多个数据库不能共享同一个备份目录。
此位置的文件系统支持 fcntl lockf
文件锁定。
例如:
[Mapping]
v_sec_node0001 = pri_bsrv01:/archive/backup
v_sec_node0002 = pri_bsrv02:/archive/backup
v_sec_node0003 = pri_bsrv03:/archive/backup
vbr 不支持将特殊的 localhost 名称作为备份主机。要将数据库节点备份到其自己的磁盘,在配置文件的 [Mapping] 部分中对主机名使用空方括号。
[Mapping]
NodeName = []:/backup/path
以下示例显示了用于指定要备份的单个节点的 [Mapping] 部分:v_vmart_node0001
。该节点被分配到备份主机 srv01
以及备份目录 /home/dbadmin/backups
。尽管备份的是单节点群集,并且备份主机和数据库节点是同一个系统,但是您可以分开指定它们。
指定备份主机和目录,并使用冒号 (:
) 作为分隔符:
[Mapping]
v_vmart_node0001 = srv01:/home/dbadmin/backups
要还原备用数据库,请按如下所示添加映射信息:
[Mapping]
targetNode = backupHost:backupDir
例如:
[Mapping]
v_sec_node0001 = pri_bsrv01:/archive/backup
v_sec_node0002 = pri_bsrv02:/archive/backup
v_sec_node0003 = pri_bsrv03:/archive/backup
本部分收集了一些基本设置,其中包括备份的名称和位置。本部分还会指出您是否正在保留多个备份文件(通过 restorePointLimit 参数指定)。
如果 dbUseLocalConnection 设置为 true,则会忽略此参数。
vbr 将多个备份保存到同一个位置,这些备份可通过硬链接进行共享。在这种情况下,listbackup 会显示带有唯一时间的通用备份前缀和日期后缀:
my_archive20111111_205841
默认值: 1
a–z
A–Z
0-9
连字符 (-)
下划线 (_)
此系列中的每次迭代(直到 restorePointLimit)均包含 snapshotName
和备份时间戳。每个系列的备份均应有描述性的唯一快照名称。完整备份和对象级别备份不能共享名称。对于大多数 vbr 任务,snapshotName
在诊断和系统表中充当有用的标识符。对于对象还原和复制任务,snapshotName
用于在共存模式操作中构建架构名称。
默认值: snapshotName
tmp
路径在所有数据库群集节点上必须相同。当 vbr 将文件从源群集节点复制到目标备份位置时,它会将此目录用作日志文件、锁定文件和其他簿记信息的临时存储。此外,vbr 还会将备份日志写入此位置。
位于此位置的文件系统必须支持 fcntl lockf (POSIX) 文件锁定。
默认值: /tmp/vbr
默认值:false
默认值: true
此参数仅可与 includeObjects 一起使用。
如果 vbr 操作包括多个 HA HDFS 群集,请使用以冒号分隔的列表,提供每个 HA HDFS 群集 XML 配置文件的目录路径。例如:
hadoop_conf_dir = path/to/xml-config-hahdfs1:path/to/xml-config-hahdfs2
此值必须与安装期间创建的引导文件中设置的 HadoopConfDir 值匹配。
此值必须与安装期间创建的引导文件中设置的 KerberosKeytabFile 值匹配。
此值必须与安装期间创建的引导文件中设置的 KerberosRealm 值匹配。
此值必须与安装期间创建的引导文件中设置的 KerberosServiceName 值匹配。
默认值: vertica
createOrReplace
:vbr 创建任何不存在的对象。如果对象已存在,vbr 会使用存档中的版本覆盖该对象。
create
:vbr 创建任何不存在的对象且不替换现有对象。如果正在还原的对象确实存在,则还原失败。
coexist
:vbr 创建每个对象的还原版本,其名称格式如下:
backup_timestamp_objectname
此方法允许现有对象和还原对象同时存在。如果附加的信息导致架构名称超过最大长度 128 个字符,则 Vertica 会截断该名称。可通过查询系统表 TRUNCATED_SCHEMATA 对原始架构名称执行反向查找。
在所有模式下,vbr 会还原当前时期的数据。对象还原模式设置不会应用于备份和完整还原。
默认值: createOrReplace
您可以按如下方式指定对象:
以
schema.objectname
形式指定表名称。例如,要从架构“finance”创建表“customers”的备份,请输入: finance.customers
如果公共表和架构具有相同的名称,则 vbr 仅备份架构。请使用
schema.objectname
约定,以避免混淆。
对象名称可包括 UTF-8 字母数字字符。对象名称不能包括转义字符、单引号 ('
) 或双引号 ("
) 字符。
使用反斜线 () 后跟一个十六进制值指定非字母数字字符。例如,如果表名称为 my table
(my
后跟一个空格字符和 table
),请按以下方式输入对象名称:
objects=my\20table
如果对象名称包含句点,请用双引号将名称括起来。
vbr
会独占使用节点映射部分,以将对象从一个数据库的备份还原到另一个数据库。请确保更新配置文件的 [Mapping] 部分,将目标数据库节点指向源备份位置。目标数据库必须具有至少与源数据库相同数量的“开启 (Up)”节点。
使用以下格式指定节点映射:
source_node = target_node
例如,可以使用以下映射将内容从一个 4 节点数据库还原至另一个 4 节点数据库。
[NodeMapping]
v_sourcedb_node0001 = v_targetdb_node0001
v_sourcedb_node0002 = v_targetdb_node0002
v_sourcedb_node0003 = v_targetdb_node0003
v_sourcedb_node0004 = v_targetdb_node0004
有关完整示例,请参阅将数据库还原到备用群集。
在使用备份主机时设置用于传输数据的选项。
增加线程数可向备份任务分配更多 CPU 资源,且可增加远程备份使用的带宽量。在很大程度上,此设置的最佳值取决于您的特定配置和要求。如果值高于 16,则不会带来额外的益处。
默认值: 1
增加线程数可向删除任务分配更多 CPU 资源,且可增加远程备份的删除操作使用的带宽量。此设置的最佳值取决于您的特定配置和要求。
默认值: 16
增加线程数可向还原任务分配更多 CPU 资源,且可增加远程备份的还原操作的带宽量。在很大程度上,此设置的最佳值取决于您的特定配置和要求。如果值高于 16,则不会带来额外的益处。
默认值: 1
默认值:false
对于硬链接本地备份,忽略配置文件中的此参数。如果在同一个配置文件中将 encrypt 和 hardLinkLocal 都设置为 true,则 vbr 会发出警告并忽略加密。
默认值:false
有关用法的详细信息,请参阅硬链接的完整备份/还原。
默认值:false
默认值: 50000
此值允许的总网络负载为节点数乘以此参数的值。例如,群集节点数为 3 且 total_bwlimit_backup 值为 100,则网络流量为 300K 字节/秒。
默认值: 0
此值允许的总网络负载为节点数乘以此参数的值。例如,群集节点数为 3 且 total_bwlimit_restore
值为 100,则网络流量为 300K 字节/秒。
默认值: 0
为了提高安全性,请将密码存储在密码配置文件中,然后限制该文件的读取访问权限。将 vbr 配置文件中的 passwordFile 参数设置为此文件。
所有密码配置参数均位于该文件的 [Passwords] 部分中。
请参阅密码文件。
硬件或软件问题可能会迫使群集中的节点发生故障。在这种情况下,一个或多个节点离开数据库。您必须先恢复这些故障节点,然后它们才能重新联接群集并恢复正常操作。
数据库中出现故障节点会影响数据库的运行方式。如果企业模式数据库的 K-safety 为 0,则丢失任何节点都会导致数据库关闭。Eon 模式数据库的 K-safety 通常不为 0(请参阅 Eon 模式数据库中的数据完整性和高可用性)。
在 K-safety 为 1 或更大的任一模式下的数据库中,数据库在丢失节点后继续正常运行。但是,数据库的性能会受到影响:
在企业模式下,另一个节点使用故障节点的数据副本替代故障节点。此节点执行的工作量必须多达通常工作量的两倍。诸如查询之类的操作将花费更长的时间,因为群集的其余部分都在等待此节点完成。
在 Eon 模式下,另一个节点将替代故障节点。Eon 模式数据库中的节点不像企业模式数据库中的节点那样维护伙伴实例投影。替代故障节点的节点将从公共存储中检索故障节点的数据以处理查询。它不会将该数据存储在存储库中。如果必须从公共存储中检索所有数据,则除了节点必须执行更多工作之外,还会减慢查询的处理速度。故障节点的性能影响通常仅限于包含它的子群集。
由于这些性能影响,您应当尽快恢复故障节点。
如果过多的数据库节点发生故障,您的数据库将失去维护 K-safety 或 仲裁的能力。在 Eon 模式数据库中,丢失 主节点也可能导致丢失 主分片覆盖率。在其中的任何情况下,数据库都会停止正常操作以防止数据损坏。数据库对 K-safety 或仲裁丢失采取的响应方式取决于它的模式:
在企业模式下,数据库将关闭,因为它无法访问它的所有数据。
在 Eon 模式下,数据库继续以只读模式运行。用来更改全局编录的操作(例如插入数据或更改表架构)将失败。但是,可以在仍然具有分片覆盖率的任何子群集上运行查询。请参阅只读模式。
要使数据库恢复正常运行,必须还原故障节点并恢复数据库。
当您重新启动故障节点或数据库时,Vertica 开始执行数据库恢复过程。K-safe 数据库的恢复模式取决于故障类型:
数据库中的一个或多个节点出现故障,但数据库继续正常运行。请参阅恢复故障节点。
在数据库丢失一个或多个节点后,数据库管理员正常关闭数据库。请参阅正常关闭后恢复
在仲裁或主分片覆盖率丢失后,Eon 模式数据库进入只读模式。请参阅恢复只读 Eon 模式数据库。
企业模式数据库由于仲裁或 K-safety 丢失而非正常关闭,或者任一模式下的数据库由于出现站点级别的故障而关闭。请参阅非正常关闭后恢复
在前三种情况下,节点会在您解决其故障后自动重新联接数据库;在第四种情况(非正常关闭)下,必须手动干预才能恢复数据库。以下几个部分更为详细地讨论了这些情况。
如果恢复操作会导致表或架构超出其磁盘配额,则恢复操作仍继续进行。然后,您必须减少磁盘使用量或增加配额,才能执行其他消耗磁盘空间的操作。有关详细信息,请参阅磁盘配额。
数据库中的一个或更多节点出现故障。但是,数据库维护了仲裁和 K-safety,因此它继续运行而不会中断。
通过使用以下方法在故障节点上重新启动 Vertica 进程来恢复这些节点:
admintools 在主机上重新启动 Vertica (Restart Vertica on Host) 选项。
管理控制台中 管理 > 子群集 (Manage > Subcluster) 页面上的上的“启动 (start)”或“重新启动 (restart)”按钮。请参阅在 MC 中启动、停止和重新启动节点。
当重新启动的节点从其他节点恢复它们的数据时,它们的状态被设置为 RECOVERING。除了最后的短暂时间以外,恢复阶段对数据库事务处理没有影响。完成恢复后,重新启动的节点状态更改为 UP。
在节点丢失后,管理员正常关闭数据库。要进行恢复:
解决所有导致节点主机关闭的硬件或系统问题。
重新启动数据库。请参阅启动数据库。
重新启动时,在关闭之前状态为 UP 的所有节点将恢复 UP 状态。如果数据库在关闭时包含一个或多个故障节点,但它们现在可用,它们会按照上一部分中的说明开始恢复过程。
处于 Eon 模式的数据库丢失了足够多 主节点,这使其进入只读模式。为了使数据库恢复正常操作,请重新启动故障节点。请参阅从只读模式恢复。
如果非正常关闭,Vertica 无法完成正常关闭过程。非正常关闭的原因包括:
企业模式数据库中的关键节点出现故障,导致部分数据库数据不可用。数据库会立即关闭以防止潜在的数据损坏。
电源故障等站点范围内的事件导致所有节点重启。
由于软件或硬件故障而导致节点上的 Vertica 进程退出。
非正常关闭会使数据库处于不一致状态 — 例如在出现故障时,Vertica 可能正在将数据写入到磁盘,因此该过程会处于未完成状态。重新启动数据库时,Vertica 会确定无法正常启动,并使用 上一个完好的时期确定数据上一次在所有节点上保持一致的时间。Vertica 会提示您接受使用建议的时期进行恢复。如果您接受,数据库将恢复并且所有在“上一个完好的时期”之后发生更改的数据都将丢失。如果您不接受,则数据库不会启动。
您也可以选择不接受建议的时期,而是从备份中恢复。您还可以通过管理工具的“高级菜单 (Advanced Menu)”选项“将数据库回退至上一个完好的时期 (Roll Back Database to Last Good Epoch)”选择一个早于“上一个完好的时期”的时期。在一些特殊情况下,这是非常有用的 — 例如在批量加载期间出现故障,这种情况下重新启动整个批次会更容易,即使必须重复完成部分工作。在大多数情况下,您应该接受建议的时期。
当移动 ROS 容器时,会更新源投影和目标投影的检查点时期 (CPE)。所有存储容器(例如 ROS 容器)的开始和结束时期都改为提交时期。发生这种情况时,不具有实际数据文件重写功能的所有列的时期会将 CPE 前进到提交 MOVE_PARTITIONS_TO_TABLE 的时期。如果在分区移动操作期间有任何节点关闭,它们会检测到存在要恢复的存储。在重新联接群集时,重新启动的节点会从其他具有正确时期的节点中恢复。
有关 Vertica 如何使用时期的详细信息,请参阅时期。
在多达 K 个节点处于离线状态的情况下,可以手动恢复数据库 — 例如,这些节点通过物理方式移除以便修复或者在恢复时无法访问。还原缺失的节点时,它们会按照在恢复场景中所述的步骤恢复并重新联接群集。
如果要重新启动的节点可以提供所有分区分段,那么即使 K 个以上的节点在启动时仍然处于关闭状态,您也可以手动恢复数据库。这种情况下,您可以从剩余的群集节点中获取所有数据,因此数据库可以成功启动。
HistoryRetentionTime 配置参数的默认设置为 0,因此当节点处于关闭状态时,Vertica 仅保留历史数据。此设置将避免使用 管理工具 的“将数据库回退至上一个完好的时期 (Roll Back Database to Last Good Epoch)”选项,因为 AHM 仍然接近当前时期并且不允许回退到早于 AHM 的时期。如果您依赖于“回退 (Roll Back)”选项来移除最近加载的数据,请考虑设置一个单天时段用于移除加载的数据。例如:
=> ALTER DATABASE DEFAULT SET HistoryRetentionTime = 86400;
有关详细信息,请参阅时期管理参数。
若某个节点处于关闭状态并需要手动恢复,当系统尝试形成一个群集时,可能需要整整一分钟或更长时间使 Vertica 进程超时。等待大约一分钟,直到系统返回手动恢复提示。切勿在数据库启动期间按 CTRL-C。
当正在运行的数据库群集中的一个节点出现故障时,或者如果任一节点丢失编录或数据目录中的任何文件,可使用管理工具或管理控制台检查故障节点的状态。
运行 管理工具。
从“主菜单 (Main Menu)”中选择在主机上重新启动 Vertica (Restart Vertica on Host),然后单击确定 (OK)。
选择要恢复的数据库主机并单击确定 (OK)。
通过从主菜单 (Main Menu) 中选择查看数据库群集状态 (View Database Cluster State) 来验证恢复状态。
数据库完全恢复后,可通过从管理工具 (Administration Tools) 主菜单 (Main Menu) 中选择查看数据库群集状态 (View Database Cluster State) 随时检查状态。
连接到群集节点(或安装了 MC 的主机)。
打开浏览器并以 MC 管理员身份连接到 MC。
在 MC 的主页 (Home) 页面上,双击最近数据库 (Recent Databases) 部分下的运行中数据库。
在概览 (Overview) 页面内,查看“数据库 (Database)”子部分下的节点状态,并查看所有节点是否均正常运行。状态将指出正常运行、关键、出现故障、正在恢复等节点的数量。
如果节点出现故障,请单击页面底部的管理 (Manage) 并检查图形。故障节点将以红色显示。
单击故障节点将其选中,然后在“节点列表 (Node List)”中单击启动节点 (Start node) 按钮。
如果多个节点错过 Vertica 过程(例如,因断电),或者在未先正常关闭 Vertica 数据库的情况下关闭服务器,数据库群集则会在下次启动时指出其之前非正常关闭。
数据库自动检测群集上次处于一致状态的时间,管理员可在此时点重新启动数据库。
从 管理工具的“主菜单 (Main Menu)”中:
验证已通过单击停止数据库 (Stop Database) 来停止数据库。
此时将显示一条消息:<dbadmin> 拥有的数据库均未运行 (No databases owned by
通过从“主菜单 (Main Menu)”中选择启动数据库 (Start Database) 来启动数据库。
选择要重新启动的数据库并单击确定 (OK)。
如果您在数据库未正常关闭后将其启动,则会显示一条指示启动失败的消息。按返回 (RETURN) 可继续恢复过程。
确定上一个完好的时期时,系统将提示您确认要启动该完好时期所在日期的数据库。选择是 (Yes) 可继续恢复过程。
Vertica 继续初始化和恢复上一个完好的时期之前的所有数据。
如果恢复用时超过一分钟,则在出现“是否要继续等待? (Do you want to continue waiting?)”提示时,您需要回复 <是 (Yes)> 或 <否 (No)>。
当所有节点的状态均变为“正在恢复 (RECOVERING)”或“开启 (UP)”时,选择 <否 (No)> 可退出此屏幕并通过管理工具的“主菜单 (Main Menu)”监控进度。选择 <是 (Yes)> 可继续显示数据库恢复时段。
无论您是按表恢复还是按节点恢复,Vertica 恢复的阶段都相同。如果是按表恢复,则在完成最后阶段后,各个表将分别变为可用。如果是按节点恢复,则数据库对象仅在整个节点完成恢复之后才可用。
在 Vertica 中执行恢复时,每个恢复的表都要完成以下阶段:
表完成最后一个阶段后,Vertica 认为它已完全恢复。此时,表可以参与 DDL 和 DML 操作。
时期代表数据库中历史数据的一个截止点。给定时期内所有提交的时间戳等于或小于该时期的时间戳。当您需要执行以下操作时,了解时期很有用:
数据库恢复Vertica 使用时期来确定上次数据在数据库群集中的所有节点之间保持一致的时间。
执行历史查询:包括
AT epoch
子句的 SELECT 语句仅返回在指定时期或之前提交的数据。
清除删除的数据:删除的数据在从数据库中清除之前不会从物理存储中移除。删除的数据只有在早于 Ancient History Mark (AHM) 时期时才能从数据库中清除。
Vertica 有一个开放时期和任意数量的封闭时期,具体取决于您的系统配置。新的和更新的数据被写入开放时期,每个封闭时期代表一个对数据库的先前提交。当使用 DML 操作(INSERT、UPDATE、MERGE、COPY 或 DELETE)提交数据时,Vertica 会写入数据,关闭开放时期,然后打开一个新时期。提交到数据库的每一行都与写入它的时期相关联。
EPOCHS 系统表包含有关每个可用封闭时期的信息。epoch_close_time
列存储提交日期和时间。epoch_number
列存储对应的时期编号:
=> SELECT * FROM EPOCHS;
epoch_close_time | epoch_number
-------------------------------+--------------
2020-07-27 14:29:49.687106-04 | 91
2020-07-28 12:51:53.291795-04 | 92
(2 rows)
随着时期在其生命周期中向前推移,它达到里程碑,Vertica 使用里程碑来执行各种操作并维护数据库的状态。下图大致描述了时期生命周期中的里程碑:
Vertica 对每个里程碑的定义如下:
当前时期 (CE):您当前正在向其中写入数据的当前开放时期。
最新时期 (LE):最近封闭的时期。
检查点时期:仅限 Enterprise 模式。节点级别的最新时期,在该时期内,数据在该节点上的所有投影之间保持一致。
上一个完好的时期 (LGE):数据在所有节点之间保持一致的最小检查点时期。
Ancient history mark (AHM):包含历史查询可访问的数据的最旧时期。
有关每个阶段的详细信息,请参阅时期生命周期。
时期生命周期由一系列里程碑组成,使您能够执行各种操作并管理数据库的状态。
Vertica 提供时期管理参数和函数,以便您可以检索和调整时期值。此外,请参阅配置时期获取有关如何为特定用例设置时期的建议。
包含您当前写入数据库的所有未提交更改的开放时期。当前时期存储在 SYSTEM
系统表中:
=> SELECT CURRENT_EPOCH FROM SYSTEM;
CURRENT_EPOCH
---------------
71
(1 row)
以下示例演示了当您提交数据时当前时期如何前进:
查询 SYSTEM
系统表以返回当前时期:
=> SELECT CURRENT_EPOCH FROM SYSTEM;
CURRENT_EPOCH
---------------
71
(1 row)
当前时期是开放时期,这意味着它是您当前正在向其中写入数据的时期。
向 orders
表中插入行:
=> INSERT INTO orders VALUES ('123456789', 323426, 'custacct@example.com');
OUTPUT
--------
1
(1 row)
每行数据都有一个隐含的时期列,用于存储该行的提交时期。您刚刚插入到表中的行没有提交,因此 epoch
列是空白的:
=> SELECT epoch, orderkey, custkey, email_addrs FROM orders;
epoch | orderkey | custkey | email_addrs
-------+-----------+---------+----------------------
| 123456789 | 323426 | custacct@example.com
(1 row)
提交数据,然后再次查询该表。提交的数据与时期 71
(即先前从 SYSTEM
系统表返回的当前时期)相关联:
=> COMMIT;
COMMIT
=> SELECT epoch, orderkey, custkey, email_addrs FROM orders;
epoch | orderkey | custkey | email_addrs
-------+-----------+---------+----------------------
71 | 123456789 | 323426 | custacct@example.com
(1 row)
再次查询 SYSTEMS
表以返回当前时期。当前时期高 1 个整数:
=> SELECT CURRENT_EPOCH FROM SYSTEM;
CURRENT_EPOCH
---------------
72
(1 row)
最近封闭的时期。在提交操作之后,当前时期成为最新时期。
LE 是存储在 EPOCHS 系统表中的最近的时期:
=> SELECT * FROM EPOCHS;
epoch_close_time | epoch_number
-------------------------------+--------------
2020-07-27 14:29:49.687106-04 | 91
2020-07-28 12:51:53.291795-04 | 92
(2 rows)
仅在企业模式下有效。每个节点都有一个检查点时期,这是该节点上的数据在所有投影之间保持一致的最近一个时期。当数据库以最佳方式运行时,检查点时期等于 LE,它总是比当前时期早一个时期。
在节点故障和恢复期间使用检查点时期。当单个节点发生故障时,该节点会尝试从其他节点重建超出其检查点时期的数据。如果故障节点无法使用这些时期中的任何一个时期来恢复数据,则故障节点将使用检查点时期来恢复数据。
使用 PROJECTION_CHECKPOINT_EPOCHS 来查询有关检查点时期的信息。以下查询返回有关用来存储 orders
投影的节点上的检查点时期的信息:
=> SELECT checkpoint_epoch, node_name, projection_name, is_up_to_date, would_recover, is_behind_ahm
FROM PROJECTION_CHECKPOINT_EPOCHS WHERE projection_name ILIKE 'orders_b%';
checkpoint_epoch | node_name | projection_name | is_up_to_date | would_recover | is_behind_ahm
------------------+------------------+-----------------+---------------+---------------+---------------
92 | v_vmart_node0001 | orders_b1 | t | f | f
92 | v_vmart_node0001 | orders_b0 | t | f | f
92 | v_vmart_node0003 | orders_b1 | t | f | f
92 | v_vmart_node0003 | orders_b0 | t | f | f
92 | v_vmart_node0002 | orders_b0 | t | f | f
92 | v_vmart_node0002 | orders_b1 | t | f | f
(6 rows)
此查询确认数据库时期正在以正确方式前进。当上一个完好的时期 (LGE) 等于 CPE 时,would_recover
列显示 f
,因为 Vertica 会尽可能优先考虑 LGE 进行恢复。is_behind_ahm
列显示检查点时期是否在 AHM 之后。在数据库或节点出现故障时,早于 Ancient History Mark (AHM) 的时期中的任何数据都不可恢复。
数据在群集中的所有节点之间保持一致的最小检查点时期。每个节点都有一个 LGE,Vertica 评估每个节点的 LGE 以确定群集 LGE。群集的 LGE 存储在 SYSTEM
系统表中:
=> SELECT LAST_GOOD_EPOCH FROM SYSTEM;
LAST_GOOD_EPOCH
-----------------
70
(1 row)
您可以通过查询预期恢复时期来检索每个节点的 LGE:
=> SELECT GET_EXPECTED_RECOVERY_EPOCH();
INFO 4544: Recovery Epoch Computation:
Node Dependencies:
011 - cnt: 21
101 - cnt: 21
110 - cnt: 21
111 - cnt: 9
001 - name: v_vmart_node0001
010 - name: v_vmart_node0002
100 - name: v_vmart_node0003
Nodes certainly in the cluster:
Node 0(v_vmart_node0001), epoch 70
Node 1(v_vmart_node0002), epoch 70
Filling more nodes to satisfy node dependencies:
Data dependencies fulfilled, remaining nodes LGEs don't matter:
Node 2(v_vmart_node0003), epoch 70
--
GET_EXPECTED_RECOVERY_EPOCH
-----------------------------
70
(1 row)
因为 LGE 是磁盘上所有最近数据的快照,所以使用它从数据库故障中恢复。管理工具使用 LGE 手动重置数据库。如果您在非正常关闭后从数据库故障中恢复,Vertica 会在重新启动期间提示您接受使用 LGE 进行恢复。
包含历史查询可访问的数据的最旧时期。AHM 存储在 SYSTEM
系统表中:
=> SELECT AHM_EPOCH FROM SYSTEM;
AHM_EPOCH
-----------
70
(1 row)
AHM 之前的时期不可用于历史查询。以下示例返回 AHM,然后在执行早于 AHM 的历史查询时返回错误:
=> SELECT GET_AHM_EPOCH();
GET_AHM_EPOCH
---------------
93
(1 row)
=> AT EPOCH 92 SELECT * FROM orders;
ERROR 3183: Epoch number out of range
HINT: Epochs prior to [93] do not exist. Epochs [94] and later have not yet closed
AHM 根据您的 HistoryRetentionTime
、HistoryRetentionEpochs
和 AdvanceAHMInterval
参数设置前进。默认情况下,AHM 每 180 秒前进一次,直到等于 LGE。这有助于减少保存到时期映射的时期数量,从而减小编录大小。AHM 无法超越 LGE。
AHM 充当从物理磁盘清除数据的截止时期。随着 AHM 前进,Tuple Mover 合并过程会清除属于 AHM 之前时期的所有已删除数据。有关自动或手动清除的详细信息,请参阅清除删除的数据。
时期存储在时期映射中,这是一个编录对象,其中包含从 Ancient History Mark (AHM) 时期开始到最新时期 (LE) 结束的封闭时期列表。时期映射越大,编录使用的内存越大。此外,AHM 用于确定从磁盘清除哪些数据。为了优化数据库性能,一定要监控数据库时期以验证它们是否正确前进。
当 Vertica 使用默认 Vertica 设置正常运行时,Ancient History Mark、上一个完好的时期 (LGE) 和检查点时期(CPE,仅限企业模式)等于最新时期,或比当前时期小 1。这将确保不使用磁盘空间来存储符合清除条件的数据,从而保持对时期映射和编录大小的控制。SYSTEM
系统表存储当前时期、上一个完好的时期和 Ancient History Mark:
=> SELECT CURRENT_EPOCH, LAST_GOOD_EPOCH, AHM_EPOCH FROM SYSTEM;
CURRENT_EPOCH | LAST_GOOD_EPOCH | AHM_EPOCH
---------------+-----------------+-----------
88 | 87 | 87
(1 row)
Vertica 提供 GET_AHM_EPOCH、GET_AHM_TIME、GET_CURRENT_EPOCH 和 GET_LAST_GOOD_EPOCH 来单独检索这些时期。
在企业模式下,您可以使用 PROJECTION_CHECKPOINT_EPOCHS 表查询检查点时期,以返回群集中每个节点的检查点时期。以下查询返回用来存储 orders
投影的任何节点的 CPE:
=> SELECT checkpoint_epoch, node_name, projection_name
FROM PROJECTION_CHECKPOINT_EPOCHS WHERE projection_name ILIKE 'orders_b%';
checkpoint_epoch | node_name | projection_name
------------------+------------------+-----------------
87 | v_vmart_node0001 | orders_b1
87 | v_vmart_node0001 | orders_b0
87 | v_vmart_node0003 | orders_b1
87 | v_vmart_node0003 | orders_b0
87 | v_vmart_node0002 | orders_b0
87 | v_vmart_node0002 | orders_b1
(6 rows)
正常运行的 AHM 对于确定数据库利用磁盘空间和执行查询的优度至关重要。当您提交 DELETE 或 UPDATE(DELETE 和 INSERT 的组合)操作时,数据不会立即从磁盘中删除。相反,Vertica 会将数据标记为删除,因此您可以使用历史查询来检索它。删除的数据会占用磁盘空间并影响查询性能,因为 Vertica 在非历史查询期间必须读取删除的数据。
时期会随着您提交数据而前进,并且标记为删除的任何数据都会在其时期超越 AHM 时由 Tuple Mover 合并过程自动清除。您可以创建自动清除策略或手动清除在 AHM 之前的时期中提交的任何已删除数据。有关其他信息,请参阅设置清除策略。
默认情况下,AHM 每 180 秒前进一次,直到等于 LGE。监控 SYSTEM
系统表以确保 AHM 正在以正确方式前进:
=> SELECT CURRENT_EPOCH, LAST_GOOD_EPOCH, AHM_EPOCH FROM SYSTEM;
CURRENT_EPOCH | LAST_GOOD_EPOCH | AHM_EPOCH
---------------+-----------------+-----------
94 | 93 | 86
(1 row)
如果您注意到 AHM 未正确前进,可能是由于下面的一种或多种原因:
您的数据库包含未刷新的投影。当您为已经包含数据的表创建投影时,便会发生这种情况。有关如何刷新投影的详细信息,请参阅刷新投影。
节点处于 DOWN 状态。当节点处于 DOWN 状态时,AHM 不能前进。有关如何解决此问题的信息,请参阅在主机上重新启动 Vertica。
确认 AHMBackupManagement
时期参数设置为 0
。如果此参数设置为 1,则 AHM 不会超越最近的完整备份:
=> SELECT node_name, parameter_name, current_value FROM CONFIGURATION_PARAMETERS WHERE parameter_name='AHMBackupManagement';
node_name | parameter_name | current_value
-----------+---------------------+---------------
ALL | AHMBackupManagement | 0
(1 row)
时期配置会影响数据库如何从故障中恢复、处理历史数据以及从磁盘中清除数据。Vertica 为系统范围的时期配置提供时期管理参数。时期管理函数使您能够对时期值进行临时调整。
当您执行历史查询时,Vertica 在由 EpochMapInterval 配置参数指定的时间内返回一个时期。例如,当您使用
AT TIME time
时期子句执行历史查询时,Vertica 在由该参数设置指定的时间内返回一个时期。默认情况下,EpochMapInterval
设置为 180 秒。您必须将 EpochMapInterval
设置为大于或等于 AdvanceAHMInterval
参数的值:
=> SELECT node_name, parameter_name, current_value FROM CONFIGURATION_PARAMETERS
WHERE parameter_name='EpochMapInterval' OR parameter_name='AdvanceAHMInterval';
node_name | parameter_name | current_value
-----------+--------------------+---------------
ALL | EpochMapInterval | 180
ALL | AdvanceAHMInterval | 180
(2 rows)
在故障恢复期间,Vertica 使用 EpochMapInterval
设置来确定将哪个时期报告为上一个完好的时期 (LGE)。
Vertica 建议您配置时期参数以创建清除策略,从而确定何时从磁盘清除已删除的数据。如果您经常使用历史查询,那么您需要在保存已删除的历史数据和从磁盘中清除历史数据之间找到一个平衡点。主动清除策略会提高磁盘利用率和查询性能,但也会限制您的恢复选项并缩小可用于历史查询的数据时段。
可通过以下两种战略来创建清除策略:
设置 HistoryRetentionTime
以指定已删除数据作为历史参考保存的时间长度(以秒为单位)。
设置 HistoryRetentionEpochs
以指定要保存的历史时期数。
有关配置每个工作流的详细信息,请参阅设置清除策略。
设置 HistoryRetentionTime
是创建清除策略的首选方法。默认情况下,Vertica 将此值设置为 0
,因此当数据库正常运行时,AHM 比当前时期小 1。您无法针对 AHM 之前的时期执行历史查询,因此您可能需要调整此设置才能在当前时间和 AHM 之间保存更多数据。调整此参数的另一个原因是,如果您使用将数据库回退至上一个完好的时期 (Roll Back Database to Last Good Epoch) 选项进行手动回退。例如,以下命令将 HistoryRetentionTime
设置为 1 天(以秒为单位)以提供更广泛的时期回退选项:
=> ALTER DATABASE vmart SET HistoryRetentionTime = 86400;
Vertica 使用 AdvanceAHMInterval
设置检查您的保留设置的状态,并根据需要使 AHM 前进。在 AHM 前进后,Tuple Mover 合并过程会自动清除在早于 AHM 的时期中删除的所有数据。
如果要禁用任何清除策略并保留所有历史数据,请将 HistoryRetentionTime
和 HistoryRetentionEpochs
设置为 -1
:
=> ALTER DABABASE vmart SET HistoryRetentionTime = -1;
=> ALTER DATABASE vmart SET HistoryRetentionEpochs = -1;
如果未设置清除策略,则可以使用时期管理函数来调整 AHM 以根据需要手动清除已删除的数据。如果您需要更新或删除错误上传的数据,手动清除非常有用。有关详细信息,请参阅手动清除数据。
要防止数据库不受重大灾难引起的站点故障的影响,请保留数据库的异地副本备用。发生灾难时,可将数据库用户切换到备用数据库。发生灾难和故障转移到异地副本期间丢失的数据量取决于完整数据库备份的保存频率。
用于灾难恢复的解决方案取决于您必须为应用程序确定的两个因素:
恢复点目标 (RPO):进行灾难恢复时,贵组织所容许的数据损失量是多少?
恢复时间目标 (RTO): 灾难发生之后,您需要以多快的速度恢复数据库?
根据 RPO 和 RTO,Vertica 建议从以下解决方案中进行选择:
双负载: 在数据库的每个加载过程中,同时加载另一个数据库。可使用现成的 ETL 软件轻松实现此目的。
定期增量备份:使用将数据库复制到其他群集中描述的过程将数据定期复制到目标数据库。切记,脚本仅复制已发生更改的文件。
存储供应商提供的复制解决方案:尽管一些用户通过 SAN 存储取得了成功,但供应商的数量和可能的配置使 Vertica 无法为 SAN 提供支持。
下表汇总了 RPO、RTO 以及每个方法的优缺点:
Vertica 支持按表执行节点恢复。与基于节点的恢复不同,按表恢复可使表在恢复期间可用,无需等待节点自身完全还原。您可以优先恢复最重要的表,确保它们立即可用。恢复后的表可支持所有 DDL 和 DML 操作。
为了提高恢复速度,Vertica 会并行恢复多个表。一次可以恢复的最大表数由 RECOVERY 资源池中的 MAXCONCURRENCY
参数控制。
节点完全恢复后,它会启用完整 Vertica 功能。
可以指定 Vertica 恢复表时所采用的顺序。此功能可确保最关键的表尽快处于可用状态。若要指定表的恢复顺序,请分配一个整数优先级值。首先恢复优先级值较高的表。例如,优先级为 1000 的表在优先级值为 500 的表之前恢复。表优先级的最大值为 64 位整数。
如果没有指定优先级,或多个表具有相同的优先级,则 Vertica 将按 OID 顺序还原表。使用查询分配优先级的方式如下:
=> SELECT set_table_recover_priority('avro_basic', '1000');
set_table_recover_priority
---------------------------------------
Table recovery priority has been set.
(1 row)
可使用以下格式通过一个查询查看所分配的优先级:
SELECT table_name,recover_priority FROM v_catalog.tables;
下一个示例显示了 Vmart 示例数据库中已确定优先级的表。这种情况下,恢复优先级最高的表将列在最前面 (DESC)。shipping_dimension
表具有最高优先级,将最先恢复。(本示例为了便于显示,使用了硬式换行。)
=> SELECT table_name AS Name, recover_priority from v_catalog.tables WHERE recover_priority > 1
ORDER BY recover_priority DESC;
Name | recover_priority
---------------------+------------------
shipping_dimension | 60000
warehouse_dimension | 50000
employee_dimension | 40000
vendor_dimension | 30000
date_dimension | 20000
promotion_dimension | 10000
iris2 | 9999
product_dimension | 10
customer_dimension | 10
(9 rows)
通过查询 V_MONITOR.TABLE_RECOVERY_STATUS 表,查看关于恢复操作的常规信息。通过查询 V_MONITOR.TABLE_RECOVERIES 表,您还可以查看关于正在还原表的恢复操作状态的详细信息。
Vertica 基于成本的查询优化器依赖数据统计信息来生成查询计划。如果统计信息不完整或已过时,优化器将倾向于使用不理想的计划来执行查询。
查询表时,Vertica 优化器会按以下方式检查统计信息:
如果表已分区,则优化器会检查该查询所需的分区最近是否已被分析过。如果已分析,则优化器将检索这些统计信息并使用它们来促进查询计划。
否则,优化器将使用表级别的统计信息(如果可用)。
如果没有可用的有效分区或表级别的统计信息,则优化器将假设数据值均匀分布,且所有投影的存储使用量均相等。
Vertica 提供了两个函数来生成有关表数据的最新统计信息:
ANALYZE_STATISTICS
和
ANALYZE_STATISTICS_PARTITION
分别收集表级别和分区级别的统计信息。计算统计数据之后,这两个函数将数据存储在数据库编录中。
这两个函数均执行以下操作:
使用 历史查询(最新时期)收集统计信息,不会进行任何锁定。
执行快速数据采样,从而加速具有大量列的相对较小的表的分析。
识别已删除的数据而不是忽略删除标记。
Vertica 还提供了多种函数来帮助您管理数据库统计信息,例如,导出和导入统计信息、验证统计信息和删除统计信息。
在收集所需统计信息后,可以运行工作负载分析器来检索有关表现不佳的查询及其根本原因的提示,并获得调整建议。
ANALYZE_STATISTICS 从存储目标表投影的所有节点中收集并聚合数据样本和存储信息。
您可以在多个级别中设置收集的范围:
ANALYZE_STATISTICS 还可以控制它收集的数据样本的大小。
如果 ANALYZE_STATISTICS 未指定表,则它将收集所有数据库表及其投影的统计信息。例如:
=> SELECT ANALYZE_STATISTICS ('');
ANALYZE_STATISTICS
--------------------
0
(1 row)
您可以按以下方式计算单个表的统计信息:
=> SELECT ANALYZE_STATISTICS ('public.store_orders_fact');
ANALYZE_STATISTICS
--------------------
0
(1 row)
当您查询系统表 PROJECTION_COLUMNS 时,它将确认已在所有表列中收集 store_orders_fact
所有投影的统计信息:
=> SELECT projection_name, statistics_type, table_column_name,statistics_updated_timestamp
FROM projection_columns WHERE projection_name ilike 'store_orders_fact%' AND table_schema='public';
projection_name | statistics_type | table_column_name | statistics_updated_timestamp
----------------------+-----------------+-------------------+-------------------------------
store_orders_fact_b0 | FULL | product_key | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | product_version | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | store_key | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | vendor_key | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | employee_key | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | order_number | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | date_ordered | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | date_shipped | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | quantity_ordered | 2019-04-04 18:06:55.747329-04
store_orders_fact_b0 | FULL | shipper_name | 2019-04-04 18:06:55.747329-04
store_orders_fact_b1 | FULL | product_key | 2019-04-04 18:06:55.747329-04
store_orders_fact_b1 | FULL | product_version | 2019-04-04 18:06:55.747329-04
...
(20 rows)
在表中,您可以将分析范围缩小到其列的子集。通过这种方式,可以为包含许多列的大型表节省大量的处理开销。如果您经常查询这些表中的特定列,则此功能十分有用。
例如,您可以仅选择那些经常查询的列,而不是收集 store_orders_fact
中所有列的统计信息:product_key
、product_version
、order_number
和 quantity_shipped
:
=> SELECT DROP_STATISTICS('public.store_orders_fact');
=> SELECT ANALYZE_STATISTICS ('public.store_orders_fact', 'product_key, product_version, order_number, quantity_ordered');
ANALYZE_STATISTICS
--------------------
0
(1 row)
如果再次查询 PROJECTION_COLUMNS
,它将返回以下结果:
=> SELECT projection_name, statistics_type, table_column_name,statistics_updated_timestamp
FROM projection_columns WHERE projection_name ilike 'store_orders_fact%' AND table_schema='public';
projection_name | statistics_type | table_column_name | statistics_updated_timestamp
----------------------+-----------------+-------------------+------------------------------
store_orders_fact_b0 | FULL | product_key | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | FULL | product_version | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | ROWCOUNT | store_key | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | ROWCOUNT | vendor_key | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | ROWCOUNT | employee_key | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | FULL | order_number | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | ROWCOUNT | date_ordered | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | ROWCOUNT | date_shipped | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | FULL | quantity_ordered | 2019-04-04 18:09:40.05452-04
store_orders_fact_b0 | ROWCOUNT | shipper_name | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | FULL | product_key | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | FULL | product_version | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | ROWCOUNT | store_key | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | ROWCOUNT | vendor_key | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | ROWCOUNT | employee_key | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | FULL | order_number | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | ROWCOUNT | date_ordered | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | ROWCOUNT | date_shipped | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | FULL | quantity_ordered | 2019-04-04 18:09:40.05452-04
store_orders_fact_b1 | ROWCOUNT | shipper_name | 2019-04-04 18:09:40.05452-04
(20 rows)
在这种情况下,只有对于那些已运行 ANALYZE_statistic,列 statistics_type 才会设置为 FULL
。剩余的表列将设置为 ROWCOUNT
,表示仅收集这些表列的行统计信息。
默认情况下,Vertica 从磁盘中收集的样本固定为统计数据的 10%。指定从磁盘中读取的数据百分比,使您在决定样本准确性和速度方面具有更大的控制权。
所收集数据的百分比影响收集时间和准确性:
百分比越小,速度越快,但返回的数据样本越小,这可能会影响直方图准确性。
百分比越大,从磁盘读取的数据越多。数据收集变慢;但数据样本更大,因而直方图准确性更高。
例如:
从 20% 的磁盘中收集 shipping_dimension
的所有投影数据:
=> SELECT ANALYZE_STATISTICS ('shipping_dimension', 20);
ANALYZE_STATISTICS
-------------------
0
(1 row)
通过将 percent
参数设置为 100,收集整个磁盘中的数据:
=> SELECT ANALYZE_STATISTICS ('shipping_dimension', 'shipping_key', 100);
ANALYZE_STATISTICS
--------------------
0
(1 row)
ANALYZE_STATISTICS 根据从所有收集的数据中随机选择的一组行来构建列直方图。无论百分比设置如何,该函数始终创建一个统计样本,其中最多包含下列两项中的较小者(近似值):
217 (131,072) 行
1 GB 内存所能容纳的行数
如果列所具有的行数少于最大样本大小,则 ANALYZE_STATISTICS 从磁盘中读取所有行并分析整个列。
下表显示设置为其他百分比的 ANALYZE_STATISTICS 如何从给定列获取统计样本:
ANALYZE_STATISTICS_PARTITION 收集和聚合指定表中一系列分区的数据样本和存储信息。Vertica 将收集的统计信息写入数据库编录。
例如,下表存储销售数据并按订单日期进行分区:
CREATE TABLE public.store_orders_fact
(
product_key int,
product_version int,
store_key int,
vendor_key int,
employee_key int,
order_number int,
date_ordered date NOT NULL,
date_shipped date NOT NULL,
quantity_ordered int,
shipper_name varchar(32)
);
ALTER TABLE public.store_orders_fact PARTITION BY date_ordered::DATE GROUP BY CALENDAR_HIERARCHY_DAY(date_ordered::DATE, 2, 2) REORGANIZE;
ALTER TABLE public.store_orders_fact ADD CONSTRAINT fk_store_orders_product FOREIGN KEY (product_key, product_version) references public.product_dimension (product_key, product_version);
ALTER TABLE public.store_orders_fact ADD CONSTRAINT fk_store_orders_vendor FOREIGN KEY (vendor_key) references public.vendor_dimension (vendor_key);
ALTER TABLE public.store_orders_fact ADD CONSTRAINT fk_store_orders_employee FOREIGN KEY (employee_key) references public.employee_dimension (employee_key);
在每个工作日结束时,您可能会调用 ANALYZE_STATISTICS_PARTITION 并收集有关最新(今天)分区的所有数据的统计信息:
=> SELECT ANALYZE_STATISTICS_PARTITION('public.store_orders_fact', CURRENT_DATE::VARCHAR(10), CURRENT_DATE::VARCHAR(10));
ANALYZE_STATISTICS_PARTITION
------------------------------
0
(1 row)
该函数为 store.store_sales_fact
中的最新分区生成一组新统计信息。如果您每天早上查询这张表昨天的销售额,优化器会使用这些统计信息来生成优化的查询计划:
=> EXPLAIN SELECT COUNT(*) FROM public.store_orders_fact WHERE date_ordered = CURRENT_DATE-1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
EXPLAIN SELECT COUNT(*) FROM public.store_orders_fact WHERE date_ordered = CURRENT_DATE-1;
Access Path:
+-GROUPBY NOTHING [Cost: 2, Rows: 1] (PATH ID: 1)
| Aggregates: count(*)
| Execute on: All Nodes
| +---> STORAGE ACCESS for store_orders_fact [Cost: 1, Rows: 222(PARTITION-LEVEL STATISTICS)] (PATH ID: 2)
| | Projection: public.store_orders_fact_v1_b1
| | Filter: (store_orders_fact.date_ordered = '2019-04-01'::date)
| | Execute on: All Nodes
与 ANALYZE_STATISTICS 一样,ANALYZE_STATISTICS_PARTITION 允许您将分析范围缩小到 表列的子集。您还可以控制它收集的数据样本的大小。有关这些选项的详细信息,请参阅收集表统计信息。
如果指定多个分区,它们必须是连续的分区。不同的统计数据集合可以重叠。例如,下表 t1 在列 c1
中已分区:
=> SELECT export_tables('','t1');
export_tables
-------------------------------------------------------------------------------------------------
CREATE TABLE public.t1
(
a int,
b int,
c1 int NOT NULL
)
PARTITION BY (t1.c1);
=> SELECT * FROM t1 ORDER BY c1;
a | b | c1
----+----+----
1 | 2 | 3
4 | 5 | 6
7 | 8 | 9
10 | 11 | 12
(4 rows)
对于此数据集,您可以在 t1
上调用 ANALYZE_STATISTICS_PARTITION 两次。连续调用会收集分区键的两个重叠范围(3 到 9 和 6 到 12)的统计信息:
=> SELECT drop_statistics_partition('t1', '', '');
drop_statistics_partition
---------------------------
0
(1 row)
=> SELECT analyze_statistics_partition('t1', '3', '9');
analyze_statistics_partition
------------------------------
0
(1 row)
=> SELECT analyze_statistics_partition('t1', '6', '12');
analyze_statistics_partition
------------------------------
0
(1 row)
=> SELECT table_name, min_partition_key, max_partition_key, row_count FROM table_statistics WHERE table_name = 't1';
table_name | min_partition_key | max_partition_key | row_count
------------+-------------------+-------------------+-----------
t1 | 3 | 9 | 3
t1 | 6 | 12 | 3
(2 rows)
如果两个统计信息集合重叠,Vertica 仅存储每个分区范围的最新统计信息。因此,在上一个示例中,Vertica 仅使用来自第二个集合的分区键 6 到 9 的统计信息。
为给定分区键范围收集的统计信息始终取代先前为该范围的子集收集的统计信息。例如,假设 ANALYZE_STATISTICS_PARTITION 调用指定了分区键 3 到 12,则收集的统计信息是之前收集的两组统计信息的超集,因此它将取代两者:
=> SELECT analyze_statistics_partition('t1', '3', '12');
analyze_statistics_partition
------------------------------
0
(1 row)
=> SELECT table_name, min_partition_key, max_partition_key, row_count FROM table_statistics WHERE table_name = 't1';
table_name | min_partition_key | max_partition_key | row_count
------------+-------------------+-------------------+-----------
t1 | 3 | 12 | 4
(1 row)
最后,ANALYZE_STATISTICS_PARTITION 收集分区键 3 到 6 的统计信息。此集合是上一个集合的子集,因此 Vertica 将保留这两个集合,并使用每个集合的最新统计信息:
=> SELECT analyze_statistics_partition('t1', '3', '6');
analyze_statistics_partition
------------------------------
0
(1 row)
=> SELECT table_name, min_partition_key, max_partition_key, row_count FROM table_statistics WHERE table_name = 't1';
table_name | min_partition_key | max_partition_key | row_count
------------+-------------------+-------------------+-----------
t1 | 3 | 12 | 4
t1 | 3 | 6 | 2
(2 rows)
ANALYZE_STATISTICS_PARTITION 可以在分区表达式已指定以下日期/时间函数之一的表中收集分区级统计信息:
ANALYZE_STATISTICS_PARTITION 具有以下要求和限制:
该表必须进行分区,并且不能包含未分区的数据。
表分区表达式必须指定单个列。支持以下表达式:
仅指定列的表达式,即对所有列值进行分区。例如:
PARTITION BY ship_date GROUP BY CALENDAR_HIERARCHY_DAY(ship_date, 2, 2)
如果列是 DATE 或 TIMESTAMP/TIMESTAMPTZ,则分区表达式可以指定支持的日期/时间函数,该函数会返回该列或其任何部分,例如月份或年份。例如,以下分区表达式指定对列 order_date
的年份部分进行分区:
PARTITION BY YEAR(order_date)
该表达式对列执行加法或减法。例如:
PARTITION BY YEAR(order_date) -1
表分区表达式不能将指定列强制转换为另一种数据类型。
Vertica 不从以下投影中收集任何统计数据:
实时聚合投影和 Top-K 投影
定义这些投影可以在表达式中包含 SQL 函数
Vertica 允许分别通过 ANALYZE_ROW_COUNT 和 ANALYZE_EXTERNAL_ROW_COUNT 获取投影和外部表的行计数。
ANALYZE_ROW_COUNT 为轻量级操作,它会收集一组最少的统计数据并聚合一个投影的行计数,将其保存在数据库编录中。在许多情况下,此数据满足许多优化器对生成最佳查询计划的要求。在以下情况下,将调用此操作:
在配置参数 AnalyzeRowCountInterval 指定的时间间隔内 - 默认情况下,每天一次。
在负载期间。当上次记录的聚合投影行计数与当前行计数之间的差异百分比超过配置参数 ARCCommitPercentage 中的设置时,Vertica 将使用给定表的当前聚合行计数数据更新编录。
调用元函数 ANALYZE_STATISTICS 和 ANALYZE_STATISTICS_PARTITION 时。
您可以通过调用 DO_TM_TASK 显式调用 ANALYZE_ROW_COUNT。例如:
=> SELECT DO_TM_TASK('analyze_row_count', 'store_orders_fact_b0');
do_tm_task
------------------------------------------------------------------------------------------------------
Task: row count analyze
(Table: public.store_orders_fact) (Projection: public.store_orders_fact_b0)
(1 row)
您可以通过设置配置参数 AnalyzeRowCountInterval 更改 Vertica 定期收集行级统计信息的时间间隔。例如,可将收集间隔更改为 1 小时(3600 秒):
=> ALTER DATABASE DEFAULT SET AnalyzeRowCountInterval = 3600;
ALTER DATABASE
ANALYZE_EXTERNAL_ROW_COUNT 计算外部表的确切行数。优化器将利用该计数来优化用于访问外部表的查询。当外部表参与联接时,这会特别有用。此函数可以让优化器确定较小的表,以便用作联接的内部输入,并有助于提高查询性能。
以下查询计算外部表 loader_rejects
中的确切行数:
=> SELECT ANALYZE_EXTERNAL_ROW_COUNT('loader_rejects');
ANALYZE_EXTERNAL_ROW_COUNT
----------------------------
0
要取消统计信息收集中期分析,请在 vsql 上执行 CTRL-C 或调用 INTERRUPT_STATEMENT() 函数。
如果要删除指定表或类型的统计信息,请调用 DROP_STATISTICS() 函数。
Vertica 通过两种方式提供给定表及其列和分区的统计信息:
查询优化器将通知您统计信息的可用性,以处理给定的查询。
系统表
PROJECTION_COLUMNS
将显示表列可用的统计信息类型以及上次更新的时间。
在谓词选择性估算期间,查询优化器可以确定直方图何时不可用或过期。如果谓词中的值超出了直方图的最大范围,则统计信息已过时。如果没有直方图可用,则没有统计信息可用于计划。
当优化器检测到过时统计信息或没有统计信息时(例如遇到没有相应直方图的列谓词时),优化器会执行以下操作:
显示并记录一条您应运行
ANALYZE_STATISTICS
的消息。
使用一个统计信息条目对
EXPLAIN
生成的查询计划进行注释。
在生成查询计划时忽略过时的统计信息。优化器会根据其他注意事项(例如 FK-PK 约束)来创建查询计划。
例如,以下查询计划片段显示没有统计信息(直方图不可用):
| | +-- Outer -> STORAGE ACCESS for fact [Cost: 604, Rows: 10K (NO STATISTICS)]
以下查询计划片段显示谓词超出了直方图的范围:
| | +-- Outer -> STORAGE ACCESS for fact [Cost: 35, Rows: 1 (PREDICATE VALUE OUT-OF-RANGE)]
系统表
PROJECTION_COLUMNS
中的两列将显示每个表列的统计状态,如下所示:
STATISTICS_TYPE
返回此列的统计信息类型,如下所示:NONE
、ROWCOUNT
或 FULL
。
STATISTICS_UPDATED_TIMESTAMP
将返回此列中最后一次收集统计信息的时间。
例如,以下示例架构定义了名为 trades 的表,该表将高度相关的列 bid
和 ask
分组在一起,并单独存储 stock
列:
=> CREATE TABLE trades (stock CHAR(5), bid INT, ask INT);
=> CREATE PROJECTION trades_p (
stock ENCODING RLE, GROUPED(bid ENCODING DELTAVAL, ask))
AS (SELECT * FROM trades) ORDER BY stock, bid;
=> INSERT INTO trades VALUES('acme', 10, 20);
=> COMMIT;
在 PROJECTION_COLUMNS
表中查询 trades
表:
=> SELECT table_name AS table, projection_name AS projection, table_column_name AS column, statistics_type, statistics_updated_timestamp AS last_updated
FROM projection_columns WHERE table_name = 'trades';
table | projection | column | statistics_type | last_updated
--------+-------------+--------+-----------------+--------------
trades | trades_p_b0 | stock | NONE |
trades | trades_p_b0 | bid | NONE |
trades | trades_p_b0 | ask | NONE |
trades | trades_p_b1 | stock | NONE |
trades | trades_p_b1 | bid | NONE |
trades | trades_p_b1 | ask | NONE |
(6 rows)
statistics_type
列将为 trades
表中的所有列返回 NONE
,而 statistics_updated_timestamp
则为空,因为尚未收集此表中的统计信息。
现在,请对 stock
列运行
ANALYZE_STATISTICS
:
=> SELECT ANALYZE_STATISTICS ('public.trades', 'stock');
ANALYZE_STATISTICS
--------------------
0
(1 row)
现在,当您查询 PROJECTION_COLUMNS
时,它将返回以下结果:
=> SELECT table_name AS table, projection_name AS projection, table_column_name AS column, statistics_type, statistics_updated_timestamp AS last_updated
FROM projection_columns WHERE table_name = 'trades';
table | projection | column | statistics_type | last_updated
--------+-------------+--------+-----------------+-------------------------------
trades | trades_p_b0 | stock | FULL | 2019-04-03 12:00:12.231564-04
trades | trades_p_b0 | bid | ROWCOUNT | 2019-04-03 12:00:12.231564-04
trades | trades_p_b0 | ask | ROWCOUNT | 2019-04-03 12:00:12.231564-04
trades | trades_p_b1 | stock | FULL | 2019-04-03 12:00:12.231564-04
trades | trades_p_b1 | bid | ROWCOUNT | 2019-04-03 12:00:12.231564-04
trades | trades_p_b1 | ask | ROWCOUNT | 2019-04-03 12:00:12.231564-04
(6 rows)
这一次,查询结果将出现几项变化:
当以下一个或多个条件成立时,您应调用 ANALYZE_STATISTICS 或 ANALYZE_STATISTICS_PARTITION:
首次加载大量数据。
刷新了新的投影。
行数发生显著变化。
向表中添加了新列。
列最小值/最大值发生显著变化。
添加了具有参照完整性约束的新主键值。应当重新分析主键和外键表。
表与所联接到的表的相对大小发生了显著变化,例如,表之前比其他表大五十倍,现在只比其他表大五倍。
重新计算直方图所必需的数据分布情况发生了显著偏差,例如,某个事件导致特定股票的交易量变得异常高。
数据库数据库长时间处于非活动状态。
运行 ANALYZE_STATISTICS 是一种有效但可能需要运行较长时间的操作。在生产环境中,您可以将其与查询和加载操作并发运行。然而,参数会给系统资源(CPU 和内存)带来很大开销,以致于给查询和加载操作带来不利影响。为了最大限度地减少开销,请考虑在那些受到重大活动影响的分区上调用 ANALYZE_STATISTICS_PARTITIONS(通常是指最近加载的分区,其中包括表的活动分区)。您可以指定表列的子集(通常是查询次数最多的列),进一步缩小这两个函数的范围。
您可调用 ANALYZE_WORKLOAD,并利用它返回的优化建议诊断和解决与统计信息有关的许多问题。如果更新统计信息后依然发现查询性能不佳,请通过 Database Designer 运行询问,然后选择增量作为设计类型。
要确定主机上安装的 Vertica 的版本,请登录到该主机并键入:
$ rpm -qa | grep vertica
此命令返回已安装包的名称,其中包含版本和内部版本号。以下示例指示 Vertica 9.3.x 和管理控制台 9.3.x 都在目标主机上运行:
$ rpm -qa | grep vertica
vertica-9.3.0-0
vertica-console-9.3.0-0.x86_64
登录到 Vertica Analytic Database 数据库后,还可以通过运行以下命令仅对该版本运行查询:
=> SELECT version();
version
-------------------------------------------
Vertica Analytic Database v9.3.0-0
诊断工具 scrutinize
从 Vertica 群集中收集大量信息。它还支持一系列选项,这些选项可让您控制所收集数据的数量和类型。收集的数据可以包括但不限于:
主机诊断和配置数据
运行时状态(正常或故障节点数)
来自安装过程、数据库和管理工具的日志文件(例如,
vertica.log、dbLog、/opt/vertica/log/adminTools.log
)
错误消息
数据库设计
系统表信息,如系统、资源、工作负载和性能
编录元数据,如系统配置参数
备份信息
scrutinize
要求将群集配置为支持管理工具实用程序。如果管理工具无法在发起主机上运行,则 scrutinize
也无法在该主机上运行。
可以使用以下命令运行 scrutinize
:
$ /opt/vertica/bin/scrutinize
未限定的 scrutinize
会从所有群集节点中收集大量信息。它会将结果存储在 .tar
文件 (
VerticaScrutinize.NumericID.tar
) 中,这可最大程度地减小对数据库性能的影响。 scrutinize
输出可以帮助诊断大多数问题,但通过省略细粒度的分析数据来减小上传大小。
scrutinize
旨在收集用于对数据库和群集进行故障排除的信息。根据您的系统配置,通过运行 scrutinize
生成的日志可能包含专有信息。如果您担心共享专有信息,请先将其从 .tar
文件中移除,然后再将其发送给 Vertica 客户支持以寻求帮助。
scrutinize
选项支持以下任务:
您必须具有数据库管理员 (dbadmin) 权限才能运行 scrutinize
。如果在 dbadmin 用户存在时以 root 用户身份运行 scrutinize
,Vertica 会返回错误。
scrutinize
需要临时磁盘空间,以便在发布最终的压缩 (.tar
) 输出之前收集数据。空间量取决于多个变量,例如 Vertica 日志和所提取系统表的大小,以及用于限制信息收集范围的用户指定选项。在 scrutinize
运行之前,它会验证临时目录至少包含 1 GB 空间;但是,实际所需的空间量可能要高得多。
可以将 scrutinize
输出重定向到另一个目录。有关详细信息,请参阅重定向 scrutinize 输出。
如果在群集上定义了多个数据库,并且多个数据库处于活动状态,或者没有一个数据库处于活动状态,则必须使用以下选项之一运行 scrutinize:
$ /opt/vertica/bin/scrutinize {--database=database | -d database}
如果在满足这些条件时省略此选项,scrutinize
会返回错误。
scrutinize
支持两个不能与任何其他选项结合使用的信息选项:
‑‑version
=> \!scrutinize --version
Scrutinize Version 10.0.1-20200426
‑‑help
‑h
=> \! scrutinize -h
Usage: scrutinize [options]
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-X LIST, --exclude-tasks=LIST
Skip tasks of a particular type. Provide a comma-
separated lists of types to skip. Types are case-
sensitive. Possible types are: Command, File,
VerticaLog, DC, SystemTable, CatalogObject, Query,
all.
...
默认情况下,scrutinize
在执行时使用临时目录 /opt/vertica/tmp
执行来编译输出。完成收集后,它会将收集内容保存到当前目录的 tar 文件中。可以使用以下两个选项重定向 scrutinize
输出:
‑‑tmpdir=path
该目录必须至少具有 1 GB 的可用空间。
您必须对该目录具有写入权限。
‑‑output_dir=path
‑o path
scrutinize
结果保存到 path 中的 tar 文件。例如:
$ scrutinize ‑‑output_dir="/my_diagnostics/"
scrutinize
可以指定用户名和密码,如下所示:
‑‑user=username
‑U username
scrutinize
使用调用用户的用户名。‑‑password=password
‑P password
scrutinize
命令的实参。如果管理员帐户(默认为 dbadmin)具有密码身份验证,请使用此选项。如果在受密码保护的数据库上省略此选项,scrutinize
会返回警告,除非设置了环境变量
VSQL_PASSWORD
。
带有特殊字符的密码必须用单引号括起来。例如:
$ scrutinize -P '@passWord**'
$ scrutinize --password='$password1*'
‑prompt‑password ‑W
scrutinize
选项可让您控制数据收集的范围。您可以根据以下条件指定数据收集的范围:
通过以下几个选项,可以限制 scrutinize
收集的数据量:
‑‑by‑second
‑‑by‑minute=boolean‑value
‑‑get‑files file‑list
‑‑include_gzlogs=num‑files
‑z num‑files
scrutinize
输出中包括 num‑files 个轮换日志文件 (vertica.log*.gz
),其中 num‑files 可以是以下值之一:
整数指定要收集的循环日志文件的数量。
all
指定收集所有轮换日志文件。
默认情况下,scrutinize
会包含三个滚动日志文件。
‑‑log-limit=limit
‑l limit
scrutinize
收集 1 GB 的日志数据。默认情况下,scrutinize
收集所有群集节点中的数据。您可以指定 scrutinize
通过以下两种方法在各个节点中收集:
‑‑local_diags ‑s
scrutinize
的主机中收集诊断数据。要从群集中的多个节点收集数据,请使用 ‑‑hosts
选项。‑‑hosts=host‑list ‑n host‑list
例如:
$ scrutinize --hosts=127.0.0.1,host_3,host_1
scrutinize
提供了几个选项,可用于指定要收集的数据的类型:
‑‑debug
‑‑diag‑dump
‑‑diagnostics
‑‑include‑ros‑info
‑‑no‑active‑queries ‑‑with‑active‑queries
scrutinize
收集此信息 (‑‑with‑active‑queries
)。‑‑tasks=tasks -T tasks
‑‑exclude
一起使用。
‑‑type=type -t type
profiling
:收集分析数据。
context
:收集摘要信息。
‑‑with‑active‑queries
‑‑no‑active‑queries
。scrutinize
选项还允许您指定要从其收集中排除的数据的类型:
‑‑exclude=tasks ‑X tasks
all
:所有默认任务
DC
:数据收集器表
File
:来自安装过程、数据库和管理工具的日志文件,例如 vertica.log
、dbLog
和 adminTools.log
VerticaLog
:Vertica 日志
CatalogObject
:Vertica 编录元数据,例如系统配置参数
SystemTable
:包含有关系统、资源、工作负载和性能的信息的 Vertica 系统表
Query
:使用 vsql 连接到数据库的 Vertica 元函数,例如 EXPORT_CATALOG()
Command
:操作系统信息,例如节点已启动的时间长度
‑‑no‑active‑queries
scrutinize
始终会收集活动查询信息 (‑‑with‑active‑queries
)。‑‑vsql-off ‑v
Query
和 SystemTable
任务。此选项可帮助您处理升级期间遇到的问题,并且通常在以下情况下使用:
Vertica 正在运行,但响应缓慢。
您尚未创建数据库,但需要帮助解决其他群集问题。
scrutinize
提供了将数据上传到 Vertica 客户支持的多个选项。
当使用上传选项时,scrutinize
不会将所有输出捆绑到单个 tar 文件中。相反,每个节点将其输出直接发布到指定的 URL,如下所示:
上传较小的上下文文件,使客户支持能够查看高层次信息。
完成 scrutinize
执行后,上传完整的诊断集合。
使用上传选项运行 scrutinize
之前:
在运行 scrutinize
的数据库管理员用户的路径中安装 cURL 程序。
确认群集中的各节点能否直接与 Internet 建立 HTTP 或 FTP 连接。
scrutinize
输出上传到 Vertica 支持提供的 URL 或 FTP 地址: ‑‑auth-upload
和 ‑‑url
。每个选项对上传进行身份验证的方式有所不同,如下所述。
‑‑auth‑upload=url
‑A url
‑‑url=url
‑u url
‑‑message=message
‑m message
scrutinize
输出的消息,其中 message 是文本字符串、文本文件的路径或用于打开要在其中编写消息的输入流的 PROMPT
。 scrutinize
会读取输入,直至您另起一行键入句点 (.)。这会关闭输入流,并且 scrutinize 会将消息写入收集到的输出中。
消息会写入 output 目录中的 reason.txt
。如果未指定消息,则 scrutinize
会生成默认消息 Unknown reason for collection
。消息通常包括以下信息:
收集/提交诊断数据的原因。
支持人员提供的案例编号和其他特定于问题的信息,以帮助 Vertica 客户支持识别您的案例并分析问题。
‑‑auth‑upload
选项使用您的 Vertica 来标识您自己:
$ scrutinize -U username -P 'password' --auth-upload="support-provided-url"
‑‑url
选项在 URL 中包括支持人员提供的 FTP 用户名和密码:
$ scrutinize -U username -P 'password' --url='ftp://username/password@customers.vertica.com/'
您可以将消息以文本字符串或文本文件的形式提供:
$ scrutinize --message="re: case number #ABC-12345"
$ scrutinize --message="/path/to/msg.txt"
或者,您可以打开输入流并键入消息:
$ scrutinize --message=PROMPT Enter reason for collecting diagnostics; end with '.' on a line by itself:
Query performance degradation noticed around 9AM EST on Saturday
.
Vertica Scrutinize Report
-----------------------------
Result Dir: /home/dbadmin/VerticaScrutinize.20131126083311
...
本部分中的故障排除建议可帮助您解决在使用 scrutinize
时可能遇到的常见问题。
要缩短收集时间,运行 scrutinize
的实例时请忽略系统表。请注意,从更少的节点进行收集并不一定会加快收集过程。
输出大小取决于系统表大小和 Vertica 日志大小。
要创建更小的 scrutinize
输出,请忽略一些系统表或截断 Vertica 日志。有关详细信息,请参阅缩小 scrutinize 数据收集的范围。
对受密码保护的数据库运行 scrutinize
可能需要您提供用户名和密码:
$ scrutinize -U username -P 'password'
导出编录时,可以将编录快速移动到其他群集。导出编录会传输架构、表、约束、投影和视图。系统表不会导出。
导出编录也可用于提供支持。
有关详细信息,请参阅 EXPORT_CATALOG 函数。
诊断审核脚本从正在运行的数据库中收集系统表内容、设计和计划对象,并将数据导出到名为 ./diag_dump_<timestamp>.tar.gz
的文件中,其中 <timestamp> 表示脚本的运行时间。
如果不使用任何参数运行脚本,系统则会提示您输入数据库密码。
/opt/vertica/scripts/collect_diag_dump.sh [ command... ]
command
以下命令使用所有实参来运行审核脚本:
$ /opt/vertica/scripts/collect_diag_dump.sh -U dbadmin -w password -c
可以分析数据库操作以评估性能。分析可以提供如下信息:
分配给每个运算符的内存量和线程数。
数据在查询执行期间的不同时间点流经每个运算符的方式。
查询是否为网络绑定。
分析数据有助于为数据库设计注意事项提供有价值的输入,例如如何最好地对投影进行分段和排序,或促进在群集中更好地分布数据处理。
例如,分析可能会显示数据倾斜,其中某些节点处理的数据比其他节点多。系统表 执行_引擎_配置文件 中的 rows produced
计数器显示每个运算符所处理的行数。比较给定运算符跨所有节点的 rows produced
可以揭示是否存在数据倾斜问题。
此部分中的主题重点介绍如何通过 vsql 语句获取配置文件数据。您还可以在管理控制台中查看分析数据。
可以在以下三个范围内启用分析:
Vertica 元函数
SHOW_PROFILING_CONFIG
显示是否在全局范围和会话范围内启用分析。在以下示例中,该函数显示在当前会话的所有类别中禁用了分析,并在所有类别中全局启用了分析:
=> SELECT SHOW_PROFILING_CONFIG();
SHOW_PROFILING_CONFIG ------------------------------------------
Session Profiling: Session off, Global on
EE Profiling: Session off, Global on
Query Profiling: Session off, Global on
(1 row)
当为给定类别启用或禁用全局分析时,该设置将在所有数据库会话中保留。可以使用
ALTER DATABASE
设置全局分析,如下所示:
ALTER DATABASE db-spec SET profiling-category = {0 | 1}
profiling-category 使用以下实参之一指定分析类别:
例如,以下语句对当前 (DEFAULT
) 数据库全局启用查询分析:
=> ALTER DATABASE DEFAULT SET GlobalQueryProfiling = 1;
可以为当前会话启用会话分析,并一直持续到您显式禁用分析或会话结束。可以使用以下 Vertica 元函数设置会话分析:
ENABLE_PROFILING ( profiling-type )
DISABLE_PROFILING ( profiling-type )
profiling-type 使用以下实参之一指定要启用或禁用的分析数据的类型:
例如,以下语句为每个查询的执行运行启用会话范围分析:
=> SELECT ENABLE_PROFILING('ee');
ENABLE_PROFILING
----------------------
EE Profiling Enabled
(1 row)
可以通过在单个 SQL 语句前面加上关键字
PROFILE
,来启用对这些语句的分析。可以分析 SELECT
语句或任何 DML 语句(例如
INSERT
、
UPDATE
、
COPY
和
MERGE
)。有关详细信息,请参阅分析单个语句。
Vertica 按优先级降序在以下范围内检查会话和查询分析:
语句分析(最高)
会话分析(如果启用了全局分析,则忽略)
全局分析(最低)
无论查询和会话分析设置如何,Vertica 始终在相关系统表中保存最少量的分析数据:QUERY_PROFILES
、QUERY_PLAN_PROFILES
和 SESSION_PROFILES
。
对于执行引擎分析,Vertica 首先检查配置参数 SaveDCEEProfileThresholdUS
的设置。如果查询运行时间超过指定阈值(默认为 60 秒),Vertica 会收集该查询的执行引擎数据,并将其保存到系统表
QUERY_CONSUMPTION
和
执行_引擎_配置文件
中。仅当查询的持续时间低于阈值时,Vertica 才使用其他范围(语句、会话、全局)的分析设置。
要禁用或最小化执行引擎分析:
将 SaveDCEEProfileThresholdUS
设置为一个非常高的值,直至其最大值 2147483647(231-1,即约 35.79 分钟)。
在会话和全局范围内禁用分析。
要分析单个语句,请在其前面加上
PROFILE
前缀。可以分析查询 (SELECT
) 语句或任何 DML 语句,例如
INSERT
、
UPDATE
、
COPY
和
MERGE
。语句返回分析摘要:
分析标识符 transaction_id
和 statement_id
查询的启动内存
所需的总内存
例如:
=> PROFILE SELECT customer_name, annual_income FROM public.customer_dimension
WHERE (customer_gender, annual_income) IN (SELECT customer_gender, MAX(annual_income)
FROM public.customer_dimension GROUP BY customer_gender);NOTICE 4788: Statement is being profiled
HINT: Select * from v_monitor.execution_engine_profiles where transaction_id=45035996274760535 and statement_id=1;
NOTICE 3557: Initiator memory for query: [on pool general: 2783428 KB, minimum: 2312914 KB]
NOTICE 5077: Total memory required by query: [2783428 KB]
customer_name | annual_income
------------------+---------------
James M. McNulty | 999979
Emily G. Vogel | 999998
(2 rows)
可以使用分析标识符 transaction_id
和 statement_id
从系统表
执行_引擎_配置文件
和
QUERY_PLAN_PROFILES
中获取此查询的详细分析信息。还可以使用这些标识符从系统表
QUERY_CONSUMPTION
中获取资源消耗数据。
例如:
=> SELECT path_id, path_line::VARCHAR(68), running_time FROM v_monitor.query_plan_profiles
WHERE transaction_id=45035996274760535 AND statement_id=1 ORDER BY path_id, path_line_index;
path_id | path_line | running_time
---------+----------------------------------------------------------------------+-----------------
1 | +-JOIN HASH [Semi] [Cost: 631, Rows: 25K (NO STATISTICS)] (PATH ID: | 00:00:00.052478
1 | | Join Cond: (customer_dimension.customer_gender = VAL(2)) AND (cus |
1 | | Materialize at Output: customer_dimension.customer_name |
1 | | Execute on: All Nodes |
2 | | +-- Outer -> STORAGE ACCESS for customer_dimension [Cost: 30, Rows | 00:00:00.051598
2 | | | Projection: public.customer_dimension_b0 |
2 | | | Materialize: customer_dimension.customer_gender, customer_d |
2 | | | Execute on: All Nodes |
2 | | | Runtime Filters: (SIP1(HashJoin): customer_dimension.custom |
4 | | | +---> GROUPBY HASH (GLOBAL RESEGMENT GROUPS) (LOCAL RESEGMENT GR | 00:00:00.050566
4 | | | | Aggregates: max(customer_dimension.annual_income) |
4 | | | | Group By: customer_dimension.customer_gender |
4 | | | | Execute on: All Nodes |
5 | | | | +---> STORAGE ACCESS for customer_dimension [Cost: 30, Rows: 5 | 00:00:00.09234
5 | | | | | Projection: public.customer_dimension_b0 |
5 | | | | | Materialize: customer_dimension.customer_gender, custom |
5 | | | | | Execute on: All Nodes |
(17 rows)
要快速识别查询和其他操作以进行分析和调试,请包含 LABEL 提示。
LABEL 提示在以下语句中有效:
EXPORT 语句:
UNION:在 UNION 的第一个 SELECT 语句中有效。Vertica 会忽略后续 SELECT 语句中的标签。
例如:
SELECT /*+label(myselectquery)*/ COUNT(*) FROM t;
INSERT /*+label(myinsertquery)*/ INTO t VALUES(1);
向一个或多个语句添加标签后,查询 QUERY_PROFILES 系统表,以查看哪些查询是使用您提供的标签运行的。QUERY_PROFILES 系统表 IDENTIFIER 列返回您之前分配给语句的用户定义标签。您还可以获取其他特定于查询的数据,这些数据可用于查询其他系统表,例如事务 ID。
例如:
=> SELECT identifier, query FROM query_profiles;
identifier | query
---------------+-----------------------------------------------------------
myselectquery | SELECT /*+label(myselectquery)*/ COUNT(*) FROM t;
myinsertquery | INSERT /*+label(myinsertquery)*/ INTO t VALUES(1);
myupdatequery | UPDATE /*+label(myupdatequery)*/ t SET a = 2 WHERE a = 1;
mydeletequery | DELETE /*+label(mydeletequery)*/ FROM t WHERE a = 1;
| SELECT identifier, query from query_profiles;
(5 rows)
可以通过查询系统表
执行_引擎_配置文件
来监控长时间运行的查询的执行。此表包含可用于内部操作和用户语句的分析计数器。可以使用 Linux watch
命令频繁地查询此表。
对实时分析数据的查询需要事务 ID。如果事务执行多个语句,则查询还需要语句 ID 来标识所需的语句。如果分析单个查询,则查询会返回语句的事务和语句 ID。还可以从
SYSTEM_SESSIONS
系统表中获取事务和语句 ID。
执行_引擎_配置文件
系统表包含可用于内部操作和用户语句的分析计数器。实时分析计数器可用于所有语句的执行,包括
合并、
恢复和
刷新等内部操作。除非在特定 SQL 语句中使用 PROFILE
关键字显式地启用分析,或者普遍地为数据库和/或当前会话启用分析,否则在语句完成后分析计数器将不可用。
有用的计数器包括:
执行时间 (µs)
生成的行数
合并阶段总数
已完成的合并阶段数
当前临时文件大小(字节数)
可以通过查询
执行_引擎_配置文件
来查看所有可用的计数器:
=> SELECT DISTINCT(counter_name) FROM EXECUTION_ENGINE_PROFILES;
若要监控分析计数器,可使用检索到的事务 ID (a000000000027
) 运行类似如下所示的命令:
=> SELECT * FROM execution_engine_profiles
WHERE TO_HEX(transaction_id)='a000000000027'
AND counter_name = 'execution time (us)'
ORDER BY node_name, counter_value DESC;
以下示例将查找每个节点上执行时间最长的运算符:
=> SELECT node_name, operator_name, counter_value execution_time_us FROM V_MONITOR.EXECUTION_ENGINE_PROFILES WHERE counter_name='execution time (us)' LIMIT 1 OVER(PARTITION BY node_name ORDER BY counter_value DESC);
node_name | operator_name | execution_time_us
------------------+---------------+-------------------
v_vmart_node0001 | Join | 131906
v_vmart_node0002 | Join | 227778
v_vmart_node0003 | NetworkSend | 524080
(3 rows)
可以使用 Linux watch
命令经常地监控长时间运行的查询。常见使用案例包括:
观察每个 Vertica 群集节点上查询计划内的运算符执行情况。
监控可能在各群集节点之间不平衡的工作负载,例如,一些节点处于空闲状态,而其他节点却处于活动状态。这种不平衡情况可能是由数据偏离或硬件问题所导致。
在以下示例中,watch
将查询每个节点上执行时间最长的运算符。该命令指定每秒重新执行一次查询:
watch -n 1 -d "vsql VMart -c\"SELECT node_name, operator_name, counter_value execution_time_us
FROM v_monitor.execution_engine_profiles WHERE counter_name='execution time (us)'
LIMIT 1 OVER(PARTITION BY node_name ORDER BY counter_value DESC);
Every 1.0s: vsql VMart -c"SELECT node_name, operator_name, counter_value execution_time_us FROM v_monitor.execu... Thu Jan 21 15:00:44 2016
node_name | operator_name | execution_time_us
------------------+---------------+-------------------
v_vmart_node0001 | Root | 110266
v_vmart_node0002 | UnionAll | 38932
v_vmart_node0003 | Scan | 22058
(3 rows)
Vertica 收集所有查询(包括那些失败的查询)的资源使用情况数据,并将这些数据汇总到系统表
QUERY_CONSUMPTION
中。这些数据包括有关每个查询的以下信息:
时钟持续时间
消耗的 CPU 周期
保留和分配的内存
发送和接收的网络字节数
读取和写入的磁盘字节数
溢出的字节数
分配的线程数
输出到客户端的行数
读取和写入的行数
可以通过查询的事务和语句 ID 获取有关各查询的信息。TRANSACTION_ID
和 STATEMENT_ID
列为每个查询语句提供唯一的键。
例如,会分析以下查询:
=> PROFILE SELECT pd.category_description AS 'Category', SUM(sf.sales_quantity*sf.sales_dollar_amount) AS 'Total Sales'
FROM store.store_sales_fact sf
JOIN public.product_dimension pd ON pd.product_version=sf.product_version AND pd.product_key=sf.product_key
GROUP BY pd.category_description;
NOTICE 4788: Statement is being profiled
HINT: Select * from v_monitor.execution_engine_profiles where transaction_id=45035996274751822 and statement_id=1;
NOTICE 3557: Initiator memory for query: [on pool general: 256160 KB, minimum: 256160 KB]
NOTICE 5077: Total memory required by query: [256160 KB]
Category | Total Sales
----------------------------------+-------------
Non-food | 1147919813
Misc | 1158328131
Medical | 1155853990
Food | 4038220327
(4 rows)
可以使用 Vertica 返回的事务和语句 ID 从 QUERY_CONSUMPTION
获取分析数据,例如,通过网络为给定查询发送的总字节数:
=> SELECT NETWORK_BYTES_SENT FROM query_consumption WHERE transaction_id=45035996274751822 AND statement_id=1;
NETWORK_BYTES_SENT
--------------------
757745
(1 row)
QUERY_CONSUMPTION
保存来自所有查询的数据,无论是否显式分析。
QUERY_CONSUMPTION
包括它从
执行_引擎_配置文件
中的计数器汇总的数据。在前面的示例中,NETWORK_BYTES_SENT
汇总了可通过 EXECUTION_ENGINE_PROFILES
中的多个计数器访问的数据。EXECUTION_ENGINE_PROFILES
的等效查询如下所示:
=> SELECT operator_name, counter_name, counter_tag, SUM(counter_value) FROM execution_engine_profiles
WHERE transaction_id=45035996274751822 AND statement_id=1 AND counter_name='bytes sent'
GROUP BY ROLLUP (operator_name, counter_name, counter_tag) ORDER BY 1,2,3, GROUPING_ID();
operator_name | counter_name | counter_tag | SUM
---------------+--------------+--------------------------------+--------
NetworkSend | bytes sent | Net id 1000 - v_vmart_node0001 | 252471
NetworkSend | bytes sent | Net id 1000 - v_vmart_node0002 | 251076
NetworkSend | bytes sent | Net id 1000 - v_vmart_node0003 | 253717
NetworkSend | bytes sent | Net id 1001 - v_vmart_node0001 | 192
NetworkSend | bytes sent | Net id 1001 - v_vmart_node0002 | 192
NetworkSend | bytes sent | Net id 1001 - v_vmart_node0003 | 0
NetworkSend | bytes sent | Net id 1002 - v_vmart_node0001 | 97
NetworkSend | bytes sent | | 757745
NetworkSend | | | 757745
| | | 757745
(10 rows)
QUERY_CONSUMPTION
和 EXECUTION_ENGINE_PROFILES
也有如下不同:
QUERY_CONSUMPTION
保存来自所有查询的数据,无论其持续时间如何,也无论是否显式分析它们。它还包括有关不成功查询的数据。
EXECUTION_ENGINE_PROFILES
仅包括来自执行长度超过设定阈值或您显式分析的查询的数据。它也不包括不成功查询的数据。
要通过查询计划及其各个 路径监控实时数据流,请查询以下系统表:
执行_引擎_配置文件
和
QUERY_PLAN_PROFILES
。这些表提供了有关 Vertica 如何执行查询计划及其各个
路径的数据:
执行_引擎_配置文件
汇总了查询执行运行。
QUERY_PLAN_PROFILES
显示实时数据流,以及每个查询计划路径所用的时间和资源。
每个查询计划路径都具有唯一的 ID,如以下
EXPLAIN
输出片段中所示。
每个表都提供特定于路径的数据。例如,QUERY_PLAN_PROFILES
为每个路径提供高级别数据,包括:
查询操作执行时长
路径操作所用的内存大小
通过网络发送/接收的数据大小
例如,您可能观察到 GROUP BY HASH
操作的执行时间为 0.2 秒,使用的内存为 100MB。
实时分析至少需要待监控事务的 ID。如果事务包括多个语句,则还需要语句 ID。可以通过对要分析的查询发出
PROFILE
来获取语句和事务 ID。随后可使用这些标识符来查询系统表 EXECUTION_ENGINE_PROFILES
和 QUERY_PLAN_PROFILES
。
有关详细信息,请参阅分析单个语句。
存储在
执行_引擎_配置文件
系统表中的实时分析计数器可用于当前执行的所有语句,包括
合并等内部操作。
如果满足以下任一条件,则可以在查询执行完成后使用分析计数器:
查询是通过
PROFILE
命令运行的
Vertica 元函数
ENABLE_PROFILING
启用了系统范围的分析。
查询运行了两秒以上。
分析计数器保存在 EXECUTION_ENGINE_PROFILES
系统表中,直至超出存储配额。
例如:
分析查询以从 EXECUTION_ENGINE_PROFILES
获取 transaction_id
和 statement_id
。例如:
=> PROFILE SELECT * FROM t1 JOIN t2 ON t1.x = t2.y;
NOTICE 4788: Statement is being profiled
HINT: Select * from v_monitor.execution_engine_profiles where transaction_id=45035996273955065 and statement_id=4;
NOTICE 3557: Initiator memory for query: [on pool general: 248544 KB, minimum: 248544 KB]
NOTICE 5077: Total memory required by query: [248544 KB]
x | y | z
---+---+-------
3 | 3 | three
(1 row)
查询系统表
QUERY_PLAN_PROFILES
。
transaction_id
、statement_id
、path_id
和 path_line_index
进行排序。
=> SELECT ... FROM query_plan_profiles
WHERE transaction_id=45035996273955065 and statement_id=4;
ORDER BY transaction_id, statement_id, path_id, path_line_index;
实时分析用于监控大型(长时间运行的)查询。请按照以下步骤监控大型查询的计划:
通过查询系统表
CURRENT_SESSION
获取要分析的查询计划的语句和事务 ID:
=> SELECT transaction_id, statement_id from current_session;
transaction_id | statement_id
-------------------+--------------
45035996273955001 | 4
(1 row)
运行查询:
=> SELECT * FROM t1 JOIN t2 ON x=y JOIN ext on y=z;
查询系统表
QUERY_PLAN_PROFILES
,并对 transaction_id、statement_id、path_id 和 path_line_index 列进行排序。
=> SELECT ... FROM query_plan_profiles WHERE transaction_id=45035996273955001 and statement_id=4
ORDER BY transaction_id, statement_id, path_id, path_line_index;
也可以使用 Linux watch
命令监控长时间运行的查询(请参阅实时分析)。
以下一系列命令会为长时间运行的查询创建表,然后查询系统表 QUERY_PLAN_PROFILES
:
创建名为 longq
的表:
=> CREATE TABLE longq(x int);
CREATE TABLE
=> COPY longq FROM STDIN;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself.
>> 1
>> 2
>> 3
>> 4
>> 5
>> 6
>> 7
>> 8
>> 9
>> 10
>> \.
=> INSERT INTO longq SELECT f1.x+f2.x+f3.x+f4.x+f5.x+f6.x+f7.x
FROM longq f1
CROSS JOIN longq f2
CROSS JOIN longq f3
CROSS JOIN longq f4
CROSS JOIN longq f5
CROSS JOIN longq f6
CROSS JOIN longq f7;
OUTPUT
----------
10000000
(1 row)
=> COMMIT;
COMMIT
通过使用 vsql \o
命令禁止查询在终端窗口上输出:
=> \o /home/dbadmin/longQprof
查询新表:
=> SELECT * FROM longq;
获取事务和语句 ID:
=> SELECT transaction_id, statement_id from current_session;
transaction_id | statement_id
-------------------+--------------
45035996273955021 | 4
(1 row)
关闭 \o
命令以便 Vertica 继续将查询计划信息保存到您已指定的文件。或者,使其保持打开状态并在查询系统表 QUERY_PLAN_PROFILES
后检查该文件。
=> \o
查询系统表 QUERY_PLAN_PROFILES
:
=> SELECT
transaction_id,
statement_id,
path_id,
path_line_index,
is_executing,
running_time,
path_line
FROM query_plan_profiles
WHERE transaction_id=45035996273955021 AND statement_id=4
ORDER BY transaction_id, statement_id, path_id, path_line_index;
QUERY_PLAN_PROFILES
表的输出会因 path_line
列而非常宽。为提高可读性,使用下面的一个或多个选项查询 QUERY_PLAN_PROFILES
:
按 transaction_id
、statement_id
、path_id
和 path_line_index
对输出排序:
=> SELECT ... FROM query_plan_profiles
WHERE ...
ORDER BY transaction_id, statement_id, path_id, path_line_index;
使用列别名减小列宽:
=> SELECT statement_id AS sid, path_id AS id, path_line_index AS order,
is_started AS start, is_completed AS end, is_executing AS exe,
running_time AS run, memory_allocated_bytes AS mem,
read_from_disk_bytes AS read, received_bytes AS rec,
sent_bytes AS sent, FROM query_plan_profiles
WHERE transaction_id=45035996273910558 AND statement_id=3
ORDER BY transaction_id, statement_id, path_id, path_line_index;
使用 vsql \o
命令将
EXPLAIN
输出重定向至文件:
=> \o /home/dbadmin/long-queries
=> EXPLAIN SELECT * FROM customer_dimension;
=> \o
Vertica 会一直保留查询数据,直至超出表的存储配额,此时它会自动清除最旧的查询,以便为新查询腾出空间。还可以通过调用以下函数之一来清除已分析的数据:
CLEAR_PROFILING 会从内存中清除已分析的数据。例如,下列命令清除常规查询运行信息的分析,如已用的查询字符串和查询持续时间。
=> SELECT CLEAR_PROFILING('query');
CLEAR_DATA_COLLECTOR 会清除与数据收集器表和函数相关的所有内存及磁盘记录,并重置 DATA_COLLECTOR 系统表中的收集统计信息。
FLUSH_DATA_COLLECTOR 会一直等到内存日志移动到磁盘,然后刷新数据收集器,使 DataCollector 日志与磁盘存储同步。
Vertica 按照配置的保留策略的规定保留它收集的历史数据。
如果分析未涵盖不理想的查询,调用下列函数之一可能有所帮助:
ANALYZE_WORKLOAD
分析系统表中保存的系统信息,并根据统计信息、系统和
数据收集器事件以及数据库-表-投影设计的组合提供优化建议。
ANALYZE_STATISTICS
从存储与指定表或列相关联的投影的所有节点中收集并聚合数据示例和存储信息。
您还可以通过 Database Designer 运行查询。请参阅增量设计。
EXECUTION_ENGINE_PROFILES 表包含每个分析计数器的数据作为表中的一行。例如,执行时间 (us) 计数器在一个行中,该行生成的计数器在另一行中。由于有许多不同的分析计数器,因此每个运算符存在多行分析数据。系统默认安装一些示例视图,以简化查看分析计数器的过程。
以下脚本将创建 v_demo
架构并将视图置于该架构中。
/opt/vertica/scripts/demo_eeprof_view.sql
每个分析计数器都有一个视图,用于简化查看单个计数器值的过程。例如,若要查看所有运算符的执行时间,请从数据库发出以下命令:
=> SELECT * FROM v_demo.eeprof_execution_time_us;
要查看可用于所有分析查询的所有计数器值:
=> SELECT * FROM v_demo.eeprof_counters;
要选择可用于所有分析查询的所有不同运算符:
=> SELECT * FROM v_demo.eeprof_operators;
以下视图可以合并:
=> SELECT * FROM v_demo.eeprof_execution_time_us
NATURAL LEFT OUTER JOIN v_demo.eeprof_rows_produced;
要查看执行时间和为特定事务生成的行以及按每个节点执行时间所排列的 statement_id
:
=> SELECT * FROM v_demo.eeprof_execution_time_us_rank
WHERE transaction_id=45035996273709699
AND statement_id=1
ORDER BY transaction_id, statement_id, node_name, rk;
要查看按每个节点执行时间所排列的前五个运算符:
=> SELECT * FROM v_demo.eeprof_execution_time_us_rank
WHERE transaction_id=45035996273709699
AND statement_id=1 AND rk<=5
ORDER BY transaction_id, statement_id, node_name, rk;
区域设置指定用户的语言、所在国家/地区以及任何特殊的可变首选项(例如排序规则)。Vertica 会根据区域设置确定某些字符串函数的行为。此外,区域设置还确定了需要进行排序和比较的各种 SQL 命令(例如聚合 GROUP BY
和 ORDER BY
子句、联接和分析 ORDER BY
子句)的排序规则。
Vertica 数据库的默认区域设置为 en_US@collation=binary
(美国英语)。可以定义供数据库中的所有会话使用的新默认区域设置,也可以覆盖单个会话的区域设置。但是,无论会话排序规则如何,请始终采用默认 en_US@collation=binary
排序规则来对投影进行排序。特定区域设置的排序功能会在查询时应用。
如果您将区域设置设为 NULL,则 Vertica 会将区域设置设为 en_US_POSIX
。您可以通过发出 vsql 元命令 \locale
将区域设置设回默认区域设置和排序规则。例如:
=> set locale to '';
INFO 2567: Canonical locale: 'en_US_POSIX'
Standard collation: 'LEN'
English (United States, Computer)
SET
=> \locale en_US@collation=binary;
INFO 2567: Canonical locale: 'en_US'
Standard collation: 'LEN_KBINARY'
English (United States)
=> \locale
en_US@collation-binary;
可以通过 ODBC、JDBC 和 ADO.net 设置区域设置。
Vertica 区域设置规范遵循 ICU 库所实施的 Unicode LDML 标准的一部分。
以下部分介绍 Vertica 如何处理区域设置。
区域设置为会话范围的设置,仅适用于在该会话中执行的查询。您无法为单个查询指定区域设置。当您启动会话时,它将从配置参数 DefaultSessionLocale
中获取区域设置。
使用默认值 en_US@collation=binary
以外的区域设置运行查询时,以下限制适用:
当左侧 NOT IN
一列或多列为 CHAR
或 VARCHAR
时,多列 NOT IN
不支持子查询。例如:
=> CREATE TABLE test (x VARCHAR(10), y INT);
=> SELECT ... FROM test WHERE (x,y) NOT IN (SELECT ...);
ERROR: Multi-expression NOT IN subquery is not supported because a left
hand expression could be NULL
test.x
和 test.y
具有 NOT NULL 约束,也会出现错误。
如果外查询含有对 CHAR
或 VARCHAR
列执行 GROUP BY
的操作,则不支持关联的 HAVING
子句子查询。在以下示例中,外查询中的 GROUP BY x
会导致以下错误:
=> DROP TABLE test CASCADE;
=> CREATE TABLE test (x VARCHAR(10));
=> SELECT COUNT(*) FROM test t GROUP BY x HAVING x
IN (SELECT x FROM test WHERE t.x||'a' = test.x||'a' );
ERROR: subquery uses ungrouped column "t.x" from outer query
使用分析函数的子查询不支持在 HAVING
子句中使用。例如:
=> DROP TABLE test CASCADE;
=> CREATE TABLE test (x VARCHAR(10));
=> SELECT MAX(x)OVER(PARTITION BY 1 ORDER BY 1) FROM test
GROUP BY x HAVING x IN (SELECT MAX(x) FROM test);
ERROR: Analytics query with having clause expression that involves
aggregates and subquery is not supported
投影数据按照默认的 en_US@collation=binary
排序规则进行排序。因此,不论会话设置如何,发布以下命令都会根据二进制排序规则生成按 col1
排序的投影:
=> CREATE PROJECTION p1 AS SELECT * FROM table1 ORDER BY col1;
在此情况下,straße
和 strasse
在磁盘上的存储位置不相邻。
按二进制排序规则排序也意味着排序优化在除二进制以外的区域设置中不起作用。如果在非二进制区域设置中创建表或投影,Vertica 会返回以下警告:
WARNING: Projections are always created and persisted in the default
Vertica locale. The current locale is de_DE
当区域设置是非二进制时,Vertica 可使用
COLLATION
函数将输入转换为按正常顺序排序的二进制字符串。
根据以下公式,此转换增加了输入所需的字节数:
result_column_width = input_octet_width * CollationExpansion + 4
CollationExpansion
配置参数的默认值为 5。
CHAR
字段会按固定长度显示,包括任何尾随空格。在内部处理 CHAR
字段时,首先会去除掉尾随空格。对于 VARCHAR
字段,尾随空格会被视为有意义的字符加以处理;但是,对使用非二进制区域设置的任一类型字符串字段进行排序或比较时,尾随空格会被忽略。
VARCHAR
和 CHAR
数据类型的最大长度参数是指可以存储在该字段中的八位字节(字节)数,而非字符数。使用多字节 UTF-8 字符时,字段的大小必须可容纳每个字符 1 到 4 个字节,具体取决于数据。
Vertica 支持指定 collation
关键字的长格式。Vertica 将长格式处理扩展到可以接受排序规则实参。
[language][_script][_country][_variant][@collation‑spec]
提供了以下语法选项:
区域设置规范字符串不区分大小写。例如,en_us
和 EN_US
是等效的。
您可以使用连字符代替下划线。例如: [-script
]
es
,英语为 en
,法语为 fr
。两字母语言代码使用 ISO-639 标准。_script
_country
FR
代表法国,CA
代表加拿大。两字母国家/地区代码使用 ISO-3166 标准。_variant
_EURO
。变体可以具有任意数量的带下划线的关键字。例如,EURO_WIN
是 Windows 计算机上欧元货币的变体。
变体代码的另一个用途是指定区域设置的排序规则(排序顺序)。例如,es__TRADITIONAL
区域设置使用传统的排序顺序,该排序顺序与西班牙的默认现代排序不同。
@collation‑spec
collation
:
@collation=collation‑type[;arg]...
排序规则可以指定一个或多个以分号分隔的实参,如下所述。
排序规则类型 设置为以下值之一:
big5han
:拉丁文按拼音排序,CJK 字符(在中文中使用)按 big5 字符集排序。
dict
:字典样式的排序(例如,在僧伽罗语中)。
direct
:印地语变体。
gb2312/gb2312han
:拉丁文按拼音排序,CJK 字符(在中文中使用)按 gb2312han 字符集排序。
phonebook
:电话簿样式的排序(例如,在德语中)。
pinyin
:拉丁文和 CJK 字符按拼音排序;即,将 CJK 字符逐个字符音译为拼音(在中文中使用)后进行顺序。
reformed
:改良后的排序规则(例如,在瑞典语中)。
standard
:每种语言的默认排序。对于 root,其为 [UCA] 顺序;对于每个其他区域设置,其与 UCA(Unicode 排序规则算法)排序相同,只是会对相应语言的某些字符进行适当的修改。下面是某些区域设置的其他选项;它们仅在某些区域设置中有效。
stroke
:拉丁文按拼音排序,不支持 CJK 字符(在中文中使用)的笔划顺序。
traditional
:传统样式的排序(例如,在西班牙语中)。
unihan
:拉丁文按拼音排序,不支持 CJK 字符(在中文中使用)的 Unihan 部首笔划排序。
binary
:Vertica 默认,提供 UTF-8 八位字节排序。
注意:
排序规则可能默认为 root(ICU 默认排序规则)。
排序规则关键字及其同义词的值无效不会导致错误。例如,以下代码不会生成错误,只是会忽略无效值:
=> \locale en_GB@collation=xyz
INFO 2567: Canonical locale: 'en_GB@collation=xyz'
Standard collation: 'LEN'
English (United Kingdom, collation=xyz)
有关排序选项的详细信息,请参阅 Unicode 区域设置数据标记语言 (LDML)。
collation
可以指定以下一个或多个实参:
如果前缀可被解析为已知的区域设置版本,则会接受错误的区域设置字符串。
例如,以下代码有效,因为可对语言进行解析:
=> \locale en_XX
INFO 2567: Canonical locale: 'en_XX'
Standard collation: 'LEN'
English (XX)
以下代码无效,因为无法对语言进行解析:
=> \locale xx_XX
xx_XX: invalid locale identifier
en_US.UTF-8
之类的 POSIX 类型区域设置在忽略编码部分“UTF-8”时有效。
Vertica 使用 icu4c-4_2_1 库及某些扩展来支持基本的区域设置/排序规则处理。这不满足 当前的区域设置处理标准:(https://tools.ietf.org/html/rfc5646)。
使用 phonebook
样式的排序规则指定德国使用的德国区域设置 (de
):
=> \locale de_DE@collation=phonebook
INFO 2567: Canonical locale: 'de_DE@collation=phonebook'
Standard collation: 'KPHONEBOOK_LDE'
German (Germany, collation=Phonebook Sort Order)
Deutsch (Deutschland, Sortierung=Telefonbuch-Sortierregeln)
指定德国使用的德国区域设置 (de
),其中 phonebook
样式的排序规则和强度设置为“次要 (secondary)”:
=> \locale de_DE@collation=phonebook;colStrength=secondary
INFO 2567: Canonical locale: 'de_DE@collation=phonebook'
Standard collation: 'KPHONEBOOK_LDE_S2'
German (Germany, collation=Phonebook Sort Order)
Deutsch (Deutschland, Sortierung=Telefonbuch-Sortierregeln)
Vertica 接受缩写形式的区域设置。可以使用缩写指定区域设置和键名对/值名称。
若要确定区域设置的缩写,请输入全称,然后查看 INFO 的最后一行,如下所示:
\locale frINFO: Locale: 'fr'
INFO: French
INFO: français
INFO: Short form: 'LFR'
指定 en
(英语)区域设置:
\locale LENINFO: Locale: 'en'
INFO: English
INFO: Short form: 'LEN'
使用 phonebook
样式的排序规则指定德国使用的德国区域设置 (de
):
\locale LDE_KPHONEBOOKINFO: Locale: 'de@collation=phonebook'
INFO: German (collation=Phonebook Sort Order)
INFO: Deutsch (Sortierung=Telefonbuch-Sortierregeln)
INFO: Short form: 'KPHONEBOOK_LDE'
使用 phonebook
样式的排序规则指定德国使用的德国区域设置 (de
):
\locale LDE_KPHONEBOOK_S2INFO: Locale: 'de@collation=phonebook'
INFO: German (collation=Phonebook Sort Order)
INFO: Deutsch (Sortierung=Telefonbuch-Sortierregeln)
INFO: Short form: 'KPHONEBOOK_LDE_S2'
Vertica 支持 Unicode 转换格式 8 或 UTF8,其中 8 表示 8 位。UTF-8 是由 Ken Thompson 和 Rob Pike 创建的 Unicode 可变长度字符编码。UTF-8 可以表示 Unicode 标准中的任何通用字符。UTF-8 的字节代码和字符分配的初始编码与 ASCII 一致。因此,对于处理 ASCII 但保留其他值的软件,UTF8 仅需略加更改,或者无需更改。
Vertica 数据库服务器要求以 UTF-8 格式接收所有数据,且 Vertica 以 UTF-8 格式输出所有数据。ODBC API 对 Windows 系统上采用 UCS-2 形式的数据进行操作,通常 UTF-8 位于 Linux 系统上。JDBC 和 ADO.NET API 对采用 UTF-16 形式的数据进行操作。在使用 API 调用向 Vertica 发送数据并接收数据时,客户端驱动程序会自动在数据和 UTF-8 之间进行转换。驱动程序不会转换通过执行 COPY 或 COPY LOCAL 语句加载的数据。
以下字符串函数会将 VARCHAR
实参视为 UTF-8 字符串(未指定 USING OCTETS
时),而不管区域设置如何。
Vertica 提供了支持国际化的字符串函数。除非另有说明,否则这些字符串函数可指定应将 VARCHAR
实参解释为八进制(字节)序列还是(对区域设置敏感的)字符序列。通过向函数添加参数 USING OCTETS
和 USING CHARACTERS
(默认)指定此信息。
下表列出了所有对区域设置敏感的字符串函数:
使用
COPY
NATIVE 解析器加载数据时,要求输入数据文件符合本附录中所述的要求。所有 NATIVE 文件必须包含:
将 NATIVE 文件加载到表中:示例部分将介绍使用 NATIVE 解析器加载数据的示例。
NATIVE 二进制文件的第一部分由文件签名构成。签名内容是固定的,列在下表中。
签名确保文件既没有被非 8 位文件传输损坏,也没有去除回车键、换行符或 null 值。如果签名完整,Vertica 将确定文件未损坏。
在文件签名之后,该文件必须定义文件中每列的宽度,如下所示。
每列宽度由其包含的数据类型确定。下表说明了每个数据类型所需的列宽度以及数据编码。
文件标题后面是包含每行数据的数据的一系列记录。每个记录以标题开头:
记录标题后面是行的列值。这些值没有分隔符。它们在数据行中的位置基于上一列数据结束的位置进行计算。大部分数据类型具有固定宽度,因此位置很容易确定。宽度可变的值(例如 VARCHAR 和 VARBINARY)以值包含的字节数计数开头。
有关每个数据类型的值如何存储在行数据中的详细信息,请参阅上一部分的表。
以下示例将展示如何创建表并加载包含单行数据的 NATIVE 文件。此表包含所有可能的数据类型。
=> CREATE TABLE allTypes (INTCOL INTEGER,
FLOATCOL FLOAT,
CHARCOL CHAR(10),
VARCHARCOL VARCHAR,
BOOLCOL BOOLEAN,
DATECOL DATE,
TIMESTAMPCOL TIMESTAMP,
TIMESTAMPTZCOL TIMESTAMPTZ,
TIMECOL TIME,
TIMETZCOL TIMETZ,
VARBINCOL VARBINARY,
BINCOL BINARY,
NUMCOL NUMERIC(38,0),
INTERVALCOL INTERVAL
);
=> COPY allTypes FROM '/home/dbadmin/allTypes.bin' NATIVE;
=> \pset expanded
Expanded display is on.
=> SELECT * from allTypes;
-[ RECORD 1 ]--+------------------------
INTCOL | 1
FLOATCOL | -1.11
CHARCOL | one
VARCHARCOL | ONE
BOOLCOL | t
DATECOL | 1999-01-08
TIMESTAMPCOL | 1999-02-23 03:11:52.35
TIMESTAMPTZCOL | 1999-01-08 07:04:37-05
TIMECOL | 07:09:23
TIMETZCOL | 15:12:34-04
VARBINCOL | \253\315
BINCOL | \253
NUMCOL | 1234532
INTERVALCOL | 03:03:03
allTypes.bin
文件的内容在下面显示为原始十六进制转储:
4E 41 54 49 56 45 0A FF 0D 0A 00 3D 00 00 00 01 00 00 0E 00
08 00 00 00 08 00 00 00 0A 00 00 00 FF FF FF FF 01 00 00 00
08 00 00 00 08 00 00 00 08 00 00 00 08 00 00 00 08 00 00 00
FF FF FF FF 03 00 00 00 18 00 00 00 08 00 00 00 73 00 00 00
00 00 01 00 00 00 00 00 00 00 C3 F5 28 5C 8F C2 F1 BF 6F 6E
65 20 20 20 20 20 20 20 03 00 00 00 4F 4E 45 01 9A FE FF FF
FF FF FF FF 30 85 B3 4F 7E E7 FF FF 40 1F 3E 64 E8 E3 FF FF
C0 2E 98 FF 05 00 00 00 D0 97 01 80 F0 79 F0 10 02 00 00 00
AB CD AB CD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 64 D6 12 00 00 00 00 00 C0 47 A3 8E 02 00 00 00
下表将此文件拆分为各个部分,并介绍了其包含的值。