WINDOWS下的程序员出身,偶尔也写一些linux平台下小程序, 后转行数据库行业,专注于ORACLE和DB2的运维和优化。 同时也是ios移动开发者。欢迎志同道合的朋友一起研究技术。 数据库技术交流群:58308065,23618606
全部博文(599)
分类: Oracle
2009-12-08 13:17:07
回顾在 Oracle 真正应用集群上部署关键业务 PHP 应用程序的实际体验。
作者:John Lim
2009 年 6 月发表
软件应用程序能够经受住硬件和软件故障的考验可靠地运行的确是件很困难的事。我们的客户(一家马来西亚银行)曾委托我们为其开发一个基于 Web 的财务应用程序,该应用程序将用于内联网设置并且对业务至关重要。这意味着,这家银行需要一个可靠的系统,并且该系统可以在任何组件出现故障时自动切换到另一台服务器。
在本文中,我将介绍我们的 RAC 应用程序的网络、体系结构和设计。然后,我将就实际体验和遇到的问题进行讨论。即使您使用 Java、.NET 或您的应用程序而非 PHP,该体验大体上也是相关的。
我们推荐使用 PHP 应用程序,在 Red Hat Linux Enterprise 4 上使用 Oracle 真正应用集群 (RAC) 将数据存储在 Oracle 数据库 10g 标准版中。我们的最终系统如下所示:
在我们的网络中,有两个负载平衡器,以主动-被动模式将 HTTP 查询分发到我们的两台应用服务器上。应用服务器运行的是 Squid 3、Apache 2.2 和 PHP 5,使用 Oracle 的客户端负载平衡和故障切换连接到 Oracle 数据库集群。Squid 3 充当端口 80 上的前端 Web 服务器。Squid 将向 Web 浏览器缓存和分发静态文件,如 Javascript、CSS 和 GIF 图像。当 Squid 检测到 .php 文件请求时,Squid 将该请求传递到 Apache,在另一个端口上监听;这样可确保 Apache 专注于 PHP 脚本的执行而不受干扰。
Oracle RAC 是一组独立的数据库服务器,它们共享相同的数据并像一个系统一样协作。我们的 RAC 是一个两节点集群,使用多路径连接到 Hitachi 存储区域网 (SAN)。我们在 SAN 上使用可识别集群的文件系统以确保我们的两个 RAC 节点能够同时对相同的文件进行写入操作,而不会损坏数据。两个 RAC 节点之间还有一个专用互联,只是一个 1GB 专用 LAN,用于进行数据同步。
为帮助您了解我们的实际体验,我需要解释一下 Oracle RAC 的体系结构。如果您希望了解更多信息,请参阅 。
Oracle RAC 使用共享所有内容的数据体系结构。这意味着所有数据存储需要对所有 RAC 节点全局可用。
要对 Linux 上的所有内容实施共享,您可以选择使用第三方集群文件系统或 Oracle 技术,如 Oracle 自动存储管理 (ASM)(一个使用原始设备存储数据的容量管理器)或 Oracle 的开源 文件系统。
在我们的示例中,使用 Oracle 数据库标准版要求使用 Oracle ASM 原始设备进行数据存储。但是,对于所有其他共享 Oracle 文件,我们选择使用 OCFS2,因为之后可以使用普通的操作系统命令(如 ls 和 cp)访问这些文件。是一种原始存储格式,针对数据库使用进行了高度优化。如果您熟悉 Oracle 数据库中的表空间和数据文件,您会发现 ASM 的组织方式与之很相似。在 Oracle 数据库中,我们将表存储在由多个物理数据文件组成的虚拟表空间中。同样,在 Oracle ASM 中,我们将这些数据文件存储在由多个物理磁盘组成的虚拟磁盘组中。
Oracle ASM 允许您创建原始磁盘的镜像组或使用已经符合 RAID 的外部原始设备。在我们的示例中,SAN 已经是 RAID 1,因此我们将 ASM 配置为使用外部冗余。
Oracle ASM 通过数据库实例进行管理。您的生产数据并不驻留在该数据库实例中。这意味着您实际上在每个 RAC 节点上运行两个数据库实例,它们各自具有单独的 SID(Oracle 数据库标识符):
要在服务器 1 上通过命令行连接到 Oracle ASM 实例,需要设置 ORACLE_SID 环境变量以在运行 sqlplus 之前连接到正确的实例:
su - oracle ORACLE_SID=+ASM1; export ORACLE_SID sqlplus / as sysdba然后,可以使用标准 ASM 视图查看和修改磁盘的配置:
v$asm_diskgroup v$asm_disk v$asm_file稍后我们讨论体验时会更多地提及 Oracle ASM。
管理数据库实例、监听器和其他服务的启动、关闭和监视。当服务出现故障时,集群件将自动为您重新启动服务。
集群通过一个专用互联进行通信,在我们的示例中,该互联是一个 1Gb 专用 LAN;这意味着您在每个数据库节点上至少需要两个网络接口卡。一个用于银行网络,另一个用于专用互联。
如果专用网络和银行网络都出现故障,集群仍然可以通过集群文件系统进行通信。在 OCFS2 文件系统中创建特殊的表决文件以允许在集群节点间交换信息作为健康检查,并在网络出现故障时仲裁集群在实例间的所有权。
集群中的每个数据库实例都有自己的配置,如 SID、spfile 和启动参数。这意味着许多熟悉的 Oracle 管理技巧仍然适用。
但是,数据实际上由所有实例共享,这意味着 Oracle 需要一些方法来同步在多个实例的内存中缓冲的数据。Oracle 使用一种名为缓存合并的技术,使用一种高速互联来管理和同步缓冲区缓存。该同步的速度可以很好地衡量 RAC 集群的效率。当我们谈到 Oracle 企业管理器时,将对此做更多的讨论。
和 的文章都对 Oracle RAC 的安装进行了大量介绍,建议读者参阅他们的文章了解有关安装过程的详细信息。而我将讨论我们遇到的一些小问题。
安装期间最大的问题是需要可靠地连接到 SAN。当您具有多个使用多路径的到 SAN 的连接时,同一个设备将会出现多次,例如 /dev/sda 和 /dev/sdc,因为有两个冗余的路径指向同一个设备。另一个问题是设备名的指定默认为基于设备的检测顺序。因此启动配置中的些许更改可能导致更改后 /dev/sda 映射到一个不同的硬盘。
这就是我们使用 Linux 多路径后台程序标准化设备映射的原因。例如,这允许我们将具有路径 /dev/sda 和 /dev/sdc 的硬盘 1 重新映射到 /dev/mapper/1HITACHI3450034。然后,我们使用更可靠的 1HITACHI* 路径进行所有设备连接。
接下来我们遇到的问题是,当我们将 1HITACHI3450034 设备格式化为 ASM 原始磁盘并认为一切良好时,结果在下一次重新启动后:ASM 失去映射,数据库被卡住,没有任何数据文件。
经过大量的失败和摸索后,才发现原来是 ASM 在 1HITACHI* 设备前检测到 sd* 设备。我们需要配置 ASM 以更改设备检测顺序。为此,可以在 /etc/sysconfig/oracleasm 中添加以下行:
ORACLEASM_SCANORDER="1HITACHI sd"这将通知 ASM 在 sd* 设备之前加载所有 1HITACHI* 设备。有关更多信息,请参见该 。
我们遇到的另一个小问题是,我们运行的是 64 位 Linux,因此我们只安装了所需的 64 位 RPM。结果出现了无法理解的错误。原来还需要安装一些 32 位 RPM。根据 metalink 文档 339510.1,我们安装了以下 RPM 的 32 位和 64 位版本:
libaio-0.3.103-3
glibc-devel-2.3.4-2.9
最后一点建议:大多数困难是由于与非 Oracle 软件和硬件接口的问题引起的。一旦解决了这些问题,只需按照文档进行设置即可。有关疑难解答,我们强烈建议您使用 My Oracle Support(以前的 MetaLink;需要有支持合同),因为我们发现它比 Google 更有用。
在应用服务器上,确保运行的是最新版的 Oracle 客户端库以确保支持 Oracle 透明应用程序故障切换 (TAF)。在我们的示例中,我们下载了 Oracle 11g 即时客户端安装程序(是的,Oracle 11g 即时客户端可与我们的 10g 数据库良好配合)和 PHP OCI8 1.3 扩展源代码。然后,我们对该扩展进行了手动编译。有关说明,请参阅。
Oracle 支持客户端连接的负载平衡和故障切换。您只需配置 TAF。
为此,您需要设置一个数据库网络服务。设置该服务最简单的方法是使用 Oracle 的数据库配置助手(以 root 身份运行 $ORACLE_HOME/bin/dbca)。转至 Services Management 页面并创建一个名为 RACSERVICE 的服务,该服务使用两个 Preferred 实例进行负载平衡并遵循基本的 TAF 策略。该助手还将对集群进行正确的配置。创建完服务后,在任一节点上打开 $ORACLE_HOME/network/admin/tnsnames.ora 并复制由数据库配置助手生成的 RACSERVICE 连接字符串。我们将在客户端使用该连接字符串,如下所示:
$RAC_CONN_STR = "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC1-VIP)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC2-VIP)(PORT = 1521)) (LOAD_BALANCE = yes) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = RACSERVICE) (FAILOVER_MODE = (TYPE = SELECT) (METHOD = BASIC) (RETRIES = 180) (DELAY = 5) ) ) )"; $conn = oci_pconnect($USERID, $PASSWORD, $RAC_CONN_STR);RAC1-VIP 和 RAC2-VIP 是 RAC 服务器使用的虚拟 IP 的主机名(必须在数据库和应用服务器的 /etc/hosts 中定义)。这些虚拟 IP 在安装期间配置用于快速连接故障切换。当一台服务器出现故障时,另一台服务器将接管公共接口上的两个虚拟 IP,确保故障切换没有延迟。
TYPE=SELECT 意味着如果当某台服务器出现故障时,Oracle 正在执行 SELECT 语句,在进行故障切换时将重新执行该 SELECT 语句,游标的指向方式会使客户端可以无缝地继续获取行。但是,INSERT 或 UPDATE 的事务仍会回滚。您还可以配置 TYPE=SESSION,这样如果首个连接失败,所有在当时正在进行的工作都将无可挽回地丢失。
METHOD=BASIC 意味着仅在任一服务器出现故障时连接到故障切换服务器。METHOD=PRECONNECT 意味着在任一服务器连接出现故障以前就连接到所有服务器;这样可避免重新连接的开销,但是意味着每个预连接的连接将占用的未使用的其他资源。
Oracle 可以使用快速应用通知 (FAN) 在发生故障切换时通知应用程序(和 Oracle 客户端)。这使得 Oracle 客户端可以立即删除终止的连接而不是在我们的 PHP 应用程序已对其进行实际调用后才删除,并通过让故障切换更快发生使 TAF 更具响应性。
可以通过执行以下程序包启用 FAN:
execute dbms_service.modify_service ( service_name => 'RACSERVICE', aq_ha_notifications => true);然后,您需要在 php.ini 中进行定义:
oci8.events = On
您还可以在服务器端调优负载平衡。Oracle 在内部有一个负载监视模块,叫做负载平衡顾问程序 (LBA),它将选择在其上运行您的连接的节点。
在 PHP 中,负载平衡仅在连接时发生(Oracle 技术中的连接负载平衡),并对 PHP 持久性连接等长时间运行的连接进行调优。或者,您可以使用以下命令将 RAC 配置为对短时间运行的会话(如 PHP 非持久性连接)进行调优:
execute dbms_service.modify_service( service_name => 'RACSERVICE', clb_goal => dbms_service.clb_goal_short); -- or clb_goal_long
PHP 应用程序集成
我们的 PHP 应用程序包含 2,900 个源代码文件,其开发历时 20 多人年。我们构建的财务软件基于我们的 专利技术,附带一个工作流引擎、一个集成的图形屏幕设计器和一个报表编写器。该软件使用开源 连接到 Oracle。
集成我们的 PHP 应用程序非常简单。我们无需更改任何 PHP 代码即可与 Oracle RAC 集成。在错误处理方面,我们在 ADOdb 中构建了一个事务管理器以跟踪所有错误并在检测到错误时自动回滚事务。我们现在考虑让未来版本的 ADOdb 可以重新启动由故障切换导致的失败事务,因为这些不是由 Oracle 自动处理的。关于 PHP 可扩展性和高可用性的 对此进行了说明。
我们确实需要对我们应用程序的 Oracle 端进行一些更改以配合 RAC 行为。在我们的应用程序中,我们大量使用序列来生成替代主键。在 Oracle RAC 中,需要一个全局锁来确保序列在节点内按顺序生成,这将降低数据库性能。
因此,我们使用以下命令重新创建了我们的替代主键序列:
CREATE SEQUENCE seqname START WITH nextvalue CACHE 100 NOORDER这可以防止 Oracle 在每次调用一个新的序列号时都调用全局锁。该方法的缺点是,尽管可以保证序列号是唯一的,但是它们不再按升序排列,并且标号可能出现不连续的现象。
运行 Oracle 时,您要做的最重要的事情之一是调整内存大小。为此,您需要计算允许的最大连接数。我们的数据库服务器有 8GB 的 RAM,我们希望将 2GB 分配给操作系统和其他我们需要运行的进程。这样,我们只剩下 6GB 供 Oracle 使用。
在我们的示例中,我们假设连接到我们的两台应用服务器的活动 Web 浏览器的峰值数大约为 100。假设一台应用服务器出现故障,这意味着我们仍然需要为 100 个连接配置每台服务器。给我们自己留有 50% 的安全余地,将两台应用服务器都配置为在预分叉模式下运行 Apache,将 MaxClients 设置为 150。注意,如果我们没有一直运行 Squid 来管理静态文件,安全余地应该留得更高些,因为所有 Apache 子进程至少有一半将在下载静态文件中被调用。
每个 Apache 子进程将运行一个 PHP 持久性连接,因此这意味着只要有一个 RAC 节点正常运行,我们将需要在数据库中配置 300 个专用服务器连接以处理两台应用服务器。在加入其他进程、来自其他 Oracle 服务器的数据库链接以及安全系数后,我们将专用服务器进程(如会话)的最大数量配置为 500。
由此,我们得出以下设置:
Oracle 参数 |
值 |
说明 |
使用的 内存 |
processes |
500 |
专用服务器进程数量,与您可与数据库建立的连接数相同 。为便于计算, 我们估计每个进程将占用 4 MB。 |
500 x ~4MB = 2GB |
sga_target |
4GB |
用于 共享全局区的内存。用于数据缓冲区和其他共享 数据结构。 |
4GB |
我们通过从应用服务器运行 SQL*Plus 对 Oracle RAC 集群故障切换进行了测试。我们测试了以下情形:
select instance_name from v$instance
使用 Oracle RAC 需要一定的学习时间。建议您了解以下内容。
花些时间了解 Oracle 企业管理器支持 Oracle RAC 的新特性。例如,最初我使用 sqlplus 维护 ASM 设置;当我发现在企业管理器中维护是多么容易时,我不禁大吃一惊。
可以通过运行以下命令启动 Oracle 企业管理器:emctl start dbconsole然后,您可以通过以下 URL 访问 Oracle 企业管理器数据库控制台:。
Oracle RAC 性能统计是 Oracle 企业管理器的另一个重要特性。衡量 RAC 执行效率的一个标准是全局缓存块访问延迟。该量度测量 Oracle RAC 同步节点间的数据块所需的时间。如果该测量值始终高于 5 毫秒,您将需要考虑采取措施减少延迟,如数据库分片(对集群进行分区以使每个节点管理一部分数据)。您可以从 Performance 选项卡查看该统计信息:
如果您使用的是 Oracle ASM 原始设备,请做好准备花费一定时间对维护系统的 IT 基础架构团队进行培训。他们不一定是 DBA,可能更熟悉用于监视磁盘使用的 Unix 工具,如 df,但是这些工具对原始设备并不奏效。而且,当您希望从原始设备复制文件时,如果您运行 Oracle 数据库 11g 或在 10g 上运行 RMAN,则需要使用 Oracle 的 asmcmd 实用程序。
为了加快学习进度,我们为他们的系统操作员提供了一个监视 Web 页面,显示 ASM 分区中的可用磁盘空间。为提取该信息,我们建立了一个 cron 作业,使用 sqlplus 连接到我们的 ASM 实例并运行以下脚本:
connect / as sysdba; spool '/path/to/asmlogfile.txt'; select name, free_mb, total_mb from v$asm_diskgroup; exit;然后,我们运行 PHP 脚本以读取 asmlogfile.txt 的内容并将其写入生产数据库实例。
在高可用性环境中管理批处理作业极具挑战性。这是因为,您需要让基础架构对您的批处理作业进行故障切换。Oracle 为您提供了相应的工具以通过 DBMS_SCHEDULER 程序对此进行管理。这使得您可以将作业与支持故障切换的数据库服务相关联。
首先,您需要在 tnsnames.ora 中定义一个使用故障切换的服务。在以下示例中,定义了 FAILOVER 而未定义 LOAD_BALANCE:这意味着,默认情况下,所有作业都将在 RAC2-VIP 上运行,除非该服务器出现故障:
FAILOVER_SERVICE = (DESCRIPTION = (FAILOVER = ON) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC2-VIP)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC1-VIP)(PORT = 1521)) (CONNECT_DATA = (SERVICE_NAME = BATCH_SERVICE) (FAILOVER_MODE = (TYPE = SELECT) (METHOD = BASIC) (RETRIES = 20) (DELAY = 1) ) ) )然后,为使用该服务的所有作业定义一个作业类:
DBMS_SCHEDULER.create_job_class( job_class_name => 'MY_JOB_CLASS', service => 'FAILOVER_SERVICE');最后,使用该作业类创建一个作业:
DBMS_SCHEDULER.create_job ( job_name => 'my_job', job_type => 'PLSQL_BLOCK', job_action => 'BEGIN NULL; END;', -- your code here start_date => trunc(SYSDATE)+23/24, -- at 11 pm repeat_interval => 'FREQ=DAILY;', job_class => 'MY_JOB_CLASS', end_date => NULL, enabled => TRUE);
我们的服务器有三个网卡,其中一个没有使用。当由于某种原因一台服务器上出现公共网络故障时,虚拟 IP 将在发生故障时切换到同一服务器上未使用的备用网卡,而不是切换到另一台服务器。这意味着,当网络恢复时,该服务器仍然不能正常工作,因为虚拟 IP 将绑定到未使用的接口卡。
要对此进行修复,我们运行以下命令将虚拟 IP 显式绑定到主机名为 node1 的服务器的 eth0 上。
srvctl modify nodeapps -n node1 -A 10.1.199.24/255.255.255.0/eth0
一旦我们克服了学习障碍,我们的 Oracle RAC 体验将是非常积极的:运行时间长、性能好。在峰值负载,有 650 个用户登录,每秒运行 70 个事务。对于我们的客户来说,他们最关心的是数据的管理可靠的、安全的,并且没有业务中断。Oracle RAC 可为他们提供这种保证。
John Lim 居住在马来西亚。他是 ADOdb(一个用于 PHP 的流行数据库)的开发人员。从 1995 年开始,John 的生活就与 Oracle 紧密相联。