admin
-
在机器学习中大量的使用
NumPy
作为其基础的数据结构,ndarray
是NumPy
的核心数据对象。对于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
官方文档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';
参考
- [1] Redo Log Analysis Using LogMiner Oracle8i New Features@oracle.com
- [2] Using LogMiner to Analyze Redo Log Files 21c@oracle.com
-
为什么需要间隙锁
关于为什么需要 “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 在表示间隙锁的时候,并没有把
GAP
或Next-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
加锁模式加锁对象/
资源加锁类型 Dm
S,REC_NOT_GAP
Dm
S
Dm
X,REC_NOT_GAP
Dm
X
Dm
S,GAP
(D(m-1),
Dm
)S
Dm
X,GAP
(D(m-1),
Dm
)X
Dm
S
(D(m-1),
Dm
]S
Dm
X
(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 A
Session 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 A
Session 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]
参考链接
- [3] https://mp.weixin.qq.com/s/OnRyMHny8VJDVIxts4WTFw
- [4] https://www.volcengine.com/docs/6313/75374
- [5] https://www.volcengine.com/docs/6313/1467064
- [6] https://www.volcengine.com/docs/6313/1521305
- [7] https://www.volcengine.com/docs/6313/1399335
- [8] https://www.volcengine.com/docs/6293/1541593
- [9] https://azure.microsoft.com/updates?id=489669
- [10] https://azure.microsoft.com/updates?id=489664
- [11] https://www.volcengine.com/docs/6293/76510
- [12] https://www.volcengine.com/docs/6293/71725
- [14] https://azure.microsoft.com/updates?id=489627
- [15] https://azure.microsoft.com/updates?id=489622
- [26] https://www.volcengine.com/docs/6438/79254
- [27] https://www.volcengine.com/docs/6438/907280
- [28] https://www.volcengine.com/docs/6438/1126562
- [30] https://www.volcengine.com/docs/6438/1476865
- [34] https://cloud.baidu.com/doc/SCS/s/Mm8wt78ac
- [42] https://aws.amazon.com/about-aws/whats-new/2025/04/amazon-memorydb-supports-ipv6/
- [44] https://cloud.tencent.com/document/product/236/115774
- [45] https://cloud.tencent.com/document/product/1003/109576
- [46] https://cloud.tencent.com/document/product/1222/117606
-
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
相关资源
- Oracle 镜像仓库官方站点:Oracle Container Registry
- Oracle 官方 GitHub 镜像列表:docker-images/OracleDatabase@GitHub
- Oracle 官方容器镜像使用说明: