跳到主要内容

· 1 分钟阅读

1. 概述

IvorySQL v4 引入了块级增量备份和增量备份合并功能,旨在优化数据库备份与恢复流程。通过 pg_basebackup 工具支持增量备份,显著降低了存储需求和备份时间。同时,pg_combinebackup 工具能够将多个增量备份合并为单个完整备份,提升了数据恢复的灵活性和效率。

2. 特性介绍

IvorySQL 的增量备份通过记录每个 checkpoint 周期内的数据变更,确保仅备份自上次备份以来发生变化的数据块。这种机制不仅减少了所需的存储空间,也缩短了备份过程中的 I/O 操作时间。此外,pg_combinebackup 工具支持将多个增量备份合并为一个完整备份,使得在数据恢复时不再需要逐个应用增量备份,大大简化了恢复流程。

img

2.1 启用增量备份特性

为启用增量备份功能,首先需要在数据库中配置相关参数并重新加载配置文件:

ALTER SYSTEM SET summarize_wal = ON;
SELECT pg_reload_conf();

接着,创建测试表并插入初始数据,以便后续进行备份操作:

CREATE TABLE sample_table(t INT);
INSERT INTO sample_table VALUES (1);
SELECT * FROM sample_table;

img

2.2 执行全量备份

使用 pg_basebackup 工具执行全量备份,并生成包含 backup_manifest 的备份文件,以便后续增量备份的基础:

pg_basebackup -Fp -D /backup/\$(date +%Y-%m-%d*%H%M%S-FULL)

此命令将全量备份数据保存至指定目录,并记录当前的备份状态。

2.3 进行增量备份

(1)在对数据进行修改后,执行增量备份以记录自上次全量备份或增量备份以来的变更。

修改表数据并执行第一次增量备份:

UPDATE sample_table SET t = 2;
SELECT * FROM sample_table;

img

执行增量备份:

pg_basebackup -Fp -D /backup/\$(date +%Y-%m-%d*%H%M%S-INCREMENTAL) -i /backup/2025-02-20_161530-FULL/backup_manifest

(2)再次修改表数据并执行第二次增量备份。

UPDATE sample_table SET t = 3;
SELECT * FROM sample_table;

img

然后执行第二次增量备份:

pg_basebackup -Fp -D /backup/\$(date +%Y-%m-%d*%H%M%S-INCREMENTAL) -i /backup/2025-02-20_161808-INCREMENTAL/backup_manifest

img

2.4 合并增量备份

合并全量备份和多个增量备份,创建新的完整备份,以便后续的恢复操作:

cd /backup
pg_combinebackup 2025-02-20_161530-FULL 2025-02-20_161808-INCREMENTAL 2025-02-20_162115-INCREMENTAL -o 2025-02-20_66666-FULL

img

注意事项:全量备份必须在参数列表的首位,增量备份需要按时间顺序排列,以确保数据恢复的完整性与一致性。

2.5 恢复合并后的备份

启动数据库并验证数据的完整性:

pg_ctl start -D /backup/2025-02-20_66666-FULL -o '-p 5435'
SELECT * FROM sample_table;

img

2.6 合并指定增量备份

如需恢复到特定的增量备份状态,可以选择合并到某个中间增量备份:

pg_combinebackup 2025-02-20_161530-FULL 2025-02-20_161808-INCREMENTAL -o 2025-02-20_77777-FULL

img

然后启动数据库并验证数据:

pg_ctl start -D /backup/2025-02-20_77777-FULL -o '-p 5436'
SELECT * FROM sample_table;

img

3. 注意事项

备份顺序

确保全量备份在合并命令的首位,增量备份按照时间顺序排列,以避免合并过程中的数据不一致性问题。

增量备份存储管理

虽然增量备份减少了存储空间的需求,但恢复操作依赖于完整的增量备份链,任何增量备份的丢失将直接导致恢复失败。因此,建议定期检查和合并增量备份,以确保备份链的完整性和可靠性。

性能监控

在执行增量备份和合并操作时,监控数据库的性能,确保操作对系统性能的影响在可接受范围内,避免在高负载时段进行备份操作。

4. 总结

IvorySQL 提供的块级增量备份与 pg_combinebackup 工具显著提升了数据库的备份与恢复效率。通过只备份变更的数据块,增量备份在减小存储占用和缩短备份时间方面展现出卓越性能,而合并增量备份功能则简化了恢复流程,提升了操作的便捷性。

这些先进的特性使得 IvorySQL 成为大规模数据库和对数据恢复要求严格的环境的理想选择,能够提供灵活而高效的备份与恢复解决方案,确保数据的安全与可用性。

· 1 分钟阅读

IvorySQL v4 基于 PostgreSQL 17,引入了逻辑复制槽同步至热备份数据库的功能。这一改进有效解决了旧版本中主数据库与备份数据库切换后逻辑复制中断的问题。对于那些追求数据高可用性和业务连续性的数据库来说,这无疑是一个重大的利好消息。它不仅提升了系统的整体稳定性,还确保了在故障发生时,数据复制过程能够无缝继续,从而最大程度地降低了业务中断的可能性。

img

复制槽同步机制

前提条件

  1. 物理复制槽:主数据库与备份数据库之间必须存在物理复制槽,作为逻辑复制槽同步的基础,确保数据在主备之间有效传输。。
  2. 配置参数
    • 在备份数据库上配置 primary_slot_name 参数,并在使用 pg_basebackup 工具时指定复制槽。这一配置步骤确保了备份数据库能够正确接收主数据库数据。
    • 启用备份数据库的 hot_standby_feedback 功能,确保其能接收并反馈 WAL 日志。这一功能的启用保证了备份数据库在接收数据时的活跃性和反馈机制的完整性。
    • primary_conninfo 参数中指定有效的数据库名称(dbname)。这一配置确保了复制过程中的目标数据库是明确且正确的。
  3. 推荐配置:在主数据库上配置 standby_slot_names 参数以保持复制槽同步的一致性。这一配置有助于维护主备数据库间复制槽的一致状态,从而提高复制过程的可靠性。

实现方式

  1. 创建逻辑复制槽: 调用 pg_create_logical_replication_slot 函数时设置 failover=true,指定复制槽同步至备份数据库。这一设置至关重要,因为它决定了复制槽在主备切换时的行为。

  2. 订阅配置: 使用 CREATE SUBSCRIPTION 语句时指定 failover=true,确保复制槽同步至备份数据库。这为数据库管理员提供了灵活的配置选项,使他们能够根据实际业务需求和环境特点定制复制策略。

  3. 手动同步: 在备份数据库上执行 pg_sync_replication_slots 函数,手动同步复制槽。这种方法提供了一种即时同步的手段,特别适用于需要立即反映主数据库变更的场景。

  4. 自动同步: 设置备份数据库的 sync_replication_slots = on,实现定期自动同步,无需重启数据库。这一自动化功能简化了数据库的维护工作,使管理员能够将更多精力投入到其他关键任务中。

下图是配置及创建同步逻辑复制槽的流程图,详细的顺序讲解了如何配置同步逻辑复制槽基础参数,以及如何创建一个同步逻辑复制槽。

img

函数与参数

  1. pg_create_logical_replication_slot
  • 新增了 failover 参数,其默认值为 false。若设置为 true,则表示该复制槽需同步至备份数据库。
  • 示例语法:
    SELECT pg_create_logical_replication_slot('test_slot', 'test_decoding', false, false, true)
  • 这一函数的引入,为数据库管理员提供了更精细的控制手段,使得逻辑复制槽的管理更加灵活和高效。
  1. pg_sync_replication_slots
  • 该函数用于手动同步复制槽至备份数据库。
  • 示例语法:
    SELECT pg_sync_replication_slots()
  • 通过这一函数,可以即时地将主数据库的变更同步到备份数据库,确保数据的一致性和完整性。
  1. sync_replication_slots
  • 在备份数据库上设置该参数为 on,可实现复制槽变更的定期自动同步,无需重启数据库。
  • 这一自动化设置极大地减轻了数据库管理员的负担,使得复制槽的维护工作更加轻松和高效。

img

img

如上图所示,为主库和备库的复制槽的状态,主库创建了逻辑复制槽 test_slots2,并定义属性 failover 为 true,开启了这个槽的故障转移功能,通过手动或自动的方式,可以将逻辑复制槽 test_slots2 的状态及信息同步至备库,当发生主备切换或者故障转移时,备库被提升为新主时,逻辑复制槽 test_slots2 的信息将会保留,从而保证订阅端间断、不丢失数据。

操作步骤与验证

手动同步复制槽

搭建主数据库与备份数据库环境,确保主数据库和备份数据库的配置正确,包括物理复制槽和流复制用户。这一过程需要仔细规划和执行,以确保复制环境的稳定性和可靠性。

  1. 创建复制槽:
  • 执行
    SELECT pg_create_logical_replication_slot('test_slot2', 'test_decoding',false, false, true)
  • 这一操作是逻辑复制槽同步的起点,它为后续的数据复制奠定了基础。
  1. 在备份数据库上手动同步复制槽:
  • 执行
    SELECT pg_sync_replication_slots()
  • 这一操作确保了复制槽的变更能够及时反映到备份数据库上,从而保证了数据的同步性和一致性。
  1. 验证复制槽同步:
  • 在备份数据库上执行查询,以确保复制槽已同步至备份数据库。
  • 执行
    SELECT \* FROM pg_replication_slots
  • 这一验证步骤是确认复制槽同步成功的关键环节,它确保了复制过程的正确性和完整性。

自动同步复制槽

  1. 在备份数据库上设置 sync_replication_slots = on
  • 执行
    ALTER SYSTEM SET sync_replication_slots = on
  • 然后执行
    SELECT pg_reload_conf()
  • 这一自动化设置使得复制槽的同步工作更加简便和高效,管理员无需手动介入即可完成同步任务。
  1. 验证自动同步:

    在主数据库插入数据,观察备份数据库复制槽的变化,以确保主数据库复制槽的变更能够自动同步到备份数据库。这一验证步骤是确认自动同步功能正常工作的关键环节,它确保了复制过程的自动化和可靠性。

img

模拟主数据库与备份数据库的切换

  1. 在备份数据库上执行主数据库提升操作:
  • 模拟主数据库故障,将主数据库关机。
  • 在备份数据库上执行
    pg_ctl -D data promote
  • 这一操作模拟了主数据库发生故障时的应急切换,是测试复制槽同步功能在实际故障场景中表现的重要步骤。
  1. 在新的主数据库上继续进行逻辑复制:

    在新的主数据库绑定 VIP,以确保逻辑复制能够继续进行。这一操作确保了在主数据库切换后,逻辑复制能够无缝地继续进行,从而保证了业务的连续性和数据的一致性。

img

img

上图为切换后的新主,保留了原有的逻辑复制槽 my_subscription 和发布信息 my_publication。并且配置其他必要参数后,恢复新备库的逻辑复制槽同步能力。

订阅端根据 VIP 路由至新主数据库,继续使用同步的逻辑复制槽,保证逻辑复制的正常数据复制。

img

总结

逻辑复制槽同步功能提升了 IvorySQL 在高可用性环境下的可靠性,解决了主数据库与备份数据库切换后逻辑复制槽丢失的问题。这一功能的引入,使得数据库面对主备切换时,保证逻辑复制槽的信息不丢失,确保了数据复制过程的连续性和一致性。

同步过程需满足以下条件:

  • 设置 failover=true 的复制槽;
  • 主数据库与备份数据库间配置物理复制槽;
  • 备份数据库启用 hot_standby_feedback
  • 备份数据库的 primary_conninfo 参数配置正确的数据库名称;
  • 推荐配置 standby_slot_names 参数以确保一致性;
  • 通过 pg_sync_replication_slotssync_replication_slots 参数,备份数据库可以手动或自动同步复制槽,确保逻辑复制能够无缝切换。

这一系列的措施和功能,共同构成了 IvorySQL 中逻辑复制槽同步功能的强大和灵活,为数据库管理员提供了强大的工具,以应对各种复杂和挑战性的数据复制场景。

· 1 分钟阅读

前言

随着数据库应用场景的多样化,用户对数据管理的灵活性和隐私性提出了更高要求。IvorySQL 作为一款基于 PostgreSQL 并兼容 Oracle 的开源数据库,始终致力于在功能上保持领先和创新。在最新发布的 4.0 版本中,IvorySQL 新增了 Oracle 兼容特性 Invisible Column(不可见列),这一功能由社区贡献者 Imran Zaheer 提供,体现了开源社区协作的力量

Invisible Column 的引入,为开发者提供了在不影响现有应用的情况下动态调整数据库结构的新选择,进一步提升了 IvorySQL 在数据灵活性管理上的能力,为用户在数据库升级、兼容性优化等方面提供了更大的便利性。

本文将详细介绍这一特性的功能、使用场景以及使用方式。

什么是 Invisible Column?

在现代数据库开发中,列的可见性管理在一定程度上影响了应用程序的灵活性与迁移效率。Oracle 12c 提供了一项强大的功能:Invisible Column(不可见列)。这是一种隐藏数据列的特性,用于增强数据安全性和实现业务逻辑。这一功能为开发者提供了灵活性和控制能力,特别是在应用程序迁移或版本升级的场景中。

在 Oracle 中,Invisible Column 是指那些对大多数 SQL 查询和工具不可见的列。通过将列设置为不可见列:

  • 它不会出现在常规的 SELECT * FROM 查询结果中。
  • 它不会在 SQL*PlusOCI 的描述操作中显示。
  • 它不会包含在基于 %ROWTYPE 属性的记录定义中。

然而,不可见列仍然存在于表中,可以通过显式指定列名来访问或引用。另外,不可见列在使用时也有限制,要注意在外部表(External Tables)、聚簇表(Cluster Tables)、临时表(Temporary Tables)中无法使用不可见列。

Invisible Column 的应用场景

1. 应用程序迁移

不可见列在应用程序迁移过程中非常有用。当我们向现有表中添加新列时,不可见列可以避免影响旧应用程序的功能。旧的应用程序不会察觉新列的存在,而新的应用程序可以显式引用这些列。从而使应用程序的在线迁移变得更加简单顺畅。

2. 敏感数据保护

某些敏感数据可以通过不可见列存储,避免被大多数默认查询工具访问,从而降低意外暴露的风险。

3. 数据模型优化

在数据模型调整或调试过程中,可以临时将一些列设置为不可见列,确保它们不会影响常规查询,避免查询结果混淆。

在 IvorySQL 中使用 Invisible Column

Invisible Column 为 IvorySQL 4.0 版本中新增加的兼容特性,使用前请先确保您的版本为 4.0。

1. 创建不可见列

可以在创建表时直接将列定义为不可见列:

CREATE TABLE employees (
emp_id NUMBER,
emp_name VARCHAR2(50),
emp_salary NUMBER INVISIBLE
);

在此示例中,emp_salary 是不可见列,对默认查询不可见:

select*from employees ;
emp_id | emp_name
--------+----------
(0 rows)

2. 向不可见列插入数据

在向表中插入数据时,可以通过显式指定列名的方式向不可见列插入数据:

INSERT INTO employees(emp_id,emp_name,emp_salary) VALUES(1,'Jack',20000);
INSERT 0 1
INSERT INTO employees(emp_id,emp_name,emp_salary) VALUES(2,'Lucy',30000);
INSERT 0 1;

不带命名列的插入不能包含不可见列:

INSERT INTO employees VALUES(3,'Peter');
INSERT 0 1

3. 显示/修改现有列为不可见列

通过 VISIBLE 关键字,可以将不可见列改回普通列:

ALTER TABLE employees MODIFY emp_salary VISIBLE;
ALTER TABLE

如果需要将现有列设置为不可见列,可以使用 INVISIBLE

ALTER TABLE employees MODIFY emp_salary INVISIBLE;
ALTER TABLE

注意,不能将所有的列设置为不可见列。

4. psql \d 元命令

在 psql 中使用 \d 元命令时不会显示该表的不可见列信息:

\d employees
Table "public.employees"
Column | Type | Collation | Nullable | Default
------------+--------------+-----------+----------+---------
emp_id | number | | |
emp_name | varchar2(50) | | |
emp_salary | number | | |

可以使用含有更多表信息的 \d+ 元命令查看该表的不可见列信息:

\d+ employees
Table "public.employees"
Column | Type | Collation | Nullable | Default | Invisible | Storage | Compression | Stats target | Description
------------+--------------+-----------+----------+---------+-----------+----------+-------------+--------------+-------------
emp_id | number | | | | | main | | |
emp_name | varchar2(50) | | | | | extended | | |
emp_salary | number | | | | invisible | main | | |
Access method: heap

5. 访问 Invisible Column

在使用 SELECT* 查询表数据时,不会显示不可见列的数据:

SELECT * FROM employees ;
emp_id | emp_name
--------+----------
1 | Jack
2 | Lucy
3 | Peter
(3 rows)

虽然不可见列对默认查询不可见,但开发者仍然可以通过显式指定列名来访问它:

SELECT emp_name,emp_salary FROM employees ;
emp_name | emp_salary
----------+------------
Jack | 20000
Lucy | 30000
Peter |
(3 rows)

结语

不可见列功能是一项设计精妙的特性,为数据库开发和管理提供了更高的灵活性和安全性。通过合理利用不可见列,开发者可以轻松应对复杂的应用迁移场景,同时保持系统的稳定性和可扩展性。

如果您有正在使用 IvorySQL 数据库的项目,不妨尝试将此功能集成到您的解决方案中,提升整体效率和可靠性。

· 1 分钟阅读

日前,IvorySQL 4.0 发布,该版本新增了兼容 Oracle 包功能的新特性。

为了大家能够更好地理解和使用 IvorySQL 4.0,本文将简要介绍实现此功能时的设计思路。

Oracle 的包是什么?

包是包含了逻辑上相关的 PL/SQL 类型、变量、常量、子过程、游标和异常的一个模式对象。包被编译并存储在数据库中,多个应用可以共享包的内容。

包总是有一个包规范,包规范中声明了公有对象,这些公有对象可以在包外被引用。

如果公有对象中包含了游标或子过程,则包必须有一个包体。包体必须定义公有游标和公有子过程的代码。包体也可以声明并定义私有对象,私有对象不能在包外被引用,但可用于包内使用。最后,包体可以有一个初始化部分,这部分用于初始化变量,做一些一次性的设置步骤和异常处理。修改包体的时候,可以不修改包规范或引用包的公有对象的数据库对象,因此可以认为包体是一个黑盒。

IvorySQL 中包的实现

从内容来看,包体与嵌套子过程类似,包规范只是定义包体对外的接口,因此,从实现角度来看,包的实现过程可以和嵌套子过程类似。

我们主要处理的工作有如下几个方面:包的创建、更新、实例化、删除以及外部过程对包规范中包对象的引用。

  • 包的创建: 修改 psql 语法,使 psql 能将整个包的创建语句整体发到服务器,在服务器中增加包的创建语法,语法结构基本上和普通函数类似,因此与函数类似,无需在 SQL 端展开。包创建经过语法解析后走 DDL 流程,调用包的创建函数,将包的内容存储到系统表里面去。
  • 包的更新: 在 SQL 端支持更新语法,经语法解析后,调用包的更新函数,更新系统表内容,调用 plisql_package_Handler 走一遍 pl_gram.y,并失效包规范元组或包体元组,这样避免在运行时编译包和包体。
  • 包的删除: 需要在 SQL 端支持其删除语法,经语法解析后,调用包的删除函数,删除系统表该包的内容。
  • 包的实例化: 在第一次引用包的时候,如果包的内容没有在内存中(具体来说是一个 hash 表,类似于 portal 的 hash 表存储),则调用包的实例化函数,将包重新实例化,实例化其实是调用 PL/iSQL 端的 compile 函数,将包规范与包体重新编译,并将编译的结果放在当前进程的内存里,包的实例化应该是将包与包体的整体内容加载到内存中。
  • 包对象引用: 在 parse 阶段,提供查找包规范中的变量,类型、子过程的接口,优先在本模式下查找包中类型,然后查找系统表中的类型,在查找子过程时,优先在嵌套函数、包中、系统表中查找。
  • 包的失效与包的状态: 包中如果全是常量与类型,则包无状态,否则包是有状态的。包的状态在访问包的变量与函数时设置,在重建包时,会让包的本地实例失效,并且本地重新编译实例化,其他进程的包实例,如果包是有状态的,访问包中变量或类型,则首次访问报包的状态丢失错误,其后正常访问。

IvorySQL 中包的设计

新增的系统表与缓存

为了存储包体与包规范内容,新增了 2 个系统表:

系统表名称作用
pg_package.h存储包规范内容
pg_package_body.h存储包体内容

对应的系统缓存则有 4 个:

系统缓存名称作用
PKGBODYOID根据包体的 OID 查找包体的元组
PKGBODYPKGID根据包规范的 OID 查找包体的元组
PKGNAMEARGSNSP根据包名和模式的 OID 查找包的元组
PKGOID根据包规范的 OID 查找包的元组

包的实例化

包的实例化,类似于函数编译,是将用字符串定义的数据,转换成结构化数据。包的内容是由包规范和包体两部分构成,因此,包的编译需要进行一些特殊处理。增加相应的新函数分别编译包规范和包体,并将结果缓存到哈希表中。

另外,为了处理在删除包与包体的时候,本地缓存会失效,在创建包缓存的时候,注册一个包的失效函数,用来处理包的失效时,需要清除包的缓存状态。

/* register invalid cache */
CacheRegisterSyscacheCallback(PKGBODYOID, InvalidatePackageCacheCallback, (Datum) 0);
CacheRegisterSyscacheCallback(PKGOID, InvalidatePackageCacheCallback, (Datum) 0);

InvalidatePackageCacheCallback 将根据 hash 值,遍历 hash 表中的每一项,更新相应包的缓存状态。

包的缓存状态用一个 char 来表示,目前只用到三位 bit,0x01 表示包规范被更新了,0x02 表示包是否有包体,0x04 表示包体被更新了。

包中对象的引用

提供查找包中函数、类型、变量的接口,供 parse 阶段使用,以下是部分函数列表。

函数名称参数返回值说明
LookupPkgTypeByTypenameConst List* namesBool missing_okPkgType*根据语法阶段构造类型名称列表,查看是否是包中的类型。
LookupPkgVarByvarnamesConst List _names, Bool missing_okPkgVar*根据变量名称,查看是否是包中的变量
LookupPkgEntryByTypenameconst List *names, bool missing_okPkgEntry 根据名称,返回包中属性(类型或变量)
LookupPkgFuncParseState *pstate, List *fargs, FuncCall *fnFuncExpr根据函数名称,查看是否是包中的函数

在 PL/iSQL 非包内函数使用包的类型时,只需将类型的地址引用过来,而使用变量时,则需做一份本地映射,当涉及到该类的变量赋值时,需要进行特殊处理。一般来说,主要是切换到包的内存上下文,然后调用包的赋值函数。

函数形参或返回值引用包的类型

该部分需要修改 pg_proc 系统表的结构,需要增加字段来记录参数类型和返回值类型的名称,故在系统表中增加参数类型名称和返回值类型名称两列即可解决。

类似于 proargnames,增加 protypenames 用于记录参数类型的类型名称,增加 rettypename 记录返回值类型的名称。只有当相关项引用包的类型时赋值,不然为空。

因为 protypnames 是一个 text 数组,因此当有一个函数参数是包中类型时,该数组就不为空,且参数类型为包的项是由 TypeName 结构字符序列化得到,其他非包的参数类型为空字符串。

standard 包

支持 sys 模式下的 standard 包,可以不用带包名进行访问包中规范的对象,用户可以自行创建 standard 包。

DISCARD PACKAGE 语法

该功能是为兼容 PostgreSQL 的 DISCARD 功能做的,目前 PostgreSQL 支持 DISCARD SEQUENCEDISCARD TEMPDISCARD PLAN 等用来删除当前 session 的序列、临时表以及计划等缓存,并且 DISCARD ALL 支持删除本 session 的 PORTAL、临时表、计划、序列等临时存储。

IvorySQL 支持 DISCARD PACKAGE 语法,并且在 DISCARD ALL 中调用函数删除本 session 的包缓存。

逻辑备份还原支持包

pg_dump 工具中,包功能也得到了支持,用户可以使用 pg_dump 对包功能在内的数据进行备份恢复。

总结

以上就是实现兼容 Oracle 包功能时的设计思路。

通过包的形式将相关的功能模块化,使得数据库的过程、函数、变量和其他编程元素组织在一起形成自包含单元,便于管理和维护。由于实现细节隐藏在包体中,提高了代码的安全性和可维护性。包体中的代码在第一次调用时被加载到内存中,后续调用可以直接使用,减少了解析和加载时间。

· 1 分钟阅读

日前,IvorySQL 4.0 重磅发布,全面支持 PostgreSQL 17,并且增强了对 Oracle 的兼容性。

本篇文章将详细描述,如何从 IvorySQL 3.x 升级到 IvorySQL 4.0。

pg_upgrade 工具介绍

pg_upgrade 工具是 PostgreSQL 内置的跨版本升级工具,能够对数据库就地升级,不需要执行导出和导入操作。IvorySQL 源自于 PG,因此也能够使用 pg_upgrade 工具进行大版本升级。

pg_upgrade 提供了升级前的兼容性检查(-c 或者 --check 选项)功能,可以发现插件、数据类型不兼容等问题。如果指定了 --link 选项,新版本服务可以直接使用原有的数据库文件而不需要执行复制,通常可以在几分钟内完成升级操作。

常用的参数包括:

  • -b bindir,--old-bindir=bindir:旧的 IvorySQL  可执行文件目录
  • -B bindir,--new-bindir=bindir:新的 IvorySQL 可执行文件目录
  • -d configdir,--old-datadir=configdir:旧版本的数据目录
  • -D configdir,--new-datadir=configdir:新版本的数据目录
  • -c,--check:只检查升级兼容性,不更改任何数据
  • -k,--link:硬链接方式升级

下面介绍一下在 CentOS 8 平台上如何使用 pg_upgrade 将 IvorySQL 升级到最新的 4.0 版本。

升级准备

首先停止旧版本的 IvorySQL 3.4 数据库:

img

然后安装新版本的 IvorySQL 4.0 数据库:

img

初始化新版 IvorySQL 4.0 数据目录:

img

检查版本兼容性:

img

最后出现 Clusters are compatible 表明两个版本之间的数据不存在兼容性问题,可以进行升级。

正式升级

img

img

看到 Upgrade Complete 说明升级已经顺利完成。

更新统计信息

pg_upgrade 会创建新的系统表,并重用旧的数据进行升级,统计信息并不会随升级过程迁移,所以在启用新版本之前,应该首先重新收集统计信息,避免没有统计信息导致错误的查询计划。

首先启动新版本数据库:

img

手动运行 vacuum 命令:

img

升级后的清理

确认没有问题后删除旧库:

img

至此,我们就完成了整个升级过程。

如果您在后续的升级过程中,遇到问题,可以在 GitHub 上提交 issue,我们将及时为您解答。同时,如果您对 IvorySQL 后续的功能迭代有任何想法或者期待,也可以提交 issue 反馈给我们。

· 1 分钟阅读

一般来说,数据库的存储系统分为内存和外存两个部分,而除了内存数据库之外,数据最终都是持久化存储的,持久化就要将数据从内存 buffer 落盘到外存,因此今天我们来聊聊 IvorySQL 中的外部存储管理器。

存储文件的分类

IvorySQL 数据库使用的存储管理器基本上与 PostgreSQL 相同,其文件目录及文件组织结构如下图:

image

从存储的角度看,文件管理分为 3 个主要部分:

  • 配置相关的文件,比如 pg_controlpostgresql.confpg_hba.confPG_VERSION 等;
  • WAL 日志文件,000000010000000000000001000000010000000000000002 等;
  • 表,索引的数据记录文件,相应的剩余空间和可见性映射文件等。

配置相关的文件在 PostgreSQL 中使用标准文件读写函数处理,比如:记录数据库状态和操作的 pg_control 文件。其中后台模块通过 ReadControlFile()WriteControlFile()UpdateControlFile()  进行读写操作。前台工具通过 get_controlfile()update_controlfile()  进行读写操作。无论前台还是后台,这些读写操作最终还是通过标准文件操作函数 open()read()write()  进行。这些对配置文件的直接操作不在 SMGR 管控范畴。

WAL 日志文件最终也是通过标准文件函数 read()write() 等等完成读写(和配置文件类似)。但不同的是,文件名需要根据 LSN 动态计算获取。

表,索引数据记录存储在 base 目录下的相应数据库的子目录下。集群级别的 catalog(系统表)数据记录变化则反映在 global 目录下。系统表的管理与用户表类似,同样采用 OID 的命名方式。

SQL 语句访问存储介质过程  

一条 SQL 语句通常按照如下路径访问一个磁盘上的数据记录:

  • 首先语句先经过 5 阶段的查询处理:parse,rewrite,analyze,plan,execute。
  • 再进到 Table/Index Access Method 层。
  • Access Method 层通常会使用 Buffer Manager 服务,对数据条目进行操作,根据操作决定是否将数据块标记为“脏”。
  • Buffer Manager 层会使用 Storage Manager 服务,根据 buffer tag 调用 smgr_read()smgr_write() 来读取或写数据到存储介质。

image

SMGR API 接口  

PostgreSQL 原本有许多 SMGR API 接口的实现,但目前只留下 Magnetic Disk(md.c)的实现(磁盘)。实际上,磁盘管理器可以支持任何类型的设备,只要操作系统为该设备提供了标准的文件系统操作接口。但是保留存储管理器(smgr.c)这个中间层,以防有人引入其他类型的存储管理器。删除中间层不会节省明显的开销,因为对存储介质的操作,比一层 c 语言函数调用昂贵的多的多。f_smgr 是一组函数指针结构,包含了 PostgreSQL 对存储所需要的函数。smgr.c 定义所有函数接口的 wrapper 函数,这些 wrapper 会最终调用实际注册的实现函数,也就是 mdXXXX 函数。

static const f_smgr smgrsw[] = {
/* magnetic disk */
{
.smgr_init = mdinit,
.smgr_shutdown = NULL,
.smgr_open = mdopen,
.smgr_close = mdclose,
.smgr_create = mdcreate,
.smgr_exists = mdexists,
.smgr_unlink = mdunlink,
.smgr_extend = mdextend,
.smgr_prefetch = mdprefetch,
.smgr_read = mdread,
.smgr_write = mdwrite,
.smgr_writeback = mdwriteback,
.smgr_nblocks = mdnblocks,
.smgr_truncate = mdtruncate,
.smgr_immedsync = mdimmedsync,
}
};

SMgrRelation 是一个重要的结构体,几乎所有和 SMGR 相关的函数都需要这个结构。

image

SMGR 关键接口函数  

  • Smgrread() 根据 5 个参数:tablespace,database,relation,forknum 和 blocknum 定位一个 8K 数据块,并将其读入指定的内存中。

    smgrread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char * buffer)

  • Smgrwrite() 根据 5 个参数定位一个 8K 数据块,然后用指定内存中的值将其覆盖。根据 skipFsync 决定是否在返回前保证数据落盘成功。

    smgrwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char * buffer, bool skipFsync)

  • smgrextend 负责在当前文件中扩展一个新的 8K 块并写入指定内存中的数据,其他与 smgrwrite 相同。

    smgrextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char * buffer, bool skipFsync)

  • smgrnblocks 返回指定表当前的 8K 数据块总数,这个值对语句处理的 planner 环节很重要。

    smgrnblocks(SMgrRelation reln, ForkNumber forknum)

  • 对象共同存在。

    smgropen(RelFileNode rnode, BackendId backend)

  • smgrclose 负责从哈希表中删除指定的 SMgrRelation 对象。

    smgrclose(SMgrRelation reln)

  • smgrtruncate 用于从文件(数据,剩余空间,可见性)中从后往前去删除指定数量的 8K 数据块,可以一次对上述 3 个 fork 文件分别去除多个数据块,最终缩小文件尺寸。

    smgrtruncate(SMgrRelation reln, ForkNumber *forknum, int nforks, BlockNumber *nblocks)

  • smgrprefetch 利用 POSIX_FADV_WILLNEED 提供的服务要求操作系统提前读取磁盘数据块到缓存中,这个动作可避免磁盘 I/O 瓶颈。

    smgrprefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)

smgrwrite 和 smgrextend 是写磁盘相关的重要函数,也是影响系统 I/O 性能的关键。它们在以下场景下被调用:

  • smgrwrite:
    • 当一个缓冲区(Buffer)被修改(即脏数据)时,调用 smgrwrite 将数据写到磁盘上的对应文件中;
    • 当事务提交时,需要确保已经将所有的脏数据写回磁盘,这就需要调用 smgrwrite;
    • 在 VACUUM 操作中,当对表的可见性信息进行更新时,相应的缓冲区可能会变为脏数据,需要通过 smgrwrite 将其写进磁盘;
  • smgrextend:
    • 当需要扩展一个文件(表或索引)时,调用 smgrextend 将文件扩展到所需的大小;
    • 创建一个新表或索引时,需要确定其初始大小,也会调用 smgrextend;
    • 执行 CLUSTER 操作时,需要将表重新组织,也可能会调用 smgrextend;
    • smgrwrite 和 smgrextend 用于确保数据的持久性,smgrwrite 用于将修改的数据写进磁盘,而 smgrextend 用于扩展文件大小。

· 1 分钟阅读

前言

在数据库高可用架构中,逻辑复制是实现数据同步和扩展的重要机制之一。通过逻辑复制,数据库管理员可以选择性地复制特定表的数据,而不必像物理复制那样进行全量数据库实例的复制。然而,逻辑复制槽的局限性在于它仅存在于主节点上,导致主备切换后新主节点无法继续向下游发送数据变更,直到重新创建或手动恢复逻辑复制槽。这种情况可能引发数据丢失,或者需要管理员手动干预,影响业务的连续性。

PostgreSQL 作为优秀的开源关系型数据库,提供了多种高可用性工具,其中 pg_failover_slot 插件专门解决了逻辑复制槽在主备切换时无法同步的问题,而 IvorySQL 作为一款基于 PostgreSQL 研发的兼容 Oracle 的数据库,同样适配该插件。

本文将详细介绍如何安装和配置 pg_failover_slot 插件,并说明它如何帮助 IvorySQL 实现无缝的逻辑复制槽同步。

逻辑复制槽的原理及局限性

在 PostgreSQL 中,逻辑复制与物理复制不同,它允许对特定表的数据变动进行行级别的精细化控制,并可以跨数据库版本和架构同步数据。逻辑复制的关键是通过逻辑复制槽记录并维护主节点上的数据变更,这些变更可以被下游的订阅者捕获和应用。

逻辑复制槽的工作原理

逻辑复制槽是 PostgreSQL 维护数据变更的缓冲机制,它存储了自上次传输以来的数据增量,直到下游订阅者成功接收这些数据。复制槽还记录了下游订阅者接收到的最后一个变更,以便在系统故障或网络问题时重新发送未完成的数据。

逻辑复制槽的局限性

逻辑复制槽仅在主节点上维护,因此在以下情况下会遇到问题:

  • 故障转移后的数据丢失:当主节点宕机并且备节点被提升为主节点时,新的主节点没有复制槽的记录。此时,订阅者无法从新的主节点获取数据变更,直到管理员在新主节点上手动创建新的复制槽。
  • 需要逻辑复制槽的手动创建及重新初始化:手动创建复制槽不仅繁琐,而且会造成数据同步的中断,可能需要重新初始化逻辑复制表,带来额外的负载和复杂度。

为了应对这些挑战,pg_failover_slot 插件能够自动同步主备节点之间的逻辑复制槽,确保故障转移时数据的连续性。

pg_failover_slot 插件简介

pg_failover_slot 是专为 PostgreSQL 开发的一个插件,旨在解决逻辑复制槽无法在主备之间同步的问题。该插件通过在主节点和备节点之间同步逻辑复制槽,确保当备节点被提升为主节点时,它能够继续向下游订阅者发送数据变更,而不需要重新创建或手动干预。

插件的主要特性包括:

  • 自动复制槽同步:pg_failover_slot 可以在主备节点之间同步逻辑复制槽,确保备节点具备相应的复制槽记录。
  • 简化的故障转移流程:在主备切换后,无需手动创建新的逻辑复制槽,保证订阅者能够立即获取新主节点上的数据变更。
  • 高可用性支持:槽的同步,pg_failover_slot 提高了系统的容错能力和可用性,减少了宕机时的管理负担。

img

安装和配置 pg_failover_slot 插件

为了在 IvorySQL 上使用 pg_failover_slot 插件,需要进行以下步骤。

系统环境准备

假设您已经在生产环境中部署了 IvorySQL,并且已经配置了主备节点环境:

  • 主节点:primary_node

  • 备节点:standby_node

确保 IvorySQL 主备架构运行正常,且已创建逻辑复制槽,并使用高可用工具来管理故障转移过程和 VIP 管理实现漂移。

下载并编译插件

首先,从 GitHub 下载并编译 pg_failover_slot 插件:

git clone

https://github.com/EnterpriseDB/pg_failover_slots.git

cd pg_failover_slots

make

make install

配置 IvorySQL

接下来,在主节点和备节点的 postgresql.conf 文件中添加 pg_failover_slot 插件到 shared_preload_libraries,并设置日志复制等级为 logical 或以上:

wal_level = logical
shared_preload_libraries = 'pg_failover_slots'

确保主库已创建物流复制槽,此为必要条件。

在备库配置:

hot_standby_feedback = on
primary_slot_name = ‘slot_name’

这一设置使数据库在启动时加载插件。完成配置后,重启主节点和备节点的 IvorySQL 实例:

pg_ctl restart -D $PGDATA

img

启动成功后,查看进程可看到 pg_failover_slot worker 的子进程,并且 show shared_preload_libraries 可查看到有 pg_failover_slots 的信息,即插件生效。

创建逻辑复制槽

使用 1521 端口进行 Oracle 模式连接,并主节点上创建带有兼容 Oracle 字段的表 t_ora_liketest3,下图显示模式为 Oracle 模式,显示 Oracle 属性字段 varchar2 的表 t_ora_like,作为基础测试表,旨在测试插件与 Oracle 兼容属性间的契合度。

img

然后在主节点上创建一个发布:

CREATE PUBLICATION my_publication FOR TABLE test3;
CREATE PUBLICATION my_publication2 FOR TABLE t_ora_like;

此时,当有订阅方通过 VIP 订阅此发布,会创建相应的逻辑复制槽,pg_failover_slot 插件将自动同步该复制槽到备节点。您无需手动在备节点上创建复制槽。

img

img

同步逻辑复制槽

备节点会同步相应的逻辑复制槽。

img

故障转移与复制槽恢复

pg_failover_slot 插件在主备节点上配置完成,它将自动管理逻辑复制槽的同步和故障转移。

故障转移流程

当主节点发生故障时,使用高可用工具进行故障转移,备节点将被提升为新的主节点,VIP 漂移到新节点。pg_failover_slot 插件会确保新主节点恢复并接管逻辑复制槽。

验证复制槽状态

在新的主节点上检查复制槽是否已经恢复:

SELECT * FROM pg_replication_slots;

新主节点上已经存在原先在旧主节点上创建的逻辑复制槽。这意味着下游订阅者可以继续从新主节点获取数据变更,确保逻辑复制的无缝衔接。

img

订阅者同步

下游订阅者无需任何额外操作,它们会通过 VIP 自动从新主节点接收更新,无需重新配置订阅。

img

img

最佳实践和注意事项

在使用 pg_failover_slot 插件时,以下最佳实践可以帮助您更好地管理和维护逻辑复制槽的高可用性:

  • 合理配置复制槽上限:在高并发或大规模复制场景中,确保 max_replication_slots 参数配置合理。IvorySQL 的 postgresql.conf 文件中的这一参数需要根据工作负载的复杂性进行调整,以避免复制槽过载。
  • 定期监控复制槽状态:使用 pg_stat_replication_slots 视图定期检查复制槽的状态,确保在主备节点之间的同步过程中没有出现延迟或丢失。结合高可用工具进行自动化故障转移,可以实现主备节点的自动化切换,VIP 的管理和漂移。pg_failover_slot 插件与高可用工具配合使用时,可以确保复制槽在故障转移过程中的无缝接管。
  • 性能优化与监控:逻辑复制在高并发时可能会对系统性能造成一定压力,因此推荐定期监控系统的 I/O 性能和 CPU 利用率,必要时调优系统配置。

总结

IvorySQL 与 pg_failover_slot 插件为高可用环境中的逻辑复制提供了强大的支持。通过自动同步主备节点间的逻辑复制槽,pg_failover_slot 解决了故障转移后需要重新初始化复制槽的难题,显著提升了逻辑复制的稳定性和可用性。

对于需要无缝故障转移且不希望因复制槽问题导致下游订阅者丢失数据的用户来说,pg_failover_slot 是一种必不可少的工具。它简化了逻辑复制槽的管理流程,减少了数据库维护的复杂性,为高可用环境中的数据一致性保驾护航。

通过本文中的步骤,可以在 IvorySQL 中配置并使用 pg_failover_slot 插件,确保您的逻辑复制环境在主备切换时始终保持高效运行。

· 1 分钟阅读

2022 年渥太华时间 5 月 26 日瀚高北美研究院负责人 Grant Zhou 在会上发表 IvorySQL 项目的技术讲解。本次会议内容主要介绍了我们是谁、IvorySQL 是什么、IvorySQL 的关键功能点、社区贡献指南、开发者指南等。重点介绍了基于 PostgreSQL 14,IvorySQL 在 PostgreSQL 基础之上添加了一些令人兴奋的功能点!!

IvorySQL 在 PostgreSQL 的基础上增加的主要功能特点包括

  • Oracle 兼容包

  • Oracle 兼容 PL/iSQL 过程语言

  • GUC 在 Oracle 和 PostgreSQL 模式之间切换

  • Oracle 兼容各种 DDL 操作的语法

  • Oracle 兼容的日期/时间函数

  • 内置 Orafce (https://github.com/orafce/orafce)

更多功能特性描述,可参阅https://www.ivorysql.org/zh-CN/releases-page的发布说明。

大家可以通过 IvorySQL 产品路线图(https://github.com/IvorySQL/IvorySQL/issues)定期查看GitHub中的问题选项卡可获得最新的功能列表。

bilibili 视频讲解链接

PGCon2022 会议回顾|IvorySQL 项目的技术讲解_哔哩哔哩_bilibili

因为时差的原因,有许多小伙伴没有看到,小助理特意为大家整理好了会议视频内容以及中文 PPT 材料,有需要的小伙伴们,关注公众号,添加小助理微信即可获得。


通过订阅邮件列表加入 IvorySQL 社区:

欢迎在Github给我们一个 ⭐

· 1 分钟阅读

IvorySQL 是一个开源项目,该项目的核心目标之一是提供 oracle 兼容功能,以便在 IvorySQL 数据库服务器上使用 oracle 代码。

在 PostgreSQL 之上提供 Oracle 兼容是一项多维任务。 从提供与 Oracle 兼容的 SQL 语法到添加对 PostgreSQL 中缺失或行为不同的数据类型的支持。 Oracle 兼容性的主要核心之一是在 PostgreSQL 中提供兼容的过程语言 PL(procedural language),它在功能和语法上与 Oracle 的 PL/SQL 兼容

为此,IvorySQL 添加了一种新的 PL 语言 PL/iSQL,它接受、理解和执行 PL/SQL 语法。 如您所知,Oracle PL/SQL 的核心特性之一是 PACKAGES(包)。Package(包)是 Oracle 中的一个模式对象,它包含一组相关功能的定义,是 Oracle 使用最广泛的特性之一。

因此,本文将介绍 Package(包),并举例说明如何用 IvorySQL 创建、使用和销毁 Oracle 样式的包

包(Package)

那么什么是 Package(包)? 包是 PL/iSQL 代码的分组,按逻辑程序单元划分。 换句话说,一个包可以被认为是相关函数、过程、变量或游标的集合,这个集合可以通过通用公共名称来访问。

IvorySQL 内置 PL/iSQL 语言,可以接受、理解和执行 PL/SQL 代码。包使用相同的语言,并且有两个主要组件。

  • 包规范(Specification)

包规范列出了可以从包外部访问的所有项目,例如函数、过程、变量和游标。这也称为公共规范。

  • 包体(Body)

包体包含包规范中列出的所有函数和过程的实现。除此之外,它还可以实现更多的功能、过程或其他元素。

这些不在包规范中的元素将被视为包的私有成员,并且只能在包内引用它们。 不允许外部访问。

包体也可以有一个特殊的代码块,称为初始化块。 这是一个特殊的块,因为这个块在每个会话中只执行一次,在包第一次被访问的最开始。

让我们看一些代码示例,看看包是如何运行的。

示例

通用示例

首先让我们设置兼容模式,这样我们就可以访问 IvorysSQL 数据库中可用的 Oracle 兼容特性。

SET compatible_mode TO oracle;

CREATE TABLE books (
id INT,
title VARCHAR2(100),
author VARCHAR2(100),
price NUMBER);
INSERT INTO books VALUES (10, 'The Hobbit', 'J. R. R. Tolkien', 10.0);
INSERT INTO books VALUES (11, 'Winnie-the-Pooh', 'A. A. Milne', 5.0);
INSERT INTO books VALUES (12, 'Peter Pan', 'James Matthew Barrie', 4.0);
INSERT INTO books VALUES (13, 'Charlie and the Chocolate Factory', 'Roald Dahl', 5.0);
INSERT INTO books VALUES (14, 'SThe Jungle Book', 'Rudyard Kipling', 9.0);
INSERT INTO books VALUES (15, 'The Little White Bird', 'James Matthew Barrie', 3.0);

让我们来创建一个简单的包。这个包声明了一个游标来列出所有可用的图书,并包含一些子程序来列出、添加和删除书籍。同时,它还声明了一些私有变量来计数和保存图书信息。

CREATE OR REPLACE PACKAGE mybooks AS
CURSOR booksinfo IS SELECT * from BOOKS;

PROCEDURE list_books;
FUNCTION add_book(title VARCHAR2(100), author VARCHAR2(100),
price NUMBER) RETURN bool;
PROCEDURE remove_book(book_title VARCHAR2(100));
END;
ivorysql$# /
CREATE PACKAGE
CREATE OR REPLACE PACKAGE BODY mybooks AS

-- declare private variables
bookinfo booksinfo%rowtype;
books_count INT;

PROCEDURE list_books AS
BEGIN
OPEN booksinfo;
RAISE INFO 'Book Info:';
RAISE INFO '';
LOOP
FETCH booksinfo into bookinfo;
EXIT WHEN NOT FOUND;

RAISE INFO ' Name = %', bookinfo.title;
RAISE INFO ' Author = %', bookinfo.author;
RAISE INFO ' Price = %', bookinfo.price;
RAISE INFO '------------------------------';
END LOOP;
RAISE INFO 'Total Books = %', books_count;
CLOSE booksinfo;
END;

FUNCTION add_book(title VARCHAR2(100), author VARCHAR2(100),
price NUMBER) RETURN bool AS
BEGIN
INSERT INTO BOOKS VALUES (
books_count + 1,
title,
author,
price);

books_count := books_count + 1;
RETURN true;

EXCEPTION WHEN OTHERS THEN
RETURN false;
END;

PROCEDURE remove_book(book_title VARCHAR2(100)) AS
BEGIN
DELETE FROM BOOKS WHERE title = book_title;

EXCEPTION WHEN OTHERS THEN
RAISE INFO 'Book % not found', book_title;
END;

-- initializer block
BEGIN
books_count := 0;
SELECT count(*) INTO books_count
FROM BOOKS;
END;
ivorysql$# /
CREATE PACKAGE BODY

让我们检验这些数据。这匿名块试图访问该包的私有成员,这将会导致错误。

ivorysql=# DECLARE
ivorysql$# nbooks int := 0;
ivorysql$# BEGIN
ivorysql$# nbooks := mybooks.books_count;
ivorysql$# RAISE INFO 'Total Books = %', nbooks;
ivorysql$# END;
ivorysql$# /
2022-05-26 16:35:32.328 PKT [63554] ERROR: package private variable ("mybooks.books_count") is not accessible

让我们使用包的子程序列出所有可用的书籍。 list_books mybooks

ivorysql=# BEGIN
ivorysql$# mybooks.list_books;
ivorysql$# END;
ivorysql$# /
INFO: Book Info:
INFO:
INFO: Name = The Hobbit
INFO: Author = J. R. R. Tolkien
INFO: Price = 10
INFO: ------------------------------
INFO: Name = Winnie-the-Pooh
INFO: Author = A. A. Milne
INFO: Price = 3
....
....
INFO: Total Books = 6
DO
ivorysql=#

让我们使用包的子程序添加一本新书。 add_book mybooks

DECLARE
added bool;
BEGIN
added := mybooks.add_book('The Cat in the Hat', 'Dr. Seuss', 10);
IF added = TRUE THEN
RAISE INFO 'new book added';
END IF;
END;
/

删除包

可以使用以下命令删除包体或完成包:

  • DROP PACKAGE BODY [package name]
ivorysql=# DROP PACKAGE BODY mybooks;
DROP PACKAGE BODY
  • DROP PACKAGE [package name]
ivorysql=# DROP PACKAGE mybooks;
DROP PACKAGE

具有不同权限的包调用

我们将创建两个具有调用者和定义者权限的包,并查看结果以了解它们之间的区别。

ivorysql=> SELECT current_user;
current_user
--------------
ivorysql
(1 row)

--创建具有 INVOKER 权限的包

CREATE OR REPLACE PACKAGE pkg_invrights AUTHID CURRENT_USER AS
PROCEDURE curr_user;
END;
/

CREATE OR REPLACE PACKAGE BODY pkg_invrights AS
PROCEDURE curr_user AS
BEGIN
RAISE INFO 'Current User: %', current_user;
END;
END;
/

--创建一个具有 DEFINER 权限的包

CREATE OR REPLACE PACKAGE pkg_defrights AUTHID DEFINER AS
PROCEDURE curr_user;
END;
/

CREATE OR REPLACE PACKAGE BODY pkg_defrights AS
PROCEDURE curr_user AS
BEGIN
RAISE INFO 'Current User: %', current_user;
END;
END;
/

让我们先查看具有调用程序权限的包。

ivorysql=> CALL pkg_invrights.curr_user;
INFO: Current User: ivorysql
CALL

让我们来看看具有定义权限的包。

ivorysql=> CALL pkg_defrights.curr_user;
INFO: Current User: ivorysql
CALL

让我们切换到另一个名为 ivuser 的用户。

ivorysql=> \c ivorysql ivuser;
You are now connected to database "ivorysql" as user "ivuser".

让我们先看看具有调用程序权限的包。

ivorysql=> CALL pkg_invrights.curr_user;
INFO: Current User: ivuser
CALL

让我们看看具有定义权限的包。

ivorysql=> CALL pkg_defrights.curr_user;
INFO: Current User: ivorysql
CALL

总结

如上所示,当调用具有调用者权限(pkg_invrights)的包时,它使用的是调用该包的用户的特权。但是,当调用(pkg_defrights)时,它仍然使用包所有者的特权。

· 1 分钟阅读

IvorySQL可以在Linux, OSX, Unix和Windows平台上构建,与PostgreSQL的编译安装基本上是一样的。本文对基于Linux的系统上编译源代码的步骤进行说明。

一、准备工作

1.1 获取 IvorySQL 源码

Windows或Linux,只要安装了git,都可以使用git下载:

git clone https://github.com/IvorySQL/IvorySQL.git

git clone https://gitee.com/IvorySQL/IvorySQL.git

或者直接去github或gitee下载都可以:

https://github.com/IvorySQL/IvorySQL

https://gitee.com/IvorySQL/IvorySQL

github会有点慢,能用则用,不行就改用gitee。

截至本稿,IvorySQL的最新发布版本是1.2,于2022年2月28日发布。

L

本文使用的源码版本也是IvorySQL 1.2。

1.2 安装依赖包

要从源代码编译IvorySQL,必须确保系统上有可用的先决条件包。 执行以下命令安装相关包:

sudo yum install -y bison-devel readline-devel zlib-devel openssl-devel wget
sudo yum groupinstall -y 'Development Tools'

说明:“Development Tools”包含了gcc,make,flex,bison。

二、编译安装

前面通过获取的源码在文件夹IvorySQL里,接下来我们就进入这个文件夹进行操作。

2.1 配置

Root用户执行以下命令进行配置:

./configure

说明:由于没有提供--prefix,默认安装在/usr/local/pgsql。

指定安装路径,如“/usr/local/ivorysql/ivorysql-1.2”:

./configure --prefix=/usr/local/ivorysql/ivorysql-1.2

注意:我们要记住指定的目录,因为系统查不出已经编译安装的程序在哪。

更多configure参数通过“./configure --help”查看。还可以查看PostgreSQL手册。

2.2 编译安装

配置完成后,执行make进行编译:

make

要在安装新编译的服务之前使用回归测试测试一下,以下命令均可:

make check
make check-world

然后安装:

make install

三、初始化数据库服务

我们这里只是简单配置一下,能本地和远程连接就可以了。

3.1 创建操作系统用户

用户root会话下,新建用户 ivorysql:

/usr/sbin/groupadd ivorysql
/usr/sbin/useradd -g ivorysql ivorysql -c "IvorySQL1.2 Server"
passwd ivorysql

3.2 创建数据目录

接下来需要创建数据目录并修改权限。在root会话下执行以下命令。

mkdir -p /ivorysql/1.2/data
chown -R ivorysql.ivorysql /ivorysql/1.2/

注意:这里没按RPM安装将数据目录放置到“/var/lib/ivorysql/ivorysql-1/data”。

3.3 环境变量

切换到用户ivorysql,修改文件“/home/ ivorysql /.bash_profile”,配置环境变量:

umask 022
export LD_LIBRARY_PATH=/usr/local/pgsql/lib:$LD_LIBRARY_PATH
export PATH=/usr/local/pgsql/bin:$PATH
export PGDATA=/ivorysql/1.2/data

使环境变量在当前ivorysql用户会话中生效:

source .bash_profile

也可以重新登录或开启一个新的用户ivorysql的会话。

3.4 设置防火墙

如果开启了防火墙,还需要将端口5333开放:

firewall-cmd --zone=public --add-port=5333/tcp --permanent
firewall-cmd --reload

说明:默认端口是5333,如果不开放该端口,外部客户端通过ip连接会失败。

3.5 初始化

在用户ivorysql下,简单执行initdb就可以完成初始化:

initdb

说明:initdb操作与PostgreSQL一样,可以按照PG的习惯去初始化。

3.6 启动数据库

使用pg_ctl启动数据库服务:

pg_ctl start

查看状态,启动成功:

pg_ctl status

pg_ctl: server is running (PID: 29549)

/usr/local/pgsql/bin/postgres

四、配置服务

这里只是简单配置一下,能本地和远程连接就可以了

4.1 客户端验证

修改 /ivorysql/1.2/data/pg_hba.conf,追加以下内容:

host    all             all             0.0.0.0/0               trust

注意:这里是trust,就是说可以免密登录。

执行以下命令加载配置:

pg_ctl reload

4.2 基本参数

通过psql连接数据库:

psql

修改监听地址:

alter system set listen_addresses = '*';

说明:默认是监听在127.0.0.1,主机外是连不上服务的。

重启服务使设置生效:

pg_ctl restart

4.3 守护服务

创建service文件:

touch /usr/lib/systemd/system/ivorysql.service

编辑内容如下:

[Unit]
Description=IvorySQL 1.2 database server
Documentation=https://www.ivorysql.org
Requires=network.target local-fs.target
After=network.target local-fs.target

[Service]
Type=forking

User=ivorysql
Group=ivorysql

Environment=PGDATA=/ivorysql/1.2/data/

OOMScoreAdjust=-1000

ExecStart=/usr/local/pgsql/bin/pg_ctl start -D ${PGDATA}
ExecStop=/usr/local/pgsql/bin/pg_ctl stop -D ${PGDATA}
ExecReload=/usr/local/pgsql/bin/pg_ctl reload -D ${PGDATA}

TimeoutSec=0

[Install]
WantedBy=multi-user.target

说明:service的写法有很多,在生产环境使用时需谨慎,请多次重复测试。

停止pg_ctl启动的数据库服务,启用systemd服务并启动:

systemctl enable --now ivorysql.service

IvorSQL数据库服务操作命令:

systemctl start ivorysql.service            --启动数据库服务
systemctl stop ivorysql.service --停止数据库服务
systemctl restart ivorysql.service --重启数据库
systemctl status ivorysql.service --查看数据库状态
systemctl reload ivorysql.service --可以满足部分数据库配置修改完后生效

五、客户端链接数据库

连接IvorySQL数据库服务的客户端工具和PostgreSQL的一样。

5.1 psql 连接

操作系统用户ivorysql会话下连接:

[ivorysql@Node02 ~]$ psql
psql (15devel)
Type "help" for help.

ivorysql=#

5.2 DBeaver 连接

DBeaver是一个功能比较强大的开源工具,连接配置如下:

L

5.3 瀚高 developer 连接

瀚高developer是瀚高自主研发的一个工具,除了可以支持瀚高数据库,还支持PostgreSQL以及IvorySQL数据库。连接配置如下:

L

如果想使用该工具,请关注公众号加入微信群“IvorySQL中国技术交流群”咨询。

六、卸载

编译安装就用编译卸载的方式卸载。

6.1 备份数据

数据目录在“/ivorysql/1.2/data”下,所以我们将该目录保护好就可以,最好停止数据库服务后做备份。

6.2 编译卸载

Root会话下切到源码目录下,分别执行以下命令:

make uninstall
make clean

删除残余目录和文件:

systemctl disable ivorysql.servicemake                  --禁用服务
mv /usr/lib/systemd/system/ivorysql.service /tmp/ --服务文件移到/tmp,删除也可以
rm -fr /usr/local/pgsql --删除残留安装目录

还有用户ivorysql以及对应的环境变量,可以根据情况是否清理。

剩下的就是数据目录“/ivorysql/1.2/data”了,请务必做好备份再做处理。

还有安装的依赖包,可根据情况决定是否卸载。

七、后记

  • 更细化的操作,可参照PostgreSQL的相关内容。

  • 大家还可以参考IvorySQL源码下自带文档:/root/IvorySQL/README.md。

  • 也可以打开网站链接:https://gitee.com/IvorySQL/IvorySQL/blob/master/README.md。

  • 有任何问题,欢迎大家到IvorySQL官方社区仓库:github.com/IvorySQL/IvorySQL 提交issue。

关于IvorySQL

IvorySQL项目是一个具有广泛生态基础和中国特色的PG开源衍生项目,是瀚高公司设计研发的一款具备强大Oracle兼容能力的开源数据库。具备高兼容性和高可用性,并致力于遵守open-source ways。

社区仓库:github.com/IvorySQL/IvorySQL

CSDN: IvorySQL

开源中国:IvorySQL

墨天轮:IvorySQL

通过订阅邮件列表加入IvorySQL社区:

还有,别忘了在Github给我们一个 ⭐