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,它们都位于模式 public 中
Ticker 守护程序
Londiste 需要一个 Ticker,该 Ticker 必须针对 P(rovider) 数据库,并且可以从另一台机器上运行。常见的用法是直接在提供程序数据库主机上运行 Ticker。
任何 Ticker 都可以托管任意数量的队列。每个队列都有一个唯一的名称,可以被任意数量的订阅者使用。
如果您有多个相同数据库的副本,可以从多个订阅者订阅同一个队列。如果您希望在多个订阅者上拥有相同源数据库的不同子集,您可以让这些订阅者使用同一个队列,但只使用其中的一部分表,或者为每组表创建一个队列。
ticker.ini
您必须配置 Ticker,以便它引用提供程序数据库,您必须在其中安装 PGQ 支持代码(C 和 PL/pgSQL,以及如果运行的是 8.3 之前的服务器版本,则还需要安装 txid contrib 模块)。
[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 并生成 Tick
现在您的配置已准备就绪,您需要在您的数据库中安装支持代码,并启动 Ticker 守护程序。为了使 PostgreSQL 模块 (.so 或 .dll) 存在,PGQ 包必须安装在托管数据库的机器上,以及安装了守护程序的机器上,该守护程序将使用本地 SQL 文件来安装附加组件。
pgqadm.py ticker.ini install pgqadm.py ticker.ini ticker -d
现在您有一个正在运行的 Ticker,您可以检查其日志,当没有活动时,这些日志的更新频率很低。PgQ 默认情况下仅在没有生成事件时每分钟发出一个 Tick,这种情况仍然存在。
复制守护程序
您需要在每个订阅者上运行一个复制守护程序。您可以将守护程序本身托管在单独的机器上,但它必须连接到订阅者数据库,并且 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 列显示了最后一次消费者请求的时间戳。在默认配置中,此列的值永远不应该超过 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>
多个提供者之间的全局唯一序列?
如果您的主键是序列,您将希望避免在中央数据库中发生冲突。有两种方法可以做到这一点,第一种是在服务器之间分割 bigint 范围,让序列从正确的值开始,而不是都从 1 开始。
更好的方法是将您的主键转换为两列键,在 PK 中添加服务器引用(全局唯一)。然后,使用继承技巧或联邦触发器技巧(移动数据)很容易知道每条联邦数据行来自哪里。
复制分区表
由于分区是在 PostgreSQL 中使用继承完成的,因此通常意味着主表具有一个触发器将修改重定向到目标子表。Londiste 是一个基于触发器的复制系统,如何将分区和复制混合和匹配?
使用 Skytools 的分区复制订阅者(例如,cube dispatcher)或简单地复制分区本身,并使用 cron 条目每月添加下一个分区,例如
故障排除
本节旨在提出对常见问题的常见解决方案,如果我们添加太多内容,它可能会成为自己的页面...
Londiste 正在吞噬我的所有 CPU,延迟正在上升
例如,如果您的系统出现了夜间故障,并且值班系统管理员忘记重启计时器,就会发生这种情况。或者,当您在单个事务中执行了大型 UPDATE 或 DELETE 操作,但现在意识到来自同一事务的每个事件都必须在订阅者站点上的同一事务中处理...
以下查询允许您计算代表多少事件,其中神奇的滴答数字来自 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 千个事件。对于如此多的事件,如果您启动 londiste,它将消耗尽可能多的内存来保存所有事件,这可能超过您的系统能够提供的内存。因此,您需要一种方法来告诉 londiste 不要一次加载所有事件。方法如下:在重启 londiste 守护进程之前,将以下旋钮添加到您的 .ini 配置文件
pgq_lazy_fetch = 500
现在,即使单个批次(包含两个滴答之间所有事件)包含大量事件,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 目录中。任何内容不匹配的表也将拥有位于 /home/tmp/repair 目录中的“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 数据库中,可以拥有某些提供者表,数据从这些表中流出,以及某些订阅者表,其他数据流向这些表。
单主只意味着任何给定主机上的任何一个给定表都不能同时被认为是提供数据和订阅远程数据。这就是多主复制,是一个完全不同的主题。
异步复制
异步意味着您在提供者(主服务器)提交时没有任何保证,您所做的更改集已在从服务器上重放。
- 缺点是,在某些情况下,提供者已成功提交了更改,但从服务器发现自己无法应用这些更改(磁盘空间不足是我想到的第一个例子)。
- 优点是,在网络故障、磁盘短缺或其他类型的中断情况下,提供者看到的影响非常小:它的队列比平时增长得更多,仅此而已。
根据您的需求,您将更感兴趣的是优势还是劣势。
计时器
计时器是一个独立的守护进程工具,负责生成滴答。这些滴答是按需生成的,每次消费者请求新的批次时都会生成,并且会像任何批次一样生成,这些批次包含 ticker_max_lag 秒的事件或 ticker_max_count 事件,以先发生者为准。
现在,londiste 是复制守护进程,实际上只是另一种类型的 PGQ 订阅者。因此,您为每个订阅者运行一个 londiste.py 守护进程。此 londiste 守护进程配置为从 pgq_queue_name 队列获取事件,该队列可以在您想要的任意多个订阅者上使用。另一方面,每个订阅者必须具有唯一的 job_name。
队列守护进程 pgqadm.py 可以托管在任何机器上,它通常是提供者主机或独立于复制数据库的第三台机器。单个计时器实例可以托管您想要的任意多个队列,但在使用 londiste.py 进行复制的情况下(请记住,您可以在 PGQ 之上使用自己的订阅者代码),需要在提供者数据库上安装队列。