# Oracle 数据库迁移与割接实践

## Oracle 数据库迁移与割接实践

## 摘要

* 一、项目背景
* 二、主要困难
* 三、迁移前准备
* 四、失败的体验：没有一帆风顺
* 五、总结

### 一、项目背景

某企业支撑系统，已经连续服务七年有余。算起来比我的工作年限还要长。

历年工程中，硬件、软件、运营团队都更新换了好几茬，单独系统核心数据库—— 一台小型机搭载Oracle 10g，附加一套磁盘阵列，从来没有动过。

随着近年的业务暴涨、负载上升、硬件老化，服务器、磁盘都时有故障发生，负载水平线逼近极限，故障率还有加速抬头的趋势。整个运营团队面临了巨大的客户压力，提升系统稳定性的巨大挑战摆在了大家面前。

### 二、主要困难

> 在“天塌地陷”的不利局面中加入进来。

#### 2.1 困难1: 团队大动荡

原运营团队，包括但不限于资深项目经理、技术负责人、多名工程师等，先后因各种原因，在很短的时间内集中离职了。在接手之前，对于该项目我一无所知，接手以后短暂的交接过程中，也很难获取多少有价值的信息。

连续发生突发重大故障，我们面临巨大的商务压力，团队内部疲惫不堪、心理压力极大、士气低落。每一次大故障，所有人都得没日没夜地处理，处理好以后还得写材料汇报，汇报之后也未必能得到客户的首肯。甚至在某种程度上说，急剧增长的故障率进一步刺激、增加了离职率。正如一位哲人说的：

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

#### 2.2 困难2: 拓扑大调整

系统的拓扑结构最初是星型：以数据库和应用服务器为核心，外挂近100台服务器。数据库服务器配置双网卡，连通内网、外网。

虽然星型结构简单易用，却也存在较大的安全隐患。在早期建设的时候，规范尚未健全，还可以用业务优先的理由搪塞过去。在本期工程中，非常明确必须要完成内外网分离的改造。

#### 2.3 困难3: 安全一票否决

Oracle 版本由10g 升级到 11g，集中管理访问权限。 最大限度地提高安全性，口令60天更换一次，不能因为更换口令中断业务；如果出现连续的错误口令访问，甚至不惜锁定数据库。

从正向的角度看，这些严格规定可以倒逼改造。在软件架构规范化不足、自动化严重不足的条件下，整个迁移期间，这个问题确实给我们造成了极大困扰。

### 三、迁移前准备

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

* 3.1 加强监控手段，降低日常故障率； 梳理需要监控的基础指标和业务指标，侧重关键业务可用性。例如，某业务的正常调度周期是3小时，部署模拟脚本，将模拟脚本的调度频率提高到5分钟一次甚至更高，通过高密度的执行，主动触发风险点，一些隐藏的问题就比较容易暴露出来。
* 3.2 重点培训新人，稳定队伍； 本质上说，这次迁移工作的首要任务不在技术、也不在数据，而在于人。 上一个团队整体流失，新补充的人员又完全没有相关经验，可以说是从0开始。基于该阶段的特殊情况，我选择了实质性暂停迁移工作，而把主要的精力投入到人员培训和组织重建上来。

  关于这块内容比较复杂，实际是另外一个主题，计划后续再发布，敬请关注。
* 3.3 梳理全局视图，调研周边系统关系链； 全局视图实际上也包括技术和人两个维度：

  1）重绘系统架构图：可以参考现有文档资料，但是主要应该立足于自主调研。绘制的过程，即是收集、整理的过程，也是制定迁移方案的思考过程。唯有自己动手，才能加深认识，做到胸有成竹。

  2）大量接触相关方面的领导、配合部门以及第三方厂家： 个人工作经历方面，独立工作的场景居多，自己能直接控制的情况居多，不太需要理会复杂的部门关系、人际关系。这项工作对于某些人来说比较容易，但是对我而言，其实是有过一段比较困难的过程。
* 3.4 数据复制 主要实现：OGG + dblink+ 自主迁移程序。

**OGG**：即Oracle Golden Gate方案。 在最早的方案中，我们打算完全依赖OGG来实现数据复制，但是在实验阶段发现，该方案有其限制条件。 第一、历史遗留系统有庞大的历史数据，如果都用OGG，无法确保新库的及时性、一致性。 第二、由于管理的不规范，存在很多该做分区而没有做分区的大表，而且这样的表，数量很多，一时还真的很难分离出来。

**DB Link**: 提供了旧－>新库之间的快速连接通道。

**自主迁移程序**：针对特殊大分区表。 例如A表是大量的原始数据，每天一个分区，每个分区约为4000万条记录，一个月就有1.2亿条。由于业务上非常重要，该表的数据必须迁移到新库。针对这个问题，我们自己编写了迁移脚本。按照“少量、多批次、并行”的原则，实现数据推送。

首先，控制每个批次提交的记录数，将每个分区4000万的数据，切分成10万一份的小切片，这样即使失败也能快速重试，还能杜绝UNDO表空间暴涨（例如exp/imp整个分区的方式）。

基于小粒度的切片，进一步就可以实现多批次、多进程的并行推送，从而保证每个commit和时间单元的推送规模都完全可控、可视。

* 3.5 双库并行部署

  在所有采集服务器开启两个入库进程，即一份原始结果同时入两套数据库。最大限度在没有额外测试系统的条件下，利用现有资源，模拟仿真正式生产环境的并发压力，同时完成负载均衡、单点故障验证测试。

并不是所有的程序都能轻易的实现双库并行，有的可能只要稍微调整配置文件，有些可能就必须修改代码，还有的可能就没法弄。从这个角度观察，第一种应该就是好的代码，灵活，适应各种可能的场景。后者则可能连配置－代码分离都没有做到，侵入式编程等。

* 3.6 转发入库组件开发 开发这个东西的时候，我一度准备中途放弃。 前两版发布测试的时候，起初都还不错，但是运行一段时间之后，就暴露出严重的性能问题，几乎不可用。多次调试都找不到原因。后来发现，最大的Bug不过是一个空指针异常的捕获不合理。 很不经意地修改几个字符之后，一切都顺畅起来，往后再也没有出一丁点问题。 很多时候可能就是这样，需要一点灵性，需要一点运气。
* 3.7 割接工具开发 系统配置收集器（例如口令配置必须保证强一致性）； 转发路径监视（ 外网跳板到内网跳板、跳板到数据库等）； 预配置／检查工具； 连接 切换 & 回退 工具；

![割接流程](http://riboseyim-qiniu.riboseyim.com/oracle-trans-cflow-1.png)

### 四、失败的体验：没有一帆风顺

> 第一次割接失败了！

**失败的体验**

第一次割接之后，系统各项功能正如我们预计的那样顺利运行。 就在我们准备庆祝成功的时候，几项关键业务的吞吐量却急剧下降。初步判断是性能问题，因为每次连接时延飙升了100多倍，高达秒级，基本是不可用的。

人工排查几次以后，看到了大量的挂起进程，一堆的锁表。而且失败来源遍布一多半的服务器。虽然不想承认，但是我们不得不做出回退的决定。

**万事留后路** 割接失败是需要承担很大压力的。 这次割接是大家都期待很久的，为了几分钟的操作，用户和我们整个团队都付出了很大的努力，调动了方方面面的资源参与进来。

如果说有什么能稍感欣慰的话，那就是我们遍历了各种可能，几乎成功，在不可知的情况出现之后，还能赶在割接窗口结束之前，快速回退。这主要得益于前期准备方案时，特别考虑了最坏的情况，认真演练了回退流程。

这种体验与技术关系不大，来自于勇气——无论是正确的，还是错误的决定。

**幽灵进程** 事后筛查发现，导致割接失败的是一个监测程序——不在生产程序清单里面，没有读统一配置，自带定时调度，零散分布在一半的机器上，早已经被人遗忘。在旧库中，这个问题其实一直存在，但是不会有什么问题。

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

![01](http://riboseyim-qiniu.riboseyim.com/oracle-trans-test1.png) ![02](http://riboseyim-qiniu.riboseyim.com/oracle-trans-test2.png) ![03](http://riboseyim-qiniu.riboseyim.com/oracle-trans-test3.png)

由于系统服务器较多，历史遗留程序瞬间就触发该机制，导致所有应用不可用。

### 五、总结

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

* 5.1 变通

  回想起来，开始设计方案时想到的几个大难题，都是通过替代方案实现的：奇葩的内外网分离方案，与IT部门关于权限问题的艰难谈判，数据复制过程中及时性的要求...... 另外，还有真实割接过程中，现场压力状态的进退困境。到处都需要权衡、选择。

  决策是一件非常艰难的事情，受到多种因素的制约，最终的决策是一个各种利益妥协的结果。正如另一位资深哲人所说：

  > 项目经理首先要学会变通。

  天下武功，无坚不摧，唯变不破。
* 5.2 韧性 按照最初的方案，我其实并不负责这项工作，后来就算参与进来，也并不负责主导。应该说起初也有侥幸心理，希望有其他人来背这个锅。为了这次迁移，前面已经生生吓走了好几拨人。

  从技术上分析，我以前没怎么搞过数据库，并不具备任何优势。 如果说还有一点可以凭借的东西，我感觉是韧性。面对未知的恐惧，敢于直面；面对不确定的方案，不断在尝试；面对失败的后果，坦然接受。

### 参考文献

* [密码延迟验证导致的系统HANG住 | yangtingkun](http://yangtingkun.net/?p=1429)
* [【翻译】为什么我的数据库变得这么慢？ | Cholerae's Blog](http://cholerae.com/2015/01/01/【翻译】为什么我的数据库变得这么慢？/)
* [关于Oracle Sharding | Oracle FAQ文档翻译| eygle.com](http://www.eygle.com/archives/2017/03/oracle_sharding.html)
* [防范攻击 加强管控 - Oracle数据库安全的16条军规 | eygle.com](<http://www.eygle.com/archives/2017/03/oracle_security_rule_16.html >)
* [How to Analyse Row Lock Contention in Oracle 10gR2 and later | kamus](http://www.dbform.com/html/2015/2317.html)
* [iptables高性能前端优化-无压力配置1w+条规则,2017| Bomb250](http://blog.csdn.net/dog250/article/details/77618319)
* [邱俊涛：如何持久化你的项目经历](http://abruzzi.github.com/2016/01/how-to-summarize-privious-project/)

  > 从项目上下来之后，需要深入思考并总结之前的经验，这种深入思考会帮助你建立比较完整的知识体系，也可以让你在下一项目中更加得心应手，举一反三。如果只是蜻蜓点水般的“经历”了若干个项目，而不进行深入的总结和思考，相当于把相同的项目用不同的技术栈做了很多遍一样，那和我们平时所痛恨的重复代码又有什么不同呢？ 邱俊涛：如何持久化你的项目经历
* [Oracle 数据库健康检查平台](https://mp.weixin.qq.com/s?__biz=MjM5MDAxOTk2MQ==\&mid=2650272655\&idx=1\&sn=9173ca8cdeafa092f1125285909634b2\&chksm=be486b99893fe28ffb96e9ae713b91a73b94b71eee0b915c4a5e2c3b75cec39eca0eeb119e79\&mpshare=1\&scene=1\&srcid=1226Q2973ERHUtX4hUpE7tXK#rd)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://riboseyim.gitbook.io/perf/technology-oracle.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
