همانطور که می دانید با رخ دادن checkpoint در اوراکل، dirty bufferهای موجود در بافرکش توسط پروسس DBWR از حافظه به دیسک نوشته خواهند شد و همچنین هدر دیتافایل و کنترل فایل توسط پروسس CKPT بروز خواهد شد. به عبارتی دیگر، در زمان checkpoint دو اتفاق زیر و یا یکی از این دو رخ خواهد داد:
1.اطلاعات dirty buffer موجود در حافظه بر روی دیسک نوشته خواهد شد.
2.هدر دیتافایل بروز خواهد شد و همچنین این بروزرسانی در کنترل فایل هم رخ خواهد داد.
انجام مکرر این عملیات در هر checkpointای، ان هم در سطح کل بانک، می تواند بسیار پرهزینه و غیر منطقی باشد به همین دلیل، متناسب با زمان و عمل انجام شده، سطح و نوع checkpoint هم متفاوت خواهد بود. به طور کلی، checkpoint را می توان به دو دسته تقسیم کرد:
COMPLETE: در این حالت تمامی عملیات مربوط به checkpoint به صورت کامل انجام خواهد شد منتها ممکن است برای همه کامپوننتهای بانک انجام شود که به ان FULL checkpoint گفته می شود و یا برای قسمتی از این کامپوننتها، که به ان PARTIAL checkpoint می گویند.
INCREMENTAL: در این نوع از checkpoint تنها قسمتی از عملیات checkpoint ان هم برای کاهش حجم dirty buffer موجود در حافظه انجام خواهد شد با این نوع از checkpoint، پروسس CKPT نقطه فعلی checkpoint را در کنترل فایل ثبت خواهد کرد و تغییری در هدر دیتافایل اعمال نخواهد شد.
در ادامه سعی داریم با جزییات بیشتری به این سوال پاسخ دهیم که چه زمانی و در چه سطحی checkpoint رخ می دهد. در زیر اسامی انوع checkpoint را می بینید:
- Full checkpoint
- Tablespace Checkpoin
- Object level checkpoint
- log switch checkpoint
- Thread checkpoint
- direct path read
- Incremental Checkpoint
در ادامه هر کدام از مدلهای checkpoint را مورد بررسی قرار خواهیم داد.
1. Full checkpoint: در این نوع از checkpoint، dirty buffer به صورت کامل و در سطح تمامی instanceها به دیسک منتقل خواهد شد و همچنین در کنار بروزرسانی هدر دیتافایل با اخرین شماره scn، اطلاعات کنترل فایل هم بروز خواهد شد. هر کدام از عملیاتهای زیر، سبب رخ دادن full checkpoint خواهند شد:
- Alter system checkpoint global
- Alter database begin backup
- Alter database close
- Shutdown Immediate/Transactional
مثال: در این مثال خواهید دید که چگونه full checkpoint سبب انتقال dirty buffer بر روی دیسک و همچنین بروزرسانی دیتافایل و کنترل فایل خواهد شد.
در ابتدا شماره فعلی هدر دیتافایل و کنترل فایل را با دستور زیر تعیین می کنیم:
select checkpoint_change#, ‘SYSTEM checkpoint in controlfile’ “SCN location” from v$database
union select distinct checkpoint_change#, ‘file in controlfile’ from v$datafile
union select distinct checkpoint_change#, ‘file header’ from v$datafile_header;
CHECKPOINT_CHANGE# |
SCN location |
14481931 | SYSTEM checkpoint in controlfile |
14481931 | file header |
14481931 | file in controlfile |
در ادامه tablespace مجزایی را برای انجام این سناریو ایجاد می کنیم:
SQL> create tablespace tbs ;
Tablespace created.
سپس با ایجاد یک جدول، اطلاعاتی را در آن درج می کنیم تا بلاک مشخصی را به عنوانdirty در حافظه داشته باشیم:
SQL> create table tb(name varchar2(10)) tablespace tbs ;
Table created.
SQL> insert into tb values(‘usef_v’);
1 row created.
با رجوع به دیتافایل مربوط به این جدول خواهیم یافت که هنوز رکورد درج شده در این جدول، به دیسک منتقل نشده است:
[root@db_host ~]# strings /oracle/oradata/DB/datafile/o1_mf_tbs_fbdgrojd_.dbf|grep usef_v
[root@db_host ~]#
با دستور زیر، تعداد بلاکهای dirty مربوط به این جدول قابل تعیین می باشد:
select count(*) from v$bh l where dirty=’Y’ and objd=(select l.object_id from dba_objects l where l.object_name=’TB’);
1
همچنین دستور زیر نشان میدهد که مجموعا 235، dirty buffer در حافظه وجود دارد:
select count(*) from v$bh l where l.DIRTY=’Y’;
235
در همین وضعیت، دستور زیر را اجرا می کنیم تا یکبار full checkpoint انجام شود:
SQL> alter system checkpoint global;
System altered.
با اجرای این دستور، بلاک به دیسک منتقل خواهد شد:
[root@db_host ~]# strings /oracle/oradata/DB/datafile/o1_mf_tbs_fbdgrojd_.dbf|grep usef_v
usef_v
[root@db_host ~]#
و هیچ dirty bufferای در حافظه نخواهیم داشت:
select count(*) from v$bh l where l.DIRTY=’Y’;
0
همچنین شماره هدر دیتافایل و کنترل فایل هم بروز خواهد شد:
CHECKPOINT_CHANGE# | SCN location |
14483824 | SYSTEM checkpoint in controlfile |
14483824 | file header |
14483824 | file in controlfile |
با رجوع به alert log، خواهیم یافت که بلافاصله بعد از شروع checkpoint و با نمایش پیامBeginning global checkpoint با اندک فاصله زمانی، پیام Completed checkpoint هم در alert log دیده خواهد شد و شماره scnای که قرار است بر روی هدر دیتافایل اعمال شود هم در این پیام قابل رویت می باشد:
2018-03-12T12:16:32.585760+03:30
Beginning global checkpoint up to RBA [0x71.3bd69.10], SCN: 14483824
Completed checkpoint up to RBA [0x71.3bd69.10], SCN: 14483824
مشاهده توامان این دو پیام، یکی از ویژگی های full checkpoint می باشد با این توضیح که اگر این نوع از checkpoint تا زمانی که به اتمام نرسیده باشد، به عملیات(در مثال ما همان دستورalter system checkpoint) اجازه نخواهد داد که به کارشان خاتمه دهند در صورتی که در انواع دیگر checkpoint، نظیر log switch checkpoint(که بعدا مفصل مورد بررسی قرار می گیرد)، بلافاصله بعد از اجرای دستور، پیام begin checkpoint قابل رویت می باشد ولی برای پایان یافتن دستور، الزامی به پایان یافتن checkpoint و همچنین مشاهده پیام Completed checkpoint وجود ندارد(البته در مورد استثناها بعدا مطالبی را خواهیم اورد).
نکته 1: اطلاعاتی از قبیل شماره scn ثبت شده در هدر دیتافایلها و نیز checkpoint position در کنترل فایل ذخیره می شود که با استفاده از ویوی v$datafile قابل رویت می باشد(اطلاعات کنترل فایل از شماره هدر دیتافایل).
نکته 2: شماره جاری scn از طریق دستور زیر قابل دستیابی می باشد:
select current_scn from v$database;
نکته 3: برای مشاهده اطلاعات کامل تر در مورد full checkpoint فعلی بانک، می توان از جدول سیستمی X$KCCRT استفاده کرد.
2.Tablespace Checkpoin: اجرای هر کدام از دستورات زیر بر روی یک tablespace، سبب بروز شدن هدر دیتافایلهای مربوط به ان tablespace خواهد شد و همچنین تمامی dirty bufferهای مربوط به فایلهای این tablespace به دیسک منتقل خواهند شد.
- Alter tablespace … offline
- Alter tablespace … begin backup
- Alter tablespace … read only
- Alter tablespace … read write
- Alter database datafile resize
مثال: وضعیت جاری هدر دیتافایلهای tablespace tbs به صورت زیر می باشد:
select file#, checkpoint_change#,TS# from V$datafile_header;
FILE# | CHECKPOINT_CHANGE# | TS# |
1 | 14483824 | 0 |
3 | 14483824 | 1 |
4 | 14483824 | 2 |
5 | 14483824 | 6 |
7 | 14483824 | 4 |
همچنین یک dirty buffer از دیتافایلهای این tablespace در بافرکش موجود است:
select l.FILE#,l.BLOCK#,l.DIRTY,l.OBJD from v$bh l where dirty=’Y’ and file#=5;
FILE# | BLOCK# | DIRTY | OBJD |
5 | 135 | Y | 75531 |
در همین وضعیت دستور زیر اجرا می کنیم:
SQL> alter tablespace tbs read only;
Tablespace altered.
با اجرای این دستور، دیتافایل مربوط به این tablespace دیگر بلاک اصلاح شده ای در بافرکش نخواهد داشت:
select l.FILE#,l.BLOCK#,l.DIRTY,l.OBJD from v$bh l where dirty=’Y’ and file#=5;
no rows selected
همچنین با اجرای این نوع از checkpoint، تنها هدر دیتافایلهای مربوط به این tablespace تغییر خواهد کرد:
FILE# | CHECKPOINT_CHANGE# | TS# |
1 | 14483824 | 0 |
3 | 14483824 | 1 |
4 | 14483824 | 2 |
5 | 14485915 | 6 |
7 | 14483824 | 4 |
نکته: استفاده از دستور alter database datafile .. resize تنها زمانی باعث checkpoint در سطح یک دیتافایل خواهد شد که اندازه ان دیتافایل کاهش یابد.
3. Object level checkpoint: با انجام یکی از عملیاتهای زیر بر روی یک شی، تمامی dirty bufferهای مربوط به ان شی، به دیسک منتقل خواهند شد.
- Drop table
- Drop table … purge
- Truncate table
- Drop Index
مثال: بلاکی از جدول tb به صورت dirty در بافرکش موجود است:
insert into tb values(‘usef’);
1 row created.
select l.FILE#,l.BLOCK#,l.DIRTY,l.OBJD from v$bh l where dirty=’Y’ and objd=(select l.object_id from dba_objects l where l.object_name=’TB’);
FILE# | BLOCK# | DIRTY | OBJD |
5 | 135 | Y | 73641 |
در همین حال جدول tb را حذف می کنیم:
SQL> drop table tb;
Table dropped.
با رجوع به بافرکش، دیگر خبری از ان dirty buffer نخواهد بود:
select l.FILE#,l.BLOCK#,l.DIRTY,l.OBJD from v$bh l where dirty=’Y’ and objd=(select l.object_id from dba_objects l where l.object_name=’TB’);
no rows selected
همچنین این بلاک اصلاح شده، به دیسک منتقل شده است:
[root@db_host ~]#strings /oracle/oradata/USEFDB/651203D2E479482DE0530688200A3BBD/datafile/o1_mf_tbs_f9o0h203_.dbf|grep usef
usef
شاید یکی از دلایل رخ دادن checkpoint در این سطح، جلوگیری از ثبت و تداخل بلاک dirty شی حذف شده بر بلاکهای شی ای که جدیدا ایجاد شده اند، باشد البته شاید!!!
4. log switch checkpoint: این نوع از checkpoint در زمان log switch رخ خواهد داد و ان قسمتی از بافرکش و dirty bufferهایی را که اطلاعات dmlای ان در redo log موجود است(redo logای که قرار است بازنویسی شود)، را به دیسک منتقل خواهد کرد! بعبارتی دیگر، به لحاظ امنیتی! قبل از بازنویسی یک redo log، باید اطمینان حاصل شود که dirty bufferهای مربوط به اطلاعات dmlای موجود در این redo log از حافظه به دیسک منتقل شده اند.
“بعد از هر log switchای، checkpointای هم رخ خواهد داد” این جمله درست است اما ملاحضاتی هم دارد. همانطور که قبلا اورده شد، در checkpoint از نوع FULL، قبل از اجرای کامل دستور، باید checkpoint هم به صورت کامل انجام شود(Completed checkpoint) در صورتی که اجرای log switch سبب استارت checkpoint خواهد شد(Beginning log switch checkpoint) ولی منتظر پایان یافتن ان نخواهد ماند(Completed checkpoint) البته به صورت استثنا، زمانی که قرار است بر روی redo logای بازنویسی صورت پذیرد کهdirty buffer مربوط به اطلاعات dmlای ان هنوز در حافظه موجود است، باید checkpoint به صورت کامل اجرا شود تا به دنبال ان، اجرای log switch هم کامل شود.
مثال: فرض کنید وضعیت فعلی هدر دیتافایل و کنترل فایل بصورت زیر می باشد:
CHECKPOINT_CHANGE# | SCN location |
1521697 | SYSTEM checkpoint in controlfile |
1521697 | file header |
1521697 | file in controlfile |
همزمان redo logای که در حال حاضر در وضعیت current قرار دارد را مشاهده می کنید:
select group#, first_change#,sequence#, status from V$log;
GROUP# | FIRST_CHANGE# | SEQUENCE# | STATUS |
1 | 1419700 | 10 | INACTIVE |
2 | 1521696 | 11 | CURRENT |
3 | 1419697 | 9 | INACTIVE |
در همین حال، جدولی را ایجاد و اطلاعاتی را به ان اضافه می کنیم:
SQL> insert into tb values(‘vahid_u’);
1 row created.
SQL> commit;
Commit complete.
با رجوع به current redo log، این رکورد قابل مشاهده می باشد:
[root@db_host ~]# strings /oracle/oradata/DB/onlinelog/o1_mf_2_f9t37v8t_.log|grep vahid_u
vahid_u
در صورتی که این اطلاعات هنوز در دیتافایل ثبت نشده است:
[root@db_host ~]# strings /oracle/oradata/DB/datafile/o1_mf_tbs_f9t5o012_.dbf|grep vahid_u
[root@db_host ~]#
در همین حال، دستور switch logfile را صادر می کنیم تا وضعیت هدر دیتافایل و نیز dirty buffer را بعد از اجرای این دستور مشاهده کنیم:
SQL> alter system switch logfile;
System altered.
بعد از اجرای این دستور، پیام شروع checkpoint را در alert log خواهیم دید:
2018-03-05T14:30:12.755340+03:30
Beginning log switch checkpoint up to RBA [0xc.2.10], SCN: 1523860
تا این لحظه، هنوز هدر دیتافایل به شماره scn جدید(1523860) تغییر نکرده است و تا زمانی که پیام Completed checkpoint در alert log دیده نشود، این شماره جدید در هدر دیتافایل دیده نخواهد شد:
CHECKPOINT_CHANGE# | SCN location |
1521697 | SYSTEM checkpoint in controlfile |
1521697 | file header |
1521697 | file in controlfile |
ولی دستور زیر نشان می دهد که dirty buffer به دیسک منتقل شده است و redo log شماره دو کماکان اطلاعات قبلی را در خود دارد:
[root@db_host ~]# strings /oracle/oradata/DB/onlinelog/o1_mf_2_f9t37v8t_.log|grep vahid_u
vahid_u
[root@db_host ~]# strings /oracle/oradata/DB/datafile/o1_mf_tbs_f9t5o012_.dbf|grep vahid_u
vahid_u
حال redo log شماره 3 در وضعیت current قرار دارد:
GROUP# | FIRST_CHANGE# | SEQUENCE# | STATUS |
1 | 1419700 | 10 | INACTIVE |
2 | 1521696 | 11 | ACTIVE |
3 | 1523860 | 12 | CURRENT |
مجدد دستور switch logfile را صادر می کنیم:
SQL> alter system switch logfile;
System altered.
با مشاهده محتوای alert log، مجددا پیام شروع checkpoint را خواهیم دید همچنین شماره جاری scn برابر است با 1524287:
2018-03-05T14:33:55.194623+03:30
Beginning log switch checkpoint up to RBA [0xd.2.10], SCN: 1524287
نکته جالب انکه، هنوز checkpoint اول کامل نشده است پس باید منتظر دو پیام completed checkpoint باشیم و هر دو scn جدید باید برای لحظه ای هم که شده، بر روی هدر دیتافایل ثبت شوند ولی با توجه به عدم رخ دادن Completed checkpoint، وضعیت هدر دیتافایل، کماکان به فرم سابق قرار دارد:
CHECKPOINT_CHANGE# | SCN location |
1521697 | SYSTEM checkpoint in controlfile |
1521697 | file header |
1521697 | file in controlfile |
با گذشت زمانی حدود پنج دقیقه(از log switch اول)، دو پیام زیر در alert log مشاهده شد و متعاقب ان، هدر دیتافایل هم به scn جدید تغییر کرده است:
2018-03-05T14:35:40.200707+03:30
Completed checkpoint up to RBA [0xc.2.10], SCN: 1523860
2018-03-05T14:35:49.302125+03:30
Completed checkpoint up to RBA [0xd.2.10], SCN: 1524287
پس شماره scn ثبت شده در هدر دیتافایل، سرانجام به عدد 1524287 تغییر خواهد کرد:
CHECKPOINT_CHANGE# | SCN location |
1524287 | SYSTEM checkpoint in controlfile |
1524287 | file header |
1524287 | file in controlfile |
پیام Checkpoint not complete
همانطور که در مبحث قبلی اشاره شد، قبل از بازنویسی یک redo log، تمامی dirty bufferهای مربوط به اطلاعات این redo log که در buffer cache وجود دارند باید توسط پروسس DBWR به دیسک منتقل شوند. حال اگر حجم این dirty bufferها زیاد باشد، ممکن است زمان زیادی نیاز باشد تا DBWR این بلاکها را به دیسک منتقل کند در این حین ممکن است پیام Checkpoint not complete در alert log مشاهده شود.
حال اگر به دلایلی چون کم بودن حجم redo log به تناسب بار کاری سیستم، ضعف کدنویسی و … قرار باشد به طور متوالی و به کررات log switch انجام شود و همچنین سرعت نوشتن dirty buffer توسط DBWR بسیار کند باشد(در زمان انجام log switchای که نیاز به complete checkpoint دارد)، این پیام بیشتر در alert log مشاهده خواهد شد(البته نقش کم بودن تعداد redo logها هم در این زمینه بسیار پررنگ است).
در انتهای مبحث مختصرا تکرار می شود که از جمله دلایل مشاهده این پیام در alert log، عدم تناسب redo log به لحاظ تعداد و حجم با بارکاری سیستم، کند بودن سرعت نوشتن در دیسک، سوییچهای متعدد(به دلایلی چون ایجاد باگ در سیستم و ….) می باشند و باید متناسب با هر کدام از این نقایص، از رخ دادن چنین اتفاقی در صورت امکان جلوگیری کرد.
مثال:در مثال زیر، سعی کردیم تا این مسئله را به صورت عملی پیاده سازی کنیم. در بلاک زیر، بلافاصله بعد از هر بار اجرای دستور insert، یکبار log switch هم رخ خواهد داد:
begin
for i in 1..1000 loop
EXECUTE IMMEDIATE ‘insert into tb values(”vahid_u”)’;
commit;
EXECUTE IMMEDIATE ‘alter system switch logfile’;
end loop;
end;
نمونه ای از خروجی alert log:
2018-03-05T17:19:06.953266+03:30
Beginning log switch checkpoint up to RBA [0x1d.2.10], SCN: 1529934
2018-03-05T17:19:06.953484+03:30
Thread 1 advanced to log sequence 29 (LGWR switch)
Current log# 2 seq# 29 mem# 0: /oracle/oradata/DB/onlinelog/o1_mf_2_f9t37v8t_.log
Beginning log switch checkpoint up to RBA [0x1e.2.10], SCN: 1529939
2018-03-05T17:19:06.958571+03:30
Thread 1 advanced to log sequence 30 (LGWR switch)
Current log# 3 seq# 30 mem# 0: /oracle/oradata/DB/onlinelog/o1_mf_3_f9t37v9w_.log
Thread 1 cannot allocate new log, sequence 31
Checkpoint not complete
Current log# 3 seq# 30 mem# 0: /oracle/oradata/DB/onlinelog/o1_mf_3_f9t37v9w_.log
2018-03-05T17:19:07.252246+03:30
Completed checkpoint up to RBA [0x1d.2.10], SCN: 1529934
2018-03-05T17:19:07.264619+03:30
Beginning log switch checkpoint up to RBA [0x1f.2.10], SCN: 1529944
2018-03-05T17:19:07.264830+03:30
Thread 1 advanced to log sequence 31 (LGWR switch)
Current log# 1 seq# 31 mem# 0: /oracle/oradata/DB/onlinelog/o1_mf_1_f9t37v6t_.log
Thread 1 cannot allocate new log, sequence 32
Checkpoint not complete
Current log# 1 seq# 31 mem# 0: /oracle/oradata/DB/onlinelog/o1_mf_1_f9t37v6t_.log
2018-03-05T17:19:10.249792+03:30
Completed checkpoint up to RBA [0x1e.2.10], SCN: 1529939
5. Thread checkpoint: در صورت رخ دادن این نوع از checkpoint، تمام dirty bufferهای مربوط به یک instance به دیسک منتقل خواهند شد. به عبارتی دیگر، یکبار full checkpoint در بانک انجام خواهد شد منتها در سطح یک instance. دستور زیر، سبب رخ دادن این نوع از checkpoint خواهد شد:
- Alter system checkpoint local
مثال: دستورات زیر تعداد بلاکهایdirty موجود در دو instance را نشان می دهند(در محیط کلاستر با دو نود):
–node1:
select count(*) from v$bh l where dirty=’Y’;
18250
–node2:
select count(*) from v$bh l where dirty=’Y’;
13734
وضعیت فعلی هدر دیتافایل و کنترل فایل به صورت زیر می باشد:
CHECKPOINT_CHANGE# | SCN location |
280393609223 | SYSTEM checkpoint in controlfile |
280393609223 | file header |
280393609223 | file in controlfile |
با اتصال به نود اول، دستور زیر را اجرا می کنیم:
SQL> alter system checkpoint local;
System altered.
با اجرای این دستور، تعداد بلاکهای dirty موجود در دو نود به صورت زیر درخواهد امد:
–node1
select count(*) from v$bh l where dirty=’Y’;
0
–node2
select count(*) from v$bh l where dirty=’Y’;
13709
همانطور که می بینید، تعداد dirty bufferهای نود اول، به عدد صفر رسیده است. همچنین با رجوع مجدد به شماره هدر دیتافایل، تغییری مشاهده نخواهد شد:
CHECKPOINT_CHANGE# | SCN location |
280393609223 | SYSTEM checkpoint in controlfile |
280393609223 | file header |
280393609223 | file in controlfile |
6.direct path read: برای انجام عملیات full table scan دو راه وجود دارد:
buffer cache read: بلاکهای جدول از دیسک به SGA منتقل می شوند و با sessionهای دیگر قابلیت به اشتراک گذاشتن را دارا می باشند.
direct path read: بلاکهای جدول به جای فراخوانی از دیسک به SGA، مستقیم به PGA منتقل می شوند با این کار نیازی به latch نخواهیم داشت که این اتفاق سرعت اجرای دستور را تسریع خواهد بخشید البته این اطلاعات برای sessionهای دیگر در دسترس نخواهد بود.
پارامترهای مخفی ای در این زمینه وجود دارند که می تواند سبب مجاب شدن optimizer برای انتخابdirect path read برای یک اجرای پرس و جو شود که از جمله این پارامترها، می توان به دو پارامتر مخفی زیر اشاره کرد:
_small_table_threshold: مقدار این پارامتر مخفی بیانگر عددی است که نشان می دهد یک جدول به ازای چند بلاک small تلقی خواهد شد و معمولا برای جدولهای کوچک، از direct path read استفاده نمی شود. مقدار پیش فرض این پارامتر، برابر با دو درصد اندازه بافرکش می باشد.
_serial_direct_read: مقدار پیش فرض این پارامتر برابر با auto می باشد که در صورت تنظیم این پارامتر به مقدار flase، دستورات سریالی امکان اجرا به صورت direct path read را نخواهند داشت.
در پرس وجوی زیر، مقدار پارامترهای مخفی مربوط به این موضوع قابل مشاهده می باشد:
SELECT ksppinm , ksppstvl, ksppdesc FROM x$ksppi x, x$ksppcv y WHERE x.indx = y.indx AND TRANSLATE(ksppinm,’_’,’#’) like ‘#%’ and ksppinm in(‘_very_large_object_threshold’,’_db_block_buffers’,’_small_table_threshold’,’_serial_direct_read’) ORDER BY ksppinm;
KSPPINM | KSPPSTVL | KSPPDESC |
_db_block_buffers | 440775 | Number of database blocks cached in memory: hidden parameter |
_serial_direct_read | auto | enable direct read in serial |
_small_table_threshold | 8815 | lower threshold level of table size for direct reads |
_very_large_object_threshold | 500 | upper threshold level of object size for direct reads |
نکته مهمی که سبب مطرح شدن این بحث در این نوشتار شد، انجام checkpoint در سطح object ان هم قبل از رویداد direct path read می باشد بعبارت دیگر، قبل از رخ دادن این رویداد، باید مطمئن بود که هیچ بلاک اصلاح شده ای(رکورد commit شده) از جدول، در SGA قرار ندارد و در صورت وجود، ابتدا باید checkpointای رخ دهد و این بلاکها را به دیسک منتقل کند و سپس همه بلاکهای این جدول از دیسک به محیط PGA فراخوانی شوند به همین دلیل، رویداد direct path read معمولا با رویداد enq: KO – fast object checkpoint همراه خواهد بود که بیانگر رخ دادن checkpoint قبل از انجام این عمل می باشد.
نکته: در صورتی که درصد بالایی از بلاکهای یک جدول در sga موجود باشند، احتمال انجام عمل direct path read برای ان جدول، بسیار کاهش خواهد یافت.
در ادامه با انجام یک سناریو نشان خواهیم داد که چگونه direct path read منجر به checkpoint در سطح object خواهد شد.
جدولی را ایجاد می کنیم که حداقلهای مطرح شده در پارامتر _small_table_threshold را دارا باشد تا الزاما دسترسی FULL TABLE SCAN به ان جدول به صورت direct path read انجام شود:
SQL> create table tb as select * from tb1;
Table created.
دستور اول امار مربوط به این جدول را بروز خواهد کرد و سپس با دستور دوم، تعداد بلاکهای این جدول را نشان داده خواهد شد:
SQL> exec dbms_stats.gather_table_stats(‘SYS’,’TB’);
PL/SQL procedure successfully completed
select owner , table_name , blocks from dba_tables l where l.table_name=’TB’;
OWNER | TABLE_NAME | BLOCKS |
SYS | TB | 325291 |
در همین حال، اطلاعات موجود در buffer cache را flush می کنیم تا بلاکی از این جدول در این فضا موجود نباشد تا از انجام عمل direct path read ممانعت کند:
SQL> alter system flush buffer_cache;
System altered.
با دستور زیر بررسی می کنیم چه تعداد از بلاکهای جدول tb در buffer cache موجود هستند:
select obj#, sum(num_buf) from X$KCBOQH where obj# = (select object_id from dba_objects where object_name = ‘TB’) group by obj#;
no rows selected
در همین حال، رکوردی را به این جدول درج می کنیم تا یک بلاکdirty از این جدول در buffer cache موجود باشد:
SQL> insert into tb(cols) values(1);
1 row created.
SQL> commit;
Commit complete.
تعداد بلاکهای dirty مربوط به این جدول که در حافظه موجود هستند را با پرس و جوی زیر می بینیم:
select count(*) from v$bh l where dirty=’Y’ and objd=(select l.object_id from dba_objects l where l.object_name=’TB’);
1
حال با دستور زیر قصد داریم به صورت full table scan به اطلاعات جدول tb دسترسی پیدا کنیم(در ابتدا trace را در سطح session فعال می کنیم):
ALTER SESSION SET TRACEFILE_IDENTIFIER = ‘usef_checkpoint’;
ALTER SESSION SET events ‘10046 trace name context forever, level 12’;
SQL> select count(*) from tb;
با دستور زیر، مجددا تعداد بلاکهای dirty مربوط به این جدول را بررسی می کنیم:
select count(*) from v$bh l where dirty=’Y’ and objd=(select l.object_id from dba_objects l where l.object_name=’TB’);
0
همانطور که می بینید، checkpoint ای در سطح جدول tb رخ داده و dirty buffer مربوط به این جدول به دیسک منتقل شده است. همچنین با رجوع به فایل trace مربوط به sessionای که این دستور در ان اجرا شده، به این نکته خواهیم رسید(enq: KO – fast object checkpoint):
WAIT #139788694540016: nam=’enq: KO – fast object checkpoint’ ela= 1025 name|mode=1263468550 2=65544 0=1 obj#=-1 tim=5265503004393
WAIT #139788694540016: nam=’PGA memory operation’ ela= 19 p1=65536 p2=1 p3=0 obj#=-1 tim=5265503004607
WAIT #139788694540016: nam=’direct path read’ ela= 129 file number=1 first dba=101881 block cnt=7 obj#=74732 tim=5265503005498
با چند بار تکرار این پروسه در گزارشهای بانک، رویداد direct path read قابل مشاهده می باشد:
SQL ID | FullPlanhash | % Activity | Event | % Event | Top Row Source | SQL Text |
appfp5bw7bs4u | 3657139763 | 38.81 | CPU + Wait for CPU | 20.90 | TABLE ACCESS – FULL | select count(*) from tb |
appfp5bw7bs4u | 3657139763 | 38 | direct path read | 17.91 | TABLE ACCESS – FULL |
دستور زیر هم تایید کننده اجرای direct path read پرس و جوی بالا خواهد بود(البته به شرط اجرای این دستور در همان session):
select event, total_waits from v$session_event where sid = (select sid from v$mystat where rownum = 1) and event like ‘%read%’;
EVENT | TOTAL_WAITS | |
direct path read | 2543 | |
در پایان این بخش باید سه نکته را اضافه کرد:
نکته اول: checkpoint در این سطح سبب تغییر هدر دیتافایل نخواهد شد.
نکته دوم: در اوراکل 10g برای parallel query از direct path read استفاده می شد ولی در اوراکل 11g، باید مقدار پارامترهای مخفی، اندازه بافرکش و همچنین تعداد بلاک جداول هم لحاظ شوند و در صورت دارا بودن صلاحیت، از این روش استفاده شود.
نکته سوم: برای اجرای حتمی FULL TABLE SCAN به صورت direct path read در سطح یک session یا دستور مشخص، می توان از پارامتر _serial_direct_read به صورتهای زیر استفاده کرد:
SQL> alter session set “_serial_direct_read”=always;
Session altered.
OR
select /*+ OPT_PARAM(‘_serial_direct_read’, always) */ count(*) from tb;
نکته چهارم: اگر مقدار پارامتر _direct_read_decision_statistics_driven بهTRUE تنظیم شده باشد، امارهای موجود در جدول col$ و tab$ مرجع خواهند بود و در غیر این صورت، تعداد بلاکها محاسبه خواهد شد و جداول مربوط به آمارگیری از مرجعیت خواهند افتاد.
7.Incremental Checkpoint: در نسخه های قدیمی اوراکل، نوشتن dirty buffer توسط پروسس dbwr تنها به بعد از log switch موکول می شد و به همین دلیل، بعضا در زمانهایی نیاز بود پروسس DBWR حجم قابل توجهی از بلاکهای اصلاح شده را به دیسک منتقل کند که این سناریو ممکن بود منجر به کندی لحظه ای سیستم شود و همچنین در صورت نیاز به instance recovery، زمان recovery هم بسیار زمانبر می شد.
بعدها اوراکل پارامترهایی را در این زمینه معرفی کرد که متناسب با این پارامترها، checkpointهایی به صورت incremental در بانک رخ خواهد داد تا میزانی از این بلاکهای dirty با یک نظم مشخصی به دیسک منتقل شوند. از جمله این پارمترها می توان به پارامتر fast_start_mttr_target اشاره کرد که بیشتر با هدف تعیین(و یا کاهش) زمان instance recovery استفاده می شود.
علاوه بر این مدل از پارامترها، از دیگر عوامل موثر در رخ دادن incremental checkpoint، می توان به نیافتن بافر ازاد در محیط buffer cache اشاره کرد.
برای انجام این نوع از checkpoint، پروسس DBWR هر سه ثانیه یکبار بیدار شده و بررسی می کند که ایا نیاز به انجام کاری در این زمینه هست یا خیر؟! در صورتی که توسط پروسس DBWR بلاکی به دیسک منتقل شود، پروسس CKPT، checkpoint position را در کنترل فایل بروز خواهد کرد البته با انجام این نوع از checkpoint، تغییری در هدر دیتافایل حاصل نخواهد شد.
مبحث incremental checkpoint در این نوشتار به صورت مختصر مورد بررسی قرار گرفت که انشالله در اینده به صورت مفصل تر به ان خواهیم پرداخت.
انشاله…