در این متن بررسی می کنیم که غیرفعال کردن Logging در سطوح object، tablespace و database چه اثراتی را بر روی عملیات ساخت و بروزرسانی Materialized Viewها به همراه خواهد داشت(مطالعه مطلب “تاثیر عملیات NOLOGGING در دیتاگارد” پیشنهاد می شود).
قبل از ورود به بحث اصلی این متن، مثالی را از نحوه تاثیر غیرفعال کردن logging برای یک جدول را مشاهده خواهید کرد.
در مثال زیر می بینید که با غیرفعال کردن logging برای جدول T1 و tablespaceای که این جدول در آن قرار دارد و همچنین دیتابیس! دو حالت زیر قابل تصور است:
SQL> select force_logging from v$database; FORCE_LOGGING -------------- NO SQL> select t.logging,t.force_logging from dba_tablespaces t where t.tablespace_name='TBS1'; LOGGING FORCE_LOGGING --------- ------------- NOLOGGING NO SQL> select logging from user_tables p where table_name='T1'; LOGGING ------- NO
*درج به صورت conventional insert که سبب ایجاد redo خواهد شد:
SQL> insert into t1 select * from sys.source$; 13756 rows inserted SQL> SELECT s.name, m.value FROM v$mystat m, v$statname s WHERE m.statistic# = s.statistic# AND s.name ='redo size'; NAME VALUE ----------- ---------- redo size 2,493,140
*درج بصورت Direct-Path INSERT که در شرایط ذکر شده مانع از ایجاد redo خواهد شد:
SQL> insert /*+append*/ into t1 select * from sys.source$; 13756 rows inserted SQL> SELECT s.name, m.value FROM v$mystat m, v$statname s WHERE m.statistic# = s.statistic# AND s.name ='redo size'; NAME VALUE ---------- ---------- redo size 1,676
همانطور که می دانید ما به ازای هر Materialized View یک جدول هم ایجاد می شود و در هنگام ساخت Materialized View و یا حتی بعد از ایجاد آن، می توان در مورد خصیصه logging این جدول اعمال نظر کرد:
SQL> create materialized view mv_nolog 2 NOLOGGING 3 BUILD IMMEDIATE 4 REFRESH COMPLETE 5 ON DEMAND 6 AS 7 select a.* from tbl1 a, tbl2 b where a.obj#=b.obj#; Materialized view created
SQL> select logging,tablespace_name from user_tables p where table_name='MV_NOLOG'; LOGGING TABLESPACE_NAME ------- --------------- NO TBS1
SQL> alter table MV_NOLOG logging; Table altered
SQL> select logging,tablespace_name from user_tables p where table_name='MV_NOLOG'; LOGGING TABLESPACE_NAME ------- --------------- YES TBS1
نکته مهم در این زمینه آن است که خصیصه logging در زمان ایجاد materialized view در حجم redo ایجاد شده و زمان ساخت آن اثرگذار خواهد بود:
SQL> create materialized view mv_nolog 2 LOGGING 3 tablespace tbs1 4 BUILD IMMEDIATE 5 REFRESH COMPLETE 6 ON DEMAND 7 AS 8 select a.* from tbl1 a, tbl2 b where a.obj#=b.obj#; Materialized view created Executed in 18.996 seconds
SQL> SELECT s.name, m.value FROM v$mystat m, v$statname s WHERE m.statistic# = s.statistic# AND s.name ='redo size'; NAME VALUE ----------- ---------- redo size 1,111,003,940
حجم archivelog ایجاد شده برای mv_nolog، برابر با 1.1GB خواهد بود:
[root@stb ~]# cd /oracle/arch [root@stb arch]# du -sh . 1.1G
در صورت استفاده از عبارت nologging در زمان ساخت MV و همچنین قرار دادن tablespace در حالت nologging، مسئله متفاوت خواهد بود:
SQL> select force_logging from v$database; FORCE_LOGGING --------------------------------------- NO SQL> alter tablespace tbs1 nologging; Tablespace altered SQL> select t.logging,t.force_logging from dba_tablespaces t where t.tablespace_name='TBS1'; LOGGING FORCE_LOGGING --------- ------------- NOLOGGING NO
SQL> create materialized view mv_nolog 2 NOLOGGING 3 BUILD IMMEDIATE 4 REFRESH COMPLETE 5 ON DEMAND 6 AS 7 select a.* from tbl1 a, tbl2 b where a.obj#=b.obj#; Materialized view created Executed in 8.763 seconds
SQL> SELECT s.name, m.value FROM v$mystat m, v$statname s WHERE m.statistic# = s.statistic# AND s.name ='redo size'; NAME VALUE ---------- ---------- redo size 687,548
همانطور که می بینید، علاوه بر کاهش حجم redo ایجاد شده، سرعت ایجاد mv هم بسیار افزایش پیدا کرده است.
در زمان بروزرسانی به روش معمول، خصیصه nologging در هیچ سطحی در نظر گرفته نمی شود:
SQL> select force_logging from v$database; NO SQL> select logging from user_tables p where table_name=’MV_NOLOG’; NO select t.logging,t.force_logging from dba_tablespaces t where t.tablespace_name=’TBS1’ NOLOGGING NO
SQL> exec DBMS_MVIEW.REFRESH(LIST => ‘MV_NOLOG’,METHOD => ‘C’); PL/SQL procedure successfully completed Executed in 97.135 seconds
SQL> SELECT s.name, m.value FROM v$mystat m, v$statname s WHERE m.statistic# = s.statistic# AND s.name =’redo size’; NAME VALUE ----------- ---------- redo size 2,199,307,708
همانطور که می بینید، حجم redo ایجاد شده، حدودا دوبرابر شده است، چرا که عملیات بروزرسانی علاوه بر دستور insert، شامل دستور delete هم می باشد.
البته بروزرسانی به روش non-atomic refresh سبب استفاده از truncate به جای delete خواهد شد و از طرفی دیگر، در صورت تنظیم خصیصه nologging از Direct-Path INSERT استفاده خواهد شد که در نتیجه کاهش حجم redo ایجاد شده را در پی خواهد داشت:
SQL> / FORCE_LOGGING_DATABASE LOGGING_TBS LOGGING_MV ------------------------- ----------- ---------- NO NOLOGGING NO
SQL> EXEC DBMS_MVIEW.REFRESH('MV_NOLOG', method => 'C', atomic_refresh => FALSE);
PL/SQL procedure successfully completed
Executed in 11.12 seconds
SQL> SELECT s.name, m.value FROM v$mystat m, v$statname s WHERE m.statistic# = s.statistic# AND s.name ='redo size'; NAME VALUE ---------- ---------- redo size 1,043,244
حجم redo ایجاد شده از 2,199,307,708 به 1,043,244 رسیده است.
ایراد این روش در آن است که MV به صورت موقت از دسترسی خارج خواهد شد.
به عنوان گزینه دیگر می توان از بروزرسانی به روش out of place استفاده کرد استفاده از این روش سبب خواهد شد تا جدول جدیدی برای mv ایجاد شود و پس از ایجاد جدول جدید، جدول قدیمی متعلق به mv حذف شود. در شرایطی که خصیصه nologging تنظیم شده باشد، ایجاد redo به حداقل ممکن خواهد رسید:
SQL> / FORCE_LOGGING_DATABASE LOGGING_TBS LOGGING_MV ------------------------- ----------- ---------- NO NOLOGGING NO
SQL> EXEC DBMS_MVIEW.REFRESH('MV_NOLOG', method => 'C', atomic_refresh => FALSE, out_of_place => TRUE);
PL/SQL procedure successfully completed
Executed in 13.447 seconds
SQL> SELECT s.name, m.value FROM v$mystat m, v$statname s WHERE m.statistic# = s.statistic# AND s.name ='redo size'; NAME VALUE ------------ ---------- redo size 939,044
البته پارامتر out_of_place محدودیتهای زیادی را هم به همراه دارد که قبل از برنامه ریزی برای استفاده از آن، بهتر است از محدودیتهای آن مطلع باشیم.
توجه: در روش out of place باید به اندازه دو mv فضای آزاد موجود باشد.
]]>برای مثال در قسمت زیر، کاربر usef مجوز select on ali.tbl1 را به کاربر vahid اهدا می کند:
SQL> create user usef identified by a; User created. SQL> create user vahid identified by a; User created. SQL> grant create session to vahid,usef; Grant succeeded.
SQL> grant select on ali.tbl1 to usef with grant option; Grant succeeded. SQL> co usef/a Coected. SQL> show user User is "USEF" SQL> grant select on ali.tbl1 to vahid; Grant succeeded.
SQL> co ali/a Coected. SQL> select grantee, grantor,privilege, table_name from user_tab_privs_made where GRANTEE='VAHID'; GRANTEE GRANTOR PRIVILEGE TABLE_NAME ----------- ---------- ---------- ---------- VAHID USEF SELECT TBL1
همانطور که می بینید، با اجرای پرس و جوی فوق توسط کاربر ali، خواهیم دید که مجوز select on ali.tbl1 توسط کاربر usef به کاربر VAHID اختصاص داده شده است. البته با اجرای ویوی USER_TAB_PRIVS_RECD هم به این نتیجه خواهیم رسید:
SQL> co vahid/a Coected. SQL> select owner,table_name,grantor,privilege,type from USER_TAB_PRIVS_RECD; OWNER TABLE_NAME GRANTOR PRIVILEGE TYPE ---------- ---------- ---------- ---------- ------------ ALI TBL1 USEF SELECT TABLE
در این شرایط با حذف کاربر usef، دسترسی داده شده به کاربر vahid از بین خواهد رفت و به تبع آن، این دو ویو هم اطلاعاتی را در این زمینه بر نمی گردانند:
SQL> drop user usef; User dropped. SQL> co ali/a Coected. SQL> select owner,table_name,grantor,privilege,type from USER_TAB_PRIVS_RECD; no rows selected SQL> select * from ALI.TBL1; ORA-00942: table or view does not exist
با اندکی تغییر در سناریوی قبلی، نقش dba را به کاربر usef اختصاص می دهیم و بعد از ان کاربر usef، دسترسی select on ali.tbl1 را به کاربر vahid اهدا می کند، با این تغییر، دو ویوی فوق، کاربر ALI را به عنوان grantor معرفی خواهند کرد:
SQL> create user usef identified by a; User created. SQL> grant dba to usef; Grant succeeded.
SQL> co usef/a Coected. SQL> grant select on ali.tbl1 to vahid; Grant succeeded.
SQL> co vahid/a Coected. SQL> select owner,table_name,grantor,privilege,type from USER_TAB_PRIVS_RECD; OWNER TABLE_NAME GRANTOR PRIVILEGE TYPE ---------- ---------- ---------- ---------- ------------------------ ALI TBL1 ALI SELECT TABLE SQL> co ali/a Coected. SQL> select grantee, grantor, table_name from user_tab_privs_made where GRANTEE='VAHID'; GRANTEE GRANTOR TABLE_NAME ----------- ---------- ---------- VAHID ALI TBL1]]>
بازسازی این فایلها از طریق rescue mode قابل انجام است که در این متن قصد داریم نحوه انجام آن را توضیح دهیم.

برای پیش بردن این سناریو، در قدم اول، محتویات boot/ را حذف می کنیم:

با اجرای این دستور، سیستم عامل می تواند به کارش ادامه دهد ولی با اولین reboot امکان استارت را نخواهد داشت و خط فرمان grub rescue را نمایش خواهد داد:

در ادامه با کمک فایل ISO که سیستم عامل را از طریق آن نصب کرده ایم، فایلهای مربوط به bootloader و kernel را بازسازی می کنیم.
بعد از mount کردن ISO و استارت مجدد، گزینه Troubleshooting را انتخاب می کنیم:

در گام بعدی Rescue a Oracle Linux system را انتخاب می کنیم.

در این مرحله عدد 1را وارد می کنیم تا file system به صورت خودکار شناسایی و mount شود.

با استفاده از دستور chroot مسیر root را به mnt/sysroot/ تغییر داده و پس از آن با کمک دستور lsblk مشخص خواهیم کرد که boot/ به کدام دیسک mount شده است:

همانطور که می بینید، boot/ به dev/sda1/ء mount شده و اطلاعاتی در آن موجود نیست.
در قدم بعدی برای ایجاد فایلهای بوت لودر grub2، دستور grub2-install را اجرا کرده و فایل boot/grub2/grub.cfg/ را هم با اجرای دستور grub2-mkconfig ایجاد می کنیم:

با اجرای این دو دستور، صرفا دایرکتوری grub2 در مسیر boot/ ایجاد خواهد شد و خبری از vmlinuz و initrd در این مسیر نیست:

برای ساخت vmlinuz و Initrd، می توانیم بسته kernel-core را reinstall کنیم:


با اجرای این دستور، vmlinuz و initrd در مسیر boot/ ایجاد می شوند:

با خارج شدن از حالتRescue خواهیم که کرنل بدرستی نصب شده است:


سیستم عامل بدون خطا استارت شده است:


]]>
برچسب:
نویسنده: خنجی