注:

使用 Oracle Cloud Infrastructure Monitoring 服务从 Oracle Autonomous Database 发布定制度量

简介

Oracle Autonomous Database 革新了采用全球首个“自主运行”数据库来管理数据的方式。Oracle Autonomous Database 正在帮助世界各地的企业的关键业务应用作为其主要数据源。

Oracle Autonomous Database 提供了许多与数据库相关的服务指标,这得益于它与 Oracle Cloud Infrastructure (OCI) Monitoring 服务的深度集成。据说,我们的许多创新客户希望进一步提高他们的监测能力。这些客户想要收集、发布和分析他们自己的有关 Oracle Autonomous Database 中存储的应用数据的度量。我们调用这些定制指标,它们是应用可以通过 OCI SDK 提供的简单 REST API 收集 OCI 监视服务的指标。

此教程展示您使用一些 PL/SQL 脚本从 Oracle Autonomous Database 发布定制度量的方式,以及在 Oracle Cloud 控制台中单击几下即可轻松地发布定制度量。

此教程以电子商务采购订单数据库方案为例来展示如何计算和收集数据度量。您将了解如何定期计算度量,该度量表示电子商务应用程序收到的每个订单状态(已履行、已接受、已拒绝等)的计数。最后,将这些定制度量发布到 Oracle Cloud Monitoring Service。

先决条件

  1. 访问 Oracle Cloud 免费层或付费账户。
  2. 共享或专用的任何类型的 Oracle Autonomous Database 实例。

    注意:本教程使用 Oracle Autonomous Database 作为 Oracle Autonomous Transaction Processing 实例,共享基础结构上只有 1 个 OCPU 和 1 TB 存储。您可以使用 Oracle Cloud 免费层账户创建此账户。

  3. 熟悉基本 PL/SQL。
  4. 熟悉 Oracle Cloud 控制台。
  5. 您可以使用任何 Oracle Database 客户端,例如 SQL Developer 或 SQL*Plus。如果您不熟悉 Oracle Autonomous Transaction Processing,请参阅连接到 Autonomous Database。对于 Oracle Autonomous Transaction Processing,请参阅 SQL Developer Web,该 Web 位于 Oracle Autonomous Transaction Processing 的 Oracle Cloud 控制台页中。使用 SQL Developer Web 时,无需使用 Oracle Wallet。
  6. ADMIN 用户访问您的 Oracle Autonomous Transaction Processing 实例。
  7. 基本熟悉 Oracle Cloud 概念,例如 OCI Monitoring ServicePostMetricData API 用于发布定制度量动态组和资源原则

解决方案概览

我们将在 Oracle Autonomous Database 实例中部署一个简单的 PL/SQL 脚本,该脚本计划定期运行,以在 OCI 监视服务中计算、收集和发布定制度量。

此外,Oracle Autonomous Database 实例可以具有专用或公共端点。Oracle Autonomous Database 与 OCI Monitoring Service 之间的通信是在 Oracle Cloud 网络中进行的,该网络超快速且具有高可用性。无需设置服务网关。

在本教程中,我们将介绍您需要了解的所有信息,直到您需要从 Oracle Autonomous Database 到 OCI Monitoring 服务获取定制度量。

概览

  1. 为 Oracle Autonomous Transaction Processing 实例创建动态组,并授权其通过策略将度量发布到 Oracle Cloud Infrastructure (OCI) 监视服务。
  2. 在 Oracle Autonomous Transaction Processing 实例中创建具有必备权限的新数据库用户或方案,或使用必要权限更新现有数据库用户或方案。
  3. 创建一个名为 SHOPPING_ORDER 的表,存储我们示例电子商务应用程序的数据(您可以根据表中存储的客户订单计算定制度量)。
  4. 定义 PL/SQL 脚本以填充 SHOPPING_ORDER 表中客户订单的随机数据。
  5. 定义 PL/SQL 脚本以计算、收集和缓冲以及在 OCI 监视服务中发布定制度量。
  6. 计划并运行步骤 4 和 5 中的脚本。
  7. 观察 Oracle Cloud 控制台上发布的定制度量。

在生产用例中,您有自己的应用程序来填充实际数据和更新。因此,在这种情况下不需要第 3 步和第 4 步。

步骤

  1. 为 Oracle Autonomous Transaction Processing 实例创建动态组,并授权其使用策略将度量发布到 Oracle Cloud Infrastructure (OCI) 监视服务。

    1. 使用以下规则为 Oracle Autonomous Transaction Processing 实例(或实例)创建名为 adb_dg 的动态组:

      ALL {resource.type = 'autonomousdatabase', resource.compartment.id = '<compartment OCID for your ADB instance>'}

      或者,您也可以选择单个 Oracle Autonomous Transaction Processing 实例,而不是区间中的所有实例:

      ALL {resource.type = 'autonomousdatabase', resource.id = '<OCID for your ATP instance>'}

      Oracle Autonomous Database 的动态组

    2. 创建 Oracle Cloud Infrastructure Identity and Access Management (IAM) 策略,以授权动态组 adb_dg 使用名为 adb_dg_policy 的策略和策略规则 Allow dynamic-group adb_dg to read metrics in compartment <Your Oracle Autonomous Transaction Processing compartment OCID> 将度量发布到 OCI 监视服务。

      现在,您的 Oracle Autonomous Transaction Processing 服务(由动态组 adb_dg 的定义涵盖)有权在同一区间中发布度量。

      您无权向 OCI 监视服务发布度量的任何 Oracle Autonomous Transaction Processing 数据库用户均为“是”。在 Oracle Autonomous Transaction Processing 上运行的 PL/SQL 仍无法将任何度量发布到 OCI 监视服务。您将在步骤 3c 中执行此操作。

  2. 在 Oracle Autonomous Transaction Processing 实例中创建具有必备权限的新数据库用户或方案,或使用必要权限更新现有数据库用户或方案。

    1. 在 Oracle Autonomous Transaction Processing 实例中创建名为 ECOMMERCE_USER 的数据库用户或方案。您可以将其创建为每个 Oracle Autonomous Transaction Processing 实例的 ADMIN 用户。如果选择使用现有用户,则可以跳过此步骤。

      CREATE USER ECOMMERCE_USER IDENTIFIED BY "Password of your choice for this User";
      

      注意:现在,我们将用户(或方案)称为 ECOMMERCE_USER,其余步骤保持不变(无论您使用的是现有用户还是新创建的用户)。

    2. 将与 Oracle Database 相关的必要权限授予 ECOMMERCE_USER

      GRANT  CREATE TABLE, ALTER ANY INDEX, CREATE PROCEDURE, CREATE JOB, 
             SELECT ANY TABLE, EXECUTE ANY PROCEDURE, 
             UPDATE ANY TABLE, CREATE SESSION,
             UNLIMITED TABLESPACE, CONNECT, RESOURCE TO ECOMMERCE_USER;
      GRANT  SELECT ON "SYS"."V_$PDBS" TO ECOMMERCE_USER;
      GRANT  EXECUTE ON "C##CLOUD$SERVICE"."DBMS_CLOUD" to ECOMMERCE_USER;
      GRANT  SELECT ON SYS.DBA_JOBS_RUNNING TO ECOMMERCE_USER;
      
    3. 为 Oracle Cloud 资源主用户启用 Oracle Database 身份证明并授予其对 db-user ECOMMERCE_USER 的访问权限。这会将您在步骤 1 中创建的动态组 adb_dg 连接到数据库用户 ECOMMERCE_USER,并授权用户将度量发布到 OCI 监视服务。有关详细信息,请参阅使用资源主用户访问 Oracle Cloud Infrastructure 资源

      EXEC DBMS_CLOUD_ADMIN.ENABLE_RESOURCE_PRINCIPAL(username => 'ECOMMERCE_USER');
      
    4. (可选)验证在上一步中完成的操作。

      注:启用 Oracle Cloud 资源主用户的 Oracle Database 身份证明后,该身份证明始终由 Oracle Autonomous Database 的 ADMIN 用户拥有。您可以验证:

      SELECT OWNER, CREDENTIAL_NAME FROM DBA_CREDENTIALS WHERE CREDENTIAL_NAME = 'OCI$RESOURCE_PRINCIPAL'  AND OWNER =  'ADMIN';
      
      -- To check if any other user, here ECOMMERCE_USER has access DB credential(hence to OCI Resource Principal), you have to check *DBA_TAB_PRIVS* view, as follows.
      SELECT * from DBA_TAB_PRIVS WHERE DBA_TAB_PRIVS.GRANTEE='ECOMMERCE_USER';
      
  3. 创建一个名为 SHOPPING_ORDER 的示例数据表,以展示对数据库表的度量的计算。

    表方案不言自明,但请注意 STATUS 列:

     DROP TABLE SHOPPING_ORDER;
     CREATE TABLE SHOPPING_ORDER (
         ID                NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY
         PRIMARY KEY,
         CREATED_DATE      TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP,
         DETAILS           VARCHAR2(1000) DEFAULT NULL,
         LAST_UPDATED_DATE TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP,
         STATUS            VARCHAR2(30 CHAR),
         TOTAL_CHARGES     FLOAT DEFAULT 0.0,
         CUSTOMER_ID       NUMBER(19)
     )
         PARTITION BY LIST ( STATUS ) 
         (   PARTITION ACCEPTED VALUES ( 'ACCEPTED' ),
             PARTITION PAYMENT_REJECTED VALUES ( 'PAYMENT_REJECTED' ),
             PARTITION SHIPPED VALUES ( 'SHIPPED' ),
             PARTITION ABORTED VALUES ( 'ABORTED' ),
             PARTITION OUT_FOR_DELIVERY VALUES ( 'OUT_FOR_DELIVERY' ),
             PARTITION ORDER_DROPPED_NO_INVENTORY VALUES ( 'ORDER_DROPPED_NO_INVENTORY' ),
             PARTITION PROCESSED VALUES ( 'PROCESSED' ),
             PARTITION NOT_FULLFILLED VALUES ( 'NOT_FULFILLED' )
         );
     /
    
     -- we move rows from one partition to another, hence we enable row movement for this partioned table
     ALTER TABLE SHOPPING_ORDER ENABLE ROW MOVEMENT;
    

    每个采购订单在其生命周期内可以具有八个状态值中的任意一个:[ACCEPTED, PAYMENT_REJECTED, SHIPPED, ABORTED, OUT_FOR_DELIVERY, ORDER_DROPPED_NO_INVENTORY, PROCESSED, NOT_FULFILLED]

  4. 阅读以下 PL/SQL 脚本;它将填充 SHOPPING_ORDER 表中的数据。在您选择的 Oracle Database 客户端上运行程序。

    该脚本首先会将 TOTAL_ROWS_IN_SHOPPING_ORDER 行数添加到包含随机生成的订单数据的 SHOPPING_ORDER 表中。然后,它将更新数据,随机更改每个 SHOPPING_ORDER 行的 STATUS 值。

    CREATE OR REPLACE PROCEDURE POPULATE_DATA_FEED IS
        ARR_STATUS_RANDOM_INDEX      INTEGER;
        CUSTOMER_ID_RANDOM           INTEGER;
        TYPE STATUS_ARRAY IS VARRAY(8) OF VARCHAR2(30);
        ARRAY STATUS_ARRAY := STATUS_ARRAY('ACCEPTED', 'PAYMENT_REJECTED',
                                        'SHIPPED', 'ABORTED', 'OUT_FOR_DELIVERY',
                                        'ORDER_DROPPED_NO_INVENTORY', 
                                        'PROCESSED', 'NOT_FULFILLED');
        TOTAL_ROWS_IN_SHOPPING_ORDER INTEGER := 15000;
        TYPE ROWID_NT IS TABLE OF ROWID;
        ROWIDS                       ROWID_NT;
    BEGIN     
        -- starting from scratch just be idempotent and have predictable execution time for this stored procedure
        -- deleting existing rows is optional 
        DELETE SHOPPING_ORDER;
    
        -- insert data
        FOR COUNTER IN 1..TOTAL_ROWS_IN_SHOPPING_ORDER LOOP
            ARR_STATUS_RANDOM_INDEX := TRUNC(DBMS_RANDOM.VALUE(LOW => 1, HIGH => 9));
            CUSTOMER_ID_RANDOM := TRUNC(DBMS_RANDOM.VALUE(LOW => 1, HIGH => 8000));
            INSERT INTO SHOPPING_ORDER (STATUS,CUSTOMER_ID) VALUES (ARRAY(ARR_STATUS_RANDOM_INDEX),CUSTOMER_ID_RANDOM);
            COMMIT;          
        END LOOP;
    
        DBMS_OUTPUT.PUT_LINE('DONE WITH INITIAL DATA LOAD');
    
        -- keep on updating the same data
        FOR COUNTER IN 1..8000 LOOP        
    
                --Get the rowids
            SELECT R BULK COLLECT INTO ROWIDS FROM (SELECT ROWID R FROM SHOPPING_ORDER SAMPLE ( 5 ) ORDER BY DBMS_RANDOM.VALUE) RNDM
            WHERE ROWNUM < TOTAL_ROWS_IN_SHOPPING_ORDER + 1;
    
                --update the table
            ARR_STATUS_RANDOM_INDEX := TRUNC(DBMS_RANDOM.VALUE(LOW => 1, HIGH => 9));
    
            FOR I IN 1..ROWIDS.COUNT LOOP
                UPDATE SHOPPING_ORDER SET STATUS = ARRAY(ARR_STATUS_RANDOM_INDEX) WHERE ROWID = ROWIDS(I);
                COMMIT;
            END LOOP;    
            --sleep in-between if you want to run script for longer duration
            --DBMS_SESSION.SLEEP(ROUND(dbms_random.value(low => 1, high => 2)));          
        END LOOP;
    
        DBMS_OUTPUT.PUT_LINE('DONE WITH POPULATE_DATA_FEED');
        EXECUTE IMMEDIATE 'ANALYZE TABLE SHOPPING_ORDER COMPUTE STATISTICS';
    END;
    /
    
  5. 让我们深入探究本教程的实际要点:用于计算定制度量并将其发布到 OCI 监视服务的脚本。在您选择的 Oracle Datbase 客户端上运行它。该脚本是闲置的,可确保您可以多次运行该脚本。现在,逐个分析脚本。

    1. 创建 SHOPPING_ORDER_METRICS_TABLE 表并使用它收集和缓冲计算的度量。

      确保数据表针对运行度量计算的查询进行了优化。您不希望这些查询在数据库上加载过多,这会干扰生产用例。

      DECLARE
          COUNT_VAR NUMBER := 0;
      BEGIN
          SELECT COUNT(*) INTO COUNT_VAR FROM ALL_TABLES WHERE TABLE_NAME = 'SHOPPING_ORDER_METRICS_TABLE';
      
          IF COUNT_VAR > 0 THEN
              DBMS_OUTPUT.PUT_LINE('TABLE EXISTS ALREADY!');
          ELSE
              -- table doesn't exist
              EXECUTE IMMEDIATE 'CREATE TABLE SHOPPING_ORDER_METRICS_TABLE(
                                  ID                 NUMBER         GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
                                  CREATED_DATE       TIMESTAMP(6)   DEFAULT CURRENT_TIMESTAMP,
                                  STATUS             VARCHAR2(30 CHAR),
                                  COUNT              NUMBER)';
          END IF;
      
      END;
      /
      
    2. 创建用于计算度量的存储过程:按状态值统计的订单数,此度量收集的时间实例

      然后,存储过程将缓冲您在上一步中创建的缓冲区表 SHOPPING_ORDER_METRICS_TABLE 中的计算度量。缓冲区用于确保您可以在将度量发布到 OCI 监视服务时再次尝试发布这些度量。

      CREATE OR REPLACE PROCEDURE COMPUTE_AND_BUFFER_METRICS IS
      BEGIN    
          -- compute simple metric for getting count order by order-status 
          -- and store in buffer table SHOPPING_ORDER_METRICS_TABLE
          INSERT INTO SHOPPING_ORDER_METRICS_TABLE (STATUS, COUNT, CREATED_DATE) 
          SELECT STATUS, COUNT(*), SYSTIMESTAMP AT TIME ZONE 'UTC' FROM SHOPPING_ORDER SO GROUP BY SO.STATUS;
      
          -- we buffer at most 1000 metric points, please configure as per your needs
          DELETE FROM SHOPPING_ORDER_METRICS_TABLE SOMT WHERE SOMT.ID NOT IN
              (SELECT ID FROM SHOPPING_ORDER_METRICS_TABLE ORDER BY CREATED_DATE FETCH FIRST 1000 ROWS ONLY);
      
          COMMIT;    
          DBMS_OUTPUT.PUT_LINE('compute and buffering done @ ' || TO_CHAR(SYSTIMESTAMP));
      END;
      /
      

      要限制缓冲区表的大小,请在缓冲区表的大小超过 1,000 行时对其进行修剪。

    3. 使用 PL/SQL 函数 PREPARE_JSON_OBJECT_FROM_METRIC_ROWS 将缓冲度量从 SHOPPING_ORDER_METRICS_TABLE 转换为 PostMetricsData API 请求中预期的 JSON 对象。

      注:

      • 此函数将最近度量数据点的 BATCH_SIZE_FOR_EACH_POST 号从 SHOPPING_ORDER_METRICS_TABLE 转换为 OCI_METADATA_JSON_OBJ JSON_OBJECT_T
      • OCI_METADATA_JSON_OBJ 是 PL/SQL 内置 JSON 数据类型 JSON_OBJECT_T 的变量。我们使用 PostMetricDataDetails(PostMetricsData API 的请求正文)中列出的相同 JSON 结构构建了 OCI_METADATA_JSON_OBJ
       CREATE OR REPLACE FUNCTION GET_METRIC_DATA_DETAILS_JSON_OBJ (
           IN_ORDER_STATUS         IN VARCHAR2,
           IN_METRIC_CMPT_ID       IN VARCHAR2,
           IN_ADB_NAME             IN VARCHAR2,
           IN_METRIC_VALUE         IN NUMBER,
           IN_TS_METRIC_COLLECTION IN VARCHAR2
       ) RETURN JSON_OBJECT_T IS
           METRIC_DATA_DETAILS JSON_OBJECT_T;
           MDD_METADATA        JSON_OBJECT_T;
           MDD_DIMENSIONS      JSON_OBJECT_T;
           ARR_MDD_DATAPOINT   JSON_ARRAY_T;
           MDD_DATAPOINT       JSON_OBJECT_T;
       BEGIN
           MDD_METADATA := JSON_OBJECT_T();
           MDD_METADATA.PUT('unit', 'row_count'); -- metric unit is arbitrary, as per choice of developer
      
           MDD_DIMENSIONS := JSON_OBJECT_T();
           MDD_DIMENSIONS.PUT('dbname', IN_ADB_NAME);
           MDD_DIMENSIONS.PUT('schema_name', SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA'));
           MDD_DIMENSIONS.PUT('table_name', 'SHOPPING_ORDER');
           MDD_DIMENSIONS.PUT('status_enum', IN_ORDER_STATUS);
           MDD_DATAPOINT := JSON_OBJECT_T();
           MDD_DATAPOINT.PUT('timestamp', IN_TS_METRIC_COLLECTION); --timestamp value RFC3339 compliant
           MDD_DATAPOINT.PUT('value', IN_METRIC_VALUE);
           MDD_DATAPOINT.PUT('count', 1);
           ARR_MDD_DATAPOINT := JSON_ARRAY_T();
           ARR_MDD_DATAPOINT.APPEND(MDD_DATAPOINT);
           METRIC_DATA_DETAILS := JSON_OBJECT_T();
           METRIC_DATA_DETAILS.PUT('datapoints', ARR_MDD_DATAPOINT);
           METRIC_DATA_DETAILS.PUT('metadata', MDD_METADATA);
           METRIC_DATA_DETAILS.PUT('dimensions', MDD_DIMENSIONS);
      
           -- namespace, resourceGroup and name for the custom metric are arbitrary values, as per choice of developer
           METRIC_DATA_DETAILS.PUT('namespace', 'custom_metrics_from_adb');
           METRIC_DATA_DETAILS.PUT('resourceGroup', 'ecommerece_adb');
           METRIC_DATA_DETAILS.PUT('name', 'customer_orders_submitted');
      
           -- since compartment OCID is fetched using ADB metadata, our custom metrics will land up in same compartment as our ADB
           METRIC_DATA_DETAILS.PUT('compartmentId', IN_METRIC_CMPT_ID);
           RETURN METRIC_DATA_DETAILS;
       END;
       /
      
       CREATE OR REPLACE FUNCTION PREPARE_JSON_OBJECT_FROM_METRIC_ROWS (
           OCI_METADATA_JSON_OBJ JSON_OBJECT_T, BATCH_SIZE_FOR_EACH_POST NUMBER
       ) RETURN JSON_OBJECT_T IS
           OCI_POST_METRICS_BODY_JSON_OBJ JSON_OBJECT_T;
           ARR_METRIC_DATA                JSON_ARRAY_T;
           METRIC_DATA_DETAILS            JSON_OBJECT_T;
       BEGIN
           -- prepare JSON body for postmetrics api..
           -- for details please refer https://docs.oracle.com/en-us/iaas/api/#/en/monitoring/20180401/datatypes/PostMetricDataDetails
           ARR_METRIC_DATA := JSON_ARRAY_T();
      
           -- PostMetrics api has soft limit of 50 unique metric stream per call, hence we cap it at 50. 
           -- For Production usecase where every metric data point is important, we can use chunking
           FOR METRIC_ROW IN (SELECT * FROM SHOPPING_ORDER_METRICS_TABLE 
                               ORDER BY CREATED_DATE DESC FETCH FIRST BATCH_SIZE_FOR_EACH_POST ROWS ONLY) LOOP
      
               --DBMS_OUTPUT.PUT_LINE('inside for loop ' || METRIC_ROW.STATUS );
      
               METRIC_DATA_DETAILS := GET_METRIC_DATA_DETAILS_JSON_OBJ(
                                   METRIC_ROW.STATUS,
                                   OCI_METADATA_JSON_OBJ.GET_STRING('COMPARTMENT_OCID'), 
                                   OCI_METADATA_JSON_OBJ.GET_STRING('DATABASE_NAME'), 
                                   METRIC_ROW.COUNT, 
                                   TO_CHAR(METRIC_ROW.CREATED_DATE, 'yyyy-mm-dd"T"hh24:mi:ss.ff3"Z"'));
               --DBMS_OUTPUT.PUT_LINE('METRIC_DATA_DETAILS '|| METRIC_DATA_DETAILS.to_clob);
               ARR_METRIC_DATA.APPEND(METRIC_DATA_DETAILS);
      
           END LOOP;
           DBMS_OUTPUT.PUT_LINE('done with for loop ');
           OCI_POST_METRICS_BODY_JSON_OBJ := JSON_OBJECT_T();
           OCI_POST_METRICS_BODY_JSON_OBJ.PUT('metricData', ARR_METRIC_DATA);
      
           RETURN OCI_POST_METRICS_BODY_JSON_OBJ;
       END;
       /
      
    4. 使用 PL/SQL 代码通过 PostMetricsData API 将这些转换的度量发布到 OCI 监视服务。

      注意:我们使用名为 POST_METRICS_DATA_TO_OCI 的 PL/SQL 函数和存储过程 PUBLISH_BUFFERED_METRICS_TO_OCI 来实现此目的。

      
       CREATE OR REPLACE FUNCTION POST_METRICS_DATA_TO_OCI(OCI_POST_METRICS_BODY_JSON_OBJ JSON_OBJECT_T, ADB_REGION VARCHAR2) 
       RETURN NUMBER 
       IS
           RETRY_COUNT                    INTEGER := 0;
           MAX_RETRIES                    INTEGER := 3;
           RESP                           DBMS_CLOUD_TYPES.RESP;
           EXCEPTION_POSTING_METRICS      EXCEPTION;
           SLEEP_IN_SECONDS               INTEGER := 5;
       BEGIN
           FOR RETRY_COUNT in 1..MAX_RETRIES  LOOP
                   -- invoking REST endpoint for OCI Monitoring API
                   -- for details please refer https://docs.oracle.com/en-us/iaas/api/#/en/monitoring/20180401/MetricData/PostMetricData
               RESP := DBMS_CLOUD.SEND_REQUEST(CREDENTIAL_NAME => 'OCI$RESOURCE_PRINCIPAL', 
                                               URI => 'https://telemetry-ingestion.'|| ADB_REGION|| '.oraclecloud.com/20180401/metrics', 
                                               METHOD =>DBMS_CLOUD.METHOD_POST, 
                                               BODY => UTL_RAW.CAST_TO_RAW(OCI_POST_METRICS_BODY_JSON_OBJ.TO_STRING));
      
               IF DBMS_CLOUD.GET_RESPONSE_STATUS_CODE(RESP) = 200 THEN    -- when it is 200 from OCI Metrics API, all good
                   DBMS_OUTPUT.PUT_LINE('POSTED METRICS SUCCESSFULLY TO OCI MONIOTRING');
                   RETURN 200;
               ELSIF DBMS_CLOUD.GET_RESPONSE_STATUS_CODE(RESP) = 429 THEN -- 429 is caused by throttling
                   IF RETRY_COUNT < MAX_RETRIES THEN
                       -- increase sleep time for each retry, doing exponential backoff
                       DBMS_SESSION.SLEEP(POWER(SLEEP_IN_SECONDS, RETRY_COUNT+1));
                       DBMS_OUTPUT.PUT_LINE('RETRYING THE POSTMETRICS API CALL');
                   ELSE
                       DBMS_OUTPUT.PUT_LINE('ABANDONING POSTMETRICS CALLS, AFTER 3 RETRIES, CAUSED BY THROTTLING, WILL BERETRIED IN NEXT SCHEDULED RUN');
                       RETURN 429;
                   END IF;
               ELSE -- for any other http status code....1. log error, 2. raise exception and then quit posting metrics, as it is most probably a persistent error 
                       DBMS_OUTPUT.PUT_LINE('IRRECOVERABLE ERROR HAPPENED WHEN POSTING METRICS TO OCI MONITORING, PLEASE SEE CONSOLE FOR ERRORS');
                       -- Response Body in TEXT format
                       DBMS_OUTPUT.put_line('Body: ' || '------------' || CHR(10) || DBMS_CLOUD.get_response_text(resp) || CHR(10));
                       -- Response Headers in JSON format
                       DBMS_OUTPUT.put_line('Headers: ' || CHR(10) || '------------' || CHR(10) || DBMS_CLOUD.get_response_headers(resp).to_clob || CHR(10));
                       -- Response Status Code
                       DBMS_OUTPUT.put_line('Status Code: ' || CHR(10) || '------------' || CHR(10) || DBMS_CLOUD.get_response_status_code(resp));
                       RETURN 500;
               END IF;
      
           END LOOP;
       END;
       /
      
       CREATE OR REPLACE PROCEDURE PUBLISH_BUFFERED_METRICS_TO_OCI IS
           OCI_METADATA_JSON_RESULT       VARCHAR2(1000);
           OCI_METADATA_JSON_OBJ          JSON_OBJECT_T;
           ADB_REGION                     VARCHAR2(25);
           OCI_POST_METRICS_BODY_JSON_OBJ JSON_OBJECT_T;
           TYPE ID_ARRAY IS VARRAY(50) OF NUMBER;
           ARRAY                          ID_ARRAY;
           TOTAL_METRICS_STREAM_CNT       NUMBER;
           HTTP_CODE                      NUMBER;
           BATCH_SIZE_FOR_EACH_POST       NUMBER:=8; -- not more than 50! as per PostMetricsData API docs
       BEGIN
           -- get the meta-data for this ADB Instance like its OCI compartmentId, region and DBName etc; as JSON in oci_metadata_json_result
           SELECT CLOUD_IDENTITY INTO OCI_METADATA_JSON_RESULT FROM V$PDBS; 
           -- dbms_output.put_line(oci_metadata_json_result);
      
           -- convert the JSON string into PLSQL JSON native JSON datatype json_object_t variable named oci_metadata_json_result
           OCI_METADATA_JSON_OBJ := JSON_OBJECT_T.PARSE(OCI_METADATA_JSON_RESULT);
      
      
           WHILE(TRUE) LOOP
               SELECT COUNT(*) INTO TOTAL_METRICS_STREAM_CNT FROM SHOPPING_ORDER_METRICS_TABLE;
               IF(TOTAL_METRICS_STREAM_CNT < BATCH_SIZE_FOR_EACH_POST) THEN
                   DBMS_OUTPUT.PUT_LINE('Only ' || TOTAL_METRICS_STREAM_CNT || ' metrics datapoints buffered(less than batch size' || BATCH_SIZE_FOR_EACH_POST || '), hence waiting for buffer to fill up');
                   EXIT;
               END IF;   
      
               OCI_POST_METRICS_BODY_JSON_OBJ := PREPARE_JSON_OBJECT_FROM_METRIC_ROWS(OCI_METADATA_JSON_OBJ, BATCH_SIZE_FOR_EACH_POST);
               ADB_REGION := OCI_METADATA_JSON_OBJ.GET_STRING('REGION');
      
               HTTP_CODE := POST_METRICS_DATA_TO_OCI(OCI_POST_METRICS_BODY_JSON_OBJ, ADB_REGION);
      
               IF(HTTP_CODE = 200) THEN
                   DBMS_OUTPUT.PUT_LINE('Deleting the published metrics');
                   DELETE FROM SHOPPING_ORDER_METRICS_TABLE WHERE ID IN 
                                       (SELECT ID FROM SHOPPING_ORDER_METRICS_TABLE 
                                       ORDER BY CREATED_DATE DESC FETCH FIRST 50 ROWS ONLY);
               END IF;    
      
               COMMIT;    
      
               -- PostMetricData API has TPS rate limit of 50, just for safety  
               -- Hence sleep for atleast seconds => (1/50) to avoid throttling
               -- DBMS_SESSION.SLEEP(seconds => (1/50));                                  
           END LOOP;
       END;
       /
      

      函数 POST_METRICS_DATA_TO_OCI 调用 PostMetricsData API。与每个 Oracle Cloud API 一样,您需要有适当的 OCI IAM 授权才能调用它。使用指定的参数 credential_name => 'OCI$RESOURCE_PRINCIPAL' 传递如下内容。

      数据库身份证明 OCI$RESOURCE_PRINCIPAL 链接到在步骤 2 中创建的动态组 adb_dg,步骤 3 中的 ECOMMERCE_USER 用户已经具有访问权限。

      因此,通过信任链,由 ECOMMERCE_USER 执行的此 PL/SQL 脚本有权将定制度量发布到 OCI 监视服务。

      RESP := DBMS_CLOUD.SEND_REQUEST(CREDENTIAL_NAME => 'OCI$RESOURCE_PRINCIPAL', 
                                              URI => 'https://telemetry-ingestion.'|| ADB_REGION|| '.oraclecloud.com/20180401/metrics', 
                                              METHOD =>DBMS_CLOUD.METHOD_POST, 
                                              BODY => UTL_RAW.CAST_TO_RAW(OCI_POST_METRICS_BODY_JSON_OBJ.TO_STRING));
      

      dbms_cloud.send_request 是一个内置的 PL/SQL 存储过程,用于调用所有其余端点(预安装在每个 Oracle Autonomous Database 中)。在此处,我们使用它来调用 OCI 监视服务 REST API。

      存储过程 PUBLISH_BUFFERED_METRICS_TO_OCI 使用我们迄今为止讨论的所有功能和过程将所有缓冲的度量发布到 OCI 监视服务。要成为执行者,它将为每个 PostMetricsData API 调用创建大小为 BATCH_SIZE_FOR_EACH_POST 的度量数据点的批处理。

  6. 计划并运行步骤 4 和 5 中的脚本。在第 4 步和第 5 步中,您在 Oracle Database 中定义了存储过程和函数,但尚未执行这些过程和函数。

    1. 运行步骤 4 中的脚本以填充 SHOPPING_ORDER 表中的数据。该脚本在具有 1 个 OCPU 和 1 TB 存储的 Oracle 自治事务处理实例上将运行大约 15 分钟。

      -- we schedule the data feed to run immediately, asynchronously and only once!
      BEGIN
           DBMS_SCHEDULER.CREATE_JOB(
                                   JOB_NAME => 'POPULATE_DATA_FEED_JOB', 
                                   JOB_TYPE => 'STORED_PROCEDURE', 
                                   JOB_ACTION => 'POPULATE_DATA_FEED',
                                   ENABLED => TRUE, 
                                   AUTO_DROP => TRUE, -- drop job after 1 run.
                                   COMMENTS => 'ONE-TIME JOB');
       END;
       /
      
       -- just for our information
       SELECT STATUS,count(*) FROM SHOPPING_ORDER GROUP BY STATUS; 
      
    2. 剩下的唯一问题是第 5 步:计算和发布到 OCI Monitoring 服务中的 PL/SQL 脚本的定期执行。

      我们使用 PL/SQL 内置存储过程 DBMS_SCHEDULER.CREATE_JOB 执行如下操作。

      创建的 Oracle Database SCHEDULED_JOB 持续 20 分钟(1,200 秒)。它执行定制度量计算并每分钟发布一次。

      对于生产用例,请根据需要进行配置。

      BEGIN
          DBMS_SCHEDULER.CREATE_JOB(
                                  JOB_NAME => 'POST_METRICS_TO_OCI_JOB', 
                                  JOB_TYPE   => 'PLSQL_BLOCK',
                                  JOB_ACTION => 'BEGIN 
                                                  ECOMMERCE_USER.COMPUTE_AND_BUFFER_METRICS(); 
                                                  ECOMMERCE_USER.PUBLISH_BUFFERED_METRICS_TO_OCI();
                                                 END;',
                                  START_DATE => SYSTIMESTAMP,                        -- start the first run immediately
                                  REPEAT_INTERVAL => 'FREQ=SECONDLY;INTERVAL=60',    -- run this PLSQL_BLOCK every 60th second 
                                  END_DATE => SYSTIMESTAMP + INTERVAL '1200' SECOND, -- this schedule is only active
                                  AUTO_DROP => TRUE,                                 -- delete the schedule after 1200 seconds, effectively after its last run
                                  ENABLED => TRUE,                                   -- enable this schedule as soon as it is created
                                  COMMENTS => 'JOB TO POST DB METRICS TO OCI MONITORING SERVICE, RUNS EVERY 10TH SECOND');
      END;
      /   
      
  7. 在 Oracle Cloud 控制台上浏览发布的定制度量。

    1. 从导航菜单中,单击 Metrics Explorer

      转到 Oracle Cloud 控制台上的度量浏览器

    2. Metrics Explorer 中,选择名称空间作为 custom_metrics_from_adb,选择 resourceGroup 作为 ecommerece_adb,选择度量名称作为您为定制度量设置的 customer_orders_submitted

      您为定制度量设置的所有元数据和维均可用。

      您可以构建 MQL 查询,以根据您的需求和用例分析这些度量。接下来,您可能需要在度量流上设置 Oracle Cloud 预警来提醒操作团队。

      这将为您选择的 Oracle Autonomous Database 度量自动执行观察循环。

      了解您发布的定制度量

结论

您已学习了如何使用适用于 PL/SQL 的 OCI SDK 使用简单的 PL/SQL 脚本将定制度量从 Oracle Autonomous Database 发出到 Oracle Cloud Infrastructure (OCI) 监视服务。Oracle Database 是许多业务工作流的“事实来源”,因此这是一个非常强大的功能。

定制度量是 OCI Monitoring 服务一流的市民,与原生指标相当。您可以使用相同的功能强大的度量查询语言来分析它们,并在它们上设置报警,以便在发生任何感兴趣或麻烦事件时通知您。

这可以为您的所有指标提供“玻璃单窗格”视图,无论是 OCI 服务还是应用和数据库生成的定制指标。

致谢

更多学习资源

docs.oracle.com/learn 上浏览其他实验室,或者在 Oracle Learning YouTube 渠道上访问更多免费学习内容。此外,访问 education.oracle.com/learning-explorer 以成为 Oracle Learning Explorer。

有关产品文档,请访问 Oracle 帮助中心