深圳手机网站设计公司建设网站需要买什么
深圳手机网站设计公司,建设网站需要买什么,网站开发技术方案与实施,哪个网站做系统好1. 物化视图到底是什么#xff1f;从“数据快照”说起
如果你用过Oracle数据库#xff0c;肯定对普通的视图#xff08;View#xff09;不陌生。它就像一个虚拟的查询窗口#xff0c;每次查询时都实时执行背后的SQL语句。但物化视图#xff08;Materialized View#xf…1. 物化视图到底是什么从“数据快照”说起如果你用过Oracle数据库肯定对普通的视图View不陌生。它就像一个虚拟的查询窗口每次查询时都实时执行背后的SQL语句。但物化视图Materialized View简称MV和它完全是两码事。你可以把物化视图理解成一个**“数据快照”或者更形象点一个“定期更新的数据副本”**。想象一下你是一家电商公司的数据分析师需要每天看销售报表。报表的数据来自一个巨大的订单表每次查询都要关联用户表、商品表做各种聚合计算慢得要命。这时候物化视图就派上用场了。你可以创建一个物化视图提前把“昨天各品类的销售总额”这个复杂查询的结果计算好并实实在在地存成一张物理表。以后你查报表就直接从这张结果表里读速度飞快。这就是物化视图的核心价值用空间换时间用预计算换查询性能。但问题来了源头的订单表每分钟都有新订单进来你那个“昨天”的销售总额快照过一会儿就过时了。怎么办这就需要“刷新”。就像手机App要定期下拉刷新获取新内容一样物化视图也需要定期从源表同步最新数据。这个刷新就是我们今天要深挖的核心。它有两种主要方式全量刷新Complete Refresh和增量刷新Fast Refresh。全量刷新好理解就是不管三七二十一把物化视图清空然后用最新的源表数据全部重新算一遍。数据量小还行如果源表有几亿条数据每次全量刷新就是一场灾难耗时耗力。所以在生产环境中我们追求的是增量刷新。它只同步自上次刷新后源表里发生变化的那一小部分数据增、删、改。这效率就高太多了。而增量刷新的灵魂就在于一个叫做“物化视图日志”Materialized View Log的东西。你可以把它想象成源表的“变更记录本”。每次源表有数据变动INSERT, UPDATE, DELETE数据库都会自动在这个“记录本”里记上一笔。当物化视图需要刷新时它不用去扫描整个庞大的源表只需要去翻看这个小小的“记录本”按照上面的记录把变更应用到自己的数据上就行了。接下来我们就从这个神奇的“记录本”开始一步步拆解整个增量刷新的运作机制。2. 物化视图日志增量刷新的“记账本”要开启增量刷新第一步必须在源表上创建物化视图日志。这个日志本质上就是一张特殊的表名字通常是MLOG$_源表名。它的作用就是忠实地记录源表的所有DML操作。2.1 创建日志主键还是ROWID创建日志时第一个关键选择是基于什么来识别一条记录Oracle提供了两种方式基于主键PRIMARY KEY和基于ROWID。默认是基于主键这也是最推荐的方式。-- 最常用的方式基于主键创建物化视图日志 CREATE MATERIALIZED VIEW LOG ON test_mv_master;这条命令执行后Oracle会自动在后台创建一张名为MLOG$_TEST_MV_MASTER的表。为什么主键方式是首选因为主键是业务上唯一标识一条记录的字段稳定且有意义。当源表发生UPDATE或DELETE时日志里记录的是这条记录的主键值物化视图端可以精准地定位到需要更新的那条数据。那什么情况下用ROWID呢ROWID是Oracle内部给每行数据分配的一个物理地址标识它更快但有个致命缺点不稳定。如果对源表执行了表移动、分区交换等操作行的ROWID可能会改变。一旦ROWID变了基于它创建的物化视图刷新就会出错因为找不到原来的那条记录了。所以除非你的源表没有主键并且数据绝对静止几乎不可能否则都老老实实用主键方式。2.2 日志里记了些什么解密MLOG$表结构创建好日志后我们来看看这个“记账本”里到底有哪些字段。查询一下MLOG$_TEST_MV_MASTER你会看到类似这样的结构字段名含义举例说明SNAPTIME$$刷新时间戳初始值是4000-01-01表示这条变更还未被任何物化视图处理。DMLTYPE$$操作类型I代表InsertD代表DeleteU代表Update。OLD_NEW$$新旧值标识N代表新值用于Insert和Update的新数据O代表旧值用于Delete和Update的旧数据。CHANGE_VECTOR$$变更向量一个RAW字段用位图标识具体更新了哪些列。USERID(示例)主键列这里记录发生变更的那行数据的主键值。我举个例子帮你理解。假设源表test_mv_master的主键是USERID。插入一条数据(USERIDA001)日志里会新增一条记录DMLTYPE$$I,OLD_NEW$$N并记录主键A001。删除这条数据日志里会新增两条记录。一条是DMLTYPE$$D,OLD_NEW$$O记录主键A001表示旧值被删除。另一条是DMLTYPE$$D,OLD_NEW$$N但所有字段为空这是一个删除标记。更新这条数据的SCORE1字段日志里会新增两条记录。一条是DMLTYPE$$U,OLD_NEW$$O记录主键A001和更新前的SCORE1值。另一条是DMLTYPE$$U,OLD_NEW$$N记录主键A001和更新后的SCORE1值。CHANGE_VECTOR$$字段会指明只有SCORE1列被更新了。这个设计非常精妙它完整记录了数据变更的前后镜像使得物化视图不仅能知道“哪条数据变了”还能知道“它从什么变成了什么”从而进行精确的增量更新。2.3 一个日志如何服务多个物化视图这是物化视图日志最强大的特性之一。一个源表上的一个物化视图日志可以被基于该源表的任意多个物化视图共享使用。这避免了为每个物化视图都维护一份变更日志带来的巨大开销。它是如何工作的关键就在SNAPTIME$$这个字段。初始状态下所有变更记录的SNAPTIME$$都被设置为一个遥远的未来时间比如4000年意味着“未被消费”。当第一个物化视图MV_A执行快速刷新时它会读取所有SNAPTIME$$为“未来”的记录处理这些变更并将这些记录的SNAPTIME$$更新为自己的本次刷新时间。注意是更新不是删除。此时日志里这些记录的SNAPTIME$$就变成了一个真实的时间戳表示“已被MV_A在X时刻处理过”。对于另一个物化视图MV_B来说它只关心那些SNAPTIME$$晚于自己上次刷新时间的记录。因为MV_A刚刷新的那些记录其SNAPTIME$$虽然变了但可能仍然晚于MV_B的上次刷新时间如果MV_B很久没刷新了所以MV_B在刷新时依然需要处理它们。只有当所有依赖这个日志的物化视图都刷新到了比某条日志记录时间更晚的时刻这条记录才算彻底“无用”才会被清理掉。这种机制保证了多个物化视图可以以不同的节奏刷新且互不干扰同时又能高效共享同一份变更数据。管理上你只需要维护一个日志大大减轻了负担。3. 实战一步步玩转快速刷新光说不练假把式我们直接上手用一个完整的例子把创建、刷新、监控的流程走一遍。假设我们有一个用户积分源表我们要为它创建两个不同用途的物化视图。3.1 环境搭建与数据准备首先创建我们的源表它必须有主键。-- 创建源表用户积分主表 CREATE TABLE user_score_master ( user_id VARCHAR2(32) NOT NULL, user_name VARCHAR2(50), total_score NUMBER(10), last_update DATE DEFAULT SYSDATE ); -- 添加主键 ALTER TABLE user_score_master ADD CONSTRAINT pk_user_score PRIMARY KEY (user_id); -- 插入一些初始数据 INSERT INTO user_score_master VALUES (U001, 张三, 1500, SYSDATE); INSERT INTO user_score_master VALUES (U002, 李四, 2300, SYSDATE); INSERT INTO user_score_master VALUES (U003, 王五, 800, SYSDATE); COMMIT;接着在源表上创建物化视图日志。这是开启快速刷新的“钥匙”。-- 创建基于主键的物化视图日志 CREATE MATERIALIZED VIEW LOG ON user_score_master;现在我们可以创建物化视图了。注意创建时我们使用BUILD DEFERRED意思是先不立即拉取数据等我们手动刷新后再有数据。刷新方式指定为REFRESH FAST表示我们希望使用快速刷新。-- 创建物化视图1全量副本 CREATE MATERIALIZED VIEW mv_user_score_full BUILD DEFERRED REFRESH FAST AS SELECT user_id, user_name, total_score, last_update FROM user_score_master; -- 创建物化视图2只关心高分用户 CREATE MATERIALIZED VIEW mv_user_score_high BUILD DEFERRED REFRESH FAST AS SELECT user_id, user_name, total_score, last_update FROM user_score_master WHERE total_score 2000;3.2 首次刷新必须是“全量刷新”创建完成后如果你直接去查这两个物化视图会发现是空的。因为它们处于BUILD DEFERRED状态。这时候进行第一次数据填充必须使用全量刷新COMPLETE。Oracle会报错明确告诉你这一点。-- 错误的尝试直接快速刷新会失败 EXEC DBMS_MVIEW.REFRESH(MV_USER_SCORE_FULL, F); -- 可能会报错ORA-23401: 物化视图日志不存在或失效 -- 正确的做法首次使用全量刷新 EXEC DBMS_MVIEW.REFRESH(MV_USER_SCORE_FULL, C); EXEC DBMS_MVIEW.REFRESH(MV_USER_SCORE_HIGH, C);执行完这两条命令两个物化视图里就有了和创建时刻源表一致的数据。你可以用SELECT * FROM mv_user_score_full;验证一下。3.3 见证增量刷新的魔力现在好戏开场了。我们对源表做一些数据变更。-- 1. 插入一条新记录低分用户 INSERT INTO user_score_master VALUES (U004, 赵六, 900, SYSDATE); -- 2. 更新一条记录让李四分数超过2000 UPDATE user_score_master SET total_score 2500 WHERE user_id U002; -- 3. 删除一条记录 DELETE FROM user_score_master WHERE user_id U003; COMMIT;这些操作会被自动记录到MLOG$_USER_SCORE_MASTER日志表中。现在我们分别刷新两个物化视图。-- 刷新第一个物化视图全量副本 EXEC DBMS_MVIEW.REFRESH(MV_USER_SCORE_FULL); -- 默认就是快速刷新(FAST)因为我们已经做过全量初始化了 -- 刷新后查询日志 SELECT * FROM MLOG$_USER_SCORE_MASTER;你会发现日志里那几条变更记录的SNAPTIME$$从“4000年”变成了当前的刷新时间。但记录并没有消失这是因为第二个物化视图mv_user_score_high还没刷新日志需要为它保留这些变更。现在刷新第二个物化视图-- 刷新第二个物化视图高分用户视图 EXEC DBMS_MVIEW.REFRESH(MV_USER_SCORE_HIGH); -- 再次查询日志 SELECT * FROM MLOG$_USER_SCORE_MASTER;此时日志很可能被清空了。因为两个物化视图都处理完了这批变更。对于mv_user_score_high来说它只关心total_score 2000的记录。所以插入的‘赵六’900分和删除的‘王五’800分对它没有影响。但‘李四’的分数从2300更新到2500这个变更它需要处理。增量刷新机制会智能地只应用相关的变更。3.4 刷新方法COMPLETE, FAST, FORCE 怎么选在DBMS_MVIEW.REFRESH过程中我们指定了C或F。这里系统讲一下三种刷新方式COMPLETE (C)彻底重建。 truncate物化视图全量重新执行定义查询。简单粗暴数据一致性最高但资源消耗大。首次刷新必须用它。FAST (F)增量刷新。利用物化视图日志只同步变更。速度极快资源消耗小。是生产环境追求的目标。FORCE (F)强制刷新。Oracle先尝试快速刷新如果条件不满足比如日志丢失、结构不兼容则自动降级为完全刷新。这是一个比较保险的选择。在实际的JOB调度中我通常这样设置METHOD F。让Oracle自己去尝试快速刷新如果失败就转全量并记录错误这样能保证数据同步任务不会因为偶发的快速刷新失败而中断。4. 高级话题性能调优与避坑指南用了物化视图和快速刷新是不是就高枕无忧了当然不是这里面有很多细节和“坑”处理不好性能反而会下降。4.1 物化视图日志的维护与清理物化视图日志表会不断增长。如果某个物化视图很久不刷新或者被遗忘了那么它对应的变更记录就会一直堆积在日志里导致日志表异常庞大影响刷新性能甚至撑爆表空间。如何监控和清理定期检查日志大小SELECT segment_name, bytes/1024/1024 AS size_mb FROM user_segments WHERE segment_name LIKE MLOG$%;查看未被及时消费的日志如果日志里有大量SNAPTIME$$还是远古未来时间的记录说明可能没有物化视图成功进行过快速刷新需要检查物化视图状态和刷新JOB。使用PURGE选项在创建物化视图日志时可以指定日志的自动清理策略。CREATE MATERIALIZED VIEW LOG ON user_score_master WITH PRIMARY KEY PURGE IMMEDIATE SYNCHRONOUS;PURGE IMMEDIATE表示一旦变更被所有物化视图消费就立即删除记录。PURGE START WITH ... NEXT ...可以用于基于时间的定期清理。但注意如果设置立即清理一旦物化视图刷新失败对应的变更记录就丢了可能导致数据不一致。生产环境要谨慎。4.2 哪些情况会导致快速刷新失效快速刷新不是万能的很多操作会破坏它对源表执行了TRUNCATE TABLE这会清空物化视图日志导致后续的快速刷新无法进行必须从头全量刷新。直接修改了物化视图日志表MLOG$绝对不要手动去删改这张表物化视图定义查询包含了不支持快速刷新的元素某些聚合函数如ROLLUP,CUBE在复杂情况下。某些类型的连接如外连接在某些版本有限制。分析函数如ROW_NUMBER(),LAG()。UNION ALL有时可以但UNION通常不行。查询中包含了SYSDATE,USER等非确定性函数。源表结构变更比如增加了一个新列而物化视图的定义没有包含它。如果这个新列被包含在物化视图日志中且发生了更新快速刷新可能会失败。通常需要重新全量初始化物化视图。4.3 多物化视图共享日志的优化策略当一个日志被多个物化视图共享时刷新顺序和时机就有讲究了。刷新频率高的视图放最后如果有一个物化视图需要每分钟刷新另一个每天刷新一次。那么应该先刷新每天的那个再刷新每分钟的那个。这样每分钟刷新时处理的日志量自上次刷新后一分钟的变更会非常小速度极快。如果顺序反过来每分钟的视图会先把日志“标记”为已处理导致每天刷新的视图每次都要处理海量的、已经被标记的日志记录效率低下。使用DBMS_MVIEW.REFRESH的列表参数批量刷新可以一次性刷新多个物化视图Oracle可能会优化内部处理过程。EXEC DBMS_MVIEW.REFRESH(LIST MV_USER_SCORE_FULL, MV_USER_SCORE_HIGH, METHOD F);考虑分区对于超大的源表可以考虑对源表和物化视图都使用分区表并结合“分区变更跟踪PCT”刷新等更高级的特性实现分区粒度的增量刷新性能提升巨大。4.4 监控与诊断常用数据字典视图出问题时别慌查这些视图USER_MVIEWS查看所有物化视图的基本信息包括刷新模式、上次刷新时间、是否可快速刷新等。STALENESS字段告诉你数据是否过期。USER_MVIEW_LOGS查看物化视图日志的信息。USER_MVIEW_REFRESH_TIMES查看每个物化视图的上次刷新时间对于理解多MV共享日志的消费进度很有帮助。MLOG$_表名直接查看日志内容是终极的调试手段。有一次我遇到一个物化视图快速刷新一直报错通过查询USER_MVIEWS发现它的LAST_REFRESH_TYPE是COMPLETE但STALENESS是NEEDS_COMPILE。仔细一查原来是源表加了个字段物化视图的定义需要重新编译一下。执行ALTER MATERIALIZED VIEW mv_name COMPILE;后就解决了。这些实战中的小问题往往就藏在这些数据字典视图里。物化视图的增量刷新本质上是一种空间换时间、计算换延迟的经典权衡。把它用好了对于构建企业级的数据仓库、报表系统、数据同步链路是一个威力巨大的武器。关键在于理解其背后的日志机制合理地设计、谨慎地维护并准备好应对各种边界情况。从我这些年的经验来看没有一劳永逸的配置只有对原理的深刻理解加上持续的监控优化才能让这个特性稳定高效地为你服务。