找回密码
 立即注册
首页 业界区 业界 Oracle DBA必备工具:自动调整重做日志文件大小和数量 ...

Oracle DBA必备工具:自动调整重做日志文件大小和数量

皮仪芳 3 天前
我们的文章会在微信公众号Oracle恢复实录和博客网站(www.htz.pw)同步更新 ,欢迎关注收藏。也欢迎大家转载,但请在文章开始处标注文章出处,谢谢!
由于博客中包含大量代码,建议通过网页浏览以获得更好的阅读体验。
背景介绍

在Oracle数据库运维过程中,合理配置重做日志(Redo Log)文件的大小和数量是非常重要的。不合适的重做日志配置可能会导致以下问题:

  • 日志切换过于频繁,影响数据库性能
  • 日志文件太小,导致归档速度跟不上
  • 日志组数量不足,影响数据库可用性
今天给大家分享一个实用的脚本工具,可以帮助DBA快速调整重做日志文件的配置。
脚本功能介绍

这个脚本(db_add_logfile.sql)主要实现以下功能:

  • 自动添加指定大小的重做日志文件组
  • 支持指定每个线程的日志组数量
  • 自动清理小于指定大小的旧日志文件
  • 支持ASM和文件系统两种存储方式
  • 支持RAC环境
  • 支持归档和非归档模式
使用方法

脚本接受三个参数:
  1. @db_add_logfile.sql [日志文件大小] [每线程日志组数量] [日志文件路径]
复制代码
参数说明:

  • 参数1:日志文件大小(默认2G)
  • 参数2:每个线程的日志组总数(默认7)
  • 参数3:日志文件路径(默认使用现有日志组的路径)
使用示例:
  1. @db_add_logfile.sql 2 8 +DATA_01
复制代码
脚本特点


  • 智能路径处理

    • 自动识别ASM和文件系统路径
    • ASM环境下自动检查磁盘组是否存在
    • 支持Windows和Linux文件系统路径格式

  • 安全性保障

    • 添加新日志组前检查路径有效性
    • 删除旧日志组时自动检查状态
    • 包含完善的错误处理机制

  • 兼容性

    • 支持Oracle RAC环境
    • 支持归档和非归档模式
    • 自动处理多线程情况

实现原理


  • 路径类型判断
  1. WHEN REGEXP_REPLACE (MEMBER, '[^\+]', '') = '+' THEN 'ASM'
  2. WHEN SUBSTR (x.MEMBER, 1, REGEXP_INSTR (x.MEMBER, '[/]', 1, 2) - 1) = '/dev' THEN '/dev'
  3. ELSE 'FS'
复制代码

  • 日志组状态处理
  1. WHILE i_group_status IN ('ACTIVE', 'CURRENT') LOOP
  2.     EXECUTE IMMEDIATE i_sql_arch2;
  3.     SELECT status INTO i_group_status FROM v$log
  4.     WHERE group# = i_delete_group.group#;
  5.     IF i_group_status != 'INACTIVE' THEN
  6.         DBMS_LOCK.SLEEP (2);
  7.     END IF;
  8. END LOOP;
复制代码
完整的代码

当前发布时的版本如下,如要获得最新版本,请公众号回复db_add_logfile,或者直接连接我。
  1. /* Formatted on 2025/6/9 23:00:19 (QP5 v5.300) */
  2. -- File Name : db_add_logfile.sql
  3. -- Purpose : Add redo log file groups to the database, specify log group number,
  4. --           specify log file size, auto delete log files smaller than specified size
  5. --           @db_add_logfile.sql (press enter)
  6. --           @db_add_logfile.sql 2 8 +DATA_01
  7. -- Date : 2016/05/19
  8. -- weixin:18081072613
  9. -- http://www.htz.pw
  10. -- 20161129 Added path input, if path is not specified, use the path of existing log groups.
  11. -- 20161129 Added support for non-archive mode and RAC, in non-archive mode, only display message for current REDO.
  12. -- 20250609 Enhanced functionality, added error handling
  13. -- 20250610 重构整个代码,并修复asm磁盘组路径传入忽略+的导致的报错,增加磁盘组是否存在的检查
  14. SET LINES 200
  15. SET PAGES 1000 HEADING ON VERIFY OFF SERVEROUTPUT ON
  16. PRO
  17. PRO Parameter 1:
  18. PRO log file size (default 2g)
  19. PRO
  20. PRO Parameter 2:
  21. PRO total group number by thread  (default 7)
  22. PRO
  23. PRO Parameter 3:
  24. PRO log file path name(for +DG_DATA)  (default old path name)
  25. PRO
  26. PRO
  27. DECLARE
  28.     path_type         VARCHAR2 (200);
  29.     path_name         VARCHAR2 (200);
  30.     i_group_current   NUMBER;
  31.     i_logfile_size    NUMBER;
  32.     i_group_number    NUMBER;
  33.     i_log_number      NUMBER;
  34.     i_group_name      NUMBER;
  35.     i_instance_id     NUMBER;
  36.     i_sql             VARCHAR2 (200);
  37.     i_sql_arch        VARCHAR2 (200) := 'alter system switch logfile';
  38.     i_sql_arch2       VARCHAR2 (200) := 'alter system archive log current';
  39.     i_group_status    VARCHAR2 (200);
  40.     i_logfile_name    VARCHAR2 (200);
  41.     i_log_mode        VARCHAR2 (200);
  42. BEGIN
  43.     DECLARE
  44.         PROCEDURE drop_logfile_group (p_group_no IN NUMBER)
  45.         IS
  46.             v_sql   VARCHAR2 (200);
  47.         BEGIN
  48.             v_sql := 'alter database drop logfile group ' || p_group_no;
  49.             DBMS_OUTPUT.put_line (v_sql);
  50.             EXECUTE IMMEDIATE v_sql;
  51.         EXCEPTION
  52.             WHEN OTHERS
  53.             THEN
  54.                 DBMS_OUTPUT.put_line (
  55.                        'Warning: Unknown error occurred when dropping log group '
  56.                     || p_group_no
  57.                     || ': '
  58.                     || SQLERRM);
  59.                 DBMS_OUTPUT.put_line ('Continue processing next log group...');
  60.         END;
  61.     BEGIN
  62.         SELECT TO_NUMBER (NVL ('&1', 2) * 1024 * 1024 * 1024)
  63.           INTO i_logfile_size
  64.           FROM DUAL;
  65.         SELECT TO_NUMBER (NVL ('&2', 6)) INTO i_group_number FROM DUAL;
  66.         SELECT NVL ('&3', 'DEFAULT') INTO path_name FROM DUAL;
  67.         SELECT log_mode INTO i_log_mode FROM v$database;
  68.         SELECT instance_number INTO i_instance_id FROM v$instance;
  69.         IF path_name = 'DEFAULT'
  70.         THEN
  71.             SELECT CASE
  72.                        WHEN REGEXP_REPLACE (MEMBER, '[^\+]', '') = '+'
  73.                        THEN
  74.                            'ASM'
  75.                        WHEN SUBSTR (x.MEMBER,
  76.                                     1,
  77.                                       REGEXP_INSTR (x.MEMBER,
  78.                                                     '[/]',
  79.                                                     1,
  80.                                                     2)
  81.                                     - 1) = '/dev'
  82.                        THEN
  83.                            '/dev'
  84.                        ELSE
  85.                            'FS'
  86.                    END
  87.                        AS path_type,
  88.                    CASE
  89.                        WHEN REGEXP_REPLACE (MEMBER, '[^\+]', '') = '+'
  90.                        THEN
  91.                            SUBSTR (x.MEMBER,
  92.                                    1,
  93.                                      REGEXP_INSTR (x.MEMBER,
  94.                                                    '[/]',
  95.                                                    1,
  96.                                                    1)
  97.                                    - 1)
  98.                        ELSE
  99.                            SUBSTR (
  100.                                x.MEMBER,
  101.                                1,
  102.                                REGEXP_INSTR (x.MEMBER,
  103.                                              '[/]',
  104.                                              1,
  105.                                              REGEXP_COUNT (x.MEMBER, '[/]')))
  106.                    END
  107.                        AS name
  108.               INTO path_type, path_name
  109.               FROM v$logfile x
  110.              WHERE ROWNUM = 1;
  111.         ELSE
  112.             SELECT CASE
  113.                        WHEN REGEXP_REPLACE (path_name, '[^\+]', '') = '+'
  114.                        THEN
  115.                            'ASM'
  116.                        WHEN SUBSTR (path_name,
  117.                                     1,
  118.                                       REGEXP_INSTR (path_name,
  119.                                                     '[/]',
  120.                                                     1,
  121.                                                     2)
  122.                                     - 1) = '/dev'
  123.                        THEN
  124.                            '/dev'
  125.                        WHEN (REGEXP_LIKE(path_name, '^[A-Za-z]:\\.*$') OR
  126.                             REGEXP_LIKE(path_name, '^/.*$'))              
  127.                        THEN
  128.                            'FS'
  129.                        ELSE
  130.                            'ERROR'
  131.                    END
  132.                        AS path_type
  133.               INTO path_type
  134.               FROM DUAL x
  135.              WHERE ROWNUM = 1;
  136.         END IF;
  137.         IF path_type = 'ERROR' THEN
  138.             DBMS_OUTPUT.put_line('Error: The specified filesystem path does not exist or has invalid format');
  139.             RETURN;
  140.         END IF;
  141.         -- 如果是ASM路径,检查磁盘组是否存在
  142.         IF path_type = 'ASM' THEN
  143.             DECLARE
  144.                 v_diskgroup_exists NUMBER;
  145.                 v_diskgroup_name   VARCHAR2(30);
  146.             BEGIN
  147.                 -- 提取磁盘组名称 (去掉+号)
  148.                 v_diskgroup_name := SUBSTR(path_name, 2, INSTR(path_name, '/') - 2);
  149.                
  150.                 -- 检查磁盘组是否存在
  151.                 SELECT COUNT(*)
  152.                 INTO v_diskgroup_exists
  153.                 FROM v$asm_diskgroup
  154.                 WHERE name = v_diskgroup_name;
  155.                
  156.                 IF v_diskgroup_exists = 0 THEN
  157.                     DBMS_OUTPUT.put_line('Error: ASM diskgroup ' || v_diskgroup_name || ' does not exist');
  158.                     RETURN;
  159.                 END IF;
  160.             END;
  161.         END IF;
  162.          
  163.         SELECT (MAX (group#) + 1)
  164.           INTO i_group_name
  165.           FROM v$logfile;
  166.         FOR c_thread IN (SELECT DISTINCT thread#
  167.                            FROM v$log)
  168.         LOOP
  169.             SELECT COUNT (*)
  170.               INTO i_group_current
  171.               FROM v$log
  172.              WHERE thread# = c_thread.thread# AND bytes >= i_logfile_size;
  173.             WHILE i_group_current < i_group_number
  174.             LOOP
  175.                 IF path_type = 'ASM'
  176.                 THEN
  177.                     i_sql :=
  178.                            'alter database add  logfile thread '
  179.                         || c_thread.thread#
  180.                         || ' '
  181.                         || CHR (39)
  182.                         || path_name
  183.                         || CHR (39)
  184.                         || ' size '
  185.                         || i_logfile_size;
  186.                 ELSIF path_type = 'FS'
  187.                 THEN
  188.                     i_logfile_name :=
  189.                         path_name || 'redo' || i_group_name || '.log';
  190.                     SELECT COUNT (*)
  191.                       INTO i_log_number
  192.                       FROM v$logfile
  193.                      WHERE MEMBER = i_logfile_name;
  194.                     WHILE i_log_number > 0
  195.                     LOOP
  196.                         i_group_name := i_group_name + 1;
  197.                         i_logfile_name :=
  198.                             path_name || 'redo' || i_group_name || '.log';
  199.                         SELECT COUNT (*)
  200.                           INTO i_log_number
  201.                           FROM v$logfile
  202.                          WHERE MEMBER = i_logfile_name;
  203.                     END LOOP;
  204.                     i_sql :=
  205.                            'alter database add  logfile thread '
  206.                         || c_thread.thread#
  207.                         || ' '
  208.                         || CHR (39)
  209.                         || path_name
  210.                         || 'redo'
  211.                         || i_group_name
  212.                         || '.log'
  213.                         || CHR (39)
  214.                         || ' size '
  215.                         || i_logfile_size;
  216.                 END IF;
  217.                 DBMS_OUTPUT.put_line (i_sql);
  218.                 EXECUTE IMMEDIATE i_sql;
  219.                 i_group_current := i_group_current + 1;
  220.                 i_group_name := i_group_name + 1;
  221.             END LOOP;
  222.             FOR i_delete_group
  223.                 IN (SELECT group#, THREAD#
  224.                       FROM v$log
  225.                      WHERE     thread# = c_thread.thread#
  226.                            AND BYTES < i_logfile_size)
  227.             LOOP
  228.                 SELECT status
  229.                   INTO i_group_status
  230.                   FROM v$log
  231.                  WHERE group# = i_delete_group.group#;
  232.                 IF i_log_mode = 'ARCHIVELOG'
  233.                 THEN
  234.                     WHILE i_group_status IN ('ACTIVE', 'CURRENT')
  235.                     LOOP
  236.                         EXECUTE IMMEDIATE i_sql_arch2;
  237.                         SELECT status
  238.                           INTO i_group_status
  239.                           FROM v$log
  240.                          WHERE group# = i_delete_group.group#;
  241.                         IF i_group_status != 'INACTIVE'
  242.                         THEN
  243.                             DBMS_LOCK.SLEEP (2);
  244.                         END IF;
  245.                     END LOOP;
  246.                     i_sql :=
  247.                            'alter database drop logfile group '
  248.                         || i_delete_group.group#;
  249.                     DBMS_OUTPUT.put_line (i_sql);
  250.                     EXECUTE IMMEDIATE i_sql;
  251.                 ELSE
  252.                     IF i_instance_id = c_thread.thread#
  253.                     THEN
  254.                         WHILE i_group_status IN ('ACTIVE', 'CURRENT')
  255.                         LOOP
  256.                             EXECUTE IMMEDIATE i_sql_arch;
  257.                             SELECT status
  258.                               INTO i_group_status
  259.                               FROM v$log
  260.                              WHERE group# = i_delete_group.group#;
  261.                             IF i_group_status != 'INACTIVE'
  262.                             THEN
  263.                                 DBMS_LOCK.SLEEP (2);
  264.                             END IF;
  265.                         END LOOP;
  266.                         drop_logfile_group (i_delete_group.group#);
  267.                     ELSE
  268.                         IF i_group_status IN ('ACTIVE', 'CURRENT')
  269.                         THEN
  270.                             DBMS_OUTPUT.put_line (
  271.                                    'manual exec : alter database drop logfile group '
  272.                                 || i_delete_group.group#
  273.                                 || ' on '
  274.                                 || c_thread.thread#);
  275.                         ELSE
  276.                             drop_logfile_group (i_delete_group.group#);
  277.                         END IF;
  278.                     END IF;
  279.                 END IF;
  280.             END LOOP;
  281.         END LOOP;
  282.     END;
  283. END;
  284. /
复制代码
使用注意事项


  • 执行脚本前需要具备足够的权限
  • 建议在业务低峰期执行
  • ASM环境下需要确保磁盘组有足够空间
  • RAC环境中注意观察所有节点的日志切换情况
更新历史


  • 2025/06/10:重构代码,修复ASM磁盘组路径问题,增加磁盘组存在性检查
  • 2025/06/09:增强功能,添加错误处理
  • 2016/11/29:增加路径输入支持,增加非归档模式和RAC支持
  • 2016/05/19:初始版本发布
总结

这个脚本工具可以帮助DBA快速调整Oracle数据库的重做日志配置,提高数据库的可维护性和性能。它的智能判断和安全机制可以让DBA放心使用,是一个值得收藏的运维工具。
以上就是这个Oracle DBA工具的详细介绍。如果您在使用过程中遇到任何问题,欢迎留言交流!
------------------作者介绍-----------------------
姓名:黄廷忠
现就职:Oracle中国高级服务团队
曾就职:OceanBase、云和恩墨、东方龙马等
电话、微信、QQ:18081072613
1.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册