POSIX多线程编程

POSIX多线程编程

POSIX多线程编程
1、相比多进程的优点
     强加了某种形式的额外内核开销,从而降低性能。

     对于大多数情形,IPC 不是对于代码的"自然"扩展。通常极大地增加了程序的复杂性。

     如果想编写可移植的多线程代码,代码可运行于 Solaris、FreeBSD、Linux 和其它平台,POSIX 线程是一种当然之选。

2、两个重要问题。
     第一个问题,新线程创建之后主线程如何运行。答案,主线程按顺序继续执行下一行程序(本例中执行 "if (pthread_join(...))")。
    
第二个问题,新线程结束时如何处理。答案,新线程先停止,然后作为其清理过程的一部分,等待与另一个线程合并或"连接"。

 当 thread_function() 完成后, pthread_join() 将返回。这时程序又只有一个主线程。当程序退出时,所有新线程已经使用 pthread_join() 合并了。这就是应该如何处理在程序中创建的每个新线程的过程。如果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。这意味着如果未对线程做正确的清理,最终会导致 pthread_create() 调用失败。

 POSIX 线程中不存在这种层次关系。虽然主线程可以创建一个新线程,新线程可以创建另一个新线程,POSIX 线程标准将它们视为等同的层次。所以等待子线程退出的概念在这里没有意义。POSIX 线程标准不记录任何"家族"信息。缺少家族信息有一个主要含意:如果要等待一个线程终止,就必须将线程的 tid 传递给 pthread_join()。线程库无法为您断定 tid。

 对大多数开发者来说这不是个好消息,因为这会使有多个线程的程序复杂化。不过不要为此担忧。POSIX 线程标准提供了有效地管理多个线程所需要的所有工具。实际上,没有父/子关系这一事实却为在程序中使用线程开辟了更创造性的方法。例如,如果有一个线程称为线程 1,线程 1 创建了称为线程 2 的线程,则线程 1 自己没有必要调用 pthread_join() 来合并线程 2,程序中其它任一线程都可以做到。当编写大量使用线程的代码时,这就可能允许发生有趣的事情。例如,可以创建一个包含所有已停止线程的全局"死线程列表",然后让一个专门的清理线程专等停止的线程加到列表中。这个清理线程调用 pthread_join() 将刚停止的线程与自己合并。现在,仅用一个线程就巧妙和有效地处理了全部清理。

 


3、函数接口

   3.1 thread_create
 让我们查看一下 pthread_create 参数。
 第一个参数 &mythread 是指向 mythread 的指针。
 第二个参数当前为 NULL,可用来定义线程的某些属性。由于缺省的线程属性是适用的,只需将该参数设为 NULL。
 第三个参数是新线程启动时调用的函数名。注意 thread_function() 接受 void * 作为参数,同时返回值的类型也是 void *。这表明可以用 void * 向新线程传递任意类型的数据,新线程完成时也可返回任意类型的数据。
 那如何向线程传递一个任意参数?很简单。只要利用 pthread_create() 中的第四个参数。本例中,因为没有必要将任何数据传给微不足道的 thread_function(),所以将第四个参数设为 NULL。


   3.2 thread_join

 正如 pthread_create() 将一个线程拆分为两个, pthread_join() 将两个线程合并为一个线程。
 pthread_join() 的第一个参数是 tid mythread。
 第二个参数是指向 void 指针的指针。如果 void 指针不为 NULL,pthread_join 将线程的 void * 返回值放置在指定的位置上。由于我们不必理会 thread_function() 的返回值,所以将其设为 NULL.

   3.3 pthread_mutex_lock() 和 pthread_mutex_unlock()

 通常使用 pthread_mutex_lock() 和 pthread_mutex_unlock() 来保护数据结构。这就是说,通过线程的锁定和解锁,对于某一数据结构,确保某一时刻只能有一个线程能够访问它。可以推测到,当线程试图锁定一个未加锁的互斥对象时,POSIX 线程库将同意锁定,而不会使线程进入睡眠状态。

 pthread_mutex_lock(pthread_mutex_t *mutex)
 pthread_mutex_lock() 接受一个指向互斥对象的指针作为参数以将其锁定。如果碰巧已经锁定了互斥对象,调用者将进入睡眠状态。函数返回时,将唤醒调用者(显然)并且调用者还将保留该锁。函数调用成功时返回零,失败时返回非零的错误代码。


 pthread_mutex_unlock(pthread_mutex_t *mutex)
 pthread_mutex_unlock() 与 pthread_mutex_lock() 相配合,它把线程已经加锁的互斥对象解锁。始终应该尽快对已加锁的互斥对象进行解锁(以提高性能)。并且绝对不要对您未保持锁的互斥对象进行解锁操作(否则,pthread_mutex_unlock() 调用将失败并带一个非零的 EPERM 返回值)。


 pthread_mutex_trylock(pthread_mutex_t *mutex)
 当线程正在做其它事情的时候(由于互斥对象当前是锁定的),如果希望锁定互斥对象,这个调用就相当方便。调用 pthread_mutex_trylock() 时将尝试锁定互斥对象。如果互斥对象当前处于解锁状态,那么您将获得该锁并且函数将返回零。然而,如果互斥对象已锁定,这个调用也不会阻塞。当然,它会返回非零的 EBUSY 错误值。然后可以继续做其它事情,稍后再尝试锁定。

 

 

   3.4 pthread_mutex_init 和 pthread_mutex_destroy()

 正如所示,pthread_mutex_init 接受一个指针作为参数以初始化为互斥对象,该指针指向一块已分配好的内存区。第二个参数,可以接受一个可选的 pthread_mutexattr_t 指针。这个结构可用来设置各种互斥对象属性。但是通常并不需要这些属性,所以正常做法是指定 NULL。

 一旦使用 pthread_mutex_init() 初始化了互斥对象,就应使用 pthread_mutex_destroy() 消除它。pthread_mutex_destroy() 接受一个指向 pthread_mutext_t 的指针作为参数,并释放创建互斥对象时分配给它的任何资源。请注意, pthread_mutex_destroy() 不会释放用来存储 pthread_mutex_t 的内存。释放自己的内存完全取决于您。还必须注意一点,pthread_mutex_init() 和 pthread_mutex_destroy() 成功时都返回零。

   3.5

4、互斥
 
 互斥对象是这样工作的。如果线程 a 试图锁定一个互斥对象,而此时线程 b 已锁定了同一个互斥对象时,线程 a 就将进入睡眠状态。一旦线程 b 释放了互斥对象(通过 pthread_mutex_unlock() 调用),线程 a 就能够锁定这个互斥对象(换句话说,线程 a 就将从 pthread_mutex_lock() 函数调用中返回,同时互斥对象被锁定)。同样地,当线程 a 正锁定互斥对象时,如果线程 c 试图锁定互斥对象的话,线程 c 也将临时进入睡眠状态。对已锁定的互斥对象上调用 pthread_mutex_lock() 的所有线程都将进入睡眠状态,这些睡眠的线程将"排队"访问这个互斥对象

 通常使用 pthread_mutex_lock() 和 pthread_mutex_unlock() 来保护数据结构。这就是说,通过线程的锁定和解锁,对于某一数据结构,确保某一时刻只能有一个线程能够访问它。可以推测到,当线程试图锁定一个未加锁的互斥对象时,POSIX 线程库将同意锁定,而不会使线程进入睡眠状态。

 

 如果放置了过多的互斥对象,代码就没有什么并发性可言,运行起来也比单线程解决方案慢。如果放置了过少的互斥对象,代码将出现奇怪和令人尴尬的错误。幸运的是,有一个中间立场。首先,互斥对象是用于串行化存取*共享数据*。不要对非共享数据使用互斥对象,并且,如果程序逻辑确保任何时候都只有一个线程能存取特定数据结构,那么也不要使用互斥对象。

 其次,如果要使用共享数据,那么在读、写共享数据时都应使用互斥对象。用 pthread_mutex_lock() 和 pthread_mutex_unlock() 把读写部分保护起来,或者在程序中不固定的地方随机使用它们。学会从一个线程的角度来审视代码,并确保程序中每一个线程对内存的观点都是一致和合适的。为了熟悉互斥对象的用法,最初可能要花好几个小时来编写代码,但是很快就会习惯并且*也*不必多想就能够正确使用它们。


 现在该来看看使用互斥对象的各种不同方法了。让我们从初始化开始。在 thread3.c 示例中,我们使用了静态初始化方法。这需要声明一个 pthread_mutex_t 变量,并赋给它常数 PTHREAD_MUTEX_INITIALIZER:


pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;


 很简单吧。但是还可以动态地创建互斥对象。当代码使用 malloc() 分配一个新的互斥对象时,使用这种动态方法。此时,静态初始化方法是行不通的,并且应当使用例程 pthread_mutex_init():


int pthread_mutex_init( pthread_mutex_t *mymutex, const pthread_mutexattr_t *attr) 

 

5 线程内幕

 5.1

 在解释如何确定在何处使用互斥对象之前,先来深入了解一下线程的内部工作机制。请看第一个例子:

 假设主线程将创建三个新线程:线程 a、线程 b 和线程 c。假定首先创建线程 a,然后是线程 b,最后创建线程 c。


    pthread_create( &thread_a, NULL, thread_function, NULL);
    pthread_create( &thread_b, NULL, thread_function, NULL);
    pthread_create( &thread_c, NULL, thread_function, NULL);


 在第一个 pthread_create() 调用完成后,可以假定线程 a 不是已存在就是已结束并停止。第二个 pthread_create() 调用后,主线程和线程 b 都可以假定线程 a 存在(或已停止)。

 然而,就在第二个 create() 调用返回后,主线程无法假定是哪一个线程(a 或 b)会首先开始运行。虽然两个线程都已存在,线程 CPU 时间片的分配取决于内核和线程库。至于谁将首先运行,并没有严格的规则。尽管线程 a 更有可能在线程 b 之前开始执行,但这并无保证。对于多处理器系统,情况更是如此。如果编写的代码假定在线程 b 开始执行之前实际上执行线程 a 的代码,那么,程序最终正确运行的概率是 99%。或者更糟糕,程序在您的机器上 100% 地正确运行,而在您客户的四处理器服务器上正确运行的概率却是零。

 从这个例子还可以得知,线程库保留了每个单独线程的代码执行顺序。换句话说,实际上那三个 pthread_create() 调用将按它们出现的顺序执行。从主线程上来看,所有代码都是依次执行的。有时,可以利用这一点来优化部分线程程序。例如,在上例中,线程 c 就可以假定线程 a 和线程 b 不是正在运行就是已经终止。它不必担心存在还没有创建线程 a 和线程 b 的可能性。可以使用这一逻辑来优化线程程序。

 5.2

 现在来看另一个假想的例子。假设有许多线程,他们都正在执行下列代码:


    myglobal=myglobal+1;


 那么,是否需要在加一操作语句前后分别锁定和解锁互斥对象呢?也许有人会说"不"。编译器极有可能把上述赋值语句编译成一条机器指令。大家都知道,不可能"半途"中断一条机器指令。即使是硬件中断也不会破坏机器指令的完整性。基于以上考虑,很可能倾向于完全省略 pthread_mutex_lock() 和 pthread_mutex_unlock() 调用。不要这样做。

 我在说废话吗?不完全是这样。首先,不应该假定上述赋值语句一定会被编译成一条机器指令,除非亲自验证了机器代码。即使插入某些内嵌汇编语句以确保加一操作的完整执行——甚至,即使是自己动手写编译器!-- 仍然可能有问题。

 答案在这里。使用单条内嵌汇编操作码在单处理器系统上可能不会有什么问题。每个加一操作都将完整地进行,并且多半会得到期望的结果。但是多处理器系统则截然不同。在多 CPU 机器上,两个单独的处理器可能会在几乎同一时刻(或者,就在同一时刻)执行上述赋值语句。不要忘了,这时对内存的修改需要先从 L1 写入 L2 高速缓存、然后才写入主存。(SMP 机器并不只是增加了处理器而已;它还有用来仲裁对 RAM 存取的特殊硬件。)最终,根本无法搞清在写入主存的竞争中,哪个 CPU 将会"胜出"。要产生可预测的代码,应使用互斥对象。互斥对象将插入一道"内存关卡",由它来确保对主存的写入按照线程锁定互斥对象的顺序进行。

 考虑一种以 32 位块为单位更新主存的 SMP 体系结构。如果未使用互斥对象就对一个 64 位整数进行加一操作,整数的最高 4 位字节可能来自一个 CPU,而其它 4 个字节却来自另一 CPU。糟糕吧!最糟糕的是,使用差劲的技术,您的程序在重要客户的系统上有可能不是很长时间才崩溃一次,就是早上三点钟就崩溃。David R. Butenhof 在他的《POSIX 线程编程》(请参阅本文末尾的参考资料部分)一书中,讨论了由于未使用互斥对象而将产生的种种情况。

6、条件变量详解

 在上一篇文章结束时,我描述了一个比较特殊的难题:如果线程正在等待某个特定条件发生,它应该如何处理这种情况?它可以重复对互斥对象锁定和解锁,每次都会检查共享数据结构,以查找某个值。但这是在浪费时间和资源,而且这种繁忙查询的效率非常低。解决这个问题的最佳方法是使用 pthread_cond_wait() 调用来等待特殊条件发生。

 

 6.1

 了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心,也是最难以理解的部分。

 调用:pthread_cond_wait(&mycond, &mymutex);
然后,pthread_cond_wait() 调用在返回前执行许多操作:
 pthread_mutex_unlock(&mymutex);
它对 mymutex 解锁,然后进入睡眠状态,等待 mycond 以接收 POSIX 线程"信号"。一旦接收到"信号"(加引号是因为我们并不是在讨论传统的 UNIX 信号,而是来自 pthread_cond_signal() 或 pthread_cond_broadcast() 调用的信号),它就会苏醒。但 pthread_cond_wait() 没有立即返回 -- 它还要做一件事:重新锁定 mutex:
    pthread_mutex_lock(&mymutex);
 pthread_cond_wait() 知道我们在查找 mymutex "背后"的变化,因此它继续操作,为我们锁定互斥对象,然后才返回。

 

 现在已回顾了 pthread_cond_wait() 调用,您应该了解了它的工作方式。应该能够叙述 pthread_cond_wait() 依次执行的所有操作。尝试一下。如果理解了 pthread_cond_wait(),其余部分就相当容易,因此请重新阅读以上部分,直到记住为止。好,读完之后,能否告诉我在调用 pthread_cond_wait() 之前,互斥对象必须处于什么状态?pthread_cond_wait() 调用返回之后,互斥对象处于什么状态?这两个问题的答案都是"锁定"。既然已经完全理解了 pthread_cond_wait() 调用,现在来继续研究更简单的东西 -- 初始化和真正的发送信号和广播进程。到那时,我们将会对包含了多线程工作队列的 C 代码了如指掌。


 6.2 初始化和清除
条件变量是一个需要初始化的真实数据结构。以下就初始化的方法。首先,定义或分配一个条件变量,如下所示:

   pthread_cond_t mycond;

然后,调用以下函数进行初始化:

    pthread_cond_init(&mycond,NULL);

瞧,初始化完成了!在释放或废弃条件变量之前,需要毁坏它,如下所示:

    pthread_cond_destroy(&mycond);

很简单吧。接着讨论 pthread_cond_wait() 调用。

 6.3 等待
一旦初始化了互斥对象和条件变量,就可以等待某个条件,如下所示:

    pthread_cond_wait(&mycond, &mymutex);

请注意,代码在逻辑上应该包含 mycond 和 mymutex。一个特定条件只能有一个互斥对象,而且条件变量应该表示互斥数据"内部"的一种特殊的条件更改。一个互斥对象可以用许多条件变量(例如,cond_empty、cond_full、cond_cleanup),但每个条件变量只能有一个互斥对象。

 6.4 发送信号和广播
对于发送信号和广播,需要注意一点。如果线程更改某些共享数据,而且它想要唤醒所有正在等待的线程,则应使用 pthread_cond_broadcast 调用,如下所示:

    pthread_cond_broadcast(&mycond);

在某些情况下,活动线程只需要唤醒第一个正在睡眠的线程。假设您只对队列添加了一个工作作业。那么只需要唤醒一个工作程序线程(再唤醒其它线程是不礼貌的!):
    pthread_cond_signal(&mycond);

此函数只唤醒一个线程。如果 POSIX 线程标准允许指定一个整数,可以让您唤醒一定数量的正在睡眠的线程,那就更完美了。但是很可惜,我没有被邀请参加会议。

 

 

 


- 作者: seekfun 2004年12月17日, 星期五 10:55  回复(0) |  引用(0) 加入博采

Informix ec程序的多线程

     Informix支持一个进程中的多个线程同时对数据库进行操作,既可以多个线程使用多个数据库连接,也可以多个线程共享一个数据库连接。
    
    无论是那种方式,都需要认真分析处理多线程的共享变量之间加锁、解锁的处理。

    本文针对多线程的EC程序中的几个问题进行说明:

    1、编译连接
   
    当只有一个完整的ec文件时,可以使用ESQL的-thread选项,ESQL会自动编译连接线程安全的informix库。
   
    当存在多个文件,包括ec、c、C文件时,可以首先使用ESQL的-c和-thread选项将ec文件编译成.o文件,然后使用连接器强制连接线程安全的库(th库)。
   
    总之要求下面几点:
        A、ESQL要加上-thread选项
        B、配置环境变量THREADLIB=POSIX
        C、保证连接的是线程安全的库。

    2、多个线程共享一个连接
       
    多个线程共享一个连接时,在连接建立好以后,可以在一个线程中使用
    EXEC SQL set connection 'con' dormant;
    将连接置于dormant状态,此时的连接可以被其它的线程激活。

    其它线程使用
    do {
     EXEC SQL set connection 'con';
    } while ( ( sqlca.sqlcode == -1802 ) );
    将连接激活,此时可以访问数据库了,而且只用本线程可以访问数据库。
    只有本线程将连接置为dormant后,才可以再被其它的线程使用。

    3、多个线程使用多个连接

    A、此时每个线程需要有自己的连接名,各个连接名必须不同。   
    B、必须仔细处理共享变量的互斥访问。


- 作者: seekfun 2004年12月17日, 星期五 10:53  回复(0) |  引用(0) 加入博采

Oracle DataGuard的failover(logical standby)

主库宕库时fail over到备库,并重建DataGuard的操作步骤(主库的文件没有丢失)


##################################################################################
###   主库宕库时fail over到备库,并重建DataGuard的操作步骤(主库的文件没有丢失)
##################################################################################

 #########################################################
 ###  注意:
 ###  首先查看一下备库的alert.log文件,会有提示信息,表明和主库失去连接
 #########################################################
 Wed Dec 8 14:16:05 2004
 RFS: Possible network disconnect with primary database
 Closing latent archivelog for thread 1 sequence 298
 EOF located at block 33 low SCN 0:296816 next SCN 0:296903
 Latent archivelog '/usr2/u01/app/oracle/oracle9201/oradata/scpdb/standby_archive/1_298.dbf'
 If you wish to failover to this standby database, you should use the
 following command to manually register the archivelog for recovery:
 ALTER DATABASE REGISTER LOGFILE '/usr2/u01/app/oracle/oracle9201/oradata/scpdb/standby_archive/1_298.dbf'
 
 ##########################################
 ### 查看当前的日志应用情况(备库) 
 ### 通过下面两个查询判断出日志应用情况
 ##########################################
 SQL> SELECT SUBSTR(FILE_NAME,1,25) FILE_NAME, SUBSTR(SEQUENCE#,1,4) "SEQ#",
  FIRST_CHANGE#, NEXT_CHANGE#, TO_CHAR(TIMESTAMP, 'HH:MI:SS') TIMESTAMP,
  DICT_BEGIN BEG, DICT_END END, SUBSTR(THREAD#,1,4) "THR#"
  FROM DBA_LOGSTDBY_LOG ORDER BY SEQUENCE#; 
 SQL> SELECT APPLIED_SCN, NEWEST_SCN FROM DBA_LOGSTDBY_PROGRESS;
  
 
1、 在备库注册丢失的log文件
 SQL> COLUMN FILE_NAME FORMAT a55;
 SQL> SELECT THREAD#, SEQUENCE#, FILE_NAME FROM DBA_LOGSTDBY_LOG L
  WHERE NEXT_CHANGE# NOT IN
  (SELECT FIRST_CHANGE# FROM DBA_LOGSTDBY_LOG WHERE L.THREAD# = THREAD#)
  ORDER BY THREAD#,SEQUENCE#;

2、 在备库注册没有应用的online redo文件
 SQL> ALTER DATABASE REGISTER LOGICAL LOGFILE
  '/database/oracle/oracle9201/oradata/scpdb/standby_archive/1_229.dbf';
 
 ALTER DATABASE REGISTER LOGICAL LOGFILE '/disk1/oracle/dbs/online_log1.log'
 *
 ERROR at line 1:
 ORA-01289: cannot add duplicate logfile

3、 在备库注册部分填充的archived redo log
 SQL> COLUMN FILE_NAME FORMAT a55
 SQL> SELECT THREAD#, SEQUENCE#, FILE_NAME FROM DBA_LOGSTDBY_LOG L
  ORDER BY THREAD#,SEQUENCE#;
 
        #########################################################
    ALTER DATABASE REGISTER  LOGICAL  LOGFILE
     '/usr2/u01/app/oracle/oracle9201/oradata/scpdb/standby_archive/1_24.dbf' 
 #########################################################
 ###   注意
 ###   在注册前可能需要首先停掉日志应用服务,注册后再重启(测试发现不需要)
 ###   SQL> ALTER DATABASE STOP LOGICAL STANDBY APPLY;
 ###   SQL> ALTER DATABASE START LOGICAL STANDBY APPLY;
 #########################################################
 
 

4、 在原来的备库上取消"延迟应用"(我们的应用不需要,因为没有使用延迟应用)
 SQL> ALTER DATABASE STOP LOGICAL STANDBY APPLY; 
 SQL> EXECUTE DBMS_LOGSTDBY.APPLY_UNSET('APPLY_DELAY');
 SQL> ALTER DATABASE START LOGICAL STANDBY APPLY;


5、判断备库的日志应用服务应用了所有的事务(一定要确认本步骤)
 SQL> SELECT APPLIED_SCN, NEWEST_SCN FROM DBA_LOGSTDBY_PROGRESS;

6、关闭备库的日志应用服务并将其切换为主库
 SQL> ALTER DATABASE STOP LOGICAL STANDBY APPLY;
 SQL> ALTER DATABASE ACTIVATE LOGICAL STANDBY DATABASE;

7、 在新的备库(旧的主库)建立database link
 SQL> EXECUTE DBMS_LOGSTDBY.GUARD_BYPASS_ON;
 SQL> CREATE DATABASE LINK DG_DL
  CONNECT TO SYSTEM IDENTIFIED BY MANAGER USING 'SCPDB.PRIMARY';
 SQL> CREATE DATABASE LINK DG_DL
  CONNECT TO SYSTEM IDENTIFIED BY MANAGER USING 'SCPDB.STANDBY';
 SQL> EXECUTE DBMS_LOGSTDBY.GUARD_BYPASS_OFF;

8、 新的standby验证database link
 SQL> SELECT * FROM DBA_LOGSTDBY_PARAMETERS@DG_DL;
 
9、 设置数据库的参数

 #######################
 ###  新的primary db
 #######################
 SQL> ARCHIVE LOG LIST;
 SQL> SHOW PARAMETER LOG_ARCHIVE_
 SQL> SHOW PARAMETER REMOTE
 
 SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='SERVICE=SCPDB.STANDBY  LGWR SYNC AFFIRM' SCOPE=BOTH;
 SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='SERVICE=SCPDB.PRIMARY  LGWR SYNC AFFIRM' SCOPE=BOTH;
 SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_3=ENABLE SCOPE=BOTH;
 SQL> ALTER SYSTEM SET archile_lag_target=1200 SCOPE=BOTH;


 ###########################
 ###  新的standby db
 ###########################
 SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='' SCOPE=BOTH;
 SQL> ALTER SYSTEM SET standby_archive_dest='/usr2/u01/app/oracle/oracle9201/oradata/scpdb/standby_archive' SCOPE=BOTH;
 SQL> ALTER SYSTEM SET standby_file_management=auto SCOPE=BOTH;
 SQL> ALTER SYSTEM SET parallel_max_servers=9

10、 在新的standby数据库(旧的主库)上启动日志应用服务
 SQL> ALTER DATABASE START LOGICAL STANDBY APPLY NEW PRIMARY dg_dl;
 SQL> ALTER DATABASE GUARD NONE;

11、验证新的备库正常
 SQL> SELECT NAME, VALUE FROM V$LOGSTDBY_STATS
  WHERE NAME LIKE 'coordinator%' or NAME LIKE 'transactions%'; 
  
  
##################################################################################
###      备库宕库时重新部署的解决方案(备库的数据库文件正常)
##################################################################################

1、重新启动备库
 SQL> STARTUP;
 
2、启动日志应用服务
  SQL> ALTER DATABASE START LOGICAL STANDBY APPLY INITIAL;
 

- 作者: seekfun 2004年12月8日, 星期三 21:41  回复(0) |  引用(0) 加入博采

Oracle Dataguard管理命令(logical standby)

Oracle Dataguard管理命令(logical standby)


#################
##  管理命令:
#################

1、注册日志的命令standby
 SQL> ALTER DATABASE REGISTER LOGICAL LOGFILE
 '/usr2/u01/app/oracle/oracle9201/oradata/scpdb/standby_archive/1_15.dbf';
   
2、设置初始化参数的一些命令

 备库:
 SQL> alter system set standby_archive_dest='/usr2/u01/app/oracle/oracle9201/oradata/scpdb/standby_archive' scope=both; 
 
 主库:
 
 
3、pfile spfile
 startup mount pfile=/usr2/u01/app/oracle/oracle9201/admin/scpdb/pfile/initscpdb.ora
 create spfile from pfile='/usr2/u01/app/oracle/oracle9201/admin/scpdb/pfile/initscpdb.ora'
 create pfile='/usr2/u01/app/oracle/oracle9201/admin/scpdb/pfile/initscpdb.ora' from spfile;  
 
4、设置主库为archive log 
 SQL> STARTUP MOUNT;
 SQL> ALTER DATABASE ARCHIVELOG;
 SQL> ALTER DATABASE OPEN;
 SQL> ARCHIVE LOG LIST; 

5、 在primary上做archive操作
SQL> ALTER SYSTEM ARCHIVE LOG CURRENT; 

6、备库的日志应用服务的错误
SQL> EXECUTE DBMS_LOGSTDBY.SKIP_ERROR('DML', 'LY', 'T3', null);
SQL> EXECUTE DBMS_LOGSTDBY.UNSKIP_ERROR('DML','LY', 'T3', null);
 
############################ 
#   备库如何监控日志应用
############################


1、 启动和停止日志应用服务
 SQL> ALTER DATABASE START LOGICAL STANDBY APPLY INITIAL;
 
 ### SQL> ALTER DATABASE STOP LOGICAL STANDBY APPLY;
 ### SQL> ALTER DATABASE START LOGICAL STANDBY APPLY; 

2、 V$LOGSTDBY
 显示正在读取redo log和应用archived redo log的进程
 
 SQL> COLUMN STATUS FORMAT A50
 SQL> COLUMN TYPE FORMAT A12
 SQL> SELECT TYPE, HIGH_SCN, STATUS FROM V$LOGSTDBY; 
 
3、 DBA_LOGSTDBY_PROGRESS
 ### 显示LSP的状态和在standby上应用的sql的信息
 
 ### 快速判断是否所有的日志信息都被应用,两个值一样的话说明状态正常
 
 SQL> SELECT APPLIED_SCN, NEWEST_SCN FROM DBA_LOGSTDBY_PROGRESS; 
 
 ### 判断日志是否被应用 
 SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YY HH24:MI:SS'; 
 SQL> SELECT L.SEQUENCE#, L.FIRST_TIME,
  (CASE WHEN L.NEXT_CHANGE# < P.READ_SCN THEN 'YES'
  WHEN L.FIRST_CHANGE# < P.APPLIED_SCN THEN 'CURRENT'
  ELSE 'NO' END) APPLIED
  FROM DBA_LOGSTDBY_LOG L, DBA_LOGSTDBY_PROGRESS P
  ORDER BY SEQUENCE#; 
 
4、 DBA_LOGSTDBY_EVENTS
 ### 此视图可以监控日志应用服务发生的事件
 ### 如果日志应用服务异常中止,那么错误信息将会显示在这个view中
 
 SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YY HH24:MI:SS'; 
 SQL> COLUMN STATUS FORMAT A60
 SQL> SELECT EVENT_TIME, STATUS, EVENT FROM DBA_LOGSTDBY_EVENTS
  ORDER BY EVENT_TIME, COMMIT_SCN; 
  
5、 DBA_LOGSTDBY_LOG
 ###  提供日志应用服务的动态信息。
 ###  DICT_BEGIN为yes的项表名词典build的begining在此redo log中,
 ###  DICT_END为yes的项表名词典build的END在此redo log中,

 SQL> SELECT FILE_NAME, SEQUENCE#, FIRST_CHANGE#, NEXT_CHANGE#,
  TIMESTAMP, DICT_BEGIN, DICT_END, THREAD# FROM DBA_LOGSTDBY_LOG
  ORDER BY SEQUENCE#;   

6、 V$LOGSTDBY_STATS
 ### 提供日志应用服务的状态和统计信息,可以看出备库的日志应用是否正常
 
 SQL> COLUMN NAME FORMAT A35
 SQL> COLUMN VALUE FORMAT A35
 SQL> SELECT NAME, VALUE FROM V$LOGSTDBY_STATS
  WHERE NAME LIKE 'coordinator%' or NAME LIKE 'transactions%'; 


7、 V$DATAGUARD_STATUS
 显示dataguard的状态
 SQL> SELECT * FROM V$DATAGUARD_STATUS;


###############################   
#    主库的一些命令
############################### 
1、监控主库的archive是否正常
 SQL> ARCHIVE LOG LIST;              ### 看自动归档是否开启
 SQL> SHOW PARAMETER LOG_ARCHIVE;    ### 看归档的目的的,状态
 SQL> SHOW PARAMETER REMOTE;         ### 看remote归档是否开启

2、 查看当前的重做日志号
 SQL> SELECT THREAD#, SEQUENCE#, ARCHIVED, STATUS FROM V$LOG;  


3、 查看最近归档的重做日志号
 SQL> SELECT MAX(SEQUENCE#) FROM V$ARCHIVED_LOG;  
 
4、 查看最近归档的重做日志文件(包括所有的目的地)
 SQL> SELECT DESTINATION, STATUS, ARCHIVED_THREAD#, ARCHIVED_SEQ#
  FROM V$ARCHIVE_DEST_STATUS
  WHERE STATUS <> 'DEFERRED' AND STATUS <> 'INACTIVE'; 
  
5、 查看日志是否在特定的站点被接收

 SQL> SELECT LOCAL.THREAD#, LOCAL.SEQUENCE# FROM
  (SELECT THREAD#, SEQUENCE# FROM V$ARCHIVED_LOG WHERE DEST_ID=1)
  LOCAL WHERE
  LOCAL.SEQUENCE# NOT IN
  (SELECT SEQUENCE# FROM V$ARCHIVED_LOG WHERE DEST_ID=3 AND
  THREAD# = LOCAL.THREAD#);
  
6、查看是否有网络错误
 SQL> SELECT DEST_ID, STATUS, ERROR FROM V$ARCHIVE_DEST WHERE DEST_ID = 3;
 
 


 
 


 

- 作者: seekfun 2004年12月8日, 星期三 21:38  回复(0) |  引用(0) 加入博采

Oracle Dataguard的配置(logical standby)

Oracle Dataguard(logical standby)的配置方法


##################################################################################
###          primary的设置
##################################################################################

条件: 1、主库的用户必须有LOGSTDBY_ADMINISTRATOR、SELECT_CATALOG_ROLE角色
 2、必须使用spfile

################################
###    需要配置的初始化参数:
################################ 
 archile_lag_target=1200
 standby_archive_dest='/usr2/u01/app/oracle/oracle9201/oradata/scpdb/standby_archive'
 standby_file_management=auto
 log_archive_dest_1='LOCATION=/usr2/u01/app/oracle/oracle9201/oradata/scpdb/archive' 
 log_archive_dest_3='SERVICE=SCPDB.STANDBY  LGWR SYNC AFFIRM'
 log_archive_dest_state_1='ENABLE'
 log_archive_dest_state_3='ENABLE'
 log_archive_start=true
 
 control_file_record_keep_time 
################################

startup mount pfile=/usr2/u01/app/oracle/oracle9201/admin/scpdb/pfile/initscpdb.ora
create spfile from pfile='/usr2/u01/app/oracle/oracle9201/admin/scpdb/pfile/initscpdb.ora'
create pfile='/usr2/u01/app/oracle/oracle9201/admin/scpdb/pfile/initscpdb.ora' from spfile;

#################################

1、设置主库为force logging模式(备库也会有)
SQL> ALTER DATABASE FORCE LOGGING;

2、验证主库为archive log模式,并且自动archive是开启的(备库也会有)
SQL> ARCHIVE LOG LIST;
SQL> SHUTDOWN NORMAL;
SQL> STARTUP MOUNT;
SQL> ALTER DATABASE ARCHIVELOG;
SQL> ALTER DATABASE OPEN;
SQL> ARCHIVE LOG LIST;

SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_1='LOCATION=/usr2/u01/app/oracle/oracle9201/oradata/scpdb/archive' SCOPE=BOTH;

3、验证LOG_PARALLELISM为1(备库的值不一样)
SQL> SHOW PARAMETER LOG_PARALLELISM;

SQL> ALTER SYSTEM SET LOG_PARALLELISM=1 SCOPE=SPFILE;
SQL> SHUTDOWN IMMEDIATE;
SQL> STARTUP;

4、确定支持的数据类型和表

SQL> SELECT DISTINCT OWNER,TABLE_NAME FROM DBA_LOGSTDBY_UNSUPPORTED
 ORDER BY OWNER,TABLE_NAME;
 
SQL> SELECT COLUMN_NAME,DATA_TYPE FROM DBA_LOGSTDBY_UNSUPPORTED
 WHERE OWNER='OE' AND TABLE_NAME = 'CUSTOMERS'; 
 
5、确定表行被唯一标识
SQL> SELECT OWNER, TABLE_NAME,BAD_COLUMN FROM DBA_LOGSTDBY_NOT_UNIQUE
 WHERE TABLE_NAME NOT IN (SELECT TABLE_NAME FROM DBA_LOGSTDBY_UNSUPPORTED);

6、确定Supplemental Logging(备库也会有)
SQL> SELECT SUPPLEMENTAL_LOG_DATA_PK, SUPPLEMENTAL_LOG_DATA_UI FROM V$DATABASE;
SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY, UNIQUE INDEX) COLUMNS;
SQL> ALTER SYSTEM ARCHIVE LOG CURRENT;
SQL> SELECT SUPPLEMENTAL_LOG_DATA_PK, SUPPLEMENTAL_LOG_DATA_UI FROM V$DATABASE;

7、生成可替换的表空间(只需执行一次,备库也会有standby_file_management=auto)
SQL> CREATE TABLESPACE logmnrts DATAFILE '/usr2/u01/app/oracle/oracle9201/oradata/scpdb/logmnrts.dbf'
 SIZE 50M AUTOEXTEND ON MAXSIZE UNLIMITED;
 
SQL> EXECUTE DBMS_LOGMNR_D.SET_TABLESPACE('LOGMNRTS');

 


##################################################################################
####     standby的设置
##################################################################################


1. 识别数据库文件和日志文件
SQL> SELECT NAME FROM V$DATAFILE;
SQL> SELECT GROUP#,TYPE,MEMBER FROM V$LOGFILE;


2. 拷贝主数据库

2.1 关闭主库
SQL> SHUTDOWN IMMEDIATE;

2.2 拷贝数据文件到临时目录
cp *.dbf /in/orabak/
rcp 192.168.2.209:/in/orabak/* .
2.3 mount主库
SQL> STARTUP MOUNT;

2.4 生成控制文件的备份
SQL> ALTER DATABASE BACKUP CONTROLFILE TO '/in/orabak/control01.ctl';


2.5 使主库处于restricted session模式
SQL> ALTER SYSTEM ENABLE RESTRICTED SESSION;

2.6 打开主库并建立standby数据库的数据字典(此步操作备库没有)
 ### Build过程将使得logMiner的词典信息保存到redo log中,便于在备库应用。
 SQL> ALTER DATABASE OPEN;
 SQL> EXECUTE DBMS_LOGSTDBY.BUILD;

2.7 diaable restricted session模式
SQL> ALTER SYSTEM DISABLE RESTRICTED SESSION;

2.8 确定最近的归档重做日志
SQL> ALTER SYSTEM ARCHIVE LOG CURRENT;
SQL> SELECT NAME FROM V$ARCHIVED_LOG
 WHERE (SEQUENCE#=(SELECT MAX(SEQUENCE#) FROM V$ARCHIVED_LOG
 WHERE DICTIONARY_BEGIN = 'YES' AND STANDBY_DEST= 'NO'));
 
cp  /usr2/u01/app/oracle/oracle9201/oradata/scpdb/archive/1_45.dbf  /in/orabak/

 


3. 生成pfile
CREATE PFILE='/in/orabak/initscpdb.ora' FROM SPFILE;


4. 拷贝文件
###  rcp 192.168.2.209:/in/orabak/* .
rcp 192.168.2.209:/in/orabak/control01.ctl .
rcp 192.168.2.209:/in/orabak/initscpdb.ora .
rcp 192.168.2.209:/in/orabak/1_45.dbf ./archive

cp control01.ctl control02.ctl
cp control02.ctl control03.ctl


5. 设置standby初始化参数

standby_archive_dest='/usr2/u01/app/oracle/oracle9201/oradata/scpdb/standby_archive'
parallel_max_servers=9


6. windows平台设置服务(unix不须)

 

7.listener.ora


8.设置sqlnet.ora
 ###  同时在主备设置,以便将来切换时使用
 SQLNET.EXPIRE_TIME=60

9.tnsnames.ora


10. mount standby数据库

startup mount pfile=/usr2/u01/app/oracle/oracle9201/admin/scpdb/pfile/initscpdb.ora
###########################
### 设为最大可用模式
###########################

### primary
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='SERVICE=SCPDB.STANDBY  LGWR SYNC AFFIRM' SCOPE=BOTH ;
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_3=ENABLE SCOPE=BOTH;

### standby
SQL> ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE AVAILABILITY
SQL> ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE PERFORMANCE


11. rename datafile


12. rename redo log file


13. turn on database guard

# alter database guard 语句控制用户对standby数据库的访问
SQL> ALTER DATABASE GUARD NONE;     # 允许对standby数据库做更新操作
SQL> ALTER DATABASE GUARD ALL;      # 不允许对standby数据库做更新操作

SQL> ALTER DATABASE OPEN RESETLOGS;


14. reset db name


15. change db name in parameter file

 

16. 生成temp表空间

SQL> SELECT * FROM V$TEMPFILE;

SQL> SELECT TABLESPACE_NAME FROM DBA_TABLESPACES WHERE
 CONTENTS ='TEMPORARY';
 
 
SQL> ALTER TABLESPACE TEMP ADD TEMPFILE
 '/usr2/u01/app/oracle/oracle9201/oradata/scpdb/temp01.dbf'
 SIZE 100M REUSE;
 
 
 
17. 注册Archived Redo Log,开始 SQL Apply Operations
 
 
SQL> ALTER DATABASE REGISTER LOGICAL LOGFILE
 '/usr2/u01/app/oracle/oracle9201/oradata/scpdb/archive/1_45.dbf'; 
 
 
SQL> ALTER DATABASE START LOGICAL STANDBY APPLY INITIAL;
 
### SQL> ALTER DATABASE STOP LOGICAL STANDBY APPLY;
### SQL> ALTER DATABASE START LOGICAL STANDBY APPLY; 
 
 
 
18. 使主数据库archive到standby数据库
 
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='SERVICE=SCPDB.STANDBY  LGWR SYNC AFFIRM' SCOPE=BOTH;
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_3=ENABLE SCOPE=BOTH;

SQL> ALTER SYSTEM ARCHIVE LOG CURRENT; 


###########################
### 设为最大可用模式
###########################

### primary
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_3='SERVICE=SCPDB.STANDBY  LGWR SYNC AFFIRM' SCOPE=BOTH ;
SQL> ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_3=ENABLE SCOPE=BOTH;

### standby(必须在mount模式下)
SQL> ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE AVAILABILITY
SQL> ALTER DATABASE SET STANDBY DATABASE TO MAXIMIZE PERFORMANCE

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值