دیتابیس اوراکل

دیتابیس اوراکل
طبقه بندی موضوعی

۶ مطلب با موضوع «19C» ثبت شده است

Bitmap Based Count Distinct SQL Function

در محیط های Data Warehouse، استفاده از کوئری هایی که در آنها count(distinct) به کار برده شده است، متداول میباشد. همانطور که میدانید، یکی از راههای بالا بردن پرفرمنس کوئری هایی که در آنها از count(distinct) استفاده شده است، استفاده از Query Rewrite در Materialized Viewها است. به دو کوئری زیر توجه کنید:

SELECT promo_id, COUNT(DISTINCT prod_id)

  FROM sh.sales

GROUP BY promo_id

ORDER BY promo_id;

 

SELECT channel_id, COUNT(DISTINCT prod_id)

  FROM sh.sales

GROUP BY channel_id

ORDER BY channel_id;

 

 

 

 

 

 

 

 

 

 

اگر بخواهیم سرعت این کوئری ها را با Materialized View (Query Rewrite) افزایش دهیم، باید برای هر کدام از این کوئری ها یک Materialized View جداگانه ایجاد کنیم. ولی هدف ما در واقع باید این باشد که تعداد کمی Materialized View ایجاد کنیم و تا حد امکان از آنها در کوئری های مختلف استفاده کنیم. ولی تا قبل از اوراکل 19c این امکان برای ما وجود نداشت.

از Oracle 19c یک سری SQL functionها در رابطه با عملیات مرتبط با bitmapها اضافه شده اند، که این فانکشن ها، سرعت اجرایCOUNT(DISTINCT)  را در کوئری ها افزایش میدهند. طبق مطلبی که در داکیومنتهای اوراکل به آن اشاره شده است، تمرکز اصلی این فانکشن ها بر روی Materialized Viewهایی است که در آنها از aggregate data استفاده شده است.

این فانکشنها عبارتند از:

  • BITMAP_BIT_POSITION
  • BITMAP_BUCKET_NUMBER
  • BITMAP_CONSTRUCT_AGG
  • BITMAP_COUNT
  • BITMAP_OR_AGG

از این فانکشنها به صورتهای مختلفی میتوان استفاده کرد، یکی از موارد استفاده آن همانطور که در بالا هم به آن اشاره شد در  Materialized View (Query Rewrite) است که در زیر با ارائه یک مثال کاربرد آن توضیح داده شده است:

CREATE MATERIALIZED VIEW sh.mv_sales

ENABLE QUERY REWRITE

AS

      SELECT promo_id,

                    channel_id,

                    BITMAP_BUCKET_NUMBER (prod_id),

                    BITMAP_CONSTRUCT_AGG (BITMAP_BIT_POSITION (prod_id))

        FROM sh.sales

    GROUP BY promo_id, channel_id, BITMAP_BUCKET_NUMBER (prod_id);

 

 

 

 

 

 

 

 

 

 

حتما یک log برای Materialized View بالا ایجاد کنید:

CREATE MATERIALIZED VIEW LOG ON sh.sales

TABLESPACE users

WITH rowid;

 

 

 

 

در زیر کاربرد Materialized View بالا را در کوئری هایی که روی جدول sales زده میشود، مشاهده میکنیم:

  SELECT promo_id, COUNT (DISTINCT prod_id)

    FROM sh.sales

GROUP BY promo_id

ORDER BY promo_id;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  SELECT channel_id, COUNT (DISTINCT prod_id)

    FROM sh.sales

GROUP BY channel_id

ORDER BY channel_id;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Document:

https://docs.oracle.com/en/database/oracle/oracle-database/19/newft/new-features.html#GUID-9DBB57A5-9F40-49F1-80D7-78F1DFD62924

https://docs.oracle.com/pls/topic/lookup?ctx=en/database/oracle/oracle-database/19/newft&id=DWHSG-GUID-03107B3C-72D3-4B6B-A922-F13FF0CF2B6D

https://danischnider.wordpress.com/2019/04/20/bitmap-based-countdistinct-functions-in-oracle-19c/

Repairing SQL Failures with the SQL Repair Advisor

 

وقتی یک دستور SQL به یک critical error برخورد میکند و failed میشود، یکی از راهها این است که برای برطرف کردن خطای آن از SQL Repair Advisor کمک بگیریم. SQL Repair Advisor آن دستور SQL را آنالیز میکند و اگر بتواند برای برطرف کردن خطای پیش آمده یک patch ارائه میدهد. اگر شما آن patch را apply کنید، optimizer با جایگزین کردن یک plan جدید سعی میکند در اجراهای بعدی آن خطا را برطرف کند.

SQL Repair Advisor را از 2 طریق میتوان اجرا کرد:

1. Cloud Cntrol

2. DBMS_SQLDIAG package subprograms

 

Running the SQL Repair Advisor Using the DBMS_SQLDIAG Package Subprograms

با استفاده از subprogramهای پکیج DBMS_SQLDIAG میتوانیم SQL Repair Advisor را اجرا کنیم:

CREATE_DIAGNOSIS_TASK: ایجاد یک diagnostic task

EXECUTE_DIAGNOSIS_TASK: اجرای diagnostic task که در مرحله قبل ایجاد شده است.

ACCEPT_SQL_PATCH: اگر در مرحله قبل patch پیشنهاد شده باشد، با استفاده از این subprogram میتوان آن patch را apply کرد.

نکته مهم: از اوراکل 19c میتوان بجای استفاده از سه subprogram بالا، تنها با استفاده از یک subprogram با نام SQL_DIAGNOSE_AND_REPAIR همه کارهای بالا را انجام داد.

مراحل استفاده از DBMS_SQLDIAG:

1- ایجاد یک diagnosis task با نام error_task

DECLARE

  rep_out   CLOB;

  t_id      VARCHAR2(50);

BEGIN

  t_id := DBMS_SQLDIAG.CREATE_DIAGNOSIS_TASK (

           sql_text => 'DELETE FROM t t1

                        WHERE t1.a = ''a'' AND

                              ROWID <> (SELECT MAX(ROWID)

                                        FROM t t2

                                        WHERE t1.a = t2.a AND

                                              t1.b = t2.b AND

                                              t1.d = t2.d)',

           task_name => 'error_task',

           problem_type => DBMS_SQLDIAG.PROBLEM_TYPE_COMPILATION_ERROR);

END;

 

2- اجرای error_task:

DBMS_SQLDIAG.EXECUTE_DIAGNOSIS_TASK ('error_task');

 

3- تولید report برای error_task:

DECLARE

    rep_out   CLOB;

BEGIN

    rep_out :=

        DBMS_SQLDIAG.report_diagnosis_task ('error_task', DBMS_SQLDIAG.type_text);

    DBMS_OUTPUT.put_line ('Report : ' || rep_out);

END;

 

4- apply کردن patch در صورت وجود:

SQL Patch شبیه SQL Profile است با این تفاوت که برخلاف SQL Profile برای رفع خطاهای compilation و execution استفاده میشود.

EXECUTE DBMS_SQLDIAG.ACCEPT_SQL_PATCH(task_name => 'error_task', task_owner => 'SYS', replace => TRUE);

 

میتوانید با استفاده از Cloud Control و subprogramهای ALTER_SQL_PATCH و DROP_SQL_PATCH در پکیج DBMS_SQLDIAG، اقدام به حذف کردن و یا disable کردن patchهای ایجاد شده نمایید.

5- اجرای مجدد آن دستور sql و تست patch ایجاد شده

 

نکته: با استفاده از پکیج DBMS_SQLDIAG میتوانید از patch ایجاد شده در یک دیتابیس export بگیرید و در یک دیتابیس دیگر import کنید.

 

DISTINCT Option for LISTAGG Aggregate

 

Category : Application Development_SQL

Oracle DB version : 19.3.0.0.0

 

تابع LISTAGG از ورژن 11gR2 به اوراکل اضافه شده است، و در ورژن های 12cR2 و 19c تغییراتی داشته است.

همانطور که در مثال زیر مشاهده می کنید، در خروجی تابع LISTAGG، مقادیر تکراری هم نمایش داده می شوند:

SELECT deptno, LISTAGG (ename, ',') WITHIN GROUP (ORDER BY ename) AS employees

  FROM emp

GROUP BY deptno

ORDER BY deptno;

 

DEPTNO EMPLYEES
10 CLARK,KING,MILLER,MILLER,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

 

 

 

 

 

 

 

 

 

 

 

تا قبل از اوراکل 19c، برای حذف این مقادیر تکراری، راه حل های مختلفی وجود داشت که یکی از آنها استفاده از تابع ROW_NUMBER بود. ولی در اوراکل 19c، با اضافه شدن کلمه کلیدی DISTINCT به این تابع خیلی راحت میتوان مقادیر تکراری را از خروجی تابع LISTAGG حذف کرد.

 

SELECT deptno, LISTAGG (DISTINCT ename, ',') WITHIN GROUP (ORDER BY ename) AS employees

FROM   emp

GROUP BY deptno

ORDER BY deptno;

 
DEPTNO EMPLYEES
10 CLARK,KING,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

 

 

 

 

 

SQL Quarantine

Category: Performance

Oracle DB Version: 19.3.0.0

 

با استفاده از Resource Manager، میتوانیم SQLها را از نظر مصرف منابع، محدود کنیم. مثلا به این صورت که SQLهایی که منابع بیشتری نسبت به thresholdهای ست شده استفاده کنند، توسط Resource Manger خاتمه یابند. تا قبل از نسخه 19c، وقتی یک SQL توسط Resource Manger متوقف میشد،  از اجرای مجدد آن ممانعت به عمل نمی آمد؛ فقط اگر مانند دفعه قبل، منابع بیشتری نسبت به thresholdهای ست شده استفاده میکرد، باز هم توسط Resource Manger متوقف میشد. که این موضوع باعث میشود که منابع سیستم هدر رود، چون آن دستور SQL مجددا اجرا شده و برای اجرا از منابع سیستم استفاده کرده است. ولی از اوراکل 19c ، میتوانیم با استفاده از  SQL Quarantine ، از اجرای مجدد SQLهایی که توسط Resource Manger خاتمه یافته اند، ممانعت به عمل آوریم.

به طور مثال فرض کنید در Resource Manger یک threshold ست شده است به این صورت که اگر زمان اجرای یک دستور SQL برای یک یوزر بیشتر از 3 ثانیه شد، آن SQL متوقف شود.

یک دستور SQL با نام SQL1 اجرا میشود. اگر SQL1 بیشتر از 3 ثانیه طول بکشد، توسط  Resource Manger خاتمه می یابد.

بعد از آن SQL Quarantine یک quarantine با نام Quarantine1 برای SQL1 ایجاد میکند و execution time مساوی 3 ثانیه را به عنوان threshold برای Quarantine1 ست میکند.

اگر SQL1 مجددا با همان  Execution Plan اجرا شود و threshold ست شده در Resource Manager نیز همچنان 3 ثانیه باشد، Quarantine1 اجازه نمیدهد که SQL1 اجرا شود، زیرا تشخیص داده است که SQL1 در نهایت توسط Resource Manager متوقف میشود.

اگر در Resource Manager، مقدار Execution Time به 2 ثانیه تغییر پیدا کند. وقتی SQL1 دوباره با همان execution plan اجرا شود، مجددا Quarantine1 اجازه اجرا به آن نمیدهد، زیرا تشخیص داده که SQL1 در نهایت توسط Resource Manager متوقف میشود، چون اجرای آن حداقل 3 ثانیه طول میکشد.

اگر در Resource Manager، مقدار Execution Time به 10 ثانیه تغییر پیدا کند. وقتی SQL1 دوباره با همان Execution Plan اجرا شود، Quarantine1 از اجرای آن ممانعتی به عمل نمی آورد، زیرا تشخیص داده است که SQL1 حداقل 3 ثانیه طول میکشد که اجرا شود، اما این احتمال وجود دارد که تا قبل از 10 ثانیه تمام شود.

 

مثالی از ایجاد شدن Quarantine براساس thresholdهای ست شده در Resource Manager

مثال زیر نشان میدهد که چگونه execution plan یک دستور SQL وقتی از thresholdهای ست شده در Resource Manger فراتر برود، قرنطینه میشود:

1. با استفاده از Resource Manager مشخص میکنیم که Execution Time برای SQLهایی که با استفاده از یوزر HR اجرا شوند 3 ثانیه باشد. برای انجام این کار مراحل زیر را طی کنید:

   1.1. ایجاد یک consumer group با نام TEST_RUNAWAY_GROUP

   1.2. اختصاص دادن یوزر HR به TEST_RUNAWAY_GROUP

   1.3. ایجاد یک resource plan با نام LIMIT_RESOURCE که حداکثر زمان اجرا را برای SQLهای اجرا شده 3 ثانیه در نظر بگیرد.

   1.4. تخصیص LIMIT_RESOURCE به TEST_RUNAWAY_GROUP

 

connect / as sysdba

 

begin

 

  -- Create a pending area

  dbms_resource_manager.create_pending_area();

 

  -- Create a consumer group 'TEST_RUNAWAY_GROUP'

  dbms_resource_manager.create_consumer_group (

    consumer_group => 'TEST_RUNAWAY_GROUP',

    comment        => 'This consumer group limits execution time for SQL statements'

  );

 

  -- Map the sessions of the user 'HR' to the consumer group 'TEST_RUNAWAY_GROUP'

  dbms_resource_manager.set_consumer_group_mapping(

    attribute      => DBMS_RESOURCE_MANAGER.ORACLE_USER,

    value          => 'HR',

    consumer_group => 'TEST_RUNAWAY_GROUP'

  );

 

  -- Create a resource plan 'LIMIT_RESOURCE'

  dbms_resource_manager.create_plan(

    plan    => 'LIMIT_RESOURCE',

    comment => 'Terminate SQL statements after exceeding total execution time'

  );

 

  -- Create a resource plan directive by assigning the 'LIMIT_RESOURCE' plan to

  -- the 'TEST_RUNAWAY_GROUP' consumer group

  -- Specify the execution time limit of 3 seconds for SQL statements belonging to

  -- the 'TEST_RUNAWAY_GROUP' group

  dbms_resource_manager.create_plan_directive(

    plan             => 'LIMIT_RESOURCE',

    group_or_subplan => 'TEST_RUNAWAY_GROUP',

    comment          => 'Terminate SQL statements when they exceed the' ||

                        'execution time of 3 seconds',

    switch_group     => 'CANCEL_SQL',

    switch_time      => 3,

    switch_estimate  => false

  );

 

  -- Allocate resources to the sessions not covered by the currently active plan

  -- according to the OTHER_GROUPS directive

  dbms_resource_Manager.create_plan_directive(

    plan              => 'LIMIT_RESOURCE',

    group_or_subplan  => 'OTHER_GROUPS',

    comment           => 'Ignore'

  );

 

  -- Validate and submit the pending area

  dbms_resource_manager.validate_pending_area();

  dbms_resource_manager.submit_pending_area();

 

  -- Grant switch privilege to the 'HR' user to switch to the 'TEST_RUNAWAY_GROUP'

  -- consumer group

  dbms_resource_manager_privs.grant_switch_consumer_group('HR',

                                                          'TEST_RUNAWAY_GROUP',

                                                          false);

 

  -- Set the initial consumer group of the 'HR' user to 'TEST_RUNAWAY_GROUP'

  dbms_resource_manager.set_initial_consumer_group('HR',

                                                   'TEST_RUNAWAY_GROUP');

 

end;

/

 

-- Set the 'LIMIT_RESOURCE' plan as the top plan for the Resource Manager

alter system set RESOURCE_MANAGER_PLAN = 'LIMIT_RESOURCE' scope = memory;

 

 

2. با یوزر HR به دیتابیس متصل شوید و دستور زیر را اجرا کنید. زمان اجرای این دستور بیشتر از 3 ثانیه است.

select count(*)

from employees emp1, employees emp2,

     employees emp3, employees emp4,

     employees emp5, employees emp6,

     employees emp7, employees emp8,

     employees emp9, employees emp10

where rownum <= 100000000;

 

این SQL توسط Resource Manger متوقف شده و خطای زیر به کاربر نمایش داده میشود:

 

ORA-00040: active time limit exceeded - call aborted

 

3. وقتی این SQL قرنطینه شد، اگر مجددا با همان plan اجرا شود، با خطای زیر مواجه میشوید:

 

ORA-56955: quarantined plan used

 

4. در viewهای زیر میتوانید آماری را در رابطه با SQLهای قرنطینه شده مشاهده کنید:

  • v$sql

select sql_text, plan_hash_value, avoided_executions, sql_quarantine

from v$sql

where sql_quarantine is not null;

 

خروجی کوئری بالا به شکل زیر است:

SQL_TEXT                           PLAN_HASH_VALUE        AVOIDED_EXECUTIONS          SQL_QUARANTINE

----------------------------       --------------------------          ------------------------------           ---------------------------------

select count(*)                  3719017987                      1                                                  SQL_QUARANTINE_3uuhv1u5day0yf6ed7f0c

from employees emp1,

     employees emp2, employees emp3,

     employees emp4, employees emp5,

     employees emp6, employees emp7,

     employees emp8, employees emp9,

     employees emp10

where rownum <= 100000000;

 

 

  • dba_sql_quarantine: این view اطلاعاتی درباره quarantine configurationهای ایجاد شده به ما میدهد:

 

select sql_text, name, plan_hash_value, last_executed, enabled

from dba_sql_quarantine;

 

خروجی کوئری بالا به شکل زیر است:

SQL_TEXT                          NAME                                                                         PLAN_HASH_VALUE     LAST_EXECUTE                                ENABLED

-------------------                   -----------------------------------                                     ---------------                    ----------------------------                      -------

select count(*)                  SQL_QUARANTINE_3uuhv1u5day0yf6ed7f0c    3719017987                  14-JAN-19 02.19.01.000000 AM   YES

from employees emp1,

     employees emp2,

    employees emp3,

     employees emp4,

     employees emp5,

     employees emp6,

     employees emp7,

     employees emp8,

     employees emp9,

     employees emp10

where rownum <= 100000000;

 
ایجاد Quarantine برای یک دستور SQL
با استفاده از پکیج DBMS_SQLQ میتوانیم برای execution plan یک دستور SQL یک Quarantine ایجاد کنیم و برای آن thresholdهایی مشابه thresholdهای Resource Manager ست کنیم.

در مثال زیر برای یکی از Execution planهای یک دستور SQL یک quarantine ایجاد شده است:

 

DECLARE

    quarantine_config   VARCHAR2 (40);

BEGIN

    quarantine_config :=

        DBMS_SQLQ.CREATE_QUARANTINE_BY_SQL_ID (

            SQL_ID            => '28bu8g33sbm4v',

            PLAN_HASH_VALUE   => '3347434651');

    DBMS_OUTPUT.put_line (quarantine_config);

END;

 

اگر execution plan را مشخص نکرده یا NULL ست کنیم، quarantine برای همه Execution Planهای آن SQL اعمال میشود، بجز آنهایی که به صورت جداگانه برایشان یک quarantine  تعریف شده باشد. در مثال زیر برای همه execution planهای یک SQL که SQL ID آن مساوی 28bu8g33sbm4v است، یک quarantine  ایجاد شده است.

 

DECLARE

    quarantine_config   VARCHAR2 (40);

BEGIN

    quarantine_config :=

        DBMS_SQLQ.CREATE_QUARANTINE_BY_SQL_ID (SQL_ID => '28bu8g33sbm4v');

END;

 

در مثال زیر هم برای همه execution planهای دستور 'select count(*) from emp' یک quarantine  ایجاد شده است.

DECLARE

    quarantine_config   VARCHAR2 (40);

BEGIN

    quarantine_config :=

        DBMS_SQLQ.CREATE_QUARANTINE_BY_SQL_TEXT (

            SQL_TEXT   => TO_CLOB ('select count(*) from emp'));

END;

 

نتیجه اجرای دستورات بالا نام Quarantine که ایجاد شده است، میباشد. که در مراحل بعدی موقع ست کردن threshold ، به آن نام نیاز میباشد.

Result:  SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b

 

مشخص کردن Threshold برای Quarantine ایجاد شده

بعد از ایجاد یکQuarantine  برای execution plan یک دستور SQL، میتوانیم با استفاده از پروسیجر DBMS_SQLQ.ALTER_QUARANTINE برای آن quarantine، threshold تعیین کنیم. وقتی هریک از thresholdهای ست شده در Resource Manager کوچکتر یا مساوی thresholdهای مشخص شده برای یک Quarantine باشند، به آن SQL اجازه اجرا داده نمیشود.

میتوانیم برای Resourceهای زیر در quarantine configuration ، threshold تعریف کنیم:

  • CPU time
  • Elapsed time
  • I/O in megabytes
  • Number of physical I/O requests
  • Number of logical I/O requests

در مثال زیر برای یک Quarantine که در مرحله قبل ایجاد کردیم ، thresholdهایی برای CPU time و Elapsed time ست می کنیم:

 

BEGIN

    DBMS_SQLQ.ALTER_QUARANTINE (

        QUARANTINE_NAME   => 'SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b',

        PARAMETER_NAME    => 'CPU_TIME',

        PARAMETER_VALUE   => '5');

 

    DBMS_SQLQ.ALTER_QUARANTINE (

        QUARANTINE_NAME   => 'SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b',

        PARAMETER_NAME    => 'ELAPSED_TIME',

        PARAMETER_VALUE   => '4');

END;

 

وقتی دستور SQL با همان plan مشخص شده در SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b اجرا شود. اگر thresholdهای ست شده در Resource Manager، برای CPU time ، 5 ثانیه یا کمتر و برای Elapse time، 4 ثانیه یا کمتر باشد،  از اجرای آن SQL ممانعت به عمل می آید.

 

بدست آوردن اطلاعات در مورد  Thresholdهای ست شده برای یک Quarantine

با استفاده از فانکشن DBMS_SQLQ.GET_PARAM_VALUE_QUARANTINE میتوانیم از یک Quarantine که قبلا ایجاد شده است، کوئری بگیریم.

با استفاده از مثال زیر میتوانیم متوجه شویم که مقدار threshold ست شده برای CPU time در SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b چقدر است:

 

DECLARE

    quarantine_config_setting_value   VARCHAR2 (30);

BEGIN

    quarantine_config_setting_value :=

        DBMS_SQLQ.GET_PARAM_VALUE_QUARANTINE (

            QUARANTINE_NAME   => 'SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b',

            PARAMETER_NAME    => 'CPU_TIME');

    DBMS_OUTPUT.put_line (quarantine_config_setting_value);

END;

Result: 5

 

 پاک کردن thresholdهای ست شده برای یک Quarantine

در مثال زیر threshold ست شده برای CPU time، از SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b حذف شده است:

 

BEGIN

    DBMS_SQLQ.ALTER_QUARANTINE (

        QUARANTINE_NAME   => 'SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b',

        PARAMETER_NAME    => 'CPU_TIME',

        PARAMETER_VALUE   => DBMS_SQLQ.DROP_THRESHOLD);

END;

 

 فعال یا غیرفعال کردن یک Quarantine

یک Quarantine به صورت پیش فرض وقتی ایجاد میشود enable است. در مثال زیر یک قرنطینه با نام      SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b که قبلا ایجاد شده بود، disable شده است:

BEGIN

    DBMS_SQLQ.ALTER_QUARANTINE (

        QUARANTINE_NAME   => 'SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b',

        PARAMETER_NAME    => 'ENABLED',

        PARAMETER_VALUE   => 'NO');

END;

 

پاک کردن یک Quarantine

Quarantineهایی که مورد استفاده قرار نگرفته اند، به صورت اتومات بعد از 53 هفته پاک میشوند، که میتوانیم با استفاده از پروسیجر زیر این تنظیم اتومات را disable کنیم.

 

BEGIN

    DBMS_SQLQ.ALTER_QUARANTINE (

        QUARANTINE_NAME   => 'SQL_QUARANTINE_3u0anzbpmfkmdc785cc9b',

        PARAMETER_NAME    => 'AUTOPURGE',

        PARAMETER_VALUE   => 'NO');

END;

 

همچنین میتوانیم با استفاده از پروسیجر زیر یک Quarantine  را به صورت دستی پاک کنیم.

BEGIN

    DBMS_SQLQ.DROP_QUARANTINE ('SQL_QUARANTINE_3z0mwuq3aqsm8cfe7a0e4');

END;

 

انتقال یک Quarantine از یک دیتابیس به دیتابیس دیگر

با استفاده از پکیج DBMS_SQLQ میتوانیم یک Quarantine را از یک دیتابیس به دیتابیس دیگر انتقال دهیم. مثلا در یک دیتابیس تستی، یک Quarantine ایجاد و تستهای لازم انجام شده است. با توجه به اینکه عملکرد خوبی داشته است، میخواهیم آن را به دیتابیس اصلی منتقل کنیم. مثال زیر مراحل انجام این کار را نشان میدهد:

1. با استفاده از SQL*Plus با یک یوزر که دسترسی admin دارد، به دیتابیس مبدا متصل میشویم و با اجرای دستور زیر یک staging table ایجاد میکنیم:

BEGIN

    DBMS_SQLQ.CREATE_STGTAB_QUARANTINE (

        staging_table_name   => 'TBL_STG_QUARANTINE');

END;

 

2. کانفیگ های مربوط به SQL Quarantine را که میخواهیم به دیتابیس مقصد منتقل کنیم با استفاده از دستور زیر به جدولی که در مرحله قبل ساخته ایم منتقل میکنیم. در مثال زیر همه quarantine هایی که نام آنها با QUARANTINE_CONFIG شروع شده است به جدول TBL_STG_QUARANTINE که در مرحله قبل ایجاد شده است اضافه میشوند:

DECLARE

    quarantine_configs   NUMBER;

BEGIN

    quarantine_configs :=

        DBMS_SQLQ.PACK_STGTAB_QUARANTINE (

            staging_table_name   => 'TBL_STG_QUARANTINE',

            name                 => 'QUARANTINE_CONFIG_%');

END;

 

3. از جدول TBL_STG_QUARANTINE با استفاه از Data Pump اکسپورت بگیرید.

4. Dump File را از دیتابیس مبدا به مقصد انتقال دهید.

5. در دیتابیس مقصد dump file مربوطه را ایمپورت کنید.

6. با استفاده از SQL*Plus با یک یوزر که دسترسی admin دارد، به دیتابیس مقصد متصل شوید و با اجرای دستور زیر بر اساس staging table که ایمپورت کرده اید، یک quarantine  ایجاد کنید:

 

DECLARE

    quarantine_configs   NUMBER;

BEGIN

    quarantine_configs :=

        DBMS_SQLQ.UNPACK_STGTAB_QUARANTINE (

            staging_table_name   => 'TBL_STG_QUARANTINE');

    DBMS_OUTPUT.put_line (quarantine_configs);

END;

 

فانکشن DBMS_SQLQ.UNPACK_STGTAB_QUARANTINE به عنوان خروجی تعداد quarantine هایی که در دیتابیس مقصد ایجاد شده است را برمیگرداند.

 

 

 

DBMS_JOB in Oracle DB 19c

 

اوراکل از نسخه 12cR2 اعلام کرد که پکیج DBMS_JOB، منسوخ (deprecateشده است. و در نسخه 19c با تبدیل هر DBMS_JOB ایجاد شده به یک جاب DBMS_SCHEDULER، یک گام هم در جهت منسوخ کردن این پکیج جلوتر رفته است.

در زیر مثالی در رابطه با این موضوع آورده شده است:

SQL> conn raheleh

SQL> select job, what from user_jobs;

no rows selected

 

SQL> select job_name, job_action from user_scheduler_jobs;

no rows selected

 

 

SQL>

CREATE TABLE raheleh.tbl1

(

    id      NUMBER,

    name    VARCHAR2 (30)

);

 

 

SQL> INSERT INTO tbl1

(SELECT user_id, username FROM dba_users)

COMMIT;

 

 

SQL>

CREATE OR REPLACE PROCEDURE raheleh.prc_dbmsjob (username IN VARCHAR2)

IS

    userid   NUMBER;

BEGIN

    SELECT id

      INTO userid

      FROM raheleh.tbl1

     WHERE name = username;

END;

 

 

SQL>

DECLARE

    job1   PLS_INTEGER;

BEGIN

    DBMS_JOB.submit (job         => job1,

                                       what        => 'begin raheleh.prc_dbmsjob (''SYSTEM''); end;',

                                       next_date   => TRUNC (SYSDATE) + 1,

                                       interval    => 'trunc(sysdate)+1');

END;

COMMIT;

 

 

SQL> select job, what from user_jobs;

 

       JOB  WHAT

----------  ------------------------------

        22  begin raheleh.prc_dbmsjob ('SYSTEM'); end;

 

 

 

SQL> select job_name, job_action from user_scheduler_jobs;

 

JOB_NAME                       JOB_ACTION

------------------------------   ------------------------------

DBMS_JOB$_22               begin raheleh.prc_dbmsjob ('SYSTEM'); end;

 

 

همانطور که در مثال بالا مشاهده کردید، وقتی یک جاب با DBMS_JOB ایجاد کردیم، اوراکل به صورت اتومات، معادل همان جاب ، یک جاب هم با DBMS_SCHEDULER ایجاد کرد که نام آن با DBMS_JOB$_* شروع میشود.

 

 

َAutomatic Indexing در اوراکل دیتابیس 19c

Category: Big Data and Data Warehousing

Oracle DB Version: 19.3.0.0

Oracle DB Type: RAC

 

ایندکس یکی از ساختارهای مهم در بالا بردن کارایی دیتابیس است. ایندکس­ها هم برای محیط OLTP که SQLهای زیادی در روز اجرا می­کنند، هم برای محیط­ های OLAP که می­خواهند بخش کوچکی از دیتا را از یک جدول بزرگ انتخاب کنند ضروری هستند. اگر همزمان با تغییر در لود اپلیکیشن، ایندکس ها آپدیت نشوند، این موضوع می تواند باعث کاهش قابل توجه عملکرد دیتابیس شود. از featureهای جدید در اوراکل دیتابیس 19c، Automatic Indexing است که برای بهبود بخشیدن کارایی دیتابیس هنگام تغییر در لود اپلیکیشن، میتواند به صورت اتومات، ایندکس­های لازم را create، rebuild یا drop کند.

 

Automatic Indexing چگونه کار می­کند؟

فرآیند Automatic Indexing در background، هر 15 دقیقه اجرا و عملیات زیر را انجام می­دهد:

  1. انتخاب ایندکس­های کاندید بر اساس میزان استفاده ستون­های جداول در دستورات SQL.
  2. ایجاد ایندکس­های کاندید به صورت invisible.
  3. اگر performance مربوط به آن SQL که ایندکس برای آن ایجاد شده است بهبود پیدا کرد، وضعیت ایندکس به visible تغییر پیدا می­کند. و از این به بعد می­تواند در دستورات SQL مورد استفاده قرار بگیرید.
  4. ولی اگر performance بهبود پیدا نکرد، وضعیت ایندکس به unusable تغییر پیدا می­کند و در نهایت طی یک فرآیند اتومات کلا پاک میشود. و آن دستور SQL در balck list  قرار میگیرد. دستورات SQL که جزو black list قرار گرفته اند، در آینده در فرآیند automatic indexing مورد بررسی قرار نمی گیرند.

نکات مهم:

  • برای استفاده از auto indexing، باید statisticها آپدیت باشند. جداولی که statistic نداشته باشند یا statistic آنها stale باشد و real-time statistic هم برای آنها در دسترس نباشد در لیست بررسی automatic indexing قرار نمی ­گیرند.
  • Automatic Indexها میتوانند به صورت single-column یا multi-column باشند.
  • Auto Indexها برای SQLهایی که اولین بار در دیتابیس اجرا میشوند، مورد استفاده قرار نمی­ گیرند.
  • میتوان تنظیم کرد که جاب مربوط به Auto Indexing برای یک ساعاتی غیرفعال شود. یا اینکه تنظیم کنید این جاب کلا با ریسورس خیلی کمی اجرا شود.

 

کانفیگ کردن Automatic Indexing در Oracle DB

فعالسازی و غیر فعالسازی Automatic Indexing

برای فعالسازی Automatic Indexing، باید از پروسیجر CONFIGURE در پکیج DBMS_AUTO_INDEX استفاده کنید، که میتواند مقادیر زیر را بگیرد.

IMPLEMENT: فعال کردن Automatic Indexing

REPORT ONLY: ایندکس­های جدید ایجاد می­شوند ولی invisible باقی می­ مانند.

OFF: غیرفعال کردن Automatic Indexing

 

وقتی می­ خواهیم این ویژگی را فعال کنیم، خطای زیر را می گیریم:

SQL> EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_MODE','IMPLEMENT');

BEGIN DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_MODE','IMPLEMENT'); END;

 

*

ERROR at line 1:

ORA-40216: feature not supported

ORA-06512: at "SYS.DBMS_SYS_ERROR", line 79

ORA-06512: at "SYS.DBMS_AUTO_INDEX_INTERNAL", line 9180

ORA-06512: at "SYS.DBMS_AUTO_INDEX", line 283

ORA-06512: at line 1

 

EE-ES: Available on Exadata. Not available on Oracle Database Appliance.

 

 

 

 

 

 

 

 

 

 

 

 

 

دلیل آن هم این است که این feature در حال حاضر فقط در نسخه های Oracle Database Exadata Cloud Service و Oracle Database Enterprise Edition on Engineered Systems فعال می­ باشد.

Notes ExaCS DBCS EE-EP DBCS EE-HP DBCS EE DBCS SE EE-ES EE SE2

Feature /

Option /

Pack

EE-ES: Available on Exadata. Not available on Oracle Database Appliance. Y N N N N Y N N Automatic Indexing

 

 

 

 

 

 

برای اینکه این feature را تست کنیم می توانیم با استفاده از روش زیر برای نسخه Enterprise Edition آن را فعال کنیم.

SQL> alter system set "_exadata_feature_on"=true scope=spfile sid='*';

[oracle@test1-rac19c ~]$ srvctl stop database -d ORCL

[oracle@test1-rac19c ~]$ srvctl start database -d ORCL

 

 

SQL> EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_MODE','IMPLEMENT');

PL/SQL procedure successfully completed.

 

SQL> SELECT parameter_name, parameter_value

       FROM dba_auto_index_config;

 

PARAMETER_NAME                      PARAMETER_VALUE
----------------------------------- --------------------
AUTO_INDEX_COMPRESSION              OFF
AUTO_INDEX_DEFAULT_TABLESPACE       
AUTO_INDEX_MODE                     IMPLEMENT
AUTO_INDEX_REPORT_RETENTION         31
AUTO_INDEX_RETENTION_FOR_AUTO       373
AUTO_INDEX_RETENTION_FOR_MANUAL
AUTO_INDEX_SCHEMA                   
AUTO_INDEX_SPACE_BUDGET             50

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

غیر فعالسازی Automatic Indexing برای یک یا چند اسکیما

وقتی Automatic Indexing را فعال می کنید به صورت پیش فرض همه اسکیماها میتوانند از این ویژگی استفاده کنند. با استفاده از دستور زیر می توانیم این ویژگی را برای یک یا چند اسکیما غیرفعال کنیم یا اصطلاحا آن اسکیما را در exclusion list قرار دهیم.

SQL> EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_SCHEMA', 'HR', allow => FALSE);

SQL> SELECT parameter_name, parameter_value

       FROM dba_auto_index_config

      WHERE parameter_name = 'AUTO_INDEX_SCHEMA';

 

PARAMETER_NAME        PARAMETER_VALUE

-------------------- ----------------------------------------

AUTO_INDEX_SCHEMA     schema NOT IN (HR)

 

 

 

 

 

 

 

 

 

و با دستور زیر میتوان یک اسکیما را از exclusion list خارج کرد:

EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_SCHEMA', 'HR', NULL);

 

 

دستور زیر همه اسکیماها را از exclusion list خارج می­کند، یعنی Auto Indexing مثل حالت پیش فرض برای همه اسکیماها فعال می­شود.

EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_SCHEMA', NULL, allow => TRUE);

 

 

تغییر retention period برای unused auto index

به صورت پیش فرض auto indexهایی که ایجاد شده ولی استفاده نشده اند، بعد از 373 روز drop می­ شوند. به صورت زیر میتوان این پیش فرض را تغییر داد:

SQL> EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_RETENTION_FOR_AUTO', '90');

PL/SQL procedure successfully completed.

 

SQL> SELECT parameter_name, parameter_value

       FROM dba_auto_index_config

      WHERE parameter_name = 'AUTO_INDEX_RETENTION_FOR_AUTO';

 

PARAMETER_NAME                 PARAMETER_VALUE

------------------------------ --------------------

AUTO_INDEX_RETENTION_FOR_AUTO  90

 

 

 

 

 

 

 

 

 

 

 

و به صورت زیر میتوان آن را به مقدار پیش فرض برگرداند:

SQL> EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_RETENTION_FOR_AUTO', NULL);

SQL> SELECT parameter_name, parameter_value

       FROM dba_auto_index_config

      WHERE parameter_name = 'AUTO_INDEX_RETENTION_FOR_AUTO';

 

PARAMETER_NAME                 PARAMETER_VALUE

------------------------------ --------------------

AUTO_INDEX_RETENTION_FOR_AUTO  373

 

 

 

 

 

 

 

 

 

مشخص کردن retention period برای non-auto indexes

اگر می­خواهید برای ایندکس­ هایی که به صورت دستی هم ایجاد شده اند، یک retention تعیین کنید که بعد از n روز که unused بودند به صورت اتومات drop شوند، می­ توانید با استفاده از روش زیر این کار را انجام دهید. در غیر اینصورت ایندکس ­هایی که به صورت دستی ایجاد شده اند به صورت اتومات drop نمی ­شوند.

SQL> SELECT parameter_name, parameter_value

       FROM dba_auto_index_config

      WHERE parameter_name = 'AUTO_INDEX_RETENTION_FOR_MANUAL';

 

PARAMETER_NAME                    PARAMETER_VALUE

--------------------------------- --------------------

AUTO_INDEX_RETENTION_FOR_MANUAL

 

 

 

 

 

 

 

 

به صورت زیر میتوان این پارامتر را تغییر داد:

SQL> EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_RETENTION_FOR_MANUAL', '60');

PL/SQL procedure successfully completed.

 

SQL> SELECT parameter_name, parameter_value

       FROM dba_auto_index_config

      WHERE parameter_name = 'AUTO_INDEX_RETENTION_FOR_MANUAL';

 

PARAMETER_NAME                  PARAMETER_VALUE

------------------------------  --------------------

AUTO_INDEX_RETENTION_FOR_MANUAL 60

 

 

 

 

 

 

 

 

 

 

 

و به صورت زیر هم میتوان آن را به مقدار پیش فرض برگرداند:

SQL> EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_RETENTION_FOR_MANUAL', NULL);

PL/SQL procedure successfully completed.

 

SQL> SELECT parameter_name, parameter_value

       FROM dba_auto_index_config

      WHERE parameter_name = 'AUTO_INDEX_RETENTION_FOR_MANUAL';

 

PARAMETER_NAME                  PARAMETER_VALUE

------------------------------  --------------------

AUTO_INDEX_RETENTION_FOR_MANUAL

 

 

 

 

 

 

 

 

 

 

 

تعیین یک tablespace مشخص برای auto indexها

می­توانیم برای ذخیره کردن Auto Indexها یک tablespace جداگانه تعریف کنیم. به این نکته توجه داشته باشید که نمیتوان از tablespaceهای پیش فرض اوراکل مثل sysaux برای این کار استفاده کرد.

SQL> EXEC DBMS_AUTO_INDEX.CONFIGURE('AUTO_INDEX_DEFAULT_TABLESPACE','TBS_AUTOIDX');

PL/SQL procedure successfully completed.

 

SQL> SELECT parameter_name, parameter_value

       FROM dba_auto_index_config

      WHERE parameter_name = 'AUTO_INDEX_DEFAULT_TABLESPACE';

 

PARAMETER_NAME                  PARAMETER_VALUE

------------------------------  --------------------

AUTO_INDEX_DEFAULT_TABLESPACE   TBS_AUTOIDX

 

 

 

 

 

 

 

 

 

 

 

ارائه یک مثال از کاربرد Auto Indexing

1. ابتدا یک جدول ایجاد می کنیم و تعداد زیادی رکورد در آن insert میکنیم.

SQL> CREATE TABLE test1.tab_test1_autoidx AS

( SELECT * FROM dba_objects );

 

SQL> INSERT INTO test1.tab_test1_autoidx

     ( SELECT * FROM test1.tab_test1_autoidx );

     COMMIT;

 

SQL> exec DBMS_STATS.GATHER_TABLE_STATS('TEST1','TAB_TEST1_AUTOIDX');

 

SQL> SELECT COUNT(*)

       FROM test1.tab_test1_autoidx;

     COUNT(*)

     ----------

     586248

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2. روی جدول ایجاد شده یک select اجرا می­ کنیم و plan را برای آن دستور مشاهده می­ کنیم.

SQL> SELECT * FROM test1.tab_test1_autoidx WHERE owner='DBSNMP';

 

SQL> EXPLAIN PLAN FOR

SELECT * FROM test1.tab_test1_autoidx WHERE owner='DBSNMP';

 

SQL> select * from table(dbms_xplan.display());

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3. اگر دستور بالا را به تعداد دفعات زیاد اجرا کنیم، بعد از طی یک زمان مشخص، مشاهده می­کنیم که یک ایندکس برای این جدول ایجاد شده است. مراحل ایجاد آن ایندکس را در view زیر می­توانیم ببینیم.

SQL> SELECT execution_name, index_name, index_owner, command, statement

      FROM dba_auto_index_ind_actions

     WHERE table_name = 'TAB_TEST1_AUTOIDX';

 

 

 

 

STATEMENT COMMAND INDEX_NAME INDEX_OWNER EXECUTION_NAME  
CREATE INDEX "TEST1"."SYS_AI_85xrrw9rhauv0"   ON "TEST1"."TAB_TEST1_AUTOIDX"("OWNER") TABLESPACE "TBS_AUTOIDX" UNUSABLE INVISIBLE AUTO  ONLINE CREATE INDEX SYS_AI_85xrrw9rhauv0 TEST1 SYS_AI_2020-12-01/10:40:22
ALTER INDEX "TEST1"."SYS_AI_85xrrw9rhauv0"   REBUILD  ONLINE REBUILD INDEX SYS_AI_85xrrw9rhauv0 TEST1 SYS_AI_2020-12-01/10:40:22
ALTER INDEX "TEST1"."SYS_AI_85xrrw9rhauv0"   VISIBLE   ALTER INDEX VISIBLE SYS_AI_85xrrw9rhauv0 TEST1 SYS_AI_2020-12-01/10:40:22

 

 

 

 

 

 

 

 

 

 

ستونی که تقریبا در تمام viewهای مرتبط با Auto Indexها مشترک است، execution_name است. بر این اساس میتوانیم بر اساس execution_name یا execution_nameهای مرتبط با یک ایندکس، اطلاعاتی از دیگر viewها راجع به آن بدست آوریم.

SQL> SELECT *

       FROM dba_auto_index_statistics

      WHERE execution_name = 'SYS_AI_2020-12-01/10:40:22';

 

 

 

 

 

 

 

 

 

 

 

 

 

لیست viewهای دیگر که میتوان از آنها در مورد Auto Indexها اطلاعات بدست آورد در زیر آمده است:

DBA_AUTO_INDEX_EXECUTIONS

DBA_AUTO_INDEX_SQL_ACTIONS

DBA_AUTO_INDEX_VERIFICATIONS

 

 

 

 

بعد از ایجاد ایندکس وقتی مجددا دستور قبل را اجرا کردم، plan آن به شکل زیر تغییر پیدا کرده بود:

SQL> EXPLAIN PLAN FOR

SELECT * FROM test1.tab_test1_autoidx WHERE owner='DBSNMP';

 

SQL> SELECT * FROM table(dbms_xplan.display());

 

 

 

 

 

 

 

 

 

 

 

 

 

 

از view زیر هم می­توانیم لیست تمام Auto Indexهای ایجاد شده در دیتابیس را مشاهده کنیم:

SELECT *

  FROM dba_indexes

 WHERE auto = 'YES';

 

 

 

 

تهیه ریپورتهای مختلف بر اساس یک بازه زمانی

میتوانیم با استفاده از یک سری پروسیجرها، گزارشهای مختلفی در رابطه با Automatic Indexها تهیه کنیم. نوع این ریپورت­ ها میتواند HTML، TEXT و XML باشد. اسامی این پروسیجرها به شرح زیر می­باشد:

DBMS_AUTO_INDEX.REPORT_ACTIVITY();

DBMS_AUTO_INDEX.REPORT_LAST_ACTIVITY();

 

 

 

یک نمونه از اجرای این ریپورت­ها در زیر آمده است:

SET SERVEROUTPUT ON

DECLARE

    report CLOB := NULL;

BEGIN

    report := dbms_auto_index.report_activity(

              activity_start => to_timestamp('2020-12-01','YYYY-MM-DD'),

              activity_end => to_timestamp('2020-12-02','YYYY-MM-DD'),

              type => 'HTML',

              section => 'ALL',

              level => 'ALL');

    dbms_output.put_line(report);

END;

 

 

 

 

 

 

 

 

 

 

 

 

 

خروجی ریپورت بالا به شکل زیر است:

 AutoIndex_Report.html

 

منابع

https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/managing-indexes.html#GUID-FAE533D7-9C9E-48C9-9A61-EBC060BF3D70

https://webcache.googleusercontent.com/search?q=cache:NvWPTTaq_csJ:https://juliandontcheff.wordpress.com/2019/02/18/automatic-indexing-in-19c/+&cd=6&hl=en&ct=clnk&gl=ir