上市企业网站建设,外贸网站源代码下载,怎样做水果外卖加盟网站,营销类网站建设需要注意的问题近期#xff0c;OceanBase的问答社区中收到了不少用户的询问#xff0c;关于OceanBase 3.x版本支持被truncate的table进入回收站的功能#xff0c;为何在升级到4.x版本后不再支持了#xff1f;为了解答大家的疑惑#xff0c;我们将通过这篇文章来浅析 OceanBase在4.x版本中…近期OceanBase的问答社区中收到了不少用户的询问关于OceanBase 3.x版本支持被truncate的table进入回收站的功能为何在升级到4.x版本后不再支持了为了解答大家的疑惑我们将通过这篇文章来浅析 OceanBase在4.x版本中为何会出现这一“功能回调”。 背景
尽管MySQL本身并不具备回收站这样的概念和功能但对于DBA同学来说误操作和误删数据却是常见的问题。若MySQL拥有类似回收站的功能那么误删的数据就可以迅速且无损地被恢复。应广大蚂蚁 DBA 的强烈要求OceanBase 在 MySQL 模式下也支持了类似于 Oracle 10g 中的回收站功能。
在 3.x 版本中当 session 级别的系统变量 ob_enable_truncate_flashback 被置为 on 时如果进行了一个 truncate table 的误操作可以通过 flashback 还原执行 truncate table 之前的表和数据。
例如通过执行如下 SQL 序列就可以把被 truncate 掉的 t1 中被 truncate 之前的数据还原到另外一张叫 truncated_t1 的表中。
show variables like recyclebin;set recyclebin on; # 开启回收站的功能show variables like ob_enable_truncate_flashback;set ob_enable_truncate_flashback on; # 开启 truncate table 进回收站的功能create table t1(c1 int);insert into t1 values(123);truncate table t1;show recyclebin;flashback table t1 to before drop rename to truncated_t1;
最近遇到很多用户和同学在问3.x 版本支持被 truncate 的 table 进回收站为什么 4.x 版本不支持了
# 下面这个系统变量在 4.x 版本已经被废弃掉了不生效了官网文档有误。set ob_enable_truncate_flashback on; truncate table t1; show recyclebin;
Empty set (0.010 sec) flashback table t1 to before drop rename to truncated_t1;
ERROR 5270 (HY000): object not in RECYCLE BIN 原因分析
原因要从 OceanBase 回收站的实现方式以及 3.x 以及 4.x 版本 truncate table 的实现方式说起。
回收站的实现方式
回收站的实现本质上是一种逻辑删除或者叫标记删除即把被删除的对象用内部标识进行记录有该标识的对象对用户不可见。进入回收站的对象只修改了对象的元数据底层数据没有任何改动。
对于回收站的实现我们先从 show recyclebin 说起show recyclebin 可以显示所有在回收站中对象信息这些对象信息都保存在 __all_recyclebin 这张内部表中每次将对象放入到回收站时都会在该表中插入一条记录FLASHBACK还原 或者 PURGE清空时会将信息从 __all_recyclebin 表中删除。
obclient [test] show recyclebin;
----------------------------------------------------------------------------------------
| OBJECT_NAME | ORIGINAL_NAME | TYPE | CREATETIME |
----------------------------------------------------------------------------------------
| __recycle_$_1_1694438429313272 | liboyang_db | DATABASE | 2023-09-11 21:20:29.314951 |
| __recycle_$_1_1694438481392392 | __idx_504469_idx | INDEX | 2023-09-11 21:21:21.392822 |
| __recycle_$_1_1694438481414600 | t1 | TABLE | 2023-09-11 21:21:21.415038 |
----------------------------------------------------------------------------------------
3 rows in set (0.011 sec)obclient [test] select object_name, original_name, type, gmt_create, database_id, table_id from oceanbase.__all_recyclebin;
-----------------------------------------------------------------------------------------------------------
| object_name | original_name | type | gmt_create | database_id | table_id |
-----------------------------------------------------------------------------------------------------------
| __recycle_$_1_1694438429313272 | liboyang_db | 4 | 2023-09-11 21:20:29.314951 | 500006 | -1 |
| __recycle_$_1_1694438481392392 | __idx_504469_idx | 2 | 2023-09-11 21:21:21.392822 | 500001 | 504470 |
| __recycle_$_1_1694438481414600 | t1 | 1 | 2023-09-11 21:21:21.415038 | 500001 | 504469 |
-----------------------------------------------------------------------------------------------------------
3 rows in set (0.002 sec)obclient [test] purge database __recycle_$_1_1694438429313272;
Query OK, 0 rows affected (0.080 sec)obclient [test] show recyclebin;
-------------------------------------------------------------------------------------
| OBJECT_NAME | ORIGINAL_NAME | TYPE | CREATETIME |
-------------------------------------------------------------------------------------
| __recycle_$_1_1694438481392392 | __idx_504469_idx | INDEX | 2023-09-11 21:21:21.392822 |
| __recycle_$_1_1694438481414600 | t1 | TABLE | 2023-09-11 21:21:21.415038 |
-------------------------------------------------------------------------------------
2 rows in set (0.011 sec)obclient [test] flashback table t1 to before drop rename to dropped_t1;
Query OK, 0 rows affected (0.145 sec)obclient [test] show recyclebin;
Empty set (0.010 sec)
为了支持 drop table 进入回收站的功能OceanBase 为每个租户都增加了一个名为 __recyclebin 的默认库对应的 database_id 都是 201004。
obclient [test]
select database_id
from oceanbase.__all_database
where database_name __recyclebin;
-------------
| database_id |
-------------
| 201004 |
-------------
1 row in set (0.008 sec)
drop table 进回收站时会修改被 drop 表的元数据信息 database_id 和 table_name相当于把表从原来的库中移除然后放入到这个 __recyclebin 库中。需要注意的是drop table 时也会将表上的索引一起放到回收站里。
obclient [test] create table t1(c1 int, index idx(c1));obclient [test] drop table t1;obclient [test] flashback table t1 to before drop rename to dropped_t1;obclient [test]
select a.schema_version, a.table_id, a.database_id, a.table_name, b.ddl_stmt_str
from oceanbase.__all_table_history a, oceanbase.__all_ddl_operation b
where a.table_id 504469 and a.table_id b.table_id and a.schema_version b.schema_version;
---------------------------------------------------------------------------------------------------------------------------------
| schema_version | table_id | database_id | table_name | ddl_stmt_str |
---------------------------------------------------------------------------------------------------------------------------------
| 1694438476085056 | 504469 | 500001 | t1 | create table t1(c1 int, index idx(c1)) |
| 1694438476174856 | 504469 | 500001 | t1 | |
| 1694438481405672 | 504469 | 500001 | t1 | |
| 1694438481414600 | 504469 | 201004 | __recycle_$_1_1694438481414600 | DROP TABLE test.t1 |
| 1694438664151120 | 504469 | 201004 | __recycle_$_1_1694438481414600 | |
| 1694438664161728 | 504469 | 500001 | dropped_t1 | flashback table t1 to before drop rename to dropped_t1 |
---------------------------------------------------------------------------------------------------------------------------------
6 rows in set (0.014 sec) 所以删除一张表进回收站整个流程如下 修改 table 的 database_id 和 table_name库名为 __recyclebin表名为 __recycle 开头的特定格式。将表放入到回收站即在 __all_recyclebin 表中增加相应的记录。对于表上的各个索引表也执行上述步骤。 flashback to before drop 是还原操作相当于 drop to recyclebin 的逆运算实现上和上述 drop to recyclebin 也都是正好相反的这里不再赘述。 purge 是清空回收站实现是先从 __recyclebin 删除这个对象的信息然后再从 __all_table 和 __all_table_history 中把这个对象真正删除这里不再赘述。 回收站中的其他对象例如 tenant、database 等实现上也都是类似的这里不再赘述。 truncate table 的实现方式
3.x 版本的 truncate table
3.x 版本的 truncate table 实现上是转换成了 drop table create table。例如执行一条 DDL truncate table t1内部会在同一个事务中执行 drop table t1 和 create table t1。
因为 3.x 版本的 truncate table 有 drop table 这个步骤并且 drop table 可以进回收站所以 3.x 版本被 truncate table 之前的表和数据会跟随 drop table 这个动作进入回收站。这也就是在 3.x 版本如果我们进行了一个 truncate table 的误操作为什么可以通过 flashback 还原执行 truncate table 之前的表和数据的原因了。
4.x 版本的 truncate table
OceanBase 在 4.x 版本新增加了一个 tablet 的概念这是一个用户不感知的物理存储层的概念表示可以迁移的数据块。基于这个 tablet4.x 的 truncate table 实现上只需要修改分区对应的 tablet不再复用 3.x 版本的 drop table create table 的流程。即在 4.x 版本的 truncate table 中实现变成了删除旧 tablet 创建新 tablet当 table 上的附属对象例如 column、constraint、foreign key、partition、index 等较多时truncate table 性能可以得到巨大的优化。
因为 4.x 版本的 truncate table 已经没有 drop table 这个步骤了所以也就不会再像 3.x 一样把 truncate 过程中 drop 掉的这个 table 给放进回收站了。4.x 上 truncate table 前后 table 的元数据不会发生变更只需要修改一下和存储相关的 tablet 即可。
4.x 版本为什么要调整 truncate table 的实现方式
在 OceanBase 还是 3.x 版本时部分客户会在业务逻辑中大量使用 truncate table 语句但是大家都发现 OB 中 truncate table 的性能低于 Oracle。性能的消耗来自于两部分系统表数据的更新和分区实体的创建。后者在 OB 3.x 版本上是需要创建 paxos 成员组的步骤较多但是在 OB 4.x 的单日志流架构下新建分区只是在日志流内数据的操作性能会大幅提升。而性能消耗的另外一部分是系统表数据的更新要消除这一部分的开销就需要改变 truncate table 的实现方式。
之前接触过一个使用 OceanBase 3.x 版本的用户他们有上万张表每张表都有几百个 column 和几百个 check 约束所以每张表在 __all_column 和 __all_constraint 系统表中都会记录上百行数据。3.x 版本每次 truncate table 操作都要在 drop table 时对这些元信息执行删除操作在 create table 时再对这些元信息执行写入操作当 table 上的附属对象较多时大量附属对象元信息的删除和写入会让 truncate table 耗费大量的时间。
而 OceanBase 4.x 中 truncate table 的实现不再修改表的元数据信息所以 __all_column、__all_constraint 等系统表的数据都不会发生改变。数据的清除通过更换 tablet 来实现。__all_table 表内会增加一列 tablet_id 表示一张表对应的 tablettruncate table 时创建一个新的空 tablet将新 tablet_id 写入 __all_table 表替换之前的旧值这样就完成了 truncate 表的操作之后旧的 tablet 即可删除。对于分区表会在 __all_part 表中记录每个分区对应的 tablet_idtruncate table 时将每个分区对应的 tablet 替换成新的空 tablet 即可。
熟悉 Oracle 的同学可以把 table_id 对应成 Oracle 的 object_id把 tablet_id 对应成 Oracle 的data_object_id上述 OceanBase 4.x 版本 truncate table 的实现方式就和 Oracle 的实现逻辑是类似的了。
其他
最后再说几个回收站功能中我个人觉得比较有意思且大家平时不容易关注到的点。
通过 original_name 来 flashback table
OceanBase 官网目前2023.09.12说通过 flashback 还原回收站里的 table 时只支持指定要恢复的数据库对象在回收站中的名称 object_name不支持直接指定其被删除前的名称 original_name。
其实 OceanBase 也支持指定其被删除前的名称 original_name如果回收站中的 table 有多个同名的 original_nameflashback 会还原最晚进回收站的那张表所以 flashback original_name 的操作可以理解成是一个栈后进先还原。该行为和 Oracle 的回收站一致。
接下来举个例子
# 创建名字为 t1 的表然后删除进回收站重复三遍
obclient [test] create table t1(c1 int);
Query OK, 0 rows affected (0.150 sec)obclient [test] drop table t1;
Query OK, 0 rows affected (0.448 sec)obclient [test] create table t1(c1 int);
Query OK, 0 rows affected (0.150 sec)obclient [test] drop table t1;
Query OK, 0 rows affected (0.448 sec)obclient [test] create table t1(c1 int);
Query OK, 0 rows affected (0.150 sec)obclient [test] drop table t1;
Query OK, 0 rows affected (0.448 sec)# 回收站中会出现三张 original_name 为 t1 的表
obclient [test] show recyclebin;
----------------------------------------------------------------------------------
| OBJECT_NAME | ORIGINAL_NAME | TYPE | CREATETIME |
----------------------------------------------------------------------------------
| __recycle_$_1_1694489277444056 | t1 | TABLE | 2023-09-12 11:27:57.450622 |
| __recycle_$_1_1694489280576008 | t1 | TABLE | 2023-09-12 11:28:00.577040 |
| __recycle_$_1_1694489499729088 | t1 | TABLE | 2023-09-12 11:31:39.729893 |
----------------------------------------------------------------------------------
3 rows in set (0.011 sec)# 第一次 flashback 的 t1 是当前回收站中最晚11:31进回收站的那张 t1
obclient [test] flashback table t1 to before drop rename to t1_1;
Query OK, 0 rows affected (0.123 sec)obclient [test] show recyclebin;
----------------------------------------------------------------------------------
| OBJECT_NAME | ORIGINAL_NAME | TYPE | CREATETIME |
----------------------------------------------------------------------------------
| __recycle_$_1_1694489277444056 | t1 | TABLE | 2023-09-12 11:27:57.450622 |
| __recycle_$_1_1694489280576008 | t1 | TABLE | 2023-09-12 11:28:00.577040 |
----------------------------------------------------------------------------------
2 rows in set (0.010 sec)# 第二次 flashback 的 t1 也是当前回收站中最晚11:28进回收站的那张 t1
obclient [test] flashback table t1 to before drop rename to t1_2;
Query OK, 0 rows affected (0.089 sec)obclient [test] show recyclebin;
----------------------------------------------------------------------------------
| OBJECT_NAME | ORIGINAL_NAME | TYPE | CREATETIME |
----------------------------------------------------------------------------------
| __recycle_$_1_1694489277444056 | t1 | TABLE | 2023-09-12 11:27:57.450622 |
----------------------------------------------------------------------------------
1 row in set (0.012 sec)# 第三次直接通过 object_name 去 flashback 了
obclient [test] flashback table __recycle_$_1_1694489277444056 to before drop rename to t1_3;
Query OK, 0 rows affected (0.093 sec)obclient [test] show recyclebin;
Empty set (0.011 sec)
通过 original_name 来 purge table
OceanBase 官网目前说通过 purge 清空回收站中的 table 时只支持指定要删除的表在回收站中的名称 object_name不支持直接指定表的名称 original_name。
其实 OceanBase 也支持指定其被删除前的名称 original_name如果回收站中的 table 有多个同名的 original_namepurge 会清空最早进回收站的那张表所以 purge original_name 的操作可以理解成是一个队列先进先清空。该行为和 Oracle 的回收站一致。
接下来举个例子
# 创建名字为 t1 的表然后删除进回收站重复三遍
obclient [test] create table t1(c1 int);
Query OK, 0 rows affected (0.143 sec)obclient [test] drop table t1;
Query OK, 0 rows affected (0.551 sec)obclient [test] create table t1(c1 int);
Query OK, 0 rows affected (0.146 sec)obclient [test] drop table t1;
Query OK, 0 rows affected (0.552 sec)obclient [test] create table t1(c1 int);
Query OK, 0 rows affected (0.145 sec)obclient [test] drop table t1;
Query OK, 0 rows affected (0.551 sec)# 回收站中会出现三张 original_name 为 t1 的表
obclient [test] show recyclebin;
----------------------------------------------------------------------------------
| OBJECT_NAME | ORIGINAL_NAME | TYPE | CREATETIME |
----------------------------------------------------------------------------------
| __recycle_$_1_1694490316467736 | t1 | TABLE | 2023-09-12 11:45:16.468070 |
| __recycle_$_1_1694490326604712 | t1 | TABLE | 2023-09-12 11:45:26.605179 |
| __recycle_$_1_1694490329719016 | t1 | TABLE | 2023-09-12 11:45:29.719260 |
----------------------------------------------------------------------------------
3 rows in set (0.017 sec)# 第一次 purge 的 t1 是当前回收站中最早11:45:16进回收站的那张 t1
obclient [test] purge table t1;
Query OK, 0 rows affected (0.178 sec)obclient [test] show recyclebin;
----------------------------------------------------------------------------------
| OBJECT_NAME | ORIGINAL_NAME | TYPE | CREATETIME |
----------------------------------------------------------------------------------
| __recycle_$_1_1694490326604712 | t1 | TABLE | 2023-09-12 11:45:26.605179 |
| __recycle_$_1_1694490329719016 | t1 | TABLE | 2023-09-12 11:45:29.719260 |
----------------------------------------------------------------------------------
2 rows in set (0.011 sec)# 第二次 purge 的 t1 是当前回收站中最早11:45:26进回收站的那张 t1
obclient [test] purge table t1;
Query OK, 0 rows affected (0.127 sec)obclient [test] show recyclebin;
----------------------------------------------------------------------------------
| OBJECT_NAME | ORIGINAL_NAME | TYPE | CREATETIME |
----------------------------------------------------------------------------------
| __recycle_$_1_1694490329719016 | t1 | TABLE | 2023-09-12 11:45:29.719260 |
----------------------------------------------------------------------------------
1 row in set (0.013 sec)# 第三次直接通过 object_name 去 purge 了
obclient [test] purge table __recycle_$_1_1694490329719016;
Query OK, 0 rows affected (0.134 sec)obclient [test] show recyclebin;
Empty set (0.010 sec) 这篇文章暂时就先写到这里了如果大家对 OceanBase 的回收站还有什么问题欢迎留言或者评论我们一起学习和探讨~ 更多内容 下面是往期的博客内容欢迎感兴趣的同学相互交流学习 《OceanBase 里的 schema 是什么》 《OceanBase 执行引擎的自适应技术》 《OceanBase 分布式下压技术》