Oracle 19C热克隆应用避坑指南

2020 年 7 月 16 日

Oracle 19C热克隆应用避坑指南

本文由 dbaplus 社群授权转载。


一、背景


随着 Oracle 11g 进入扩展支持阶段,Oracle 19C 作为 12C 家族中最终稳定版,已被多数公司熟知及应用于生产。本人所在公司也在尝试对 19C 进行部署、测试、升级、迁移,于是借此机会将热克隆这个特性做了一番测试。


二、使用热克隆的前提


1、需要 12C R2 及以上版本


在 12C R1 中,要克隆 PDB,源 PDB 必须在克隆操作期间处于静止状态,因此它需要源 PDB 停机,通俗的讲这是种“冷克隆”。


从 12C R2 及以后的版本中开始支持“热克隆”,即 Oracle 数据库支持使用联机克隆的功能。当源 PDB 以读写方式打开的状态下,完全不需要中断源 PDB 中的操作,无须应用程序停机,就可以进行克隆操作。


2、必须使用 local undo


当使用 share undo 的情况下,需要将 share undo 转成 local undo 后才可以使用热克隆。可以在 upgrade 模式下用 alter database local undo on 进行转换。


三、工作原理


从下面三张图可以看出不管是本地克隆、远程克隆,还是 non-cdb 克隆,都是类似 rman 方式进行备份恢复。热克隆会有以下 3 个阶段:


第一阶段:当热克隆开始时(t0),对源 PDB 的数据文件按块进行读取,直到源 PDB 最后一个块被读取并将其复制到目标 PDB 时(t1),此时 t0-t1 时间段内可能对已经复制的一些块进行了更改。那么,在此阶段,目标 PDB 可能与源 PDB 在物理上不一致。


第二阶段:将 t0-t1 之间对源 PDB 所做的更改传至目标 PDB,进行重做应用。在此阶段,目标 PDB 将成为 t1 时源 PDB 的物理副本,但这里即包括了已提交的事务,也包括未提交的事务,因此可能在事务上不一致。


第三阶段:截止至 t1 时,源 PDB 中包含所有已提交的事务,所有未提交的事务将进行回滚,目标 PDB 将是截至 t1 时源 PDB 的事务一致的副本。由此可见,实现热克隆的关键是本地撤销,因此热克隆必须使用 local undo。



图 1 本地 PDB 克隆



图 2 远程 PDB 克隆



图 3 远程 non-cdb 克隆


四、常见应用场景


1、本地克隆


1)通过 seed 模板克隆


此方式主要应用于使用 seed 模板创建一个全新的 PDB。


① 查看 pdb 的状态


SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ---------------- -------------- ---------- 2 PDB$SEED READ ONLY NO
复制代码


② 查看 seed 模板的 datafile


SYS@ora19c>select con_id,name from v$datafile where con_id=2;
CON_ID NAME---------- ------------------------------------------------------------ 2 /u01/app/oracle/oradata/ORA19C/pdbseed/system01.dbf 2 /u01/app/oracle/oradata/ORA19C/pdbseed/sysaux01.dbf 2 /u01/app/oracle/oradata/ORA19C/pdbseed/undotbs01.dbf
复制代码


③ 利用 seed 模板进行新 PDB 的克隆,无需对源库执行任何操作,指定数据文件转换目录映射


SYS@ora19c>CREATE PLUGGABLE DATABASE pdb1 ADMIN USER pdb_mgr1 IDENTIFIED BY oracle roles=(dba) file_name_convert=('/u01/app/oracle/oradata/ORA19C/pdbseed','/u01/app/oracle/oradata/ORA19C/pdb1');
Pluggable database created.
复制代码


④ 打开新的 PDB 进行验证


SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1 MOUNTED
SYS@ora19c>alter pluggable database pdb1 open;
Pluggable database altered.
SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1 READ WRITE NO
SYS@ora19c>select con_id,name from v$datafile where con_id=3;
CON_ID NAME---------- -----------------------------------------------------------------3 /u01/app/oracle/oradata/ORA19C/pdb1/system01.dbf3 /u01/app/oracle/oradata/ORA19C/pdb1/sysaux01.dbf3 /u01/app/oracle/oradata/ORA19C/pdb1/undotbs01.dbf
复制代码


2)克隆一个已存在的 PDB


此方式常用于将已存在的 PDB 快速的在本地创建镜像,拥有与源 PDB 完全相同的数据、结构、用户、权限等。


① 将刚创建的 PDB1 创建一个 u1 用户并授权,验证克隆是否会克隆用户及权限


SYS@ora19c>alter session set container=pdb1;Session altered.
SYS@ora19c>create user u1 identified by oracle;
User created.
SYS@ora19c>grant connect,resource to u1;
Grant succeeded.
复制代码


② 通过已存在的 PDB1 克隆出 PDB2,源库可以在 read write 模式下直接进行操作


SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1 READ WRITE NO
SYS@ora19c>create pluggable database pdb2 from pdb1 file_name_convert=('pdb1','pdb2');
Pluggable database created.
SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1 READ WRITE NO 4 PDB2 MOUNTED
复制代码


③ 打开新创建的 PDB 进行验证


SYS@ora19c>alter pluggable database pdb2 open;
Pluggable database altered.
SYS@ora19c>select con_id,name from v$datafile where con_id=4;
CON_ID NAME---------- ----------------------------------------------------------------- 4 /u01/app/oracle/oradata/ORA19C/pdb2/system01.dbf 4 /u01/app/oracle/oradata/ORA19C/pdb2/sysaux01.dbf 4 /u01/app/oracle/oradata/ORA19C/pdb2/undotbs01.dbf
复制代码


④ 验证克隆的新库是否存在源库的用户及权限


SYS@ora19c>conn u1/oracle@192.168.8.101/pdb2Connected.U1@192.168.8.101/pdb2>select * from session_privs;
PRIVILEGE----------------------------------------SET CONTAINERCREATE INDEXTYPECREATE OPERATORCREATE TYPECREATE TRIGGERCREATE PROCEDURECREATE SEQUENCECREATE CLUSTERCREATE TABLECREATE SESSION
U1@192.168.8.101/pdb2>select * from session_roles;
ROLE--------------------------------------------------------CONNECTRESOURCESODA_APP
复制代码


2、远程克隆


1)克隆远程已存在的 PDB


此方式常用于将已存在的 PDB 快速的在异机之间创建镜像,拥有与源 PDB 完全相同的数据、结构、用户、权限等。


①源库 pdb_mgr1 用户授 create pluggable database 权限


SYS@ora19c>alter session set container=pdb1;
Session altered.
SYS@ora19c>grant create pluggable database to pdb_mgr1;
Grant succeeded.
复制代码


② 目标 CDB 中创建 db link


SYS@ora19c>create public database link lk_pdb1 connect to pdb_mgr1 identified by oracle using '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.8.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1)))';
Database link created.
复制代码


③ 执行远程克隆操作,源库无须进行其它操作,可以在 read write 下操作


SYS@ora19c>CREATE PLUGGABLE DATABASE pdb1_r FROM pdb1@lk_pdb1 file_name_convert=('pdb1','pdb1_r');
Pluggable database created.
复制代码


④ 打开新创建的 PDB 进行验证


SYS@ora19c>alter pluggable database pdb1_r open;Pluggable database altered.
SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1_R READ WRITE NO
SYS@ora19c>select con_id,name from v$datafile where con_id=3;
CON_ID NAME---------- -------------------------------------------------------------- 3 /u01/app/oracle/oradata/ORA19C/pdb1_r/system01.dbf 3 /u01/app/oracle/oradata/ORA19C/pdb1_r/sysaux01.dbf 3 /u01/app/oracle/oradata/ORA19C/pdb1_r/undotbs01.dbf
复制代码


2)远程克隆 Non-CDB


此方式常用于 Non-CDB 异机迁移 CDB 生成新的 PDB。


① 查看源库的状态


SYS@noncdb>select name,cdb,con_id from v$database; NAME                        CDB           CON_ID--------------------------- --------- ----------NONCDB                      NO                 0
复制代码


② 源库 pdb_mgr1 用户授 create pluggable database 权限


SYS@noncdb>grant create pluggable database to system;
Grant succeeded.
复制代码


③ 目标 CDB 中创建 db link


SYS@ora19c>create public database link lk_noncdb connect to system identified by oracle using '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.8.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=noncdb)))';
Database link created.
SYS@ora19c>select name,cdb,con_id from v$database@lk_noncdb;
NAME CDB CON_ID--------------------------- --------- ----------NONCDB NO 0
复制代码


④ 执行 noncdb 的远程克隆


SYS@ora19c>CREATE PLUGGABLE DATABASE noncdb_pdb FROM noncdb@lk_noncdb file_name_convert=('/u01/app/oracle/oradata/NONCDB','/u01/app/oracle/oradata/ORA19C/noncdb_pdb');
Pluggable database created.
复制代码


⑤ 打开新的 PDB 进行验证


SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1_R READ WRITE NO 5 NONCDB_PDB MOUNTED
SYS@ora19c>alter pluggable database NONCDB_PDB open;
Warning: PDB altered with errors.
复制代码


⑥ open 失败,执行 nocdb to pdb 的脚本


SYS@ora19c>alter session set container=NONCDB_PDB;
Session altered.
SYS@ora19c>@?/rdbms/admin/noncdb_to_pdb.sql
复制代码


⑦ 打开新创建的 PDB 进行验证


SYS@ora19c>alter pluggable database NONCDB_PDB open;
Pluggable database altered.
SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1_R READ WRITE NO 5 NONCDB_PDB READ WRITE NOSYS@ora19c>select con_id,name from v$datafile where con_id=5;
CON_ID NAME---------- --------------------------------------------------------------------- 5 /u01/app/oracle/oradata/ORA19C/noncdb_pdb/system01.dbf 5 /u01/app/oracle/oradata/ORA19C/noncdb_pdb/sysaux01.dbf 5 /u01/app/oracle/oradata/ORA19C/noncdb_pdb/undotbs01.dbf 5 /u01/app/oracle/oradata/ORA19C/noncdb_pdb/users01.dbf
复制代码


五、特殊应用场景


1、子集克隆


从 12.1.0.2 开始,引入了 User Tablespaces,简单的说就是可以按表空间(用户创建的)来克隆 PDB。比如,当前 PDB1 中,用户新建了两个表空间 ts1,ts2,克隆只需要 ts1 表空间中的数据,那么我们可以用 USER_TABLESPACES 子句只克隆 PDB1 中的 ts1 表空间,这样大大的缩短了时间和不必要的空间开销。对于拆分数据也很有用,可以把一个库按照表空间拆分。


语法:


  • USER_TABLESPACES=ALL 默认,所有表空间都克隆;

  • USER_TABLESPACES=NONE 所有用户创建的表空间都不克隆;

  • USER_TABLESPACES=(‘ts1’) 指定只克隆ts1;

  • USER_TABLESPACES=ALL EXCEPT(‘ts1’) 除了ts1之外,其他表空间都克隆。


1)源库创建表空间 ts1,ts2


SYS@ora19c>create tablespace ts1 datafile '/u01/app/oracle/oradata/ORA19C/pdb1/ts1.dbf' size 10m;
Tablespace created.
SYS@ora19c>create tablespace ts2 datafile '/u01/app/oracle/oradata/ORA19C/pdb1/ts2.dbf' size 10m;
Tablespace created.
复制代码


2)进行子集克隆,只克隆 ts1 表空间


SYS@ora19c>CREATE PLUGGABLE DATABASE pdb1_z FROM pdb1 file_name_convert=('pdb1','pdb1_z') user_tablespaces=('ts1');
Pluggable database created.
复制代码


3)打开新创建的 PDB 进行验证


SYS@ora19c>alter pluggable database pdb1_z open;
Pluggable database altered.
SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1 READ WRITE NO 4 PDB2 READ WRITE NO 6 PDB1_Z READ WRITE NO
SYS@ora19c>select con_id,name from v$datafile where con_id=6;
CON_ID NAME---------- ------------------------------------------------------------ 6 /u01/app/oracle/oradata/ORA19C/pdb1_z/system01.dbf 6 /u01/app/oracle/oradata/ORA19C/pdb1_z/sysaux01.dbf 6 /u01/app/oracle/oradata/ORA19C/pdb1_z/undotbs01.dbf 6 /u01/app/oracle/oradata/ORA19C/pdb1_z/ts1.dbf
复制代码


仅元数据的子集克隆,使用 no data,创建语法:


create pluggable database pdb_nodata from pdb1 file_name_convert=('pdb1','pdb1_nodata') no data;
复制代码


2、利用可刷新 PDB 的功能进行数据迁移


可刷新 PDB 功能是建立在热克隆的基础之上的。


当生产 PDB 数据量非常大,需要在很短的窗口时间进行数据迁移,当有了可刷新 PDB 和热克隆的功能后,一切将变得简单。无需考虑克隆需要花多长时间,因为源数据库无需停机。当目标 PDB 变得陈旧时,我们可以对其刷新,应用自上次刷新以来积累的所有增量。即使源数据库非常庞大,增量重做通常也将小得多。最后只在需要做割接时将源 PDB 置为 read only 后进行一次增量刷新。


刷新 PDB 须注意以下几点:


  • 源库必须开启归档日志和local undo;

  • 可以手动刷新或者自动定时刷新,但刷新时目标端必须是mounted状态;

  • 在不刷新期间,目标端可以以只读模式打开;

  • 如果需以读写模式打开目标端,则必须将refresh mode设置为none,设置none之后就无法再回退回其它刷新模式;

  • 刷新PDB必须使用dblink,dblink可以指向同一个CDB,也可以指向不同CDB。


1)在目标 PDB 创建 db link


SYS@ora19c>create public database link lk_pdb1 connect to pdb_mgr1 identified by oracle using '(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.8.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=pdb1)))';
Database link created.
复制代码


2)通过 db link 创建 refresh PDB


SYS@ora19c>CREATE PLUGGABLE DATABASE pdb1_ref FROM pdb1@lk_pdb1 file_name_convert=('pdb1','pdb1_ref') REFRESH MODE EVERY 60 MINUTES;
Pluggable database created.
SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1_R READ WRITE NO 4 PDB1_REF MOUNTED 5 NONCDB_PDB READ WRITE NO
复制代码


3)当 PDB 处于 REFRESH 模式时只能有 mounted 和 read only 两种状态


SYS@ora19c>alter pluggable database pdb1_ref open;alter pluggable database pdb1_ref open*ERROR at line 1:ORA-65341: cannot open pluggable database in read/write mode
SYS@ora19c>alter pluggable database pdb1_ref open read only;
Pluggable database altered.
SYS@ora19c>select pdb_id,pdb_name,refresh_mode from cdb_pdbs;
PDB_ID PDB_NAME REFRES---------- --------------- ------ 2 PDB$SEED NONE 4 PDB1_REF AUTO 5 NONCDB_PDB NONE 3 PDB1_R NONE
复制代码


4)PDB 只能在 mounted 状态下使用 REFRESH 功能


SYS@ora19c>alter pluggable database refresh;   alter pluggable database refresh*ERROR at line 1:ORA-65025: Pluggable database PDB1_REF is not closed on all instances.
Alert log:PDB1_REF(4):PDB1_REF(4):ERROR:PDB needs to be closed for auto refreshPDB1_REF(4):Completed: alter pluggable database refresh
复制代码


5)源 PDB 创建测试数据


U1@192.168.8.101/pdb1>create table t1 as select * from dba_objects;
Table created.
U1@192.168.8.101/pdb1>select count(*) from t1;
COUNT(*)---------- 72359
复制代码


6)模拟应用侧停应用,将源端 PDB 置为 read only


SYS@ora19c>alter pluggable database pdb1 close immediate;
Pluggable database altered.
SYS@ora19c>alter pluggable database pdb1 open read only;
Pluggable database altered.
复制代码


7)目标端手动刷新,应用最近的增量,观察目志是否正常


SYS@ora19c>alter pluggable database pdb1_ref refresh;
Pluggable database altered.
Alert log:2020-02-19T13:23:44.457060+08:00alter pluggable database pdb1_ref refresh2020-02-19T13:23:45.940479+08:00Applying media recovery for pdb-4099 from SCN 2793352 to SCN 2793357Remote log information: count-1thr-1, seq-12, logfile-/u01/app/oracle/product/db_1/dbs/archparlog_1_12_4aa635f6_1029786031.arc, los-2752894, nxs-18446744073709551615PDB1_REF(4):Media Recovery Start2020-02-19T13:23:45.942469+08:00PDB1_REF(4):Serial Media Recovery startedPDB1_REF(4):max_pdb is 92020-02-19T13:23:45.996021+08:00PDB1_REF(4):Media Recovery Log /u01/app/oracle/product/db_1/dbs/archparlog_1_12_4aa635f6_1029786031.arc2020-02-19T13:23:46.257650+08:00PDB1_REF(4):Incomplete Recovery applied until change 2793357 time 02/19/2020 13:23:092020-02-19T13:23:46.264473+08:00PDB1_REF(4):Media Recovery Complete (ora19c)Completed: alter pluggable database pdb1_ref refresh
复制代码


8)目标端 PDB 关闭刷新模式


SYS@ora19c>ALTER PLUGGABLE DATABASE pdb1_ref REFRESH MODE NONE;
Pluggable database altered
复制代码


9)拉起目标端 PDB


SYS@ora19c>ALTER PLUGGABLE DATABASE pdb1_ref open read write;
Pluggable database altered.
SYS@ora19c>show pdbs;
CON_ID CON_NAME OPEN MODE RESTRICTED---------- ------------------------------ ---------- ---------- 2 PDB$SEED READ ONLY NO 3 PDB1_R MOUNTED 4 PDB1_REF READ WRITE NO 5 NONCDB_PDB MOUNTED
复制代码


10)应用连接新 PDB,校验业务


SYS@ora19c>conn u1/oracle@192.168.8.102/pdb1_refConnected.
U1@192.168.8.102/pdb1_ref>select count(*) from t1;
COUNT(*)---------- 72359
复制代码


六、热克隆中常见的错误


  • 错误一

  • ORA-65040: operation not allowed from within a pluggable database
    
  • 解决方法: alter session set container=cdb$root;

  • 错误二

  • ORA-17628: Oracle error 1031 returned by remote Oracle server
    ORA-01031: insufficient privileges
    
  • 解决方法: 到源库里对用户授create pluggable database权限即可。

  • 错误三

  • ORA-19504: failed to create file '/u01/app/oracle/oradata/ORA19C/pdb1'
    ORA-27038: created file already exists
    
  • 解决方法: 文件映射路径问题,将“文件夹—文件夹”或“文件—文件”进行一一对应。

  • 错误四

  • ORA-65005: missing or invalid file name pattern for file-/u01/app/oracle/oradata/ORA19C/pdb1/system01.dbf
    
  • 解决方法: 路径错误或注意路径中的大小写。

  • 错误五

  • ORA-01578: ORACLE data block corrupted (file # 72, block # 33609)
    ORA-01110: data file 72: '/u01/app/oracle/oradata/ORA19C/pdb1_ref/system01.dbf'
    ORA-26040: Data block was loaded using the NOLOGGING option
    
  • 解决方法: 创建可刷新PDB时,源端未开启归档模式。


七、小结


热克隆的方式目前都已经比较成熟,并且可以灵活使用,适合多种应用场景。既可以应用于快速创建生产环境的完整副本或子集副本,也可以应用于较短停机时间的迁移。业务中断时间短,甚至无需业务中断,操作简单,不易出错,但某些场景下对环境要求较高。


作者介绍


王毅斌, 新炬网络数据库专家。精通 Oracle、MySQL 等数据库运维技术,拥有 Oracle OCM、MySQL OCP 等认证,具有丰富的系统架构设计、数据迁移等经验,擅长 Oracle SQL 优化,参与多个电信行业核心系统的优化。


原文链接


https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650791392&idx=1&sn=b44a752c58c8818de6577b5c9ea1fd67&chksm=f3f96875c48ee163af6813d4d509ee8bfc313fbdf000c58efda3766fb241a6e66be4f71c8b05&scene=27#wechat_redirect


2020 年 7 月 16 日 10:071224

评论

发布
暂无评论
发现更多内容

linux入门系列18--Web服务之Apache服务2

黑马腾云

Apache Linux centos linux运维

linux入门系列19--数据库管理系统(DBMS)之MariaDB

黑马腾云

MySQL Linux centos linux运维 MariaDB

Android | dagger细枝篇

哈利迪

android

架构师第十二周作业

傻傻的帅

巧用HashMap一行代码统计单词出现次数

程序那些事

jdk hashmap 统计字数

你真的懂 Java 的 main 方法吗

Rayjun

Java

Flink-有状态算子的扩缩容-12

小知识点

scala 大数据 flink

架构师第十二周学习总结

傻傻的帅

比特币挖矿到底挖的是什么?

CECBC区块链专委会

比特币 区块链 数字货币

视频AI第一步-动作识别数据集

flow

打通微信和钉钉服务是一种怎样的体验?

Ceelog

go 微信 钉钉 微信公众号

程序的机器级表示-控制

引花眠

计算机基础

linux入门系列18--Web服务之Apache服务1

黑马腾云

Linux centos apche linux运营 centos网站部署

更新丢失、写偏、幻读:数据库事务从快照隔离到可序列化

青菜年糕汤

数据库 数据库事务 分布式数据库 数据库设计 分布式系统

【持续更新~】常遗忘却可以变更好的心态

CoderJ

个人成长

SpringBoot+Tess4j实现牛逼的OCR识别工具

小隐乐乐

ARTS打卡 第14周

引花眠

微服务 ARTS 打卡计划

linux入门系列17--邮件系统之Postfix和Dovecot

黑马腾云

Linux centos Dovecot Postfix 邮件系统

算法导论

华宇法律科技

算法

央行数字货币钱包上线后又秒关 兑换了的 DCEP 别担心

CECBC区块链专委会

数字货币 央行

二叉树深度和大文件排序

escray

学习 面试 面试题 面试现场

ARTS Week9

丽子

ARTS 打卡计划

Go: ElasticSearch客户端学习

陈思敏捷

go golang elasticsearch elastic go-elasticsearch

搬家,又一次和过往告别

王磊

程序人生

基于区块链的社会治理探索

CECBC区块链专委会

区块链 大数据 信息技术

图解Node(上)——直击灵魂的十条拷问

执鸢者

前端 原理 Node

linux入门系列20--Web服务之LNMP架构实战

黑马腾云

php MySQL Linux centos ngnix

Python作业留底--《菜鸟教程》Python 练习和习题

Geek_f6bfca

oeasy教您玩转linux010108到底哪个which

o

什么是分散式金融(DeFi)?

志学Python

去中心化金融 defi

JVM的早期优化与晚期优化

Edison

JVM JVM虚拟机原理

Oracle 19C热克隆应用避坑指南-InfoQ