简介:Sybase Replication Server高级使用与故障处理指南,面向数据库运维工程师、系统管理员及需要维护分布式数据复制环境的从业者,重点解决复制队列阻塞、服务器迁移、权限配置等常见问题。资源为docx格式,共1个文件,压缩包约68KB,内容精炼但覆盖全面,已有58人学习下载。文档从配置优化入手,说明复制分区大小宜设为数据流量6倍(一般2GB)、最大线程数应大于连接数的两倍加3、适当增加复制内存等调优建议;同时强调专用sa用户创建、RSSD_prim权限、RSM客户端连接等关键注意事项。针对复制服务器迁移,梳理断开复制代理、静止队列、删除重建分区、归零第二截断点等完整操作顺序;对DSI线程异常导致的队列阻塞,给出连续执行resume connection跳过事务、通过admin who/sqt定位并清空问题队列的排错思路。常用命令如admin health、rs_config、resume connection亦一并汇总,无论是日常维护还是故障应急,均可作为实用操作参考。
1. Sybase Replication Server是异步分发的好手:先搞清它解决什么
刚接触Sybase Replication的同事,很容易把它当成一个“把主库整个复制到备库”的黑匣子:装完RepServer,配好连接,就以为万事大吉。实际跑两轮就会发现,它不复制整个库,只复制你显式标记过的表;它不做实时强一致同步,而是基于日志的异步分发。这个差别决定了所有后续配置和调优的走向。Sybase Replication Server(下文简称RepServer)适合报表库分流、跨机房数据汇总、多级分发这类场景,不适合拿来做要求读写强一致的横向扩展。它的价值在于:主库事务提交后,RepAgent异步读取日志并转发给RS,RS再把指令重放到订阅端。整个链路不阻塞主库事务,代价是目标端数据天然有一段时间延迟。想用好它,先接受这个“异步”前提。
2. 搭一套最小Sybase复制环境:从RepServer初始化到第一条订阅生效
2.1 先把链路角色分清楚:主库、RepAgent、RepServer、DSI
很多人第一次排错时被一堆术语绕晕,其实这条链只有四个角色。主库ASE上必须启动一个叫RepAgent的进程,它专职读取主库事务日志,把被标记表的变更整理成复制消息。这些消息先落入RepServer本地的稳定队列,再由RepServer分发给目标端。目标端上真正干活的是DSI进程,它把消息转换成SQL语句或命令批量应用到订阅表。
为什么要多绕一层队列,而不是让RepAgent直接写目标端?因为队列给了整个系统解耦能力:目标端短暂不可用、表被锁、网络抖动时,复制消息不会丢,而是积在稳定队列里等DSI恢复后继续消费。这个设计也带来了一个常见坑——队列所在磁盘空间不够。因此选型时,RepServer所在服务器的磁盘IO和空间,重要性不亚于CPU。
我自己见过的最短稳定链路是:主库ASE + 一个RepAgent + 一个RepServer + 一台目标ASE。三台机器各司其职,不要图省事全放一台机器,否则日志磁盘和稳定队列抢IO,复制延迟会变得非常难看。
2.2 用rs_init初始化RepServer并配置主库连接
常见做法是先安装好ASE和RepServer二进制,然后用rs_init创建RepServer的系统库和配置文件。rs_init是交互式工具,填好RepServer名称、sa口令、主库连接信息这些基础项即可。它做两件事:创建RepServer要用的系统库,并生成启动脚本。初始化完成后,用startserver启动RepServer:
startserver -f RUN_REP_SRV然后用isql连上RepServer,创建到主库的连接。这里的关键是把主库的库名、服务器名、登录用户名都写对:
-- 在RepServer上执行 create connection to ASE_SRV.mydb set username sa set password YourPass with log transfer on go这条命令的意思是:RepServer要和主库ASE_SRV上的mydb数据库建立复制连接,并打开日志传输。没有这一句,后面建复制定义和订阅都会报“找不到主库”。口令填错时不会当场报错,等到订阅激活后看DSI日志才发现连不上目标库,所以连接创建后先执行admin who确认连接状态,别急着往下配。
2.3 建复制定义和订阅:让第一张表开始复制
连接通之后,在RepServer上创建复制定义。复制定义相当于告诉RS:“主库mydb里这张orders表,这些列参与复制,主键是order_id。”
-- 在RepServer上执行 create replication definition rep_orders with primary at ASE_SRV.mydb with all tables named "orders" primary key (order_id) replicate columns (order_id, user_id, amount, create_time) go这里有两个细节值得注意。第一,主键必须存在于参与复制的列里,否则RS无法在目标端定位更新和删除;第二,replicate columns只列四列是一种常见优化,减少日志消息体积和DSI执行成本。需要整表复制时,省略replicate columns子句即可,但通常不建议在宽表上这么做,网络和队列压力都会上去。
接下来建订阅,订阅把复制定义绑定到目标库:
-- 在RepServer上执行 create subscription sub_orders for rep_orders with replicate at ASE_REP.reportdb go订阅创建成功,意味着从这一刻起,主库orders表上的新增、修改、删除都会异步落到目标库。最后还要回到主库ASE,把这张表标记为可复制:
-- 在主库ASE上执行 use mydb go sp_setreplicate orders, 'true' go这一步非常容易漏。漏掉的症状是:复制定义和订阅都正常,但主库上新写入的数据就是不动。原因是RepAgent只读取打上复制标记的表。把sp_setreplicate放在创建复制定义之后执行,是为了避免表结构检查时出现“表未标记”的干扰性报错。跑完这几步,用目标端查一下数据是否同步,最小环境就算搭通了。
3. 把复制参数调到长期稳定:队列、DSI与日志保留的取舍
3.1 复制不退化的前提:主库日志要留得住
RepAgent读主库日志,读到哪里算到哪里,主库才能把之前的日志截断。如果RepAgent因为网络问题、自身故障或消息过大一直没读,主库日志会越积越满。Sybase ASE在日志满时会让写事务阻塞,这在业务侧的表现就是“主库突然慢了,但CPU不高”。
常见的备份策略是定期dump transaction,但配合Replication时要注意:不要在主库上执行dump transaction with truncate_only来强行截断日志。这会让日志截断点越过RepAgent尚未读取的位置,导致复制消息丢失。RepAgent要重新从头拉日志或重建订阅,属于比较重的故障恢复动作。
要让日志留得住,核心思路不是加大日志空间,虽然那也是必要措施之一。更稳妥的做法是:监控RepAgent的推进状态,一旦发现sp_rep_agent状态异常或者延迟读数超过阈值,立刻处理,而不是等日志满。把日志空间设成“够用三天高峰增量”是我见过比较稳的基线。
3.2 DSI是吞吐瓶颈:调指令批量和事务批量
链路里最容易成为瓶颈的不是网络,而是目标端DSI。DSI干的事是把复制消息翻译成SQL再提交到目标库。逐条提交效率太低,所以RS提供了批量参数:
-- 在RepServer上执行 sp_configure "dsi_cmd_batch_size", 500 godsi_cmd_batch_size控制DSI一次读取多少条消息作为一批。调大后吞吐量上升,但目标端事务粒度变大,锁持有时间也变长。如果是报表库这种低并发写入的环境,调到500甚至更高都没问题;如果目标端还有别的业务在写入,建议从100起步观察锁冲突。
相邻参数dsi_sql_batch_size控制的是一个批次内SQL文本的总字节数。两者是“条数和字节数哪个先到都触发提交”的关系。好消息是,sp_configure改完对新连接生效,不用重启RepServer。坏消息是,正在跑的DSI连接不会自动收掉旧参数,通常要等当前批次结束,或者在一个维护窗口内重启复制更稳。
3.3 一张表把关键参数列清楚
调参最怕凭感觉乱改。我把日常排障中真正有用的参数整理成了一张表,按生效范围分成RepServer侧和ASE侧。不同Sybase版本的默认值不完全一样,所以表里不写死默认数字,以你自己环境sp_configure的输出为准。
| 参数 | 所在侧 | 作用 | 调整建议 |
|---|---|---|---|
| dsi_cmd_batch_size | RepServer | DSI单批处理的消息条数 | 目标端并发低可调大,有锁冲突就调小 |
| dsi_sql_batch_size | RepServer | DSI单批SQL的字节上限 | 事务大时设大,避免一条大消息拆成多批 |
| dsi_in_order | RepServer | 是否按主库顺序提交 | 默认保持顺序;不要为提速关闭 |
| dsi_recovery_delay | RepServer | DSI恢复后重连目标库的间隔 | 目标库重启后调小能加快恢复 |
| max repagent threads | ASE主库 | RepAgent处理日志的线程数 | 大变更量时调大,配合CPU核数 |
修改主库侧参数示例如下:
-- 在主库ASE上执行 sp_configure "max repagent threads", 6 go调参后别只看状态不看数据。最有效的验证方式是选一张大表做批量UPDATE,观察队列积压曲线和DSI吞吐是否匹配。如果队列积压一直在涨,DSI却没满负荷,问题多半不是参数太小,而是目标端表缺索引导致每一条消息执行都很慢。这种时候调大批次数反而加剧锁竞争,先给目标表上的主键和常用查询列建好索引,再回头调参。
提示:dsi_in_order只在极少数目标端场景下值得冒险关闭。一旦关闭,目标库的数据可能短暂乱序,对外查询会出现瞬时不一致,业务不能接受就别碰它。
4. Sybase Replication高频坑与排查:日志被截断、DDL丢失、重复键
4.1 主库日志被截断:延迟堆积与大事务回滚的起点
现象:白天复制延迟从秒级慢慢涨到小时级,主库日志段报警,最后业务写库开始失败。看RepAgent日志,发现它反复尝试读取日志但推进不了。
原因:最常见的是有人对主库执行了dump transaction with truncate_only。这个命令在很多环境里被当作清理日志的快捷方式,但在复制环境下它会破坏RepAgent的日志扫描连续性。另一种原因是RepAgent进程本身异常退出后未重启,日志截断保护暂时失效。
解决:先重启RepAgent让日志扫描恢复,然后立即确认日志空间余量:
-- 在主库ASE上执行 use master go sp_rep_agent 'mydb', 'status' go如果已经出现日志满,优先扩大日志段,而不是强行截断日志。恢复后观察sp_rep_agent输出中最近一次扫描的日志页号是否持续向前移动。移动正常后,队列里的积压会由DSI慢慢消化。这个坑的教训是:复制环境下,日志清理策略要交给RepAgent的状态决定,不能为了腾空间无差别truncate。
4.2 改了表结构不复制:DDL的坑
现象:主库给orders表加了一个新列,跑了半天,目标端查不到这个列,后续复制还报错。
原因:默认情况下,RepServer的复制定义只管DML,不管DDL。主库加列、删列、改类型,这些操作不会自动传给订阅端。目标端表和复制定义的结构不匹配后,DSI执行带新列的INSERT时就会报列无效。
解决:常见做法是把DDL做成双端执行的脚本。生产环境里更省事的方式是给DBA定一个规矩:任何涉及复制表的结构变更,先在目标库执行相同DDL,再在主库执行。顺序不能反,反了会有短暂的时间窗让主库把新结构消息发到还没改表的目标端。
-- 先在目标库ASE_REP上执行 alter table reportdb..orders add remark varchar(200) null go -- 再在主库ASE_SRV上执行 alter table mydb..orders add remark varchar(200) null go如果已经翻车,目标端必须手工补上缺失列,然后让DSI从错误点继续。是否需要重建订阅取决于错误类型:结构性报错一般补齐就好;如果是数据级错位,宁可重建订阅也别凑合。
4.3 身份列重复与目标端触发器二次执行
现象:复制正常,但目标端频繁报唯一键冲突,或者明明没业务写目标表,某些字段的数据被莫名改掉。
原因:第一类问题多半出在identity列。主库表的identity列值在INSERT时被RepAgent原样带过来,如果目标端表也定义成identity,ASE会自动生成新值,和RS带过来的值冲突,或者下一次业务插入撞上已存在的值。第二类问题是目标端表的触发器还在生效,DSI应用复制消息时触发了触发器,触发器里又没有区分来源,把数据又改了一遍。
解决:对于identity列,目标端表结构不要用identity属性,改成普通列,让RS显式插入主库的值即可。对于触发器,保留业务触发器没问题,但要在触发器逻辑最前面加判断来源的代码:
create trigger trg_orders_noop on reportdb..orders for insert, update, delete as begin if is_replication_system = 1 return end gois_replication_system是ASE提供的系统函数,返回1表示当前会话来自复制系统。加上这支判断,DSI的写入就不会触发连锁业务逻辑,而正常业务写入不受影响。这个坑属于“配置时多写一行,排障时少熬一晚”的典型。
4.4 初始化不一致:先灌数据还是先订阅
现象:订阅建好,增量数据正常同步,但目标端的历史数据和主库对不上,差了一段。
原因:执行顺序错了。常见的错误做法是先在目标端用bcp把主库当前数据倒入目标表,再创建订阅。这中间存在一个时间窗:bcp导出的时刻和订阅生效的时刻不一致,主库在这段时间内的增量没有被任何复制消息捕获。这个时间窗的缺口,订阅恢复不了。
解决:标准化顺序是先确认主库日志不会在短时间内被截断,然后对目标库做一次数据装载,装载完成后立刻创建订阅,让订阅接续装载起始时刻之后的日志。更稳妥的方式是利用ASE的dump database和load database做整库初始化,这样目标库的物理快照和日志起点是对齐的。如果装载起点和订阅起点之间隔了太久,宁可重做一次装载,也别让数据带着缺口上线。
5. 复制延迟到底卡在哪:用admin命令把链路一段段拆开
5.1 先确认RepAgent活着并定位读取日志的进度
复制延迟是结果,不是原因。排查时先看主库侧的RepAgent。用isql连主库ASE执行:
use master go sp_rep_agent 'mydb', 'status' go输出里重点看RepAgent的运行状态和最近活动时间。如果状态不是active,或者最近活动时间是几分钟之前,说明RepAgent要么没起来,要么在处理一个超大事务时卡住了。这时重启RepAgent并不能解决卡住的问题,得先去主库日志里找有没有未提交的大事务。RepAgent读取日志的进度落后越多,主库日志越危险,所以这一步判断延迟时要尽量快。
5.2 用admin who和admin disk_space看队列与DSI
RepAgent活着且推进正常,接下来看RepServer侧。用isql连RepServer:
admin who, rsi go这个命令列出所有RepAgent连接。看连接状态是否正常,以及最后转发消息的时间。紧跟着看DSI:
admin who, dsi goDSI这里主要看两点:是否处于running状态,以及是否卡在一个长时间执行的批次里。如果状态是wait或blocked,目标端很可能有锁竞争,去目标ASE查一下sp_who确认。
再看稳定队列的积压情况:
admin disk_space go输出会显示RepServer本地稳定队列各段的使用量。队列用量持续高位,说明消息生产快于消费,瓶颈在DSI一侧;队列用量低但主库日志仍然增长很快,说明RepAgent读取消息的速率没跟上写入速率,方向在主库侧。结合admin disk_space和前面两段admin who,基本能把延迟定位到具体环节,而不是靠猜。
5.3 延迟排查的顺序:不要一上来就重灌数据
给一套我实际用的排查流程,按顺序走基本能锁定问题。第一步看主库日志空间和RepAgent状态;第二步看RepServer队列积压;第三步看DSI状态和目标端锁;第四步看目标端错误日志中的DSI报错。
# 主库ASE:看日志空间和RepAgent isql -Usa -P密码 -SASE_SRV <<EOF use master go sp_rep_agent 'mydb', 'status' go EOF# RepServer:看连接与队列 isql -Usa -P密码 -SREP_SRV <<EOF admin who, rsi go admin who, dsi go admin disk_space go EOF# 目标ASE:看锁与阻塞 isql -Usa -P密码 -SASE_REP <<EOF sp_who go EOF这套命令跑下来还没找到原因的情况极少。真遇到,再去目标端库的SQL错误日志里翻DSI相关报错,那里会有比admin输出更详细的错误号。整个排查过程十五分钟以内应该能定位,不建议直接走重灌数据的路线,重灌虽然见效快,但可能掩盖真实原因,下一轮故障还会来。
提示:稳定队列即使显示为空,也不代表没有积压。DSI正在执行的大批次消息在admin disk_space里可能体现不出来,要结合DSI的执行时间判断。
6. 别动不动就drop subscription:check subscription才是后悔药
当目标表因为结构错位或数据缺口要重新对齐时,很多人下意识先drop subscription再create subscription。这个操作的代价是,如果目标表数据还在,重新创建订阅并不会自动把历史数据补平,反而可能让订阅起点变得难以界定。更好的做法是用check subscription来校验订阅一致性,让RS重新确认订阅定义和目标表结构是否匹配。
-- 在RepServer上执行 check subscription sub_orders for rep_orders with replicate at ASE_REP.reportdb go这个命令不产生数据搬运,它只做元数据和状态校验。执行成功后,订阅状态会被标记为一致,之后增量继续正常复制。适用场景是:目标表结构在双端已手工对齐、数据层面确认没问题,只想恢复订阅关系的场景。如果数据确实有缺口,check subscription不会帮你补,必须先修数据再check。
我在这上面吃过一次亏:一次目标端表被运维误清空了一部分数据,我直接drop并重建订阅,结果新数据正常同步,老数据的缺口让报表结果错了整整一周。后来改成先补数据、再check subscription,再也没有因为重建订阅的起点问题返工。养成这个习惯后,复制维护变得从容很多:能不drop就不drop,重建前先把数据修好,再用check subscription把订阅拉回正常状态。希望这个技巧帮到你少走一次弯路。
本文还有配套的精品资源,点击获取