最佳工程实践—Oracle数据库迁移割接实录

概要

本次分享将从一次实际的数据库割接案例出发,通过介绍系统迁移过程中的项目背景、数据同步方案、工具开发、仿真测试、割接心理素质等方面展开,管窥工程实践过程中鲜为人知的幕后故事。

项目背景

某企业支撑系统,已经连续服务七年有余。算起来比我的工作年限还要长。
历年工程中,硬件、软件、运营团队都更新换了好几茬,单独系统核心数据库—— 一台小型机搭载Oracle 10g,附加一套磁盘阵列,从来没有动过。随着近年的业务暴涨、负载上升、硬件老化,服务器、磁盘都时有故障发生,负载水平线逼近极限,故障率还有加速抬头的趋势。整个运营团队面临了巨大的客户压力,提升系统稳定性的巨大挑战摆在了大家面前。

数据:CPU平均空闲率26.7% ,隔三差五就有空闲率逼近0%的异常告警单,繁忙的磁盘IO。

《最佳工程实践—Oracle数据库迁移割接实录》 割接前_io_error

主要困难

困难1: 团队大动荡
我是在“天塌地陷”的不利局面中加入到项目组。原运营团队,包括但不限于资深项目经理、技术负责人、多名工程师等,先后因各种原因,在很短的时间内集中离职了。在接手之前,我对该项目一无所知,接手以后很短暂的交接过程中,很难获取多少有价值的信息。

此外,该系统连续突发重大故障,项目组不但要解决问题,还要面临巨大的心理压力、商务压力,整个团队疲惫不堪、士气低落。每一次重大故障,所有人都得没日没夜地干活,处理好以后还需要写汇报材料,汇报之后也未必能得到客户的肯定。甚至在某种程度上说,急剧增长的故障率,进一步刺激、助涨了离职率。正如一位哲人所说:

降低故障率是提升团队幸福指数的首要保障。— 弗拉基米.耶维奇.严

困难2: 拓扑大调整
在技术上,本次迁移的另一个难点是系统拓扑结构的调整。系统的拓扑结构最初是星型:以数据库和应用服务器为核心,外挂近100台各类采集服务器。采集服务器的网络又分为DCN网和公网两个部分。
星型结构虽然简单易用,但是安全隐患也非常明显。在早期建设的时候,规范尚未健全,都是在核心服务器中配置双网卡,连通内网、外网。在本期工程中,非常明确必须要完成内外网分离改造。

《最佳工程实践—Oracle数据库迁移割接实录》 迁移拓扑
《最佳工程实践—Oracle数据库迁移割接实录》 迁移前拓扑
《最佳工程实践—Oracle数据库迁移割接实录》 迁移后拓扑

困难3: 安全一票否决
Oracle 部署主版本由10g 升级到 11g,加强管理访问权限。最大限度地提高安全性,口令60天更换一次,同时要求不能因为更换口令而中断业务;如果出现连续的错误口令访问,甚至可以不惜锁定数据库。
从安全的角度看,类似这些规定可以降低风险。但是在软件架构规范化不足、自动化严重不足的条件下,却给我们的迁移工作增加了难度。

迁移前准备

基于上述三大问题,在正式迁移之前主要做了下列几项工作:

  1. 加强监控手段,降低日常故障率。梳理需要监控的基础指标和业务指标,侧重关键业务可用性。例如,某业务的正常调度周期是3小时,部署模拟脚本,将模拟脚本的调度周期提高到5分钟一次甚至更高,通过高频率的调用执行,主动触发风险点,一些隐藏的问题就比较容易暴露出来。
  2. 重点培训新人,稳定队伍。本质上说,这次迁移工作的首要任务不在技术、也不在数据,而在于人。上一个团队整体流失,新补充的人员又完全没有相关经验,可以说是从0开始。基于该阶段的特殊情况,我选择了实质性暂停迁移工作,而把主要的精力投入到人员培训和组织重建上来。关于这块内容比较复杂,实际是另外一个主题,计划后续再发布,敬请关注。
  3. 梳理全局视图。全局视图包括两个维度:技术和人。
    第一,重绘系统架构图。部分参考现有文档资料,但是主要立足于自主调研。绘制的过程,即是收集、整理的过程,也是制定迁移方案的思考过程。唯有自己动手,才能加深认识,做到胸有成竹。
    第二,重新梳理系统干系人。主要通过大量接触各方面的领导、配合部门以及第三方厂家。个人工作经历方面,独立工作的场景居多,自己能直接控制的情况居多,不太需要理会复杂的部门关系、人际关系。这项工作对于某些人来说比较容易,但是对我而言,其实是有过一段比较困难的过程。

迁移前准备:数据同步

主要实现方案:OGG + DBLINK + 自主定制迁移程序。

Oracle Golden Gate
在最早的方案中,我们打算完全依赖Oracle Golden Gate (以下简称OGG)。

《最佳工程实践—Oracle数据库迁移割接实录》 ogg workflow

但是在实验阶段发现,实施该方案有其限制条件。
首先,历史遗留系统有庞大的历史数据,如果都用OGG,无法确保新库的及时性、一致性。其次、由于管理的不规范,存在很多该做分区而没有做分区的大表,而且迭代过程中本身会产生数量众多、非必要的表,一时还真的很难分离出来。

EXP/IMP

连接源库执行:

exp userid=username/pass 
    file=/oradata5/ogg/dp/part.dmp 
    log=/oradata5/ogg/dp/part.log 
    buffer=819200 feedback=10000 GRANTS=n INDEXES=n COMPRESS=n RECORD=n TRIGGERS=n  CONSTRAINTS=n  
    parfile=/oradata5/ogg/dp/part.par
    
vi part.par
tables=(
    TABLE_A:P_20160531,
    TABLE_B:P_20160526,
    TABLE_C:P_20160526
)

连接目标库执行(注意NLS_LANG保持一致):

imp username/pass@dbnms file=part.dmp buffer=819200 log=part_imp.log ignore=y

** DATABASE LINK **
dblink建立之后,连接其中一个库就可以对两个库执行SQL,它的优势是提供了旧库->新库之间的连接通道。也就仅仅是个通道而已。

insert into TableA(….)  select … from TableA@linkname where colltime=‘’;

**定制迁移程序 **在某些特殊情况下,无论使用 OGG 和 DBLINK 都会存在一定问题。
例如A表是大量的原始数据,每天一个分区,每个分区约为4000万条记录,一个月就有1.2亿条。由于业务上非常重要,该表的数据必须迁移到新库。这种情况下,我们就只能自己编写迁移脚本,实现数据推送。

坚持“少量、多批次、并行”的原则。首先,控制每个批次提交的记录数,将每个分区4000万的数据,切分成10万一份的小切片,这样即使失败也能快速重试,还能杜绝UNDO表空间暴涨(例如exp/imp整个分区的方式)。基于小粒度的切片,进一步就可以实现多批次、多进程的并行推送,从而保证每个commit和时间单元的推送规模都做到failed范围可控、同步进度可视。

迁移前准备:工具开发 & 测试

  1. 转发入库组件
    在跳转服务器起守护进程,监听特定端口请求,再“bond”到下一跳服务器。逻辑比较简单,伪代码如下:
public void run() {
    
        try{    
                
                ServerSocket serverSocket = new ServerSocket(local_port);
                
                while (true) {
                    Socket socket = serverSocket.accept();              
                    ProxyWorker worker = new ProxyWorker(socket, remote_ip, remote_port);           
                    worker.start();
                }
                
        }catch(Exception e) {
                //====异常处理  
        }
}

从实现的角度看,再网络设备上配置转发的话,效率应该会更高,但是客户只扔给你服务器,其它让你自己实现。谁让我们是乙方呢。不过从后来的效果上看还不错,基于自主实现的转发机制,让我们可以记录每一个数据库连接的时间消耗、“看到”当前连接,为更细粒度的负载分析、troubleshooting等提供了更多可能。

  1. 割接工具
    所有可能要遇到的操作,即使是非常简单的一句命令,能固化的坚决固化,能批量执行的坚决批量执行,因为对割接来说,最宝贵的是时间。特别是遗留系统,配置不一致的地方很多,开发工具的过程,也是检查、提升系统一致性的过程。
    系统配置收集器;
    转发路径监视( 外网跳板到内网跳板、跳板到数据库等);
    割接前预配置/割接后检查工具;
    连接 切换 & 回退 工具等(事实将会证明每一行回退代码都是非常必要的);
    测试工具(批量插入/删除数据,对比分析SQL执行的时间长度);
    制作割接后检查验证清单。

《最佳工程实践—Oracle数据库迁移割接实录》 割接流程

  1. 高仿真测试
    为了在迁移之前确认新库的可用性,我们采用了双库并行的策略。
    即在所有采集服务器开启两个入库进程,让一份原始结果同时入两套数据库。最大限度在没有额外测试系统的条件下,利用现有资源,模拟仿真正式生产环境的并发压力,提前优化新库参数设置,也同时完成负载均衡、单点故障验证测试。

并不是所有的程序都能轻易的实现双库并行,有的可能只要稍微调整配置文件,有些可能就必须修改代码,还有的可能就做不到。从这个角度观察,第一种应该就是好的代码,灵活适应各类场景。灵活性低的应用程序往往缺乏设计,甚至都没有做到配置-代码分离,存在大量侵入式编程等。

没有一帆风顺

第一次割接失败了!

失败的体验
第一次割接之后,系统各项功能正如我们预计的那样顺利运行。就在我们准备庆祝成功的时候,几项关键业务的吞吐量却急剧下降。初步判断是性能问题,因为每次连接时延飙升了100多倍,高达秒级,基本是不可用的。
人工排查几次以后,看到了大量的挂起进程,一堆的锁表。而且失败来源遍布一多半的服务器。虽然不想承认,但是我们不得不做出回退的决定。

**万事留后路 **
割接失败是需要承担很大压力的。这次割接是大家都期待很久的,为了几分钟的操作,用户和我们整个团队都付出了很大的努力,调动了方方面面的资源参与进来。
如果说有什么能稍感欣慰的话,那就是我们遍历了各种可能,几乎成功,在不可知的情况出现之后,还能赶在割接窗口结束之前,快速回退。这主要得益于前期准备方案时,特别考虑了最坏的情况,认真演练了回退流程。
这种体验与技术关系不大,来自于勇气——无论是正确的,还是错误的决定。

幽灵进程
事后筛查发现,导致割接失败的是一个监测程序——不在生产程序清单里面,没有读统一配置,自带定时调度,零散分布在一些机器上,早已经被人遗忘。

新数据库的版本是Oracle 11g 。为了提高安全性,防止暴力破解数据库中用户的密码,Oracle默认提供了一种机制:延长失败尝试响应。它的策略是:在连续使用错误密码反复尝试登录时,从第四次错误尝试开始,每次增加1秒的延迟,最长延迟目前是10秒。使用这种手段可以相对比较有效的防治用户密码的暴力破解。

第一次割接后,历史遗留程序瞬间就触发该机制,导致应用不可用。

《最佳工程实践—Oracle数据库迁移割接实录》 01.png

《最佳工程实践—Oracle数据库迁移割接实录》 02.png

《最佳工程实践—Oracle数据库迁移割接实录》 03.png

割接后

我们后来是如何发现幽灵进程的呢?
没有什么绝招。单曲播放:“完善配置检查工具->模拟执行->发现解决问题”。
直到你清楚地理解系统里的每一个进程,幽灵自然无所遁形。一切都是功到自然成。
最后的割接非常平静。

《最佳工程实践—Oracle数据库迁移割接实录》 割接后的数据库CPU利用率

总结

虽然这次的迁移不甚完美,事情本身也谈不上宏大,简短的一篇更不可能穷举所有的问题和细节,
但是有几点思考还是想和大家交流:

  1. 知识图谱
    就以数据同步技术为例,网上关于exp/imp,OGG , DBLINK等的资料可谓多如牛毛,然而对于新手来说并没有什么用!因为这些材料顶多只能算缩略版单一技术手册(基本套路:原理、安装、配置、运行,简单DEMO),无法展现不同工具在技术全局中的位置和关联关系,更不要说真实现场中的系统架构和决策经验部分。

  2. 变通
    开始设计方案时想到的几个大难题,都是通过替代方案实现的:奇葩的内外网分离方案,与IT部门关于权限问题的艰难谈判,数据复制过程中及时性的要求…… 真实割接过程中,现场压力状态的进退困境。到处都需要权衡、选择。
    决策是一件非常艰难的事情,受到多种因素的制约,最终的决策是一个各种利益妥协的结果。正如另一位资深哲人所说:“项目经理首先要学会变通。——瓦西里.杨” 诚如斯言,天下武功,无坚不摧,唯变不破。

  3. 韧性
    按照最初的方案,我其实并不负责这项工作,后来就算参与进来,也并不负责主导。应该说起初也有侥幸心理,希望有其他人来背这个锅。为了这次迁移,前面已经生生吓走了好几拨人。从技术上分析,我以前没怎么搞过数据库,并不具备任何优势。如果说还有一点可以凭借的东西,我感觉是韧性。面对未知的恐惧,敢于直面;面对不确定的方案,不断在尝试;面对失败的后果,坦然接受。

事情能做成,感觉很好,我喜欢干成事的感觉。

《最佳工程实践—Oracle数据库迁移割接实录》 掌握分散知识点=>成体系实践应用

    原文作者:RiboseYim
    原文地址: https://www.jianshu.com/p/a0939b39276f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞