admin

  • 随着 MySQL 对 JSON 类型的原生支持,操作 JSON 数据已变得非常高效与强大。在过去数年的版本中,MySQL 也在不断地增强 JSON 处理相关的功能。在 JSON 处理中需要非常频繁的使用“JSON Path” 语法,而这部分又是略微复杂的部分,本文将系统的介绍如何在 MySQL 中使用 JSON Path,包括语法规则、各种通配符用法、递归匹配等高级技巧,并通过丰富示例帮助开发者快速掌握。

    什么是 JSON Path?

    JSON Path 是一种表示法,用来描述如何在 JSON 文档中定位数据。类似于文件系统路径,JSON Path 指引着从 JSON 根节点出发,逐步深入结构内部。在 MySQL 中,几乎所有的 JSON相关的函数都会使用到,包括:JSON_EXTRACT()JSON_SET()JSON_REPLACE()JSON_REMOVE()JSON_CONTAINS()等。

    我们看到的场景的写法类似于:$.name$.colors[0]$.store**.price等。

    基础语法说明

    JSON Path的基础语法,遵循以下规则:

    • $:表示 JSON 文档的根节点。
    • .:用于访问对象中的属性。
    • ["key"]:另一种访问对象属性的方式,适合处理特殊字符的 key。
    • [index]:访问数组中的元素。
    • *:通配符,匹配所有子元素。
    • **:递归通配符,匹配所有嵌套层级的元素
    • [start to end]:数组范围选择

    JSON Path 基本示例

    示例表与示例数据

    创建带 JSON字段的表,并写入数据:

    CREATE TABLE t1 (
        id INT PRIMARY KEY AUTO_INCREMENT,
        data JSON
    );
    
    INSERT INTO t1 (data) VALUES
    ('{
      "name": "Alice",
      "age": 25,
      "email": "alice@example.com"
    }');

    提取对象字段

    这里使用基本的$.name引用根节点中属性为name的对象,示例如下:

    mysql> SELECT JSON_EXTRACT(data, '$.name') FROM t1;
    +------------------------------+
    | JSON_EXTRACT(data, '$.name') |
    +------------------------------+
    | "Alice"                      |
    +------------------------------+
    1 row in set (0.00 sec)

    也可以使用如下等价的写法data->'$.name'

    mysql> SELECT data->'$.name' FROM t1;
    +----------------+
    | data->'$.name' |
    +----------------+
    | "Alice"        |
    +----------------+

    访问数组元素

    初始化如下数据:

    -- truncate table t1;
    
    INSERT INTO t1 (data) VALUES
    ('{
      "colors": ["red", "green", "blue"]
    }');

    先访问colors属性,再查找该数组对象的第一个元素(注意:编号是0),故 JSON Path$.colors[0],示例如下:

    mysql> SELECT data->'$.colors[0]' AS first_color FROM t1;
    +-------------+
    | first_color |
    +-------------+
    | "red"       |
    +-------------+

    访问数组的范围

    除了像上述展示的,可以使用数值访问数组外,还可以使用0 to 1这样的语法表示一个范围,并访问数组中的多个元素:

    mysql> SELECT data->'$.colors[0 to 1]' AS first_color FROM t1;
    +------------------+
    | first_color      |
    +------------------+
    | ["red", "green"] |
    +------------------+
    
    mysql> SELECT data->'$.colors[1 to 1]' AS first_color FROM t1;
    +-------------+
    | ["green"]   |
    +-------------+
    
    mysql> SELECT data->'$.colors[1 to 2]' AS first_color FROM t1;
    +-------------------+
    | ["green", "blue"] |
    +-------------------+

    使用通配符

    准备示例数据

    为了展示相关的示例,这里先给出一个更为复杂的示例数据:

    CREATE TABLE t1 (
        id INT PRIMARY KEY AUTO_INCREMENT,
        data JSON
    );
    -- truncate table t1;
    
    INSERT INTO t1 (data) VALUES
    ('{
      "store": {
        "book": [
          {
            "category": "fiction",
            "title": "Harry Potter",
            "price": 29.99
          },
          {
            "category": "fiction",
            "title": "Lord of the Rings",
            "price": 49.99
          }
        ],
        "bicycle": {
          "color": "red",
          "price": 19.95
        }
      }
    }');

    使用通配符查询所有book

    则可以使用如下的搜索表达式:$.store.book[*]

    SELECT data->'$.store.book[*]' AS all_books 
    FROM t1;
    
    mysql> SELECT data->'$.store.book[*]' AS all_books
        -> FROM t1;
    +--------------------------------------------------------------+
    | all_books                                                    |
    +--------------------------------------------------------------+
    | [{"price": 29.99, "title"...}, {"price": 49.99, "title"...}] |
    +--------------------------------------------------------------+

    这里比较容易错误的写成:$.store.book.*$.store.book.[*]$.store.book*

    使用通配符递归查询

    列出所有书店中的书名

    依旧使用上述的数据,这里可以使用递归的通配符(**)查询结构中所有title属性的取值,则'$.store**.title'

    SELECT data->'$.store**.title' AS all_books 
    FROM t1;
    +---------------------------------------+
    | all_books                             |
    +---------------------------------------+
    | ["Harry Potter", "Lord of the Rings"] |
    +---------------------------------------+

    当然,也可以改成直接从“根”处开始递归查找,即$**.title

    mysql> SELECT data->'$**.title' AS all_books  FROM t1;
    +---------------------------------------+
    | all_books                             |
    +---------------------------------------+
    | ["Harry Potter", "Lord of the Rings"] |
    +---------------------------------------+

    类似的,我们还可以取出所有的价格:

    SELECT 
      data->'$.store**.price' AS book_prices 
    FROM t1;
    +-----------------------+
    | book_prices           |
    +-----------------------+
    | [29.99, 49.99, 19.95] |
    +-----------------------+

    小结

    熟悉 MySQL JSON Path Syntax 可以让开发者更加高效操作 JSON 数据。更多参考:

  • 标题: OceanBase创始人阳老师荣誉退休,致敬并祝福;Databricks10亿美金收购创新数据库Neon;MySQL 30周年中国区活动下周举行

    重点更新

    OceanBase创始人阳老师荣誉退休,致敬并祝福[11]

    RedisSSPL协议改成更被社区接受的AGPL协议, 并发布 8.0 版本 [10]

    Databricks 持续并购,将以 10 亿美元收购极具创新架构的开源数据库 Neon[1][2] 。Neon 提供了Serverless、扩展能力极强、具备非常好的AI特性的数据库服务,Neon 在架构上最大限度的利用了云基础设施能力,如对象存储的性能/持久性/扩展性/可复制性等、EC2的快速扩展/随时可用等,再叠加了PostgreSQL极强的生态能力。

    MySQL 30周年中国区活动将于下周再成都举行,感兴趣的可以去现场围观[3]。时间:5月23日-24日(13:00-17:50);地址:成都市武侯区吉庆四路188号IMC国际广场成都书声科技(叫叫)四楼演播厅[3]

    其他值得关注的产品更新包括:

    • Google MCP 数据库工具箱支持 Bigtable 连接器 [63]
    • OCI上Oracle Database 23ai 支持使用数据库内转换模型与 Select AI RAG 结合使用,简化向量模型处理[84]

    更新详情

    阿里云
    • RDS MySQL标准版云盘只读实例新增128核512 GB规格(mysqlro.x4.16xlarge.1c)[x]。
    • RDS MySQL 高可用系列升级为集群系列—高可用系列升级为集群系列时支持保留数据库代理。[4]
    Azure(微软云)
    • Cosmos DB for MongoDB 中的数据 API 发布 [5]
    GCP(谷歌云)
    • BigQuery 新增多项 SQL 功能中[9]
    • Bigtable Studio 支持导出查询结果 [13]
    • AlloyDB 支持在 AlloyDB Studio 中使用 IAM 身份验证 [27]
    • 支持使用 Cloud SQL for PostgreSQL 备份迁移到 AlloyDB for PostgreSQL[39]
    • Dataflow 支持构建向量嵌入的提取、转换和加载 (ETL) 数据到AlloyDB [41]
    • Cloud SQL Enterprise Plus 版支持AI 辅助故障排除 [44]
    • Google MCP 数据库工具箱支持 Bigtable 连接器 [63]
    Oracle云
    • Oracle Database 23ai 支持使用数据库内转换模型与 Select AI RAG 结合使用,简化向量模型处理[84]
    火山云(字节)
    • 云数据库 MySQL 支持恢复数据至原实例和批量回档场景下,支持极速恢复,加速恢复任务,提升整体恢复效率[8]
    • MongoDB 新增支持设置默认白名单,设置默认白名单后,新建实例时会自动选中默认白名单。[89]
    • MongoDB 事件中心新增支持展示事件描述信息,当执行了按备份文件恢复新实例、按时间点恢复到新实例或恢复已删除实例操作后,您可以在事件中心找到对应的事件操作,并在事件描述中查看源实例和新实例的实例 ID 信息。[90]
    百度云
    • ((2025-5-9)) PegaDB支持本地盘[94]
    AWS(亚马逊云)
    • RDS 支持 MySQL 8.0.42[95] 8.4.5 [96]
    • Aurora MySQL 3.09(兼容 MySQL 8.0.40)现已正式发布[99]
    • Aurora 和 RDS for PostgreSQL、MySQL 和 MariaDB 现提供 R8g 和 M8g 实例的预留实例[101]
    • Aurora 和 RDS for PostgreSQL、MySQL 和 MariaDB 现提供适用于 R7i 和 M7i 实例的预留实例[102]
    • RDS for PostgreSQL 支持次要版本 17.5、16.9、15.13、14.18 和 13.21[109]
    • Aurora PostgreSQL Limitless 数据库现在支持 PostgreSQL 16.8[111]
    腾讯云
    • TDSQL-C MySQL 版、云数据库 MySQL发布数据库代理版本1.3.17。[124][130]
    • TDSQL-C MySQL、云数据库 MySQL 只读分析引擎发布了全新内核版本1.2404.24.0与2.2410.6.0。[125]
    • 云数据 MySQL 支持备份保留设置功能,可避免实例误删除导致备份被立即销毁,确保数据可追溯和可恢复性。[126]
    • TDSQL-C MySQL 版、云数据库 MySQL 只读分析引擎实例支持对实例配置进行变更。[127][132]
    • TDSQL-C MySQL 版、云数据库 MySQL 只读分析引擎实例支持了多节点高可用能力。[128][133]
    • TDSQL-C MySQL 版、云数据库 MySQL 只读分析引擎实例支持了配置告警能力。[129][134]
    • ((2025-05-09)) 云数据库 SQL Server 支持表级别账号授权,可针对某一个账号对表的权限进行授予[135]

    参考链接

  • Oracle 云(即Oracle Cloud Infrastructure,简称OCI )的免费策略大概是所有云厂商中最为彻底与直接的。对于注册的账号,可以持续免费使用主要的云资源,包括虚拟主机(VM.Standard.E2.1.Micro)、数据库(1OCPU 16GB内存)等。

    相比于 AWS 的 一个月免费、GCP的 $300 代金券,OCI在免费策略上是最具诚意的云厂商。在使用方式上,也更具有诚意,不太因为使用超时、忘记关闭、规格选择错误等因素,而造成误收费,这些在AWS、GCP上都是很容易发生的。

    创建免费的数据库

    创建免费的 HeatWave MySQL 实例

    在创建数据库时,就可以选择“Always Free”选项,这时,在后续规格选择的时候,就不会误选成计费实例了。

    开启免费的 HeatWave 实例

    HeatWave是最近几年Oracle/MySQL最重要的发展方向,可以非常好的支持各种复杂与分析类的查询,很好的弥补了MySQL在分析能力上的短板。

    “Always Free” 也非常友好的支持了 HeatWave 相关的特性,可以让开发者非常好的体验HeatWave相关功能。

    创建实例账号

    创建数据库的管理员账号:

    查看免费实例

    在完成实例创建后,实例详情,可以看到“Always Free”标签:

    创建虚拟主机

    选择免费的虚拟主机规格

    这里需要注意,目前支持的免费规格,需要在分类“Virtual Machine->Specialty and previous generation”中选择:

    虚拟主机的基础选项

    查看实例状态

    在虚拟主机的实例列表页,可以查看该实例,并且看到免费标签“Always Free”:

  • This content is password-protected. To view it, please enter the password below.

  • 理解 NumPy 中的高维数组

    ·

    在机器学习中大量的使用NumPy作为其基础的数据结构,ndarrayNumPy的核心数据对象。对于ndarray高维数组的一个非常容易产生的误解是,使用数学中的矩阵(或者叫“行列式”)概念去尝试理解更高维的场景,或者使用更高维空间去理解,这样都会导致难以较好的理解更高维(5或6维)的数组。本文使用较为直观的示例和可视化的展示,更为“标准”(文档推荐的)的方式去理解ndarray的更高维数组。更多详细内容,可以参考阅读:

    问题

    在机器学习中,经常要对多维的数组做各种操作。对高维数组建立更好的直觉理解,则有利于去理解这些操作。例如,我们考虑右侧的代码,想一想该代码的输出是什么?

    >>> import numpy as np
    >>> np.array([[[1],[2]],[[3],[4]]]).shape
    (考虑输出是什么)

    要回答这个问题,则需要建立对于多维数组结构的理解。

    文档中对于高维数组理解的建议

    NumPy: the absolute basics for beginners中有如下一段话:

    It is familiar practice in mathematics to refer to elements of a matrix by the row index first and the column index second. This happens to be true for two-dimensional arrays, but a better mental model is to think of the column index as coming last and the row index as second to last. This generalizes to arrays with any number of dimensions.

    “矩阵”是“线性代数”的主要研究对象,一个\( m \times n \)的矩阵即是一个平面上的\( m \)行\( m \)列的行列式。一种常见的向高维扩展的思考方式是,会将三维数组扩展为三维空间中的数组。但,这样的扩展,非常不利于去理解更高维的数组。这里提到的方案是这样:“a better mental model is to think of the column index as coming last and the row index as second to last.”。

    这种理解,也是本文的核心,概况如下:

    • 总是将最后一个维度理解为列维度
    • 总是将倒数第二个维度理解为行维度
    • 剩余的维度,则是通过层的方式去构建

    从 4×5 的数组开始

    先通过直观的书写表达,看看这样的数组应该是怎样的。

    一般的矩阵(行列式表示):

    \begin{bmatrix}
    0 & 1 & 2 & 3 & 4 \\
    5 & 6 & 7 & 8 & 9 \\
    10 & 11 & 12 & 13 & 14 \\
    15 & 16 & 17 & 18 & 19 \\
    \end{bmatrix}

    本文推荐的理解方式:

    \[
    \begin{bmatrix}
    [0 & 1 & 2 & 3 & 4] \\
    [5 & 6 & 7 & 8 & 9] \\
    [10 & 11 & 12 & 13 & 14] \\
    [15 & 16 & 17 & 18 & 19]
    \end{bmatrix}
    \]

    numpy的输出:

    >>> np.arange(20).reshape(4,5)
    array([[ 0,  1,  2,  3,  4],
           [ 5,  6,  7,  8,  9],
           [10, 11, 12, 13, 14],
           [15, 16, 17, 18, 19]])

    如果按照“矩阵”思想去理解这个矩阵很简单。但这里,我们重新按照上述的原则去理解这个数组。即:

    • 最后一个维度(即第二个维度),该维度的长度是5,将其理解为维度
    • 倒数第二个维度,即第一个维度,该维度的长度是4,将其理解为维度

    形式上,这与一般的矩阵,是完全一致的。只是,思维方式,反过来了。

    再考虑 3x4x5 的数组

    这个数组已经不能用简单的平面表示了,这里使用了符合上述描述的形式描述,“剩余的维度,则是通过层的方式去构建”,则有:

    本文推荐的理解方式:

    \[
    \begin{array}{r c}
    \text{Layer 1:} &
    \left[
    \begin{array}{c}
    [0 & 1 & 2 & 3 & 4] \\
    [5 & 6 & 7 & 8 & 9] \\
    [10 & 11 & 12 & 13 & 14] \\
    [15 & 16 & 17 & 18 & 19] \\
    \end{array}
    \right]
    \\
    \text{Layer 2:} &
    \left[
    \begin{array}{c}
    [20 & 21 & 22 & 23 & 24] \\
    [25 & 26 & 27 & 28 & 29] \\
    [30 & 31 & 32 & 33 & 34] \\
    [35 & 36 & 37 & 38 & 39] \\
    \end{array}
    \right]
    \\
    \text{Layer 3:} &
    \left[
    \begin{array}{c}
    [40 & 41 & 42 & 43 & 44] \\
    [45 & 46 & 47 & 48 & 49] \\
    [50 & 51 & 52 & 53 & 54] \\
    [55 & 56 & 57 & 58 & 59] \\
    \end{array}
    \right]
    \end{array}
    \]

    >>> np.arange(60).reshape(3,4,5)
    array([[[ 0,  1,  2,  3,  4],
            [ 5,  6,  7,  8,  9],
            [10, 11, 12, 13, 14],
            [15, 16, 17, 18, 19]],
    
           [[20, 21, 22, 23, 24],
            [25, 26, 27, 28, 29],
            [30, 31, 32, 33, 34],
            [35, 36, 37, 38, 39]],
    
           [[40, 41, 42, 43, 44],
            [45, 46, 47, 48, 49],
            [50, 51, 52, 53, 54],
            [55, 56, 57, 58, 59]]])

    这时,矩阵的想法就不太好用了。这里继续按照上面的原则,考虑:

    • 最后一个维度,即这里的第三个维度,该维度的长度是5,将其理解为维度
    • 倒数第二个维度,即这里第二个维度,该维度的长度是4,将其理解为维度
    • 倒数第三个维度(第一个维度)该维度长度是3,将其理解为行列式前面的

    循着这样的思考模式,不断地叠加更多的“层”,就可以理解更高维度的数组了。

    考虑 2x3x4x5 的数组

    这里先试用“层”的思维,可视化的表示该数组如下:

    \[
    \left[
    \begin{array}{c}
    \text{Layer}^{(0)}_1
    \left[
    \begin{array}{c}
    \text{Layer}^{(1)}_1
    \left[
    \begin{array}{c}
    [000 & 001 & 002 & 003 & 004] \\
    [005 & 006 & 007 & 008 & 009] \\
    [010 & 011 & 012 & 013 & 014] \\
    [015 & 016 & 017 & 018 & 019] \\
    \end{array}
    \right] \\
    \text{Layer}^{(1)}_2
    \left[
    \begin{array}{c}
    [020 & 021 & 022 & 023 & 024] \\
    [025 & 026 & 027 & 028 & 029] \\
    [030 & 031 & 032 & 033 & 034] \\
    [035 & 036 & 037 & 038 & 039] \\
    \end{array}
    \right] \\
    \text{Layer}^{(1)}_3
    \left[
    \begin{array}{c}
    [040 & 041 & 042 & 043 & 044] \\
    [045 & 046 & 047 & 048 & 049] \\
    [050 & 051 & 052 & 053 & 054] \\
    [055 & 056 & 057 & 058 & 059] \\
    \end{array}
    \right]
    \end{array}
    \right] \\
    \text{Layer}^{(0)}_2
    \left[
    \begin{array}{c}
    \text{Layer}^{(1)}_1
    \left[
    \begin{array}{c}
    [060 & 061 & 062 & 063 & 064] \\
    [065 & 066 & 067 & 068 & 069] \\
    [070 & 071 & 072 & 073 & 074] \\
    [075 & 076 & 077 & 078 & 079] \\
    \end{array}
    \right] \\
    \text{Layer}^{(1)}_2
    \left[
    \begin{array}{c}
    [080 & 081 & 082 & 083 & 084] \\
    [085 & 086 & 087 & 088 & 089] \\
    [090 & 091 & 092 & 093 & 094] \\
    [095 & 096 & 097 & 098 & 099] \\
    \end{array}
    \right] \\
    \text{Layer}^{(1)}_3
    \left[
    \begin{array}{c}
    [100 & 101 & 102 & 103 & 104] \\
    [105 & 106 & 107 & 108 & 109] \\
    [110 & 111 & 112 & 113 & 114] \\
    [115 & 116 & 117 & 118 & 119] \\
    \end{array}
    \right]
    \end{array}
    \right]
    \end{array}
    \right]
    \]

    >>> np.arange(120).reshape(2,3,4,5)
    array([[[[  0,   1,   2,   3,   4],
             [  5,   6,   7,   8,   9],
             [ 10,  11,  12,  13,  14],
             [ 15,  16,  17,  18,  19]],
    
            [[ 20,  21,  22,  23,  24],
             [ 25,  26,  27,  28,  29],
             [ 30,  31,  32,  33,  34],
             [ 35,  36,  37,  38,  39]],
    
            [[ 40,  41,  42,  43,  44],
             [ 45,  46,  47,  48,  49],
             [ 50,  51,  52,  53,  54],
             [ 55,  56,  57,  58,  59]]],
    
    
           [[[ 60,  61,  62,  63,  64],
             [ 65,  66,  67,  68,  69],
             [ 70,  71,  72,  73,  74],
             [ 75,  76,  77,  78,  79]],
    
            [[ 80,  81,  82,  83,  84],
             [ 85,  86,  87,  88,  89],
             [ 90,  91,  92,  93,  94],
             [ 95,  96,  97,  98,  99]],
    
            [[100, 101, 102, 103, 104],
             [105, 106, 107, 108, 109],
             [110, 111, 112, 113, 114],
             [115, 116, 117, 118, 119]]]])

    继续按照上面的原则,考虑:

    • 最后一个维度,即这里的第四个维度,该维度的长度是5,将其理解为维度
    • 倒数第二个维度,即这里第三个维度,该维度的长度是4,将其理解为维度
    • 倒数第三个维度,即第二个维度,该维度的长度是3,将其理解为行列式前面的
    • 倒数第四个维度,即第一个维度,该维度的长度是2,将其理解为行列式前面的层的层,也就是上述的“Layer”

    使用这样的模式,就可以将一个多维数组的表示平面化。并且注意到,这与ndarray输出的形式是几乎完全一致的。

    回到前面的问题

    有了上面的可视化展示以及上面逐步的介绍,应该可以更容易理解前面NumPy: the absolute basics for beginners所提到的直觉“a better mental model is to think of the column index as coming last and the row index as second to last”。

    有了这个直觉,我们再来考虑最前面提到的问题:

    >>> import numpy as np
    >>> np.array([[[1],[2]],[[3],[4]]]).shape

    最内层的列,就是最后的维度长度,这里是 1,所以就是 ? x 1;该列所对应的行数,就是倒数第二个维度的长度,这里做如下的格式化,可以看到有两行,所以这是一个? x 2 x 1的数组;再向上看一层,共有两个该2x1的数组,故,该数组的shape时:2x2x1

    [
      [
        [1],
        [2]
      ],
      [
        [3],
        [4]
      ]
    ]

    再确认最后的输出:

    >>> import numpy as np
    >>> np.array([[[1],[2]],[[3],[4]]]).shape
    (2, 2, 1)

    最后

    这种理解的核心即是“a better mental model is to think of the column index as coming last and the row index as second to last”,简单概括如下:

    • 总是将最后一个维度理解为列维度
    • 总是将倒数第二个维度理解为行维度
    • 剩余的维度,则是通过层的方式去构建

  • Oracle LogMiner 使用

    ·

    Oracle官方文档Using LogMiner to Analyze Redo Log Files[2]中,对该功能有详细的介绍,包括了LogMiner的配置与使用、数据过滤、补充日志(Supplemental Logging)、使用示例等。

    Oracle 何时引入的LogMiner?

    自 1999 年发布 Oracle 8i 的时候,正式引入 LogMiner 功能(参考:Redo Log Analysis Using LogMiner[1])。该功能支持以SQL的形式分析redo中的数据,最初考虑的应用场景,主要还是偏于故障恢复、异常诊断、审计等,但是该功能的潜力很大,现在已经逐步成为Oracle CDC的主流方案之一。

    目前已经有很多的集成/同步工具都使用LogMiner进行变化数据获取,虽然,目前官方依旧不推荐这么做,文档中的原文如下:“Note:LogMiner is intended for use as a debugging tool, to extract information from the redo logs to solve problems. It is not intended to be used for any third party replication of data in a production environment.”

    根据经验来看,LogMiner用于数据集成并没有什么太大的问题。

    打开补充日志

    ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;

    查看归档日志

    SQL> SELECT name FROM v$archived_log ORDER BY FIRST_TIME DESC FETCH FIRST 3 ROWS ONLY;
    
    NAME
    ----------------------------------------------------------------------------------------------------
    /u03/app/oracle/fast_recovery_area/ORCLCDB/archivelog/2025_04_24/o1_mf_1_761_n0mbccbd_.arc
    /u03/app/oracle/fast_recovery_area/ORCLCDB/archivelog/2025_04_24/o1_mf_1_760_n0mbcc62_.arc
    /u03/app/oracle/fast_recovery_area/ORCLCDB/archivelog/2025_04_24/o1_mf_1_759_n0m6tdfp_.arc

    添加需要解析的日志文件

    BEGIN
      DBMS_LOGMNR.ADD_LOGFILE(
        LOGFILENAME => '/PATH_TO_YOUR_ARCHIVE/o1_mf_1_761_n0mbccbd_.arc',
        OPTIONS => DBMS_LOGMNR.NEW
      );
    END;
    /

    例如,实际的SQL可能是如下的样子:

    BEGIN
      DBMS_LOGMNR.ADD_LOGFILE(
        LOGFILENAME => '/u03/app/oracle/fast_recovery_area/ORCLCDB/archivelog/2025_04_24/o1_mf_1_955_n0mwc01v_.arc',
        OPTIONS => DBMS_LOGMNR.NEW
      );
    END;
    /
    
    o1_mf_1_955_n0mwc01v_.arc
    o1_mf_1_909_n0mv4sc3_.arc
    o1_mf_1_908_n0mv4ohs_.arc

    启动LogMiner

    启动时,可以带不同的参数以指定LogMiner不同的行为。

    使用在线数据字典启动
    EXECUTE DBMS_LOGMNR.START_LOGMNR( -
       OPTIONS => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);
    使用日志中的数据字典启动
    EXECUTE DBMS_LOGMNR_D.BUILD( -
        OPTIONS => DBMS_LOGMNR_D.STORE_IN_REDO_LOGS);

    获取LogMiner中的变更数据

    获取变更数据
    SELECT 
      SCN,
      TIMESTAMP,
      OPERATION,
      SQL_REDO,
      SQL_UNDO,
      SEG_OWNER,
      TABLE_NAME,
      USERNAME
    FROM 
      V$LOGMNR_CONTENTS
    WHERE 
        OPERATION IN ('INSERT', 'UPDATE', 'DELETE')
    --  AND SEG_OWNER = 'TEST_USER'
        AND (TABLE_NAME = 'T2' OR TABLE_NAME = 't2')
    ORDER BY TIMESTAMP DESC
    FETCH FIRST 3 ROWS ONLY;

    退出 LogMiner

    EXECUTE DBMS_LOGMNR.END_LOGMNR();

    解析未归档的 Redo 日志

    除了解析归档之外,LogMiner 可以直接解析当前正在使用的 redo 文件。先根据小节“获取当前正在使用的redo文件”中的SQL获取当前正在使用的 redo 文件,然后在添加日志文件时,像上述添加归档一样添加即可。

    BEGIN
      DBMS_LOGMNR.ADD_LOGFILE(
        LOGFILENAME => '/u04/app/oracle/redo/redo003.log',
        OPTIONS => DBMS_LOGMNR.NEW
      );
    END;
    /

    使用上述(小结“获取变更数据”)的SQL可以获得如下的输出:

           SCN TIMESTAMP OPERATION	  SQL_REDO
    ---------- --------- ------------ ---------------------------------------------
       3175619 24-APR-25 INSERT       insert into "SYS"."T2"("ID") values ('31');
       3175846 24-APR-25 INSERT       insert into "SYS"."T2"("ID") values ('32');

    一些常见的 SQL

    获取归档日志

    获取最新的ARCHIVE LOG的列表,以及对应的SCN号范围:

    SELECT 
      FIRST_CHANGE#,
      TO_CHAR(FIRST_TIME, 'YYYY-MM-DD HH24:MI:SS'),
      NEXT_CHANGE# ,
      TO_CHAR(NEXT_TIME, 'YYYY-MM-DD HH24:MI:SS'),
      NAME 
    FROM  
      V$ARCHIVED_LOG 
    ORDER BY NEXT_CHANGE# DESC 
    FETCH FIRST 3 ROWS ONLY;
    强制切换归档日志文件
    ALTER SYSTEM SWITCH LOGFILE;
    获取当前正在使用的redo文件
    SELECT A.GROUP#,A.MEMBER, B.STATUS
    FROM   V$LOGFILE A
    JOIN   V$LOG B ON A.GROUP# = B.GROUP#
    WHERE  B.STATUS = 'CURRENT'; 

    参考