• 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'; 

    参考

  • 为什么需要间隙锁

    关于为什么需要 “Gap Locks” 或者 “Next-key Locks” ,在MySQL的文档“17.7.1 InnoDB Locking”的小节中有较为详细的介绍,这里不再赘述。这里使用一个具体的示例,以便开发者方便构造与观察间隙锁。

    Repeatable-Read隔离级别下,在整个事务的过程中,数据需要保持一致,经常需要使用间隙锁对数据或资源进行保护。例如,在如下的事务中:

    DROP TABLE IF EXISTS t1;
    CREATE TABLE t1 (
      id int,
      nick varchar(32),
      age int,
      KEY ind_n (nick),
      PRIMARY KEY (id)
    );
    
    INSERT INTO t1 VALUES ( 1, "a", 27 );
    INSERT INTO t1 VALUES ( 11, "k" ,23 );
    INSERT INTO t1 VALUES ( 24, "x" ,22 );
    
    START TRANSACTION;
    SELECT * FROM t1 WHERE nick >= "k" for update;
    +----+------+------+
    | id | nick | age  |
    +----+------+------+
    | 11 | k    |   23 |
    | 24 | x    |   22 |
    +----+------+------+
    2 rows in set (0.00 sec)  Warnings: 0

    在上述事务中,为了实现将 SELECT 涉及的“相关记录”全部加上“排它锁”,从而阻止其他事务对该部分数据进行修改。即,如果有任何其他的事务,尝试修改该事务中的“相关记录”,都需要被阻塞。这里的“相关记录”,具体是指:WHERE nick >= "k" 查询扫描到的索引入口(二级索引),以及对应的数据(即主键入口)。

    试想,如果有其他事务尝试写入一条 nick = 'm'的记录,那么上述的...for update语句则也会返回该记录。为了阻止上述的不一致,上述事务不仅要对单个记录或索引入口进行加锁,还需要对索引入口之间的间隙进行加锁。

    具体的,该案例中详细的锁信息如下:

    mysql> SELECT
        ENGINE_TRANSACTION_ID AS TRX_ID,     OBJECT_NAME,     
        INDEX_NAME, LOCK_MODE,     LOCK_STATUS,     
        LOCK_DATA    FROM performance_schema.data_locks 
      WHERE LOCK_TYPE="RECORD";
    +--------+-------------+------------+---------------+-------------+------------------------+
    | TRX_ID | OBJECT_NAME | INDEX_NAME | LOCK_MODE     | LOCK_STATUS | LOCK_DATA              |
    +--------+-------------+------------+---------------+-------------+------------------------+
    |  10703 | t1          | ind_n      | X             | GRANTED     | supremum pseudo-record |
    |  10703 | t1          | ind_n      | X             | GRANTED     | 'k', 11                |
    |  10703 | t1          | ind_n      | X             | GRANTED     | 'x', 24                |
    |  10703 | t1          | PRIMARY    | X,REC_NOT_GAP | GRANTED     | 11                     |
    |  10703 | t1          | PRIMARY    | X,REC_NOT_GAP | GRANTED     | 24                     |
    +--------+-------------+------------+---------------+-------------+------------------------+

    上述的锁信息显示,该事务获得了索引 (a,supremum)间隙的排它锁,以及对应的数据记录锁。

    如果说上述案例中的 SELECT ... FOR UPDATE在实际中不那么常见的话,类似的直接使用UPDATE语句也需要类似的加锁信息,这里使用SELECT ... FOR UPDATE展示则更为直接。

    InnoDB 中“间隙锁”的表示

    理解 InnoDB 锁的困难有很多,其中一个非常大的困难就是理解在 InnoDB 间隙锁的表示,因为 InnoDB 使用了一个比较反直觉的模式去实现间隙锁。具体的:InnoDB 在表示间隙锁的时候,并没有把 GAPNext-Key当做一个“资源”。而是依旧把Record当做资源,然后,以“锁类型的”(Lock Mode)不同的取值表示记录锁或间隙锁。例如,

    • Lock Mode取值为S,GAP时,则表示间隙锁,锁类型为S
    • Lock Mode取值为S,REC_NOT_GAP时,则表示记录锁,锁类型为S

    锁的对象,均为索引入口或主键入口。

    关于该实现,在InnoDB Data Locking – Part 2 “Locks”中有着较为详细的描述:

    “Even without knowing too much about how databases like InnoDB operate, we can guess, that sometimes the operation involves just the record, sometimes the gap before a record, and at yet another times we need to access both, the record and a gap. One way to model that, would be to consider records and gaps to be two different kinds of resources which you can lock independently. Current InnoDB implementation takes a different approach: there is just one resource for each point, but there are multiple kinds of access right you can request for it, and the access right specifies if you need the row, the gap or both parts. One benefit of this is that it is optimized for the most common case where you need both.”

    InnoDB Data Locking – Part 2 “Locks”

    锁类型与加锁模式

    这里使用如下表格表示了InnoDB在进行加锁时常见的加锁对象与类型。这里Dm表示具体的加锁数据,例如索引入口数据或主键数据。详细的表格如下:

    LOCK_DATA
    加锁目标/资源
    LOCK_MODE
    加锁模式
    加锁对象/
    资源
    加锁类型
    DmS,REC_NOT_GAPDmS
    DmX,REC_NOT_GAPDmX
    DmS,GAP(D(m-1),Dm)S
    DmX,GAP(D(m-1),Dm)X
    DmS(D(m-1),Dm]S
    DmX(D(m-1),Dm]X

    通常的,

    • performance_schema中看到的Dm取值可能是这样:0x000000000207
    • show engine innodb status\G中看到的Dm会是:0: len 4; hex 00000001; asc ;;
    • D(m-1)则表示在索引中,Dm前面的一个索引入口或主键取值
    • (...)表示开区间;(...]表示半开半闭区间

    show engine innodb status\G 输出示例:

    RECORD LOCKS space id 27 page no 4 n bits 72 index PRIMARY of table `sysbenchdb`.`t1` trx id 10094 lock mode S locks rec but not gap waiting
    Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
     0: len 4; hex 00000001; asc     ;;
     1: len 6; hex 00000000276d; asc     'm;;
     2: len 7; hex 810000008d0110; asc        ;;
     3: len 1; hex 61; asc a;;
     4: len 4; hex 8000000c; asc     ;;

    UPDATE 语句的间隙锁

    环境说明

    mysql> show variables like '%iso%';
    +-----------------------+-----------------+
    | Variable_name         | Value           |
    +-----------------------+-----------------+
    | transaction_isolation | REPEATABLE-READ |
    +-----------------------+-----------------+
    1 row in set (0.01 sec)

    场景构造

    Session ASession B
    DROP TABLE IF EXISTS t1;
    CREATE TABLE t1 (
    id int,
    nick varchar(32),
    age int,
    KEY ind_n (nick),
    PRIMARY KEY (id)
    );

    INSERT INTO t1 VALUES ( 1, "a", 27 );
    INSERT INTO t1 VALUES ( 11, "k" ,23 );
    INSERT INTO t1 VALUES ( 24, "x" ,22 );
    START TRANSACTION;
    UPDATE t1 SET age = 127 WHERE nick = "k";

    观测间隙锁

    mysql> SELECT      
      ENGINE_TRANSACTION_ID AS TRX_ID,     
      OBJECT_NAME,  INDEX_NAME, LOCK_MODE,     
      LOCK_STATUS,  LOCK_DATA
    FROM performance_schema.data_locks WHERE LOCK_TYPE="RECORD";
    +--------+-------------+------------+---------------+-------------+-----------+
    | TRX_ID | OBJECT_NAME | INDEX_NAME | LOCK_MODE     | LOCK_STATUS | LOCK_DATA |
    +--------+-------------+------------+---------------+-------------+-----------+
    |  10644 | t1          | ind_n      | X,GAP         | GRANTED     | 'x', 24   |
    |  10644 | t1          | ind_n      | X             | GRANTED     | 'k', 11   |
    |  10644 | t1          | PRIMARY    | X,REC_NOT_GAP | GRANTED     | 11        |
    +--------+-------------+------------+---------------+-------------+-----------+

    可以看到,在索引上(KEY ind_n (nick))的锁范围为:(a,x)。这个范围由两个锁构成,分别是:

    • X,GAP : 'x', 24 对应的范围为 (k,x)
    • X : 'k', 11 对应的范围为 (a,k]

    故,上述两个范围,共同构成的范围为:(a,x),锁类型都是排它锁(X)。

    测试验证

    可以尝试写入一个在 (a,k)(k,x)范围为的记录,观察阻塞情况。继续上述的两个Session,并执行如下的SQL 语句进行观察:

    Session ASession B
    START TRANSACTION;
    INSERT INTO t1 VALUES (2,"c",32);
    -- blocking / waiting
    ROLLBACK
    START TRANSACTION;
    INSERT INTO t1 VALUES (2,"m",32);
    -- blocking / waiting
    ROLLBACK

    参考链接

  • 标题:阿里云开源RDS MCP Server发布;火山云MySQL发布Sequence、Flashback Query等功能

    重要更新

    阿里云数据库发布开源RDS MCP Server,支持快速实例创建、实例状态诊断等功能:现已开源|阿里云RDS发布MCP Server能力,解锁数据库“对话即运维”新体验

    火山云数据库 MySQL 发布一系列重磅功能,包括了Sequence Engine[5]、Flashback Query[6]、5.7到8.0大版本升级等[7]

    更新详情

    火山云(字节)
    • 云数据库 MySQL 支持对表/列级别进行账号权限控制[4]
    • 云数据库 MySQL 8.0 版本实例提供了 Sequence Engine,用于获取唯一递增序列值[5]
    • 云数据库 MySQL 8.0 实例中提供闪回查询(Flashback Query)功能[6]
    • 支持了云数据库 MySQL 5.7 到 8.0 的升级功能[7]
    • 云数据库 PostgreSQL 开始支持支持 17 [26]
    • 云数据库 PostgreSQL提供 1.5.2 版本的 pg_repack 插件 [27]
    • 在 PostgreSQL 13 及以上版本实例中提供 0.8.0 版本 pg_vector 插件 [28]
    • 云数据库 PostgreSQL 支持插件 pg_partman、pg_jobmon [30]
    • 缓存数据库 Redis 版支持了跨地域备份恢复到新实例[8]
    • 缓存数据库 Redis 版支持连接级别的读写分离策略[11]
    • 缓存数据库 Redis 版支持了 ExTimeSeries 数据结构[12]
    Azure(微软云)
    • Azure Database for PostgreSQL 17 性能管理服务器参数现支持修改 [9]
    • Azure Database for PostgreSQL 迁移服务支持时间序列数据库 [10]
    • Azure Cosmos DB 发布用于 NoSQL 的分片 DiskANN [14]
    • 基于 vCore 的 Azure Cosmos DB for MongoDB 支持 Mongo 8.0 [15]
    百度云
    • 托管 Redis 内存型支持按时间点恢复数据[34]
    AWS(亚马逊云)
    • Amazon MemoryDB 现已支持 Internet 协议版本 6 (IPv6) [42]
    腾讯云
    • TDSQL-C MySQL 版、云数据库 MySQL 只读分析引擎发布了全新内核版本1.2404.23.0与2.2410.5.0。[44][45]
    • 数据库管理 DMC 支持了云数据库 SQL Server[46]

    参考链接

  • Docker 大大简化了数据库的安装,特别是在产品测试阶段的时候,可以让开发者以最快速的方式体验技术产品,尤其是当这个技术产品已经非常复杂的时候。

    Oracle 官方提供了哪些镜像

    Oracle 镜像官方页面

    Oracle 在官方站点中列出了所有支持的产品,以及对应的仓库列表。 Oracle 镜像仓库的官方页面:

    仓库官方页: https://container-registry.oracle.com/

    找到软件对应的子仓库

    这里关注 Oracle Database 相关的仓库,故选择第一个仓库列表页。在这里可以看到有很多的子仓库,可以用于安装不同的 Oracle 数据库版本或组件:

    选择版本与镜像站点

    进入单个子仓库,在页面的最底下可以看到,该仓库有哪些版本的镜像可以使用,例如,这里选择了 express 子仓库,在页面最底端找到支持的版本列表:

    另外,这里还提供了一些可供选择的镜像列表,开发者可以根据自己的地理位置选择合适的镜像站点。

    安装 Oracle 数据库

    拉取镜像

    docker pull container-registry.oracle.com/database/express:latest
      or:  
    docker pull container-registry.oracle.com/database/express:21.3.0-xe
      or:
    docker pull container-registry.oracle.com/database/express:18.4.0-xe

    这里的测试选择了express:18.4.0-xe版本进行安装。查看本地的镜像:

    docker image ls
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    REPOSITORY                                            TAG         IMAGE ID      CREATED       SIZE
    container-registry.oracle.com/mysql/community-server  9.1         f1f889678a73  6 months ago  606 MB
    container-registry.oracle.com/database/express        18.4.0-xe   364598d20118  4 years ago   6.03 GB

    创建 Oracle 数据库的容器

    docker container create \
       -it \
       --name oracle-18ex \
       -p 1521:1521 \
       -e ORACLE_PWD=oracledocker \
       container-registry.oracle.com/database/express:18.4.0-xe

    启动容器

    docker start oracle-18ex

    观察启动状态

    docker logs -f oracle-18ex
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    ORACLE PASSWORD FOR SYS AND SYSTEM: oracledocker
    Specify a password to be used for database accounts. Oracle recommends that the password entered should be at least 8 characters in length, contain at least 1 uppercase character, 1 lower case character and 1 digit [0-9]. Note that the same password will be used for SYS, SYSTEM and PDBADMIN accounts:
    Confirm the password:
    Configuring Oracle Listener.
    Listener configuration succeeded.
    Configuring Oracle Database XE.
    Enter SYS user password:
    ****************
    Enter SYSTEM user password:
    *************
    Enter PDBADMIN User Password:
    *************
    Prepare for db operation
    7% complete
    Copying database files
    29% complete
    Creating and starting Oracle instance
    30% complete
    31% complete
    34% complete
    38% complete
    41% complete
    43% complete
    Completing Database Creation
    47% complete
    50% complete
    Creating Pluggable Databases
    54% complete
    71% complete
    Executing Post Configuration Actions
    93% complete
    Running Custom Scripts
    100% complete
    Database creation complete. For details check the logfiles at:
     /opt/oracle/cfgtoollogs/dbca/XE.
    Database Information:
    Global Database Name:XE
    System Identifier(SID):XE
    Look at the log file "/opt/oracle/cfgtoollogs/dbca/XE/XE.log" for further details.
    
    Connect to Oracle Database using one of the connect strings:
         Pluggable database: bd127ae4faab/XEPDB1
         Multitenant container database: bd127ae4faab
    Use https://localhost:5500/em to access Oracle Enterprise Manager for Oracle Database XE
    The Oracle base remains unchanged with value /opt/oracle
    #########################
    DATABASE IS READY TO USE!
    #########################
    The following output is now a tail of the alert.log:
    2025-04-17T03:40:43.663079+00:00
    XEPDB1(3):Resize operation completed for file# 10, old size 358400K, new size 368640K
    2025-04-17T03:40:44.483587+00:00
    XEPDB1(3):CREATE SMALLFILE TABLESPACE "USERS" LOGGING  DATAFILE  '/opt/oracle/oradata/XE/XEPDB1/users01.dbf' SIZE 5M REUSE AUTOEXTEND ON NEXT  1280K MAXSIZE UNLIMITED  EXTENT MANAGEMENT LOCAL  SEGMENT SPACE MANAGEMENT  AUTO
    XEPDB1(3):Completed: CREATE SMALLFILE TABLESPACE "USERS" LOGGING  DATAFILE  '/opt/oracle/oradata/XE/XEPDB1/users01.dbf' SIZE 5M REUSE AUTOEXTEND ON NEXT  1280K MAXSIZE UNLIMITED  EXTENT MANAGEMENT LOCAL  SEGMENT SPACE MANAGEMENT  AUTO
    XEPDB1(3):ALTER DATABASE DEFAULT TABLESPACE "USERS"
    XEPDB1(3):Completed: ALTER DATABASE DEFAULT TABLESPACE "USERS"
    2025-04-17T03:40:44.930766+00:00
    ALTER PLUGGABLE DATABASE XEPDB1 SAVE STATE
    Completed: ALTER PLUGGABLE DATABASE XEPDB1 SAVE STATE

    登录容器中的 Oracle 数据库

    在上面的输出中可以看到,安装时会默认创建如下数据库:

    Database Information:
    Global Database Name:XE
    System Identifier(SID):XE

    根据在上述 docker 命令中指定的密码,则可以使用如下的命令登录数据数据库:

    docker exec -it oracle-18ex sqlplus sys/oracledocker@XE as sysdba
      or:
    docker exec -it oracle-18ex sqlplus system/oracledocker@XE

    登录后,会有如下提示输入:

    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    
    SQL*Plus: Release 18.0.0.0.0 - Production on Thu Apr 17 03:56:27 2025
    Version 18.4.0.0.0
    
    Copyright (c) 1982, 2018, Oracle.  All rights reserved.
    
    Last Successful login time: Thu Apr 17 2025 03:49:37 +00:00
    
    Connected to:
    Oracle Database 18c Express Edition Release 18.0.0.0.0 - Production
    Version 18.4.0.0.0
    
    SQL>

    授权协议与认证

    如果你要使用 Oracle 数据库的企业版的话,在拉取镜像前则需要先登录官网“同意”相关的协议,并使用docker login的方式进行认证,然后才可以拉取镜像。

    requested access to the resource is denied

    如果没有认证或提前在官网统一协议,则可能遇到如下报错:requested access to the resource is denied

    docker pull container-registry.oracle.com/database/enterprise:12.2.0.1
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    Trying to pull container-registry.oracle.com/database/enterprise:12.2.0.1...
    Error: initializing source docker://container-registry.oracle.com/database/enterprise:12.2.0.1: reading manifest 12.2.0.1 in container-registry.oracle.com/database/enterprise: requested access to the resource is denied

    或者:invalid username/password: authentication required

    docker pull container-registry-tokyo.oracle.com/database/enterprise:12.2.0.1
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    Trying to pull container-registry-tokyo.oracle.com/database/enterprise:12.2.0.1...
    Error: initializing source docker://container-registry-tokyo.oracle.com/database/enterprise:12.2.0.1: unable to retrieve auth token: invalid username/password: authentication required

    解决上述问题,首先需要登录Oracle镜像的官方站点,并同意相关协议,然后使用docker login完成认证,后即可下载。

    登录Oracle仓库站点并同意协议

    例如,如果需要下载“Oracle Database Enterprise Edition”,则需要先进入对应仓库站点:链接,并在页面的右侧栏点击协议并同意协议:

    docker login

    同意协议后,就可以使用docker login登录账号并进行镜像的下载了。

    Enterprise Edition的Docker安装

    在参考 Oracle 企业版官方文档(参考)进行安装部署的时候,在 AlmaLinux 部署时会遇到如下的问题:

    docker exec -it oracle-1202ee sqlplus / as sysdba
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    Error: crun: executable file `sqlplus` not found in $PATH: No such file or directory: OCI runtime attempted to invoke a command that was not found

    当前的绕过方案是,进入容器的bash,然后再执行即可:

    [root@oracle-docker-test ~]# docker exec -it oracle-1202ee bash
    Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
    [oracle@8bb1ec09ec5e /]$ sqlplus / as sysdba
    
    SQL*Plus: Release 12.2.0.1.0 Production on Fri Apr 18 02:37:30 2025
    
    Copyright (c) 1982, 2016, Oracle.  All rights reserved.
    
    
    Connected to:
    Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
    
    SQL>

    创建容器时的参数

    在创建容器的时候,可以使用命令行进行部分启动参数的配置。例如,默认启动时,是没有开启归档日志的(Archive Logs)的,则可以通过添加如下容器构建参数:

    -e ENABLE_ARCHIVELOG=true

    完整的命令:

    docker container create \
       -it \
       --name oracle-1202ee \
       -p 1521:1521 \
       -e ENABLE_ARCHIVELOG=true \
       -e ORACLE_PWD=oracledocker \
       container-registry.oracle.com/database/enterprise:12.2.0.1

    相关资源

  • 标题:Google AlloyDB自持内置自然语言查询功能;达梦2024年营收为10.4亿,同比增长31%

    重要更新

    达梦发布2024年度报告,该年年度营收达10.4亿,同比增长31.49%,净利润为3.6亿。[1]

    Google AlloyDB 发布自然语言查询功能,该功能可以让开发者通过简单的配置,就可以在数据库内部实现自然语言转SQL并进行查询。[2]

    更新详情

    阿里云
    • RDS MySQL全面提升本地盘实例(含主实例和只读实例)的存储空间上限,最高支持16000 GB,满足用户存储扩容需求。[4]
    • RDS MySQL的秒级加列(Instant Add Column)通过变更数据字典的元数据来优化ADD COLUMN 操作。该功能避免了传统DDL操作对全表数据的修改或重建,实现加列操作在秒级内完成,且不受表数据量影响。[5]
    • RDS PostgreSQL ESSD云盘实例支持下载带表头信息的CSV格式备份文件,可用于数据分析或离线归档等需求。[6]
    火山云(字节)
    • veDB MySQL 支持闪回查询,通过闪回查询功能,能够直接查询过去某段时间时间内的历史数据。[7]
    • veDB MySQL 支持 RETURNING 语法,支持在执行 INSERT、REPLACE、UPDATE、DELETE 等 DML 语句时,直接返回受影响的行数据 [8]
    AWS(亚马逊云)
    • Bedrock 知识库现在支持 Aurora PostgreSQL 和 MongoDB Atlas 矢量存储的混合搜索 [18]
    • ElastiCache for Memcached 推出水平、垂直自动扩展功能[20][21]
    腾讯云
    • 数据库管理 DMC 支持了云数据库 SQL Server [22]
    • 云数据库 SQL Server 优化重启功能,支持选择重启的生效时间[23]

    参考链接

  • InnoDB 的锁容易被忽略的细节是关于“隐式锁”(即:implicit locks)的存在。表现上,有的锁是存在的,但在使用SHOW ENGINE INNODB STATUS或者performance_schema.data_locks中却查看不到。最为常见的隐式锁是在写入(INSERT)时,当前事务会持有该记录对应的锁,但是在系统中,通常是查看不到的。但,如果发生了该锁冲突(或竞争)时,系统中则可以看到此类锁信息。

    本文重现了较为常见的隐式锁场景,包括:数据写入(INSERT)时的隐式锁、根据主键操作是可能产生的二级索引隐式锁等。帮助开发者能够更系统的理解,InnoDB 的锁机制。

    写入数据产生的隐式锁

    准备数据

    DROP TABLE IF exists t1;
    
     CREATE TABLE `t1` (
      `id` int unsigned,
      `nick` varchar(32),
      `age` int,
      UNIQUE KEY `uk_n` (`nick`)
    );
    mysql> desc t1;
    +-------+--------------+------+-----+---------+
    | Field | Type         | Null | Key | Default |
    +-------+--------------+------+-----+---------+
    | id    | int unsigned | YES  |     | NULL    |
    | nick  | varchar(32)  | YES  | UNI | NULL    |
    | age   | int          | YES  |     | NULL    |
    +-------+--------------+------+-----+---------+

    mysql> INSERT INTO t1 VALUES (1, 'a' , 12),(20,'z',29);
    Query OK, 2 rows affected (0.01 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    mysql> select * from t1;
    +------+------+------+
    | id   | nick | age  |
    +------+------+------+
    |    1 | a    |   12 |
    |   20 | z    |   29 |
    +------+------+------+
    2 rows in set (0.00 sec)

    构建隐式锁

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> INSERT INTO t1 VALUES (8, 'h' , 32);
    Query OK, 1 row affected (0.00 sec)

    查看锁信息:

    > SELECT 
        ENGINE_TRANSACTION_ID AS TRX_ID,
        OBJECT_NAME,
        INDEX_NAME,
        LOCK_TYPE,
        LOCK_MODE,
        LOCK_STATUS,
        LOCK_DATA 
      FROM performance_schema.data_locks;
    +--------+-------------+------------+-----------+-----------+-------------+-----------+
    | TRX_ID | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA |
    +--------+-------------+------------+-----------+-----------+-------------+-----------+
    |  10165 | t1          | NULL       | TABLE     | IX        | GRANTED     | NULL      |
    +--------+-------------+------------+-----------+-----------+-------------+-----------+

    可以看到,在 data_locks 表中没有任何关于事务中写入数据相关的锁。这是,因为这是一个隐式的锁,在没有任何锁竞争的情况下,系统并不会将该类型的锁展示出来(注:这可能与底层的存储和实现有关,隐式锁在实现上可能就没有“显式”的存储在锁相关的数据结构中)。

    构建锁竞争/隐式转显式

    这里通过在另一个事务中尝试并发写入一条冲突的记录,来构建锁竞争:

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> INSERT INTO  t1 VALUES (9,'h',17);
    ...

    该事务执行时,则会陷入锁等待。这时,再次查看锁信息如下:

    +--------+-------------+------------+-----------+---------------+-------------+---------------------+
    | TRX_ID | OBJECT_NAME | INDEX_NAME | LOCK_TYPE | LOCK_MODE     | LOCK_STATUS | LOCK_DATA           |
    +--------+-------------+------------+-----------+---------------+-------------+---------------------+
    |  10165 | t1          | NULL       | TABLE     | IX            | GRANTED     | NULL                |
    |  10168 | t1          | NULL       | TABLE     | IX            | GRANTED     | NULL                |
    |  10165 | t1          | uk_n       | RECORD    | X,REC_NOT_GAP | GRANTED     | 'h', 0x000000000214 |
    |  10168 | t1          | uk_n       | RECORD    | S             | WAITING     | 'h', 0x000000000214 |
    +--------+-------------+------------+-----------+---------------+-------------+---------------------

    这时候,可以看到事务10165,持有一个记录锁,该锁是一个排它记录锁(X,REC_NOT_GAP ),加锁对象是'h', 0x000000000214(注,这是一个唯一索引的入口,前面'h'是唯一索引值,后面的0x000000000214部分是该表的InnoDB内置rowid)。

    主键/二级索引操作相关的隐式锁

    InnoDB 的锁管理和实现确实一个超级复杂的部分(”mega-complicated“)。隐式锁的使用场景也非常多,如果对此不了解的话,那么在观察 InnoDB 的锁信息时,是会有很多的困惑的。这里再列举一类也算,较为常用的隐式锁:“主键索引/二级索引”相关的隐式说。即:

    • 当对记录进行操作时,即便是通过主键扫描,也可能对二级索引进行加锁
    • 当对记录进行操作时,即便是通过二级索引扫描,也可能对主键进行加锁

    这类场景的加锁,通常都是会存在隐式锁。

    主键操作时二级索引上的隐式锁

    在下面的测试中,我们先主键 id = 8的记录进行删除操作,然后通过系统表data_locks观察该事务是否持有二级索引相关的锁;而后,在另一个事务中,通过二级索引(nick = 'Henry')对该记录进行操作(共享读),而后再重新观察前面事务的锁状态。

    在下面的测试可以观察到,在Session B没有开始前;在 Session ADELETE语句是观测不到二级索引上的锁的;但当Session B尝试去锁定二级索引上的入口时,再次观察Session A上的锁信息,就可以看到,在Session A没有任何操作的情况下,多出了一个额外的、持有的二级索引上的锁,该锁原本是一个“隐式锁”,在发生锁竞争后,转化为一个“显式锁”。即便是在Session B因为等待操作或结束了,Session A持有的已经转化的“显式锁”也不会再回退了。详细测试如下。

    准备数据

    DROP TABLE IF exists t1;
     CREATE TABLE `t1` (
      `id` int unsigned,
      `nick` varchar(32),
      `age` int,
      PRIMARY KEY (`id`),
      UNIQUE KEY `uk_n` (`nick`)
    );
    mysql> INSERT INTO t1 VALUES (1,"Alice",12);
    mysql> INSERT INTO t1 VALUES (8,"Henry",27);
    mysql> INSERT INTO t1 VALUES (16,"Peter",15);
    mysql> show variables like '%iso%';
    +-----------------------+----------------+
    | Variable_name         | Value          |
    +-----------------------+----------------+
    | transaction_isolation | READ-COMMITTED |
    +-----------------------+----------------+

    构建隐式锁

    Session A

    mysql> START TRANSACTION;
    mysql> DELETE FROM t1 WHERE id = 8;
    
    mysql> SELECT
        ->     ENGINE_TRANSACTION_ID AS TRX_ID,
        ->     INDEX_NAME,LOCK_TYPE,
        ->     LOCK_MODE, LOCK_STATUS,
        ->     LOCK_DATA
        ->   FROM performance_schema.data_locks;
    +--------+------------+-----------+---------------+-------------+-----------+
    | TRX_ID | INDEX_NAME | LOCK_TYPE | LOCK_MODE     | LOCK_STATUS | LOCK_DATA |
    +--------+------------+-----------+---------------+-------------+-----------+
    |  10317 | NULL       | TABLE     | IX            | GRANTED     | NULL      |
    |  10317 | PRIMARY    | RECORD    | X,REC_NOT_GAP | GRANTED     | 8         |
    +--------+------------+-----------+---------------+-------------+-----------+

    Session B

    隐式锁转换为显式锁

    继续上述两个Sessions的操作:

    Session A

    Session B

    mysql> START TRANSACTION;
    mysql> SELECT * FROM t1 WHERE nick = 'Henry' FOR SHARE;
    ( Waiting )
    ...(Query Locks From performance_schema.data_locks like above)...
    +-----------------+------------+-----------+---------------+-------------+------------+
    | TRX_ID          | INDEX_NAME | LOCK_TYPE | LOCK_MODE     | LOCK_STATUS | LOCK_DATA  |
    +-----------------+------------+-----------+---------------+-------------+------------+
    |           10317 | NULL       | TABLE     | IX            | GRANTED     | NULL       |
    | 421929568337920 | NULL       | TABLE     | IS            | GRANTED     | NULL       |
    |           10317 | uk_n       | RECORD    | X,REC_NOT_GAP | GRANTED     | 'Henry', 8 |
    | 421929568337920 | uk_n       | RECORD    | S,REC_NOT_GAP | WAITING     | 'Henry', 8 |
    |           10317 | PRIMARY    | RECORD    | X,REC_NOT_GAP | GRANTED     | 8          |
    +-----------------+------------+-----------+---------------+-------------+------------+
    (... Abort last statement...)
    ERROR 1205 (HY000): Lock wait timeout exceeded; 
    try restarting transaction
    ...(Query Locks From performance_schema.data_locks like above)...
    +--------+------------+-----------+---------------+-------------+------------+
    | TRX_ID | INDEX_NAME | LOCK_TYPE | LOCK_MODE     | LOCK_STATUS | LOCK_DATA  |
    +--------+------------+-----------+---------------+-------------+------------+
    |  10317 | NULL       | TABLE     | IX            | GRANTED     | NULL       |
    |  10321 | NULL       | TABLE     | IS            | GRANTED     | NULL       |
    |  10317 | uk_n       | RECORD    | X,REC_NOT_GAP | GRANTED     | 'Henry', 8 |
    |  10317 | PRIMARY    | RECORD    | X,REC_NOT_GAP | GRANTED     | 8          |
    +--------+------------+-----------+---------------+-------------+------------+

    最后

    一些理解

    “隐式锁”可以理解为,在某些条件下,这里一定是存在“锁”的,所以,既然一定是存在的,并且这类场景可能还比较广泛,那么为了节省存储空间与操作,就省略了此类“锁”的表示。例如,通常,如果事务写入了一条数据,那么该事务一定是持有该数据的排它锁的。

    但,当真的有其他事务也尝试去获取该“隐式锁”的时候,那么为了便于进行锁检测与管理,则会重新将该锁表示出来。并且,也不再有必要重新转化为隐式锁。

    所以,如果有人问,你是否可以把当前数据库的所有的锁情况,都打印或记录下来,这是做不到的,也是没有必要的。事实上,“隐式锁”是广泛存在的,但因为通常并没有那么锁竞争,这些“隐式锁”也就一直不会被表示出来。

    一块拼图

    通常,隐式锁是可以被忽略的,如上述示例,这可能是一个没有任何竞争的锁。但,当出现对应的锁竞争时,则会变得可见。一般地,是可以不用关注隐式锁的,但,如果希望能够对 InnoDB 锁有非常系统的了解,这也是一块重要的“拼图”。