概述

假设你是一名数据分析师,你的任务是从包含数百万行数据的数据库中,反复检索特定的信息,每次查询都需要执行耗时耗资源的计算,你可以通过物化视图来提高查询效率。
什么是物化视图?
物化视图是一种特殊的视图,它存储了视图定义中查询执行的结果,这个过程就是物化。通过保存某些耗时操作的结果,方便在查询时直接查询已经预计算好的数据,避免重复执行这些耗时耗资源的操作,通过空间换时间来加速查询。
物化视图和普通视图有什么区别?
普通视图只是存储了视图的定义,用户每次查询视图的时候,都会重新执行视图定义中的查询语句。物化视图有一个容器表用于存储视图定义中查询语句执行的结果,用户查询视图时,只需要查询容器表即可。物化视图为了保存结果的时效性,会定期执行视图查询语句刷新容器表中的结果,这样用户在查询时,无需实时计算。这种预先计算的方式大大提升了查询性能。

功能介绍

OceanBase在V4.3.0版本支持的物化视图(Materialized View, MV)可以适用于如下场景:

  1. 数据汇总:汇总每天、每周或每月的销售数据、统计用户行为数据等;
  2. 统计信息报表数据生成:报表系统需要定期生成固定格式的数据报告;
  3. 复杂查询优化:对于特别消耗资源的查询,可以将结果物化避免查询重复计算;
  4. 分发数据:冗余多份数据放到不同的区域中,便于各个区域的人就近访问数据;
  5. 监控数据的预聚合:监控数据会按照不同的时间间隔进行展示;
    名词介绍:
  6. 视图(View):是一种虚拟表,与包含数据的实际表不同,视图只保存了一个查询的结果集的结构。
  7. 物化视图(Materialized View,MV):物化视图是一种数据库对象,它与传统的视图不同,物化视图将查询结果作为数据实际存储于数据库中。
  8. 基表(Base Table):物化视图所依赖的源表,物化视图的数据来自于这些表的查询结果。
  9. 全量刷新(Complete Refresh):全量刷新是指完全重新计算物化视图中的数据。
  10. 增量刷新(Incremental Refresh):也可以叫:快速刷新(Fast Refresh),只针对自上次刷新以来基表数据发生变动的部分刷新到物化视图中。
  11. 物化视图日志(Materialized View Log,mlog):用于记录基表数据变更的日志。

    语法

    OceanBase物化视图的语法主要兼容Oracle物化视图的语法,MySQL模式下的语法和Oracle模式下语法保持一致。

    创建物化视图

CREATE MATERIALIZED VIEW view_name [column_list] [table_option_list] [partition_option] [refresh_clause] AS view_select_stmt;

column_list:
    (column_name [, column_name ...])

refresh_clause:
    REFRESH [COMPLETE | FAST | FORCE] [mv_refresh_on_clause]
    | NEVER REFRESH

mv_refresh_on_clause:
    [ON DEMAND] [[START WITH expr] [NEXT expr]]

功能:
在创建物化视图的时候,可以根据视图中的查询语句来生成对应物化视图的schema,通过这个schema生成建表语句创建物化视图的表,然后把查询的结果插入到这个表中。创建物化视图的同时会执行一次全量刷新,这个全量刷新和创建物化视图是一个事务,如果刷新失败也算是创建物化视图失败。

  • 支持创建分区物化视图;
  • 物化视图的刷新策略支持;
  • COMPLETE:全量刷新
  • FAST:表示快速刷新,即增量刷新
  • FORCE:先尝试增量刷新,不行选择用全量刷新
  • NEVER REFRESH:物化视图只在创建的时候刷新,创建以后就不允许刷新了
  • 支持配置刷新的时机,配置自动刷新的时间间隔;
  • 只支持普通表作为物化视图的基表;
  • 基于物化视图可以创建索引表,创建方式与普通表相同;
    注意事项:
    对物化视图的基表执行DDL操作,可能会导致物化视图无法用预期的模式刷新。对于全量刷新,物化视图和基表对应列类型匹配,就可以刷新;对于增量刷新,需要在基表上创建物化视图日志,这类基表的一些DDL操作会被拦截,一般不删除物化视图日志,就仍然可以增量刷新。物化视图收集统计信息。可以通过analyze table或者call dbms_stats.gather_table_stats('database_name', 'table_name') 来收集统计信息。
    使用限制:
  • 不支持 on prebuilt table 方式创建物化视图 ;
  • 不支持指定 build immediate/deferred;不支持可更新的物化视图;
  • 不支持 ON COMMIT、ON STATEMENT 模式;
  • 不支持修改物化视图属性,如刷新方法,物化视图重命名,刷新时机等;
  • 不支持xmltype类型;
  • 不支持表级恢复;
  • 不支持基于普通视图、物化视图、同义词及外表创建物化视图;
  • 暂不支持创建列存格式的物化视图;

    删除物化视图

    物化视图会占用物理空间,所以当不需要物化视图的时候,建议删除物化视图。

DROP MATERIALIZED VIEW [IF EXISTS] materialized_view_list [opt_drop_behavior];

materialized_view_list:
    [ database. ]materialized_view [,[ database. ]materialized_view]...

opt_drop_behavior:
    RESTRICT | CASCADE

注意:单独drop MV的时候,MV不进回收站。drop database的时候会随database进回收站。

创建物化视图日志

OceanBase增量刷新的物化视图是通过物化视图日志支持的,物化视图日志(materialized view log,mlog)是数据库中用于跟踪物化视图基表数据变化的一种结构,它记录了自上次刷新物化视图以来基表中发生的所有数据更改,包括插入、更新和删除操作,在创建增量刷新的物化视图前,需要先创建mlog,创建mlog时可以指定哪些列的数据变化被跟踪,指定mlog中数据的清除时间。
物化视图日志和基表是一一绑定的,基于这个基表创建的所有增量刷新的物化视图都利用同一个mlog来更新数据,所以mlog的创建一定要结合物化视图的创建来考虑,创建物化视图需要的列,在mlog中都应该包含。

CREATE MATERIALIZED VIEW LOG ON [database.] table [parallel_clause] [with_clause] [mv_log_purge_clause];

parallel_clause:
    NOPARALLEL 
    | PARALLEL integer

with_clause:
    WITH [ {PRIMARY KEY | ROWID | SEQUENCE} 
            [ { , PRIMARY KEY | , ROWID | , SEQUENCE }]... ] 
        (column_name [, column_name]...) 
        [new_values_clause]

new_values_clause:
    {INCLUDING | EXCLUDING} NEW VALUES

mv_log_purge_clause:
    PURGE {IMMEDIATE [ SYNCHRONOUS ]
        | START WITH datetime_expr [NEXT datetime_expr]
        | [START WITH datetime_expr] NEXT datetime_expr
        }

说明:

  • mlog是OceanBase一张普通的表,一个表只能有一个物化视图日志,命名遵循Oracle的命名方式,其schema名称为“mlog$_table”,其中“table”为基表的名称。
  • parallel_clause指定创建mlog的并行度。
  • with_clause定义物化视图日志需要跟踪变化的字段。
  • including new values:记录每个操作的以后得新值。
  • mv_log_purge_clause指物化视图日志的清理时机,默认是IMMEDIATE,所以增量刷新完成后,物化视图日志中的数据就会被清除。

示例:

--mysql模式
create materialized view log on t1 purge start with sysdate() next sysdate() + interval 1 day;

--oracle模式(oracle模式中建议使用current_date表示当前时区时间,因为sysdate不受时区影响)
create materialized view log on t1 purge start with current_date next current_date + 1;

注意事项:
多个MV共享一个mlog的时候,需要最后一个MV更新完成以后,才能删除对应的数据。如果某个MV长时间不更新。那么会导致mlog占用的空间非常大,导致空间爆炸。

使用限制:

  • 不支持可更新物化视图日志;
  • 不支持除普通表以外的对象上创建物化视图日志;
  • mlog暂不支持lob列/生成列;
  • mlog不支持JSON、XML、GIS 和 UDT类型;
  • mlog不支持表级恢复;
  • 不支持修改物化视图属性;
  • 不支持基于物化视图日志创建索引;
  • 基表和 mlog 具有绑定关系,删除基表前需要先删除 mlog ;
  • mlog 的名字长度有 64 字节的限制;
  • 暂时不支持指定 partition;

    删除物化视图日志

    DROP MATERIALIZED VIEW LOG ON [database.] table;

注意:

  • 删除物化视图日志时,如果基表正处于某个运行的事务中,则直到该事务结束前,删除操作都会阻塞。
  • mlog单独drop的时候不进回收站。

物化视图原理

物化视图在OceanBase的内部schema中实际上是两个表:一个是物化视图本身的schema,和普通的view类似;另外一个是隐藏的普通表,这个普通表用于存储实际的物化视图数据,我们称之为物化视图的容器表。由于物化视图的数据是基于其定义的查询结果预先计算的,当基表数据发生变化时,物化视图的内容可能变得过时。为了保持数据一致性,通常需要通过手动或自动的方式刷新物化视图,更新其内容以反映基表的最新状态。可操作的刷新方法:全量刷新和增量刷新。

全量刷新

只要当前基表的列与物化视图对应的列类型还是匹配的,就符合全量刷新条件;否则无法全量刷新。OceanBase使用异地刷新的方式进行全量刷新,即创建一个隐藏表,在隐藏表上执行刷新语句,然后切换原表和隐藏表。因此,全量刷新操作需要额外的空间,并且会全量重建索引(如果有的话)。全量刷新可能是一个非常耗时的过程,尤其是在需要读取和处理大量数据的情况下。因此,在执行全量刷新之前,需要考虑处理全量刷新所需的时间。
图片

增量刷新

增量更新会根据更新周期内更新的数据计算出物化视图增量部分来更新到物化视图中,增量更新的代价比全量更新的代价小很多。增量更新的原始数据会存储在物化视图日志里面,所以如果要创建增量刷新的物化视图,需要先基于基表创建物化视图日志。物化视图日志也使用普通的内部表存储,会存储所有更新行的旧值和新值。
图片
上图是增量更新的整体框架图。对于写入来说,所有的DML修改语句会通过DAS同时更新基表和物化视图日志,后台任务(maintenance task)会定期的把增量更新刷到MV中。mlog中红色部分是已经刷过的数据,delete模块会把已经刷过的数据删除掉。对于查询,QUERY模块会同时查询MV和mlog中未更新部分的数据,合并起来返回给用户。保证查询到的数据是最新的。使用一个例子来描述增量更新
图片
如上图所示,原表t1和mv m1的定义如下:

create table t1 (c1 int primary key, c2 int);
create materialized view m1 as select count(c1) from t1;

更新序列为

insert into t1 values(3,4);
insert into t1 values(5,6);
insert into t1 values(8,3);
update t1 set c1 = 7 where c1 = 8;

对应的mlog为mlog$_t1。old?为N表是新值,Y表示是旧值m1的更新值可以通过 (select count(c1) from m1 where old = 'N') - (select count(c1) from m1 where old = 'Y')获得。
OceanBase使用就地刷新的方法进行增量刷新,即直接在物化视图上执行刷新语句。如果增量数据过多,可能导致增量刷新比全量刷新还慢。物化视图的使用通常会结合一些聚合函数,全量刷新对于查询条件没有约束,但是增量刷新需要依赖mlog的维护,在OceanBase的V4.3.0版本的物化视图中,增量刷新的物化视图的查询语句存在要求,当前仅支持有限的带group by聚合,并且使用方式需要严格遵循如下要求:

  • select item 中包含所有 group by 列;
  • 聚合函数不包含 distinct,且参数为基本列;
  • select item 包含 count(*),其它支持聚合函数及其它要求,min/max 暂时不支持;

image.png

示例:

create table t1(c1 int primary key, c2 int, c3 int, c4 int);
create materialized view log on t1 with sequence (c2, c3) including new values;
create materialized view t1_mv1 refresh fast on demand as select c2 as c2, count(*) cnt, count(c3) cnt_c3, sum(c3) sum_c3 from t1 group by c2;
create materialized view t1_mv2 refresh fast on demand as select count(*) cnt, count(c3) cnt_c3, sum(c3) sum_c3 from t1;
create materialized view t1_mv3 refresh fast on demand as select count(c3) cnt_c3, sum(c3) sum_c3 from t1;
create materialized view t1_mv4 refresh fast on demand as select c2 as c2, c3 as c3, count(*) cnt, count(c3) cnt_c3, sum(c3) sum_c3 from t1 group by c2, c3;
create materialized view t1_mv5 refresh fast on demand as select c2 as c2, count(*) cnt, count(c3) cnt_c3, sum(c3) sum_c3, avg(c3) avg_c3, avg(c3) * sum(c3)/c2 calc1, c2+sum(c3) calc2 from t1 group by c2;
create materialized view t1_mv6 refresh fast on demand as select c2 as c2, count(*) cnt, count(c3) cnt_c3, sum(c3) sum_c3, count(c3*c3) cnt_c3_2, sum(c3*c3) sum_c3_2, STDDEV(c3) stddev_c3 from t1 group by c2;

**
功能对比**
image.png
功能限制

  • 不支持实时物化视图;
  • 不支持查询重写;
  • 增量刷新的物化视图存在比较多限制:
  • 不支持聚合函数为 MAX/MIN;
  • 不支持内联视图/union/子查询等场景;
  • 不支持包含 rownum/rand 等不能稳定输出值的表达式;
  • group by 场景(MAV),仅支持 SUM/COUNT 及使用这两种构造的聚合函数,聚合函数中仅支持简单column,必须输出 COUNT(*) 与 group by 列;rollup 不支持,having 不支持;
  • 包含 distinct 时,由于可增量更新的 MJV 和 MAV 中 select 输出列是唯一的,不需要考虑 distinct,可以直接禁止或移除 distinct;order by 不支持;
  • window function 不支持;
  • 不带group by的语句,必须是scalar aggregate;
  • 不支持设置并行度;

全量刷新的物化视图

创建物化视图创建一个交易表orders作为物化视图的基表,交易表中记录了用户的商品交易信息;向交易表中写入3条交易数据,然后创建一个记录某个区域交易额的物化视图MV1,MV1是基于region分组,通过sum函数来聚合销售额,物化视图创建时会先做一次全量刷新,所以成功创建物化视图后,物化视图中会基于已有数据做汇总;

obclient> create table orders (order_id int primary key, user_id int, item_id int, item_count int, item_price int, region varchar(100));
Query OK, 0 rows affected (0.81 sec)

obclient>insert into orders values(1, 10001, 1, 20, 100, 'HZ'), (2, 10002, 1, 10, 150, 'BJ'), (3, 10001, 2, 50, 50, 'SH');
Query OK, 3 rows affected (0.09 sec)
Records: 3  Duplicates: 0  Warnings: 0

obclient>create materialized view mv1 as select region, sum(item_count * item_price) from orders group by region;
Query OK, 0 rows affected (4.24 sec)

obclient>select * from mv1;
+--------+------------------------------+
| region | sum(item_count * item_price) |
+--------+------------------------------+
| HZ     |                         2000 |
| BJ     |                         1500 |
| SH     |                         2500 |
+--------+------------------------------+
3 rows in set (0.11 sec)

手动刷新物化视图

命令

DBMS_MVIEW.REFRESH (‘mv_name’, ‘method’, refresh_parallel);

使用物化视图设置的刷新选项刷新物化视图

DBMS_MVIEW.REFRESH(‘mv1’);

指定刷新选项为‘c’做全量刷新

DBMS_MVIEW.REFRESH(‘mv2’, ‘c’);

通过指定refresh_parallel设置本次刷新的并行度,默认值为1,目前只影响全量刷新,增量刷新不受此参数影响。

DBMS_MVIEW.REFRESH(‘mv3’, refresh_parallel => 8);

持续向交易表中插入新的数据,在手动触发刷新前,物化视图中的数据不会有任何变化;MV1在创建时没有设置任何属性,在这种情况下,创建的物化视图只能通过手动触发全量刷新来更新物化视图中的数据,为了加速物化视图的刷新,可以设置刷新并发度,如:并发度设置为2;刷新成功后,可以看到,物化视图中汇总了新的交易数据;可以通过系统视图(DBA_MVREF_RUN_STATS)查看物化视图刷新任务的具体信息。

obclient>insert into orders values(4, 10002, 2, 10, 100, 'SH'), (5, 10003, 1, 2, 20, 'HZ');
Query OK, 2 rows affected (0.04 sec)
Records: 2  Duplicates: 0  Warnings: 0

obclient>select * from mv1;
+--------+------------------------------+
| region | sum(item_count * item_price) |
+--------+------------------------------+
| HZ     |                         2000 |
| BJ     |                         1500 |
| SH     |                         2500 |
+--------+------------------------------+
3 rows in set (0.04 sec)

obclient>call dbms_mview.refresh('mv1', 'c', refresh_parallel => 2);
Query OK, 0 rows affected (2.82 sec)

obclient>select * from mv1;
+--------+------------------------------+
| region | sum(item_count * item_price) |
+--------+------------------------------+
| HZ     |                         2040 |
| SH     |                         3500 |
| BJ     |                         1500 |
+--------+------------------------------+
3 rows in set (0.11 sec)

obclient> select MVIEWS,METHOD,START_TIME,END_TIME from OceanBase.DBA_MVREF_RUN_STATS;
+--------+--------+---------------------+---------------------+
| MVIEWS | METHOD | START_TIME          | END_TIME            |
+--------+--------+---------------------+---------------------+
| mv1    | c      | 2024-04-01 15:08:18 | 2024-04-01 15:08:22 |
+--------+--------+---------------------+---------------------+
1 row in set (0.10 sec)

删除物化视图

当不需要物化视图后,可以通过drop语句删除物化视图。

obclient>drop materialized view mv1;
Query OK, 0 rows affected (0.68 sec)

增量刷新的物化视图

创建物化视图日志

在创建增量物化视图前,需要先创建物化视图日志,物化视图日志创建时需要将创建物化视图相关的列都包含,物化视图日志创建成功后,可以通过desc查看物化视图日志的表结构。

obclient>create materialized view log on orders with primary key (item_count, item_price, region) including new values;
Query OK, 0 rows affected (1.23 sec)

obclient>desc mlog$_orders;
+------------+--------------+------+-----+---------+-------+
| Field      | Type         | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| SEQUENCE$$ | bigint       | NO   | PRI | NULL    |       |
| order_id   | int(11)      | NO   |     | NULL    |       |
| item_count | int(11)      | YES  |     | NULL    |       |
| item_price | int(11)      | YES  |     | NULL    |       |
| region     | varchar(100) | YES  |     | NULL    |       |
| DMLTYPE$$  | varchar(1)   | YES  |     | NULL    |       |
| OLD_NEW$$  | varchar(1)   | YES  |     | NULL    |       |
+------------+--------------+------+-----+---------+-------+
7 rows in set (0.05 sec)

创建增量刷新物化视图

成功创建物化视图日志后就可以创建增量刷新的物化视图,创建时为物化视图的字段命名,且定义物化视图为增量刷新,自动刷新物化视图的间隔为5分钟;物化视图创建成功后可以通过show create view查看视图的定义。

obclient>create materialized view mv1(region, c, sum_price, count) refresh fast on demand start with sysdate() next sysdate() + interval 5 minute as select region, count(*), sum(item_count * item_price), count(item_count * item_price) from orders group by region;
Query OK, 0 rows affected (3.41 sec)

obclient>show create view mv1;
+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+
| View | Create View                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | character_set_client | collation_connection |
+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+
| mv1  | CREATE MATERIALIZED VIEW `mv1` DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 AS select `test`.`orders`.`region` AS `region`,count(*) AS `c`,sum((`test`.`orders`.`item_count` * `test`.`orders`.`item_price`)) AS `sum_price`,count((`test`.`orders`.`item_count` * `test`.`orders`.`item_price`)) AS `count` from `test`.`orders` group by `test`.`orders`.`region` | utf8mb4              | utf8mb4_general_ci   |
+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------+----------------------+
1 row in set (0.12 sec)

手动增量刷新物化视图

向表中插入新的交易数据,从mlog中可以知道,order_id=6的交易数据是新插入的,在系统自动调度增量刷新前先手动触发一次增量刷新,刷新成功后可以看到mlog中的数据被清空了,物化视图中也汇总了新的数据。

obclient>insert into orders values(6, 10001, 3, 30, 70, 'HZ');
Query OK, 1 row affected (0.05 sec)

obclient>select * from mlog$_orders;
+------------+----------+------------+------------+--------+-----------+-----------+
| SEQUENCE$$ | order_id | item_count | item_price | region | DMLTYPE$$ | OLD_NEW$$ |
+------------+----------+------------+------------+--------+-----------+-----------+
|          1 |        6 |         30 |         70 | HZ     | I         | N         |
+------------+----------+------------+------------+--------+-----------+-----------+
1 row in set (0.04 sec)

obclient>call dbms_mview.refresh('mv1', 'f');
Query OK, 0 rows affected (0.55 sec)

obclient>select * from mlog$_orders;
Empty set (0.04 sec)

obclient>select region, sum_price from mv1;
+--------+-----------+
| region | sum_price |
+--------+-----------+
| HZ     |      4140 |
| BJ     |      1500 |
| SH     |      3500 |
+--------+-----------+
3 rows in set (0.03 sec)

自动增量刷新物化视图

创建物化视图的时候,定义了“start with sysdate() next sysdate() + interval 5 minute”,系统会自动每5分钟做一次物化视图的增量刷新。再写一条交易数据,通过scheduler jobs视图查看MV1后台刷新的任务情况;后台刷新任务执行后MV1中的数据发生了变化。

obclient>insert into orders values(8, 10001, 3, 30, 70, 'SH');
Query OK, 1 row affected (0.05 sec)

obclient>select JOB_NAME,JOB_ACTION,START_DATE,REPEAT_INTERVAL,LAST_START_DATE,NEXT_RUN_DATE from OceanBase.DBA_SCHEDULER_JOBS where JOB_ACTION like "%mv1%";
+----------------------------------+-------------------------------------------------------+----------------------------+-------------------------------+----------------------------+----------------------------+
| JOB_NAME                         | JOB_ACTION                                            | START_DATE                 | REPEAT_INTERVAL               | LAST_START_DATE            | NEXT_RUN_DATE              |
+----------------------------------+-------------------------------------------------------+----------------------------+-------------------------------+----------------------------+----------------------------+
| MVIEW_REFRESH$J_1125899906849015 | DBMS_MVIEW.refresh('test.mv1', refresh_parallel => 1) | 2024-04-01 15:22:38.000000 | sysdate() + interval 5 minute | 2024-04-01 15:22:45.577379 | 2024-04-01 15:27:45.000000 |
+----------------------------------+-------------------------------------------------------+----------------------------+-------------------------------+----------------------------+----------------------------+
1 row in set (0.03 sec)

obclient>select * from mlog$_orders;
Empty set (0.04 sec)

obclient>select region, sum_price from mv1;
+--------+-----------+
| region | sum_price |
+--------+-----------+
| HZ     |      4140 |
| BJ     |      1500 |
| SH     |      5600 |
+--------+-----------+
3 rows in set (0.04 sec)

删除数据

删除物化视图,删除物化视图日志,以及基表,由于mlog和基表有绑定关系,所以要求先删除mlog,才能删除基表。

obclient>drop materialized view mv1;
Query OK, 0 rows affected (0.60 sec)

obclient>drop table orders;
ERROR 1235 (0A000): drop table with materialized view log is not supported
obclient>drop materialized view log on orders;
Query OK, 0 rows affected (1.53 sec)

obclient>drop table orders;
Query OK, 0 rows affected (0.41 sec)

兼容视图

MySQL模式支持Oracle兼容的系统视图
image.png
Oracle模式支持Oracle兼容的系统视图
image.png

后续计划

OceanBase 接下来将在4.3.1版本中增强物化视图的能力,包括:

  • 支持查询改写。
  • 增量刷新支持多表join。
  • 异步刷新实时物化视图。

OceanBase技术站
22 声望122 粉丝

海量记录,笔笔算数