Londiste 教程 (Skytools 2)
此页面包含 **历史信息或已弃用文章**。
Londiste 是 Skytools 的异步主备复制系统,建立在 PGQ 之上。
本教程适用于 skytools 2.x。Skytools 3.x 中的工具和命令已发生更改。
如果你只是想运行一个测试安装,那么你只需要阅读第 1 部分:安装和设置。以下部分将提供一些关于如何解决常见任务、排查安装问题以及更多关于其工作原理的见解。也许后面的部分应该转移到专门的 wiki 页面。
配置、构建和安装
注意:Skytools 是一个基于 Python 的应用程序,需要安装 Python-Postgres 驱动程序 'psycopg2'。您可以在 psycopg2 主网站找到更多信息和下载:Psycopg2。请注意,psycopg2 的 2.4.2 和 2.4.3 版本存在已知的数据丢失问题,不应与 Skytools 或 Londiste 一起使用。
下载并解压缩发布版本后,您需要配置、构建和安装所需的组件。
> tar zxf skytools-2.x.x.tar.gz > cd skytools-2.x.x
'configure' 脚本的两个重要选项是 --prefix 和 --with-pgconfig。这些选项用于选择 skytools 可执行文件将所在的安装目录 ('prefix') 以及工具应配置为与其配合使用的已安装的 postgres 版本 ('with-pgconfig')。在本例中,我们将安装到 /opt/skytools-2.x.x,并针对 /opt/postgres-9.0.1/ 中的 postgres 安装进行配置。
> ./configure --prefix=/opt/skytools-2.x.x --with-pgconfig=/opt/postgres-9.0.1/bin/pg_config
配置步骤完成后,您就可以构建和安装了。
> make > sudo make install
接下来,您还需要构建和安装工具的 Python 部分。
> python setup.py build > sudo python setup.py install
完成这两个步骤后,所有 skytools/londiste 组件(包括 Python 和 SQL)都应该安装在正确的位置。
设置
让我们考虑以下简单情况
- 一个提供者,源数据库为 P
- 一个订阅者,目标数据库为 S
- 要复制的几个表,T1 和 T2,以及一个序列 S1,它们都在 schema public 中
Ticker 守护程序
Londiste 需要一个 Ticker,它必须定位到 P(rovider) 数据库,并且可以从另一台机器上运行。常见的用法是在提供者数据库主机上直接运行 Ticker。
任何 Ticker 都可以托管任意数量的队列。每个队列都有一个唯一的名称,可以被任意数量的订阅者使用。
如果你有多个相同数据库的副本,你可以从多个订阅者订阅同一个队列。如果你想在多个订阅者上拥有相同源数据库的不同子集,你可以让它们使用同一个队列,但只使用其中一部分表,或者为每个表集设置一个队列。
ticker.ini
您必须配置 Ticker 以便它引用提供者数据库,您必须在其中安装 PGQ 支持代码(C 和 PL/pgSQL,如果运行的是 8.3 之前的服务器版本,还需要安装 txid 扩展模块)。
[pgqadm] job_name = myticker_name db = dbname=P # how often to run maintenance [seconds] maint_delay = 600 # how often to check for activity [seconds] loop_delay = 0.1 logfile = ~/log/%(job_name)s.log pidfile = ~/pid/%(job_name)s.pid
job_name 只是在日志文件和 pid 文件名称中用于参考。它与 Ticker 托管的队列无关。
安装 Ticker 并生成滴答
现在您的配置已准备就绪,您需要在数据库中安装支持代码并启动 Ticker 守护程序。为了使 PostgreSQL 模块 (.so 或 .dll) 存在,PGQ 包必须安装在托管数据库的机器上,并且还需要安装在托管守护程序的机器上,守护程序将使用本地 SQL 文件来安装附加组件。
pgqadm.py ticker.ini install pgqadm.py ticker.ini ticker -d
现在您已经运行了 Ticker,您可以检查其日志,在没有活动的情况下,日志更新频率很低。PgQ 默认情况下仅在没有产生事件的情况下每分钟发出一次滴答,这种情况仍然存在。
复制守护程序
您需要在每个订阅者上运行一个复制守护程序。您可以将守护程序本身托管在单独的机器上,但它必须连接到订阅者数据库,并且必须在托管守护程序的机器和托管数据库的机器上都安装 londiste(skytools)包。通常,在订阅者数据库主机上运行订阅者守护程序是最简单的。
p-to-s.ini
首先,您需要一个配置文件来告诉 londiste 如何找到提供者和订阅者数据库,以及在哪里记录日志文件等。一个良好的做法是创建一个专用的系统用户(如果运行的是 Unix 变体)并将守护程序日志文件放在例如 /var/log/londiste 中,并将 pid 文件放在 /var/run/londiste 中。这是一种解决权限问题的简单方法。
[londiste] job_name = test_to_subcriber provider_db = dbname=P port=6000 host=127.0.0.1 subscriber_db = dbname=S port=6000 host=127.0.0.1 # it will be used as sql ident so no dots/spaces pgq_queue_name = testing logfile = /var/log/londiste/%(job_name)s.log pidfile = /var/run/londiste/%(job_name)s.pid
job_name 可以是您想要的任何名称,它只用于命名文件以及使用 SQL PgQ API 时作为消费者名称。pgq_queue_name 是将在安装步骤中自动为您创建的 pgqadm.py 队列的名称。
安装 Londiste
现在,使用新的配置文件,安装起来就很容易了。
londiste.py p-to-s.ini provider install londiste.py p-to-s.ini subscriber install
如您所见,您可以直接从订阅者在提供者上安装 londiste 软件。这要求您在提供者和订阅者上都安装整个 skytools 包。
启动复制
现在您可以开始将事件从提供者复制到订阅者了!
londiste.py p-to-s.ini replay -d
在您想要的所有机器上重复这些订阅者步骤,每次都指向不同的订阅者数据库,您将拥有一个主数据库和多个从数据库。
在添加表之前启动重播过程非常重要,因为如果您在注册消费者之前产生事件,您将永远无法获得这些事件。
添加表和序列
无论订阅者数量多少,此步骤只需执行一次。
londiste.py p-to-s.ini provider add public.T1 public.T2 londiste.py p-to-s.ini provider add-seq public.S1
然后您将相同的元素添加到订阅者中。
londiste.py p-to-s.ini subscriber add public.T1 public.T2 londiste.py p-to-s.ini subscriber add-seq public.S1
如果您关注 londiste 日志 (tail -f),您将看到 londiste 为添加表而执行的所有步骤。您必须在订阅者上创建表,这使您能够在订阅者上使用与提供者不同的索引。如果订阅者模式具有外键引用,则您显然需要创建引用的表并将它们添加到复制集中。Londiste 会在执行初始 COPY 时删除外键约束,并在之后立即重新添加它们。
常见任务
将所有提供者表添加到订阅者
使用以下命令即可轻松完成。
londiste.py <ini> provider tables | xargs londiste.py <ini> subscriber add
检查订阅者状态
此查询在提供者节点上运行,并提供有关每个队列和消费者的信息。
SELECT queue_name, consumer_name, lag, last_seen FROM pgq.get_consumer_info();
lag 列显示了我们本文开头提到的相同延迟,last_seen 列显示了最新消费者请求的 timestamptz。在默认配置中,此列的值不应超过 60 秒(并且很快,但不是即时)。
从以前的消费者中删除所有队列事件
当您使用 londiste 时,您很容易遇到想要清除所有安装以干净地重新开始的情况。或者,可能是您的架构中有一个以前的 PGQ 消费者已弃用,您希望 PGQ 忘记它。
要使 PGQ 停止为永远不会返回的消费者累积事件,请使用以下 API
SELECT pgq.unregister_consumer('queue_name', 'consumer_name');
或者使用 pgqadm.py 工具
$ pgqadm.py <ticker.ini> unregister queue_name consumer_name
向复制的表中添加列
这种情况的处理过程很简单
- 在所有订阅者上添加列
- BEGIN; -- 在提供者上
- 在提供者上添加列
- SELECT londiste.provider_refresh_trigger('queue_name', 'tablename');
- COMMIT;
从复制的表中删除列
- 在同一个事务中从提供者中删除列并更改触发器
- 当 londiste 经过删除时刻时,观察延迟
- 在订阅者上删除列
这里的技巧是在队列中不再存在引用它的事件时,才在订阅者上删除列。
在订阅者端添加自定义触发器
默认情况下,londiste 会认为订阅者表上存在的触发器是您只是在那里恢复了提供者模式,尽可能地偷懒。如果您打算在订阅者上运行自定义触发器,则必须按照以下步骤告诉 londiste。
- 在订阅者上创建自定义触发器
- londiste.py p-to-s.ini subscriber add public.T1
- londiste.py p-to-s.ini subscriber restore-triggers public.T1
注意:当 londiste 停止时,它会再次删除触发器。
您甚至可以为恢复触发器指定一个特定的触发器名称,只有这个触发器会被重新安装。
联邦数据库
如果您想要一个中央数据库,其中包含由多个主数据库提供的数据的实时副本,该怎么办?
当您意识到 PostgreSQL 和 londiste 都在内部按模式限定的名称引用表时,这很容易做到。好吧,对于 PostgreSQL 来说并不完全是那样,但很类似。
因此,解决方案是在每个提供者上创建一个全局唯一的本地模式,并在中央订阅者上创建所有这些模式。然后,您可以独立地将 provider1.table 和 provider2.table 复制到中央服务器上具有相同模式限定名称的表中。
您可以在中央服务器上创建一个 federation.table 表,并使用继承或订阅者触发器将数据移动到主表。表继承允许数据保留在 londiste 放置它们的表上,而订阅者触发器允许您使用不同的分区方案(按日期范围而不是来源服务器)或在中央服务器上拥有统计信息(或成本)计算触发器。
通过触发器联合数据
当使用订阅者触发器时,请注意 londiste.py 对触发器的处理以及以下命令
$ londiste.py <ini> subscriber restore-triggers <schema.table>
多个提供者之间的全局唯一序列?
如果您的主键是序列,您将希望避免在中央数据库中发生冲突。有两种方法可以做到这一点,第一种方法是通过让序列从正确的数值开始而不是都从 1 开始,在服务器之间分割一个 bigint 范围。
更好的方法是将您的主键变成一个两列主键,在 PK 中添加一个服务器引用(全局唯一)。然后,通过使用继承技巧或联合触发器技巧(移动数据),可以轻松知道每条联合数据的来源。
复制分区表
由于分区是在 PostgreSQL 中使用继承完成的,因此通常意味着主表有一个触发器来将修改重定向到目标子表。Londiste 是一种基于触发器的复制系统,如何将分区和复制混合使用?
要么使用来自 Skytools 的分区复制订阅者(参见例如 cube dispatcher),要么简单地复制分区本身,并使用 cron 条目来每月添加下一个分区,例如
故障排除
本节旨在提出常见问题的通用解决方案,如果我们添加太多内容,它可能会成为自己的页面...
Londiste 正在占用我所有的 CPU,延迟正在上升
例如,如果您在夜间出现故障,并且值班系统管理员忘记重新启动计时器,就会发生这种情况。或者,当您在一个事务中执行了一个大型 UPDATE 或 DELETE,但现在意识到来自同一个事务的每个事件都必须在订阅者站点上的同一个事务中进行处理...
以下查询允许您计算代表多少事件,其中神奇的 tick 数字来自 pgq.subscription 中的 sub_last_tick 和 sub_next_tick 列。
SELECT count(*) FROM pgq.event_1, (SELECT tick_snapshot FROM pgq.tick WHERE tick_id BETWEEN 5715138 AND 5715139 ) as t(snapshots) WHERE txid_visible_in_snapshot(ev_txid, snapshots);
在我们的案例中,这超过了 500 万和 400,000 个事件。对于如此多的事件,如果您启动 londiste,它会消耗尽可能多的内存来容纳所有事件,这可能超过您的系统能够提供的内存。因此,您需要一种方法来告诉 londiste 不要一次加载所有事件。以下是如何操作:在重新启动 londiste 守护进程之前,将以下旋钮添加到您的 .ini 配置文件中
pgq_lazy_fetch = 500
现在,即使单个批次(包含两个 tick 之间的全部事件)包含大量事件,londiste 也会懒惰地一次获取 500 个事件或更少。这个数字似乎是一个不错的选择,因为它是单个批次中事件数量的默认 PGQ 设置。只有当计时器没有运行,或者您在单个事务中生成超过该数量的事件时,才会超过这个数字。
检测保持表同步的问题
如果您怀疑某些表没有保持同步,以下是可以运行的两个命令(即使是从 cron),以密切关注表的同步状态
要执行计算和空间密集的逐行比较,这将检测“public”模式中所有表之间的任何差异,您可以运行以下命令(替换正确的模式名称、londiste.py 和 config.ini 文件路径名)
rm -rf /home/tmp/repair && mkdir -p /home/tmp/repair && cd /home/tmp/repair && londiste.py config.ini subscriber tables | perl -nle 'if ( m/^(public\S+)/ ) { print $1; }' | xargs --replace=XX londiste.py config.ini repair XX 2>&1
命令执行完毕后,输出将位于 /home/tmp/repair 目录中。任何内容不匹配的表也将有一个“repair”脚本位于 /home/tmp/repair 目录中。您可以在订阅者表上运行该脚本,使其与主表同步。
此方法的轻量级版本使用“compare”命令而不是“repair”命令来查找每个表中不匹配的行数,并且只有在发现问题时才生成输出
londiste.py config.ini subscriber tables | perl -nle 'if ( m/^(public\S+)/ ) { print $1; }' | xargs --replace=XX londiste.py config.ini compare XX 2>&1 | /usr/bin/grep "do not match"
概念
在 londiste 术语中,主服务器称为提供者,其从服务器称为订阅者,订阅者从队列中轮询的数据称为要重放的事件。
为了让提供者能够将事件分组到批次中,它需要有一个参考计时器,由 pgqadm.py 提供。
词汇表
取自 PGQ 文档
- 事件
- 由生产者创建的原子数据块。在 PgQ 中,事件是服务于该队列的某个表中的一条记录。事件记录包含一些用于 PgQ 的系统字段,以及由生产者填充的多个数据字段。PgQ 既不检查也不强制执行事件类型。事件类型是消费者和生产者必须达成一致的东西。PgQ 保证每个事件至少被看到一次,但消费者有责任确保事件最多被处理一次(如果需要)。
- 批次
- PgQ 旨在提高效率和吞吐量,因此事件被分组到批次中以进行批量处理。创建这些批次是 PgQadm 的主要任务之一,并且每个队列都有几个参数可以用来调整批次的大小和频率。消费者以这些批次接收事件,并根据业务需求单独处理事件或以批次处理事件。
- 队列
- 事件存储在队列表中,即队列。多个生产者可以写入同一个队列,多个消费者可以从队列中读取。事件保留在队列中,直到所有消费者都看到它们为止。我们使用表轮换来减少硬盘 I/O。队列可以包含任意数量的事件类型,生产者和消费者必须就传递的事件类型和编码方式达成一致。例如,Londiste 生产者端可以为比消费者端需要的更多表生成事件,因此消费者只订阅它需要的那些表,而其他表的事件会被忽略。
- 生产者
- 将事件推送到队列的应用程序。生产者可以使用任何能够在 Postgres 中运行存储过程的语言编写。
- 消费者
- 从队列中读取事件的应用程序。消费者可以使用任何能够与 Postgres 交互的语言编写。SkyTools 包含几个用 Python 编写的有用消费者,可以按原样使用或作为编写更复杂消费者的良好起点。
主从复制
Londiste 是一种单主到多个从的解决方案。这意味着任何给定的表都必须看到其数据从其主服务器流向其从服务器。
现在,在同一个 PostgreSQL 数据库中,可以有一些提供者表,数据从这些表中流出,以及一些订阅者表,一些其他数据流向这些表。
单主只意味着任何给定主机上的任何一个给定表不能同时被视为提供数据和订阅远程数据。这就是多主复制,这是一个完全不同的主题。
异步复制
异步意味着在提供者(主服务器)提交时,您无法保证您所做的更改集已在从服务器上重放。
- 缺点是,在某些情况下,提供者已经愉快地提交了从服务器发现无法应用的更改(设备上没有剩余空间是我的第一个想法)。
- 优点是,在网络故障、磁盘不足或其他类型的故障情况下,提供者几乎不会受到影响:它的队列会比平时增长得更快,仅此而已。
根据您的需求,您会更关心优势或劣势。
计时器
计时器是一个独立的守护进程工具,负责生成 tick。这些 tick 是按需生成的,每次消费者请求新批次时,都会生成这些 tick,并且会生成这些 tick,例如,任何批次都包含 ticker_max_lag 秒的事件或 ticker_max_count 个事件,以先发生者为准。
现在,londiste 是复制守护进程,实际上只是另一种类型的 PGQ 订阅者。因此,您为每个订阅者运行一个 londiste.py 守护进程。此 londiste 守护进程被配置为从 pgq_queue_name 队列获取事件,该队列可以在尽可能多的订阅者上使用。另一方面,每个订阅者必须具有唯一的 job_name。
队列守护进程 pgqadm.py 可以托管在任何机器上,它通常是提供者主机或第三台机器,独立于复制数据库。单个计时器实例可以托管尽可能多的队列,但在使用 londiste.py 进行复制时(请记住,您可以在 PGQ 之上使用自己的订阅者代码),需要在提供者数据库上安装该队列。