| 免费下载:IBM® DB2® Express-C 9.7.2 免费版 或者 DB2® 9.7 for Linux®, UNIX®, and Windows® 试用版 |
|---|
| 下载更多的 IBM 软件试用版,并加入 IBM 软件下载与技术交流群组,参与在线交流。 |
MySQL 是当前最常用的数据库服务器,可以和 PHP 编程语言一起用于构建动态 Web 应用程序。但是,DB2 是另一个 PHP 支持的流行数据库,比 MySQL 有明显优势,这使得它最终成为许多应用程序的理想之选。
该系列文章说明了为什么将 PHP 应用程序迁移到 DB2,如何准备迁移、执行和支持它,以及如何根据作者最近的迁移经验来处理潜在风险。本文提供了许多代码、配置样例以及帮助项目顺利进行的资源指南。
利用成功的真实转换中得来的示例和经验,您会了解到这是一个简单的项目,有很多这方面的文档记录,该项目能提供一些显而易见的好处。
该 4 部分的系列文章分享了成功地将生产级的关键任务型 PHP 内部网应用程序从 MySQL 迁移到 DB2 的经验,该案例被 IBM 全球 4,000 名用户使用,以支持 ibm.com 内容制作。
- 第 1 部分介绍了迁移准备的步骤。
- 第 2 部分介绍了迁移数据库的步骤。
- 第 3 部分介绍了转换 PHP 代码的步骤。
- 第 4 部分介绍了部署和支持应用程序的步骤。
本系列文章旨在让您了解将 PHP 应用程序从 MySQL 迁移到 DB2 通常需要什么,什么样的资源对您有帮助,以及在 2010 年初 IBM 项目团队如何完成任务。
如果您对 MySQL 到 DB2 的迁移做过调查,那么您可能已经从产品资料和性能基准测试中了解到了 DB2 所能提供的价值,或者可能已经从 DB2 文档或 IBM Redbooks® 中的比较(包括MySQL to DB2 Conversion Guide,参见 参考资料)中了解到它的一些功能。
您可能还知道 DB2 Express-C 是免费的、功能齐全的关系数据服务器,可在 Cloud 或 Amazon EC2 中使用 IBM Smart Business Development and Test 轻松安装和评估它。这些资料的链接可以在 参考资料 部分中找到。
本文为您提供了如何成功执行实际迁移的真实示例,该示例就是 2010 年 IBM 常用的 PHP 内部网应用程序,用于支持 ibm.com Web 网站各部分中发布的内容的日常管理。
在阅读完本系列之后,您将能够完成类似的迁移,了解需要执行的工作项目的时间和依赖关系,预测潜在风险,了解在何处寻找对各个步骤的支持。所有这些都会使您更坚定地选择 DB2,并在当前构建于 MySQL 上的 PHP 应用程序中最大程度地利用它。
本系列文章旨在分享从 MySQL 到 DB2 的内部 IBM 迁移中得到的经验,为您提供执行类似迁移的可用资源信息。本文并不是一个全面的、适用于所有场景的迁移指南。
为了确定适合您的方法,请参阅 MySQL to DB2 Conversion Guide 或者联系 Software Migration Project Office (SMPO) 获取免费的迁移估算。参考资料 部分提供了相关链接。
本文介绍了案例研究中采取的五个主要步骤,将 PHP 应用程序代码从使用 MySQL 驱动程序和语法迁移到使用 DB2。如果认为需要的话,在进行转换之前,请参考 将 PHP 应用程序从 MySQL 移动到 DB2,第 1 部分:为您的迁移做好准备,了解整个迁移过程的步骤安排。
- 第 1 步:开始进行代码迁移的第一步
-
- 确保 PHP 配置已经更新,能支持 DB2
- 更新单个 SQL 语句,能支持 DB2 语法
- 如果需要的话,使用 DB2 用户定义的函数模拟本机 MySQL 函数
- 如果需要,请将逻辑部分从 SQL 转到 PHP 中
- 第 2 步:开始进行代码迁移的第二步
-
- 检查用来支持适当的隔离级别所需的改动
- 将查询重新组织成逻辑工作单元,以获取更好的集成性并提稿性能
- 第 3 步:与相关利益者进行最初的业务用例测试
-
- 请利益相关者执行那些在原有系统中已经熟悉的用例
- 捕捉测试错误,让程序人员将其作为缺陷进行分析并修复
- 第 4 步:解决瓶颈问题,并确定功能基线
-
- 功能验证后,根据用户的反馈提高系统性能
- 重点关注 DB2 能自动修复哪些故障,因为 DB2 就是引入的最大的变更
- 通过观察操作系统资源的使用情况来解决 PHP 性能瓶颈
- 第 5 步:评估代码迁移基线
-
- 不断重复上述步骤后,就可以宣告已完成代码转换
- 备份系统,在控制系统中标记为里程碑
- 做好准备,以便进行下一项任务:应用程序部署
对于本文中的样例,Project Tracking Tool (PTT) 的源代码中包含数百个 PHP 文件。代码库包含函数库、数据转换对象和管理类中的面向对象代码,还有用来呈现用户界面的各种 HTML 模板片段和辅助文件。
PTT 数据库可供各种函数使用,将信息发布到 ibm.com 上。全世界超过 4,000 个用户通过 PHP Web 前台访问和修改 PTT 数据库。在任意时刻,系统中活跃用户都有几百人。
代码部署在单个 Apache Web 服务器上,它会将 mod_php 作为共享模块加载。
本例中,现有的代码将会为新的 DB2 系统进行更新,这主要是通过修改嵌入的 SQL 来完成。还需要做一些细微的修改,以更新 PHP 配置来使用 DB2 驱动程序并调整数据库连接代码,从而使用新的连接字符串。本文还介绍并实现了一些对应用程序架构的改进,以更好地达到模型-视图-控制器 (MVC) 结构模型,从而提升应用程序质量、结构和可维护性。
在样例代码转换之前,需要在 Windows 工作站上安装以下的组件,用于进行转换。
- 源数据库的 MySQL 副本
- 为了验证代码的每一项修改,复制原有系统很重要,可将原系统的功能用作参考,与原系统逐一对照,验证新系统的变更。您可以使用与 将 PHP 应用程序从 MySQL 移到 DB2,第 2 部分:迁移您的数据 一样的系统。
- 在本地或测试服务器上安装了数据服务器驱动程序的 DB2。
- 安装 DB2 会在工作站上创建新的目标数据库。一般来说,不需要与生产环境中使用的版本一样,但为了确保所有功能都兼容,我们建议使用相同版本。在本文的示例中,安装了 DB2 Enterprise Server Edition Version 9.7.2。请确保数据服务器驱动程序能够提供必要的 PHP 客户端库。您可以使用与 将 PHP 应用程序从 MySQL 移到 DB2,第 2 部分:迁移您的数据 相同的系统。
- 在 ibm_db2 扩展版或 PDO_IBM (PHP Data Objects) 中内置的 PHP 版本
- 下载最新版的 Zend Server,并选择 DB2 扩展版选项,它需要安装额外的软件包。
- IDE,如 Zend Studio 或 Eclipse PHP Development Tools(PDT)
- 使用可读取 PHP 的 IDE,例如 Zend Studio 或 Eclipse PHP Development Tools,这样可以简化 PHP 开发。由于您需要维护 PHP 应用程序,因此最好您已经有了用得很顺手的工具。
一定要记录下下配置决策和经验教训,以便在部署时重复使用这些步骤。还可以考虑在重要目标达成的关键里程碑处将 Windows 操作系统的快照保存为虚拟镜像,使用该镜像作为配置备份以及后续代码改进对比的基线。
如果想要获取物理机器配置的镜像,可以使用免费的 VMware vCenter Converter。或者,可以考虑使用云来处理这些手动变更。您可以使用 Amazon EC2 Linux and DB2 AMIs,或者注册 IBM SmartCloud(以前称为 Development and Test on the IBM Cloud)。通过使用虚拟计算机,您一开始就不必花费精力购买服务器硬件并安装操作系统和 DB2,这样可以节省时间、加快迁移过程,并给予您更大的信心,来测试这些最能满足您需求的配置。参考资料 中提供了这些产品的链接。
代码转换过程中重要的第一步就是配置新的 PHP 和 DB2 基础架构,并翻译应用程序使用 PHP 和 SQL 访问数据的方式。这一步包含以下子步骤:
确保现有的 PHP 配置已作修改,能支持 DB2 驱动程序,匹配服务器数据库编码,并且能通过详细的警告和错误通知来提供诊断信息。
为了更新 PHP 代码能支持 DB2,通常需要更改代码中每个使用 ibm_db2 扩展函数的地方,或者更新实现 PDO (PHP Data Objects) 接口的 PDO_IBM 连接字符串。如果在基于 MySQL 的系统中使用 PDO 作为数据库抽象库,那么代码变动是很小的。只要修改连接字符串就行。但即使所用得是程序式的 ibm_db2 扩展,也很简单。只要在代码中将以 mysql_ 或 mysqli_ in 开头的函数替换成 db2_ prefixed 前缀就可以了。请参见 IBM RedbookDeveloping PHP Applications for IBM Data Servers(请参阅 参考资料)中第 6 章第 6.3 节中的函数映射表和代码样例。
这两个方法都依靠 DB2 客户端作为连接 DB2 服务器的桥梁,因此需要保证已配置 DB2 客户端,能匹配远程服务器上的设置。尤其要注意的是,确保 DB2 客户端的代码页与 DB2 服务器一致;否则,字符集会出错。例如,运行 清单 1 中所示的命令,将客户端编码设置为 UTF-8,这就样就匹配了 将 PHP 应用程序从 MySQL 移到 DB2,第 2 部分:迁移您的数据 清单中的 CREATE DATABASE 命令。
清单 1. 将 DB2 客户端代码页设置为 UTF-8
db2set db2codepage=1208 |
为了确保在安装运行时客户端的每个工作站或服务器上的设置都正确,请运行 清单 2 中的命令。
清单 2. 查看 DB2 配置变量
db2set -all |
最后,在开发过程中,如果能捕获所有错误消息和警告,并将它们显示在浏览器上,这会对您有所帮助。在 php.ini 文件中,更新 清单 3 中所示的参数。请记住,在移动到生产环境时,请将它们改回简单的设置。本系列文章的第 4 部分会提供一个样例,在部署到生产环境时会使用更合适的错误报告机制。
清单 3. 在 php.ini 中设置错误控制参数
display_errors = On error_reporting = E_ALL & ~E_NOTICE |
下一步,更新单个 SQL 语句以支持 DB2 语法,或者修改它们,用稍微不同的方式访问数据并获得相同的结果。对于大多数应用程序,这一步骤中主要是大量的代码更新和验证测试。请参考 RedbookMySQL to DB2 Conversion Guide 中第 8.1 节 Data Manipulation Language differences and similarities,查看常见语法转换列表。以下提供了更多的案例研究应用程序中的情形。
- SELECT 子句通配符行为
- SELECT 语句使用通配符(星号字符)从多个表中选择除了所有列之外特定的列,这在 MySQL 中是合法的,但在 DB2 中是不允许的。在这种情况下,请将表名称(或别名)限定符前缀添加到通配符,指定要插入的每个表的每一列,或者简单地用一个通配符列出每个表的所有列。清单 4 显示了如何修改这些查询。
清单 4. 对比 MySQL 和 DB2 中合法的列和表通配符
-- In MySQL: SELECT R.NAME, * FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID; -- In DB2: SELECT R.NAME, U.* FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID; -- Or SELECT R.NAME, U.ID, U.NAME, U.ROLE_ID, U.DEPART_ID FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID; |
- SELECT 子句使用 LIMIT 设定最大结果集的大小
LIMIT是 MySQL 中独有的、非标准的关键字,可以使用它来指定从查询中返回的最大行数值。DB2 使用FETCH FIRST n ROWS ONLY语法提供同样的功能。清单 5 显示了如何重写 DB2 查询。
清单 5. 在 MySQL 和 DB2 中设置从查询返回的最大行数值
-- In MySQL: SELECT * FROM ROLE LIMIT 10; -- In DB2: SELECT * FROM ROLE FETCH FIRST 10 ROWS ONLY; |
- GROUP BY 子句
- MySQL 允许使用 GROUP BY,且无需指定聚合函数没有对其执行任何操作的每个列的名字。但在 DB2 中,这是非法的,因为结果集会包含歧义结果,这让许多用户都无法接受。清单 6 显示了如何在 DB2 查询中明确设置列。
清单 6. 对比 MySQL 和 DB2 中合法的 GROUP BY 聚合函数
-- In MySQL: SELECT R.ID, R.NAME, COUNT(U.ID) AS NUM FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID GROUP BY R.ID; -- In DB2: SELECT R.ID, MIN(R.NAME), COUNT(U.ID) AS NUM FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID GROUP BY R.ID; -- Or SELECT R.ID, R.NAME, COUNT(U.ID) AS NUM FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID GROUP BY R.ID, R.NAME; |
在 清单 6 中,您可以更新 GROUP BY,因为 R.ID 和 R.NAME 在表中都是唯一的键。清单 7 显示了另一种 GROUP BY 查询,它不如 清单 6 中的查询容易处理。
清单 7. 需要针对 DB2 修改 MySQL 中有歧义的 GROUP BY
-- In MySQL: SELECT R.ID, R.NAME, COUNT(U.ID) AS NUM FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID GROUP BY R.NAME; |
本例中,R.ID 是表中唯一的键,而 R.NAME 不是。如果 ROLE 表中有重复的 R.NAME 值,则不能使用 MIN(R.ID) 替代 R.ID,并且无法将 R.ID 添加到 GROUP BY 子句中。如何转换这个 SQL 取决于您想要什么样的结果。清单 8 显示了适用于该情形的一些选项。
清单 8. DB2 中已翻译的 GROUP BY 查询
-- In DB2: -- Option 1, if you want to get same result as what you get in MySQL, the SQL is: SELECT (SELECT RL.ID FROM ROLE RL WHERE RL.NAME = R.NAME FETCH FIRST 1 ROW ONLY) AS ID, R.NAME, COUNT(U.ID) AS NUM FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID GROUP BY R.NAME; -- Option 2, if role name is same, treat it as same role. In this case, -- the result is a little different from MySQL version in that there is no R.ID: SELECT R.NAME, COUNT(U.ID) AS NUM FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID GROUP BY R.NAME; -- Option 3, if role ID is different, treat them as different role. -- In this case, the result is totally different than the MySQL version: SELECT R.ID, R.NAME, COUNT(U.ID) AS NUM FROM USER U, ROLE R WHERE U.ROLE_ID = R.ID GROUP BY R.ID, R.NAME; |
- MySQL 中提供的是 REPLACE INTO,而 DB2 中提供的是 MERGE
- MySQL 提供了 REPLACE INTO 子句。DB2 提供了 MERGE 子句来实现类似但不完全相同的用途。为了达到相同的效果,请创建一条新记录,如果它具有同样的键或唯一的值,则用它替代现有的记录。清单 9 显示了如何检查表中的唯一值,以及如何更新或插入值。
清单 9. 针对 DB2 转换 MySQL 的 REPLACE INTO 语法
-- In MySQL: REPLACE INTO ROLE (ID, NAME, DESCRIPTION) SELECT ID, NAME, DESCRIPTION FROM ROLE_TMP; -- In DB2: MERGE INTO ROLE R USING (SELECT ID, NAME, DESCRIPTION FROM ROLE_TMP) RT ON (R.ID = RT.ID) WHEN MATCHED THEN UPDATE SET (ID, NAME, DESCRIPTION) = (RT.ID, RT.NAME, RT.DESCRIPTION) WHEN NOT MATCHED THEN INSERT (ID, NAME, DESCRIPTION) VALUES (RT.ID, RT.NAME, RT.DESCRIPTION); |
- JOIN 子句
- 当对两个表执行外部连接并从第三个表检索数据时,要在 DB2 中设置外部连接中连接到 join 关键字的表。MySQL 允许以任何顺序列出它们。清单 10 显示了所需的变更。
清单 10. 对比 MySQL 和 DB2 JOIN 语法
-- Both of the following work in MySQL: SELECT * FROM USER U, ROLE R LEFT JOIN DEPARTMENT D ON U.DEPT_ID = D.ID; -- Or SELECT * FROM ROLE R, USER U LEFT JOIN DEPARTMENT D ON U.DEPT_ID = D.ID; -- Only this one works in DB2: SELECT * FROM ROLE R, USER U LEFT JOIN DEPARTMENT D ON U.DEPT_ID = D.ID; |
- 转义字符
- 在 MySQL 中,字符序列 \ 表示一个反斜杠转移单引号 ',但在 DB2 中,单引号必须要再加一个单引号来实现转义 ''(即两个单引号)。清单 11 显示了一个转义字符串序列,它使用了一个引号作为省略号。
清单 11. MySQL 使用反斜杠作为单个引号转义,而 DB2 再使用了一个引号
-- In MySQL: SELECT * FROM ROLE WHERE DESCRIPTION = 'It\'s a super admin role'; -- In DB2: SELECT * FROM ROLE WHERE DESCRIPTION = 'It''s a super admin role'; |
- 在 DB2 中无法检查范围
- MySQL 在 SELECT 子句中会经检查数据类型的范围,但 DB2 无法检查范围。因此 清单 12 中查询 2月 1 日到 2 月 30 日的 SQL 语句在 DB2 中无效,但在 MySQL 是有效的。
清单 12. MySQL 不会针对数据类型检查 WHERE 子句中的值范围
-- MySQL allows you to specify February 30th as part of the range SELECT * FROM USER WHERE BIRTHDAY BETWEEN '1980-02-01' AND '1980-02-30'; |
- 插入到非默认值的 NOT NULL 列中
- 在 MySQL 中,如果要执行一个不提供 NOT NULL 列值的 INSERT 语句,此命令没有问题。MySQL 会自动提供一个默认值,即使您在创建表格时也没有为此列定义默认值。但在 DB2 中,如果未定义该值,则必须在 INSERT 语句中为 NOT NULL 列提供值。清单 13 中的 SQL 语句显示了不同的行为。
清单 13. MySQL 和 DB2 以不同方式处理 NULL 值的插入
-- Works in both MySQL and DB2 CREATE TABLE TEST1 (ID INTEGER, NAME VARCHAR(20) NOT NULL); -- Works in MySQL, doesn't work in DB2 INSERT INTO TEST1 (ID) VALUES(1); -- Works in both MySQL and DB2 CREATE TABLE TEST2 (ID INTEGER, NAME VARCHAR(20) NOT NULL DEFAULT ''); -- Works in both MySQL and DB2 INSERT INTO TEST2 (ID) VALUES(1); |
- 将 MySQL 函数转换为 DB2 中类似的函数
- 除了 MySQL 特定的 SQL,您还可以使用内置的 MySQL 函数。可以使用 SQL 标准函数或 DB2 中具有相同功能的函数来替换它。请参考附录 A:MySQL to DB2 Conversion Guide Redbook 中的映射 MySQL 内置函数和操作符(请参阅 参考资料)。
根据以上所述,如果找不到 DB2 中具有相同功能的函数,那么可以重写 SQL,以不同的方式访问数据。或者,您可以在 DB2 中模拟函数或者将逻辑移到 PHP 代码中,如以下章节所示。
如果更新 DB2 语法中的 SQL 语句也不能获得与 MySQL 查询相同的结果,那么您可以在 DB2 SQL 中以用户定义函数 (UDF) 的形式实现一个工作区,用它来模拟内置的MySQL 函数。
样例场景中有几处使用 MySQL 和 PHP 的地方,在通过更新应用程序来使用 DB2 时,需要使用不同方法来实现。尤其是,可以将一些功能从 MySQL 移动到 PHP 中,例如日期翻译。或者您可以模拟 DB2 中的函数(例如创建 DB2 中的用户定义函数),获得与 MySQL 中一些函数近似的功能,包括 UNIX_TIMESTAMP() 和 NOW()。MySQL to DB2 Conversion Guide Redbook 中的附录 B(请参阅 参考资料)解释了二者的不同之处,并且给出了解决变更的方法。Daniel Krook 的来自客户迁移经验的技巧也很有用(请参阅 参考资料)。
DB2 的 DML 与 MySQL 不同。本文讲述了如何将嵌入式 SQL 从 MySQL 转换到 DB2。尽管如此,不需要将所有语法从 MySQL 转换到 DB2,尤其是 MySQL 内置函数。MySQL to DB2 Conversion Guide Redbook 中的附录 A “映射 MySQL 内置函数和操作符”(请参阅 参考资料)列出了很多 MySQL 内置函数。通常情况下,可以将很多函数都转换为 DB2 UDF。您需要确定哪些常用内置函数需要转换。对于样例场景,需要使用 DB2 UDF 转换 MySQL 中常用的函数或寄存器。以下是需要转换的 MySQL 函数和寄存器。
- CURRENT_DATE() / CURDATE()
- DATE_FORMAT()
- DATEDIFF()
- FROM_UNIXTIME()
- NOW()
- PERIOD_DIFF()
- TO_DAYS()
- UNIX_TIMESTAMP()
- WEEKDAY()
- YEARWEEK()
幸运的是,这些函数中很多已经实现,而且能免费重用,如文章 “DB2 basics:Fun with dates and times” 中所示(请参阅 参考资料)。如果您使用的是 DB2 v9.7.2 或其更高版本,那么可以选择设置新的兼容向量 DB2_COMPATIBILITY_VECTOR=MYS。
DB2 生成用户定义函数很容易,因为可以用 SQL 编写函数,而不是像 MySQL 那样使用 C 语言。
清单 14 这是一个 UDF 示例,您可以用它来模拟 MySQL 的非标准化但经常被使用的 NOW() 函数。可以在应用程序样例中的多处地方使用模拟。
清单 14. 模拟内置的 MySQL NOW() 函数的 DB2 UDF 定义
CREATE FUNCTION NOW() RETURNS TIMESTAMP NO EXTERNAL ACTION BEGIN ATOMIC RETURN SELECT CURRENT TIMESTAMP FROM SYSIBM.DUAL; END |
还有其他一些用户定义函数模拟常用的内置 MySQL 函数,在 MySQL to DB2 Conversion Guide Redbook 附录 B 中已经列出这些函数(请参阅 参考资料)。
可能会出现无法将 MySQL 函数或语法转换成对应的 DB2 格式的情况。在这种情况下,请查看 PHP 是否支持此功能并且使用函数或扩展替代它。例如,MySQL 提供了一个调用 INET_ATON() 的 SQL 函数,它在 DB2 中就没有对应的函数。为 INET_ATON() 编写一个用户定义函数很容易出错,并且过程很繁琐。幸运的是,PHP 提供了 ip2long() 函数,它提供了同样的功能,而且不依赖于数据库向量。
PHP 代码已经更新,能够修改嵌入 SQL 语句,并按与 MySQL 类似的方式读取或更新数据。下一步是要重新调整查询,以实现非功能化目标,获得更好的数据集成和性能提升。这一步包含以下子步骤:
一旦将嵌入在基于 MySQL 的应用程序中的单个查询转换成 DB2 形式, 就需要考虑如何通过相应的改进来利用 DB2 并发性和数据集成特性。看看需要进行哪些修改,然后才能支持数据库并发连接之间适当的隔离级别。
DB2 中有四个隔离级别来控制并发性。隔离级别决定了事务如何对其他用户隐藏正在进行的数据变更。隔离级别有:
- 可重复读取隔离级别 (RR)
- 锁住所有行,直到事务结束。
- 读取稳定隔离级别 (RS)
- 锁住符合谓词条件的行,直到事务结束。
- 游标稳定隔离级别 (CS)
- 只锁住游标指向的行。这是默认操作。
- 未提交读取隔离级别 (UR)
- 不锁住任何行,除非数据发生变化。
从性能和数据集成角度考虑哪个隔离级别适合您的应用程序。当隔离级别从未提交读取 (UR) 移动到可重复读取 (RR) 过程中,并发性下降,数据集成性提高。RR 隔离级别保证了最大程度的数据集成,同时以高性能为代价。相反,UR 保证了最高的性能,但以一部分数据并发性为代价。图 1 演示了性能和数据集成之间的关系。
图 1. DB2 中的隔离级别
您可以对不同层次设置隔离级别,从单个查询直到连接和会话。如果有一个包含大量读取操作的工作负载,那么您可能想将隔离级别设置为未提交读取 (UR),从而不需要在每个语句中设置隔离级别。如果同时包含读取和写入操作,您可能会关注修改单个查询,而不是修改默认级别,即游标稳定性 (CS)。
您可能还会考虑您用来优化数据库访问的游标类型。ibm_db2 和 PDO_IBM 驱动程序都支持两种类型的游标:只能前进 (forward-only) 的游标和可滚动游标。默认情况下使用只能前进的游标。一般来说,默认设置适用于大多数情况,在这些情况下,您可以按照顺序遍历结果集。而在性能方面,只能前进的游标优于可滚动游标。但某些情况下,您需要游标既能向前又能向后,此时需要使用可滚动游标。在样例场景中,将游标设置为对每个必要的查询使用 清单 15 中的命令。
清单 15. 在 ibm_db2 函数和 PDO 驱动程序中定义 DB2 滚动类型
-- For ibm_db2
db2_exec($connection_resource, $sql, array('cursor' => DB2_SCROLLABLE));
-- For PDO_IBM
$DB_PDO->prepare($sql, array(PDO::ATTR_CURSOR, PDO::CURSOR_SCROLL));
|
请参阅 Developing PHP Applications for IBM Data Servers Redbook 的第 4.2 小节,其中使用了 PHP 和 DB2 数据库(请参阅 参考资料),您可以从该小节中查阅更多的信息。
下一步要将查询组成逻辑单元,而不是使用单独的查询。在代码迁移的这一步,如果您知道 SQL 语句在接收数据,并且语句像在原有应用程序中一样修改数据,则需要考虑对数据访问查询和更新进行重新分组。这样做是为了匹配 MySQL 的性能级别,同时确保 DB2 的数据集成级别。
- 减少语句与数据库的连接
- 一般来说,与 DB2 相比,在 MySQL 中构建到数据库的连接更快一些。因此,如果减少每个 HTTP 请求的总连接数,将一个连接用于多个查询,则可以提高处理器、磁盘和网络性能。对于样例场景,查询已经合并,包括获取用户账户信息来执行更深入的前端连接(而不是使用若干后续查询),如 清单 16 中所示。但这是以预取过多数据为代价,在样例场景中,性能获得了提升,提取数据的数量也有所减少,而且程序开发人员有更好的方法来确定实际从给定页获取多少数据,这也节约了资源。
清单 16. 整合查询
-- Two individual queries that require two trips to the data server on the page SELECT FIN_PROJECT_MANAGER AS PM, PROJNAME FROM PROJECT WHERE ID = $id; SELECT EMAIL, FIRSTNAME, LASTNAME FROM USER WHERE ID = $pm; -- Single consolidated query that retrieves the same information in one trip SELECT U.EMAIL, U.FIRSTNAME, U.LASTNAME FROM USER U, PROJECT P WHERE U.ID = P.FIN_PROJECT_MANAGER AND PROJECT.ID = $id |
- 向维护性更好的 MVC 架构迈进一大步
- 在使用模式-视图-控制器 (MVC) 架构模式构建的设计良好的三层 Web 应用程序中,控制器处理来自用户的 Web 请求,然后调用一个命令为获取结果做准备。结果对象会转换成视图(通常是通过一个简单的 HTML 模板表示),在页面上显示响应数据。原有的应用程序不使用 MVC 模式。该应用程序将查询直接嵌入模板中,有效地将三种责任组织到一个页面中。
通过采用将这些查询分组的 MVC 最佳实践,例如在一个模式调用中获取用户的所有用户账户信息和所有项目,视图页只需显示信息即可,无需逐步获取更多的信息。该方法将应用程序从数据集移动到业务对象集中,通过将数据访问逻辑从布局和页面设计中分离,帮助您更好地组织代码。
- 更好地分配计算工作负载
- 由于样例场景中已经有一个单独的 Web 服务器和一个单独的数据库服务器,Web 服务器主要处理大量的 HTTP 流量。Web 服务器通过 PHP 模块执行服务器端逻辑。为了通过降低 Web 浏览器花费在 mod_php 中的 CPU 和内存损耗来提升 HTTP 服务器上的性能,需要将一些业务逻辑从数据服务器上分离出来。这会额外地降低网络流量(因为它减少了 Web 和数据服务器的流量),并且通过处理所访问的统一级别的数据来增加吞吐量。
- 通过对服务器上的查询进行分组来提升数据集成
- 对于事务集成,最终也是最重要的收益是将逻辑分组为服务器上的原子事务单元,这些事务单元使用了一些存储过程、触发器和用户定义函数。您会发现,在以下情况时,在应用程序(如 PTT)中创建任务更可靠一些:
- 将所有数据作为整体转移到数据库中
- 多个 INSERT 语句作为一个单元一起执行
- 将一个成功或失败错误返回给用户
清单 17. 将更新分组到一个存储过程中
CREATE PROCEDURE BILLING_TYPE_UPDATE (IN p_date DATE)
BEGIN
-- Update for project's billing type
FOR row AS
SELECT *
FROM proj_billingtype_snapshot
WHERE end_date IS NULL
AND start_date = p_date
DO
-- Execute two updates in one transaction
t1: begin atomic -- Transaction begins
-- First update
UPDATE fin_attributes
SET proj_type = row.billing_type
WHERE project_id = row.proj_id;
-- Second update
UPDATE fin_attributes_archive
SET proj_type = row.billing_type
WHERE project_id = row.proj_id
AND year = YEAR(NOW());
end t1; -- Transaction ends
END FOR;
END
|
DB2 所有版本都支持事务,但大多数 MySQL 存储引擎,包括默认的 MyISAM 类型,都不支持事务。应用程序样例使用了默认的 MyISAM 表类型,而开发人员试图在代码层采用数据集成。这对 DB2 是一个很好的过渡,因为 DB2 将事务管理委托给了数据库,而这是 DB2 所擅长的。这样做还可以减少代码的长度和复杂性,以及应用程序中某个 PHP 页所需的各种移动部分。请参阅 RedbookPHP Applications for IBM Data Servers 中的第 5.2.8 小节 “事务和隔离级别”(请参阅 参考资料),以了解关于 DB2 中并发性的更多信息。
到目前为止,我们已经有了一个可作为整体进行测试的功能系统,至少有一组能组成特定组件的相关函数。例如,创建一个与父项目有关联的新任务在 PPT 系统中是一个完全可以独立实现的用例。发送部分内容(如产品标题和描述到翻译服务则)是另一个用例,可以单独测试。这一步包含以下子步骤:
在这一步中,会开始让感兴趣的利益相关者执行一系列曾在原有系统上使用的用例。要保证同样的测试工作在新系统上与预期一样。
这几年来,样例场景使用了相对简单的格式进行用户接受测试。因此,形成了测试档案,以测试新的功能并且避免现有代码的回归问题。测试组合已经扩展,能够让关键功能核心符合 PPT 的功能。这些测试通常封装了一组相关的步骤,以达到业务目标,而且他们重新绘制了用例图。与静态用例模型的不同之处在于测试一般存储在电子表格格式以及截图和表格中,其中的行已经更新,无论一组步骤或单个步骤成功或失败,测试者都能输入。表 1 显示一个样例用户测试案例格式,验证任务创建是否与预期一样。
表 1. 用户接受测试示例
| 范围 | 测试 | 预期结果 | 通过? |
|---|---|---|---|
| 1.创建一个新任务 | 1.1 登录系统。 | 显示欢迎页面。 | 是 |
| 1.2.打开任务页面。 | 加载新任务。 | 是 | |
| 1.3 填写并保存表单。 | 显示成功消息和唯一的任务 ID 链接。 | 是 | |
| 2.批准任务 | 2.1 登录系统。 | 显示欢迎页面。 | 是 |
| 2.2.定位到任务列表。 | 加载任务列表。 | 是 | |
| 2.3 点击任务旁的批准按钮。 | 显示成功信息通知,发送 email。 | 否 |
请记住,让测试者投入适当时间来执行用户接受测试并且注意任何问题,这非常重要。如果跳过这些测试,就可能忽略错误。同样的原因,要记录谁在什么时候测试,还要记录所报告的结果,以加强责任。
这一步包含对用户接受测试者报告的问题的系统性处理。从测试者处收到用户接受测试后,所报告的错误会分配给开发人员供他们验证和修复。例如,表 1 中的测试 2.3 会作为一项新故障被分配。
当以迭代方式迁移系统时,开发人员应该将 DB2 中已迁移的函数与 MySQL 源系统进行对比,确保特定的工作单元运行结果与预期的一样。与用户接受测试一样,可以使用单元测试框架(如 PHPUnit)自动处理此过程,这种处理方式很有价值。由于没有很好地将示例代码库划分成 MVC 代码,这导致它自身很容易地进行编程式测试,因此没有使用自动化单元测试或持续集成系统。
大量用例完成之后,倒数第二步是开始测试迭代,取得系统的当前状态作为备份和基线,以用作函数和性能验证参考。迭代变化再次满足稳定的里程碑,这对获取备份很重要。可以是传统文件格式或是 SQL 备份,或者可以是完整的操作系统镜像。用这些保存点防止过程中的错误,或者作为比较基线,也很重要。
一旦完成利益相关者指定的用户接受测试之后,就该提升系统性能了。这一步包含以下子步骤:
对于这个准备步骤,这里要重申的是,您应该对整个系统已确认的功能有充分了解,从而了解非功能性需求的任何变化,例如性能提升,不会对已经达成的功能需求造成负面影响,或造成任何代价。这一步骤在验证功能之后执行,这样做有两个理由。首先,确保发生任何非功能性的变化之前,已经存在已知的、良好的、签署过的功能基线。其次,它可以帮助您避免迁移过程中的过早优化陷阱。当您知道某个查询或方法对某一个案例会更快时,不应盲目地将该查询或方法套用到整个代码库中,尤其在有迹象表明存在性能问题时。
例如,如果后期出现验证回归问题,则需要保存用户验收测试历史记录,以确认是否对某些方法进行了检测并通过了测试,从而可以使用它们验证回归问题。
此时的测试应重点关注 DB2 能自动修复什么,因为 DB2 就是引入的最大的变化。当您改变代码、单元测试并执行 UAT 时,应该识别瓶颈并根据需要进行改进。要遵循的一条重要理念就是只解决单元测试和 UAT 测试中已知的问题,而不是对潜在的性能问题来源进行假设。完成此步骤之后,可以记录下已经做了哪些更改,以防止应用程序以后遇到类似问题。
根据一些最佳实践,在过早优化与进行所有修复之间有一些微妙的平衡。这是应用程序声明周期中一个迭代过程。关于过早优化的危险,请参阅侧边栏。
有一项技术很有用,即实现一个函数,就像在 PPT 应用程序中所做的那样,监控运行超过 60 秒的脚本并发送错误报告。本 系列文章 第 4 部分将介绍此类函数的一个样例。
根据报告中的可用信息,您就可以发现问题是由 PHP 执行慢还是查询慢引起的。对于 PHP 问题,Zend Studio 或 Eclipse PDT 中的调试器可帮助您定位问题。对于查询问题,您可以使用 IBM Data Studio 定位问题并给出修复意见。图 2 显示了某个查询的访问计划图样例。
图 2. 在 IBM Data Studio 中调优查询
如果已经消除来自 DB2 和 PHP 的应用程序性能问题,那么接下来您可以利用 John Coggeshall 在 “Zend Enterprise PHP Patterns” 中介绍的经过检验而可靠最佳实践(请参阅 参考资料),以迭代方式定位其他瓶颈。简言之,您可以查看通常会成为应用程序瓶颈的三大方面,看看能否能够通过简单的硬件更新或调优分配给虚拟机的资源来解决一些性能问题:
- CPU
- 内存
- 磁盘
Zend 书中介绍了很多工具,可以用它们来确定 Linux 操作系统的瓶颈,还有很多与 Windows 工具相关的技术,可在开发时、将更新后的应用程序部署到测试或分级服务器之后、部署应用程序之后使用它们。
在经过 1-4 步完整的迭代之后(每个步骤中还包含一些子步骤),您应该已经在 Windows 工作站上实现了一个功能化数据库系统,并记录做了哪些更改,存在哪些问题。如果您使用的是虚拟机,那么应该获得了 Windows 系统的镜像作为快照,既可将其用作功能存储点,也可将其用作比较将来性能变化的基线。当然还有一个选择,就是在 IBM 或 Amazon Cloud 中使用虚拟镜像,并以相同方式使用它。您也许会遇到几种不同的代码更新,看看哪种最适合您的环境。
如果您对步骤 1-4 中使用的 Windows 工作站上的系统感到满意,您可以在版本控制系统中将已转换的代码作为发行版,并在部署前为最终步骤准备好基础架构,如本 系列文章 中的第 4 部分所示。
本系列文章旨在让您了解将 PHP 应用程序从 MySQL 迁移到 DB2 通常需要什么,什么样的资源对您有帮助,并了解 IBM 项目团队在 2010 年初是如何完成任务的。
本文是本系列的第 3 部分,您将:
- 了解已转换的 PHP 代码。
- 了解如何更新 DB2 应用程序。
- 了解转换后如何测试和调优代码。
在第 4 部分,您将了解如何部署已迁移的应用程序并处理持续的支持。
作者非常感谢 Leons Petrazickis 和 Ambrish Bhargava 对本文的审阅和评论。
学习
- 使用 RSS 摘要 及时了解本系列后续文章信息。(查找更多关于 developerWorks 内容的 RSS 摘要 的信息)。
- 使用免费的来自 IBM Redbooks 的 MySQL to DB2 Conversion Guide 来计划和执行您的迁移。
- 查阅免费的 Developing PHP Applications for IBM Data Servers IBM Redbook,通过一个简单的应用程序来了解如何配置、运行和测试一个使用 DB2 的 PHP 应用程序。
- 查阅 John Coggeshall 和 Morgan Tocker 合著的 Apress 书籍 Zend Enterprise PHP Patterns ,了解 PHP 应用程序和架构的最佳实践和技术。
- 下载和使用免费的 DB2 9.7:IBM Data Movement Tool,它可以帮助您将数据库对象和数据从 MySQL 迁移到 DB2。
- 访问 Information Center 主题,了解更多关于将现有数据库反向构建到数据模型的方法。
- 阅读 InfoSphere 文档,了解更多关于如何使用 IBM 的基于 Eclipse 的工具将一个物理模型转换到 DDL 的方法。
- 查看 Daniel Krook 的博客 Writing SQL for both MySQL and DB2 ,了解将 ISV 的应用程序迁移到 DB2 的过程。
- 请参阅 DB2 基础: 日期和时间的使用,了解更多关于可以转换的 MySQL 函数和寄存器的知识。
- 阅读 推荐读物:DB2 for Linux, UNIX, and Windows 应用程序开发,了解有关开发问题的更多信息。
- 阅读 推荐读物:DB2 for Linux, UNIX, and Windows 数据库管理,了解更多与管理问题相关的信息。
- 阅读由 IBM Press 出版,Raul F. Chong、Xiaomei Wang、Michael Dang 和 Dwaine R. Snow 合著的书籍 Understanding DB2:Learning Visually with Examples, 2nd Edition,深入了解 DB2 及其特性。
- 阅读由 IBM Press 出版,George Baklarz 和 Paul C. Zikopoulos 合著的书籍 DB2 9 for Linux, UNIX, and Windows:DBA Guide, Reference, and Exam Prep, 6th Edition,了解更多关于 DB2 应用程序开发管理的知识。
- 通过 利用 MySQL 技能学习 DB2 Express:DB2 与 MySQL 的备份和恢复 了解保持数据安全的策略。
- 阅读 利用 MySQL 技能学习 DB2 Express:DB2 与 MySQL 的管理任务和基本任务,深入了解工具、SQL 语法、并发选项和许可。
- 阅读 利用 MySQL 技能学习 DB2 Express:DB2 与MySQL 数据转移,了解 MySQL 和 DB2 中可用的导入和导出工具。
- 阅读文章 利用 MySQL 技能学习 DB2 Express:DB2 与 MySQL 图形用户界面 ,比较管理数据库系统的可视化工具的特性。
- 阅读 PHP 官方文档中的 IBM DB2 函数 和 PDO 驱动程序 ,了解 DB2 的两个推荐的 PHP 接口。
- DB2 for Linux, UNIX, and Windows 最佳实践 页面包含一系列文章、介绍常见问题的最优解决方法,以及如何最恰当地利用工具,如 使用 Rational Data Architect V7 实现信息建模 ,本文的案例研究也使用了这个工具。
- 快速查看 了解 IBM Smart Business Development and Test on the IBM Cloud(以前称为 Development and Test on the IBM Cloud)。
- 在 DB2 for Linux, UNIX, and Windows 最佳实践:DB2 成本削减策略 上了解关于 DB2 性能优化的介绍。
- 在 DB2 最佳实践: 编写并调优查询语句以优化性能 上了解关于 DB2 查询优化的介绍。
- 在 从 Oracle 迁移到 DB2 找到更多从其他数据库、应用程序服务器和操作系统迁移到 IBM 软件的信息。
- 在 developerWorks 中国网站 Information Management 专区 了解关于信息管理的更多信息。查找技术文档、how-to 文章、培训、下载、产品信息以及更多内容。
- developerWorks 技术活动 和 网络广播:随时关注这些活动中的技术。
- 在 Twitter 上关注 developerWorks。
获得产品和技术
- 下载 针对数据库专业人员的 IBM DB2 工具包,了解 DB2 特性,利用您现有的数据库管理和应用程序开发技术,加入 DB2 社区,并准备通过认证。
- 联系 Software Migration Project Office DB2 迁移团队,进行免费的迁移评估。
- 通过 IBM Smart Business Development and Test on the IBM Cloud 在 IBM Cloud 上创建和验证您的 PHP/DB2 应用程序。
- 试用 DB2 on Amazon EC2。
- 获取 Zend Server ,这是一个完整的企业级 Web 应用服务器,可用于运行和管理运行于 Linux、Windows 或 IBM i 上要求具有较高可靠性、性能和安全性的 PHP 应用程序。
- Zend Server 包含了 DB2 驱动程序,但是如果您有自己的 PHP 配置,您可以在 PECL 或 Windows binaries on SourceForge 上下载驱动扩展的源代码。
- 了解更多关于 DB2 培训与认证 信息,或者参加 Information Management 培训营。
- 下载和安装免费的 DB2 Express-C 数据服务器。
- 使用 Rational Software Architect 或 InfoSphere Data Architect 执行逻辑和物理数据建模。
- 使用 Optim Development Studio 开发和优化基于 DB2 的应用程序。
- 使用免费的 VMware vCenter Converter 将物理主机转换成虚拟镜像。
- 使用可以直接从 developerWorks 下载的 IBM 产品评估试用版软件 构建您的下一个开发项目。
讨论
- 参与论坛讨论。
- 在 PHP forum:Developing PHP applications with IBM Information Management products (DB2 and Informix) 论坛上搜索、监控和提交您的问题。
- 在 DB2 Application Enablement 论坛上提交关于 IBM Data Management Tool 工具的问题。
- 参与 developerWorks 博客 并加入 developerWorks 中文社区,developerWorks 社区是一个面向全球 IT 专业人员,可以提供博客、书签、wiki、群组、联系、共享和协作等社区功能的专业社交网络社区。