复盘eygle在甲骨文大会上演讲中的示例,看看什么是大师的由点及面

盖总(eygle)在刚结束的甲骨文大会的演讲中,通过一个简单的UPDATE语句,为我们展示了什么叫由点及面的优化,什么叫由点及面的知识覆盖度,不在于这个案具体如何操作,更应关注或更值得我们借鉴的是这种学习态度和方法思路,大师是如何炼成的?我想这个案例可以带给我们一些启迪。

下面就复盘一下这个案例的整个过程,注:版权归盖总(eygle)所有~

问题描述:

问题的标题是:“并行更新成为系统瓶颈”

SQL:

UPDATE /*+ parallel(a, 8) */ tbl_a a
SET name = (SELECT name FROM tbl_b WHERE id = a.id),
        class = (SELECT class FROM tbl_b WHERE id = a.id)
WHERE a.id IN (SELECT /*+ parallel(b, 8) */ id FROM tbl_b b);

现象是这条SQL执行时间非常长,从介绍看是有2.5分钟。

优化过程:

1. 为了以下可以更清楚地说明问题,对这个SQL做了简化处理,我们需要优化的是这条SQL:

UPDATE tbl_a a
SET name = (SELECT name FROM tbl_b WHERE id = a.id),
        class = (SELECT class FROM tbl_b WHERE id = a.id)
WHERE a.id IN (SELECT id FROM tbl_b b);

我们创建两张模拟表:

SQL> create table tbl_a(
          id number,
          name varchar2(5),
          class varchar2(5));
Table created.

SQL> create table tbl_b(
          id number,
          name varchar2(5),
          class varchar2(5));
Table created.

SQL> create sequence seq_a cache 1000;
Sequence created.

SQL> create sequence seq_b cache 1000;
Sequence created.

插入一些随机数据:

begin
  for i in 1 .. 100000 loop
    insert into tbl_a values (seq_a.nextval, dbms_random.string(‘U‘, 5), dbms_random.string(‘U‘, 5));
  end loop;
  commit;
end;
/
PL/SQL procedure successfully completed.

SQL> select count(*) from tbl_a;
  COUNT(*)
------------
     100000

begin
  for i in 1 .. 10000 loop
    insert into tbl_b values (seq_b.nextval, dbms_random.string(‘U‘, 5), dbms_random.string(‘U‘, 5));
  end loop;
  commit;
end;
/
PL/SQL procedure successfully completed.

SQL> select count(*) from tbl_b;
  COUNT(*)
------------
      10000

2. 执行原SQL语句

SQL> set timing on
SQL> UPDATE tbl_a a
          SET name = (SELECT name FROM tbl_b WHERE id = a.id),
                 class = (SELECT class FROM tbl_b WHERE id = a.id)
          WHERE a.id IN (SELECT id FROM tbl_b b);
10000 rows updated.

Elapsed: 00:00:07.42

需要7秒多的时间(虽然和示例中2.5分钟有差距,但仅为了说明优化的问题,时间上的差距可以忽略)。

3. 第一次优化

我们从这个SQL中可以看到,更新TBL_A表的ID列,但TBL_B表的SELECT有三次,即三次的全表扫描,那么要是能减少TBL_B表检索的次数,执行时间肯定可以减少。

SQL> UPDATE tbl_a a
          SET (name, class) = (SELECT name, class FROM tbl_b WHERE id = a.id)
          WHERE a.id IN (SELECT id FROM tbl_b b);
10000 rows updated.

Elapsed: 00:00:04.04

这样的调整是符合SQL语法的,执行时间变为了4秒多,效果显著。

4. 第二次优化

虽然执行时间减少了接近一半,但SQL中还是对TBL_B执行了两次扫描,是否还可以减少一次?

SQL> UPDATE (SELECT b.name b_name, b.class b_class, a.name, a.class
                      FROM tbl_a a, tbl_b b
                      WHERE a.id = b.id)
          SET name = b_name, class = b_class;
SET name = b_name, class = b_class
    *
ERROR at line 4:
ORA-01779: cannot modify a column which maps to a non key-preserved table

Elapsed: 00:00:00.01

这样就做到了只扫描一次TBL_B表,直接对子查询更新,但此时报了一个错误,ORA-01779,

这就引出了non key-preserved table的概念。非键值保存表,杨长老的博客(http://blog.itpub.net/4227/viewspace-195889/)中提到过这个错误:

“造成这个错误的原因是更新的列不是事实表的列,而是维度表的列。换句话说,如果两张表关联,其中一张表的关联列是主键,那么另一张表就是事实表,也就是说另一张表中的列就是可更新的;除非另一张表的关联列也是主键,否则这张表就是不可更新的,如果更新语句涉及到了这张表,就会出现ORA-1799错误。如果是两张表主键关联,那么无论更新那个表的字段都可以。

其实这个限制的真正原因是Oracle要确保连接后更新的内容可以写到一张表中,而这就要求连接方式必须是1对N或者1对1的连接。这样才能确保连接后的结果集数量和事实表一致。从而使得Oracle对连接后子查询的更新可以顺利的更新到事实表中。”

a.id=b.id,我们是用TBL_B的id列作为条件更新,需要确保这列只会对应到TBL_B表的一行记录,可以为表TBL_B的id列设置主键、唯一索引或唯一约束,三种操作,这里选择设置唯一约束:

SQL> alter table tbl_b add constraint uq_b_id unique(id);
Table altered.

再次执行:

SQL> UPDATE (SELECT b.name b_name, b.class b_class, a.name, a.class
                      FROM tbl_a a, tbl_b b
                      WHERE a.id = b.id)
          SET name = b_name, class = b_class;
10000 rows updated.

Elapsed: 00:00:00.12

执行时间一下仅为0.12秒。

上面如果TBL_A的ID列设置为主键,则为1对1的连接,如果仅是TBL_B的ID列为唯一约束,则为1对N的连接。

总结:

通过两次优化,执行时间从7秒降到了0.12秒,虽然这里的示例数据未必和实际情况一致,但成比例的缩放足以说明这个问题,从这个案例可以看出,优化的本质就是少做事,原始SQL执行三次全表扫描,那目标就是减少全表扫描的次数,第一次优化的操作可能相对容易想到,但第二次优化的操作,就需要知道可以有这种语法,而且出现了ORA-01799的错误,还需要知道这种错误的根本原因是什么,才能有可行的解决方法。

问题还没完,以上说明了SQL语句的优化,下面就是针对这条SQL展开的知识。

假设上面的TBL_A和TBL_B表是属于用户bisal的,此时新建一个用户phibisal,并授予最简单的权限:

SQL> create user phibisal identified by phibisal;
User created.

SQL> grant create session to phibisal;
Grant succeeded.

bisal用户创建这两张表的public同义词:

SQL> create public synonym tbl_a for bisal.tbl_a;
Synonym created.

SQL> create public synonym tbl_b for bisal.tbl_b;
Synonym created.

然后授予phibisal用户对TBL_A表的读和更新权限:

SQL> grant select, update on tbl_a to phibisal;
Grant succeeded.

此时phibisal登录后执行:

sqlplus phibisal/phibisal

SQL> UPDATE (SELECT b.name b_name, b.class b_class, a.name, a.class
                      FROM tbl_a a, tbl_b b
                      WHERE a.id = b.id)
          SET name = b_name, class = b_class;
                FROM tbl_a a, tbl_b b
                                    *
ERROR at line 2:
ORA-00942: table or view does not exist

会提示TBL_B不存在,因为用户没有该表的任何权限,(注:此处和eygle的示例中反馈不同,他提示的是ORA-01031: insufficient privileges)
如果授予phibisal对TBL_B表的读权限,

SQL> grant select on tbl_b to phibisal;
Grant succeeded.

此时可以完成更新:

sqlplus phibisal/phibisal

SQL> UPDATE tbl_a a
          SET (name, class) = (SELECT name, class FROM tbl_b WHERE id = a.id)
          WHERE a.id IN (SELECT id FROM tbl_b b);
10000 rows updated.

但用如下SQL会提示权限错误:

UPDATE (SELECT b.name b_name, b.class b_class, a.name, a.class
                      FROM tbl_a a, tbl_b b
                      WHERE a.id = b.id)
          SET name = b_name, class = b_class;
                FROM tbl_a a, tbl_b b
                                    *
ERROR at line 2:
ORA-01031: insufficient privileges

即这种子查询更新会因没有TBL_B表的UPDATE权限报错。
但如果使用如下with语法,则可以正常执行:

SQL> UPDATE
(WITH tmp AS (
              SELECT b.name b_name, b.class b_class, a.name, a.class
              FROM tbl_a a, tbl_b b
              WHERE a.id = b.id)
             )
SET name = b_name, class = b_class;
10000 rows updated.

做得更彻底一些:

SQL> revoke update on tbl_a from phibisal;
Revoke succeeded.

撤消了phibisal用户对TBL_A的更新权限,按理说,phibisal用户不应该能再更新TBL_A表了。
使用上面两个调整后的SQL,确实如此:

sqlplus phibisal/phibisal

SQL> UPDATE (SELECT b.name b_name, b.class b_class, a.name, a.class
                      FROM tbl_a a, tbl_b b
                      WHERE a.id = b.id)
          SET name = b_name, class = b_class;
                FROM tbl_a a, tbl_b b
                                    *
ERROR at line 2:
ORA-01031: insufficient privileges     

SQL> UPDATE tbl_a a
          SET (name, class) = (SELECT name, class FROM tbl_b WHERE id = a.id)
          WHERE a.id IN (SELECT id FROM tbl_b b);
UPDATE tbl_a a
       *
ERROR at line 1:
ORA-01031: insufficient privileges

但是,奇怪的是如下SQL可以执行:

SQL> UPDATE
(WITH tmp AS (
              SELECT b.name b_name, b.class b_class, a.name, a.class
              FROM tbl_a a, tbl_b b
              WHERE a.id = b.id)
              SELECT * FROM tmp
             )
SET name = b_name, class = b_class;
10000 rows updated.

这就从原理规则上,违背了权限控制,看下版本:

SQL> select banner from v$version where rownum=1;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production

这就是2014年7月提出的一个bug,在11.2.0.3、11.2.0.4、12.1等版本中都存在的一个问题,需要修正这个bug,相当于使用with语法,可以绕过用户权限,对没有权限的表进行DML操作。

总结:

精髓不在于这个bug,而是在于从一条简单的UPDATE语句,可以派生出如此丰富的知识,可谓举一反三,受益匪浅。一方面需要我们能够从原理上理解每一个概念,另一方面也要培养自己举一反三,知识点由点及面的想法,做到真正的触类旁通,这样才能逐渐向大师靠拢,向大师学习。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-14 12:39:05

复盘eygle在甲骨文大会上演讲中的示例,看看什么是大师的由点及面的相关文章

让工程科技造福人类、创造未来—在2014年国际工程科技大会上的主旨演讲

在2014国际工程科技大会发表主旨演讲 女士们,先生们,朋友们: 在这个美好的时节,国际工程科技大会在北京隆重召开,这是世界工程科技界和中国工程科技界的一件盛事.我很高兴有机会同来自世界各地的工程科技专家学者见面,也很愿意聆听大家对工程科技发展.人类社会未来的高见. 首先,我谨代表中国政府和中国人民,并以我个人的名义,向大会的召开,表示衷心的祝贺!向出席大会的全体代表,表示诚挚的欢迎!向国际工程与技术科学院理事会会议的召开,表示衷心的祝贺! 工程科技与人类生存息息相关.温故而知新.回顾人类文明历

Google I/O 大会上的 Android Things 亮点汇总

借助 Android Things,您可以大规模构建和维护 IoT 设备.我们最近发布了?Android Things 1.0?正式版,它将为生产设备提供长期支持,帮助您轻松地将 IoT 设备从原型设计推进到商品化. Android Things 是今年 Google I/O 大会的一大主角,我们对它进行了重点介绍,从演讲和代码实验室到交互式演示以及寻宝游戏,旨在通过这些方式激发开发者社区的兴趣和活力.本次大会展示了许多新颖有趣的产品,下面我们来逐一盘点. 演示 我们在 I/O 大会上推出了一些

[转]小品:宋江同志在梁山泊招安动员大会上的讲话

时间:2012-12-27 15:35 来源:未知 作者:zhao123 点击: 564 次 小品:宋江同志在梁山泊招安动员大会上的讲话 作者: 大宋和谐会馆12/08/2012:华岳论坛 - http://www.hua-yue.net/ 同志们: 今天我们在忠义堂隆重集会,只有一个主题:接受招安.我跟卢俊义同志交换了意见,我们一致认为,从现在起,务必把梁山泊的工作重点由继续造反坚决转移到接受招安上来.这是关系到梁山党生死存亡.前途命运的无比重大的政治决策.接受招安与继续造反,是两个阶级.两条

Google Play在GDC大会上发布全新工具及游戏

原文标题: Google Play在GDC大会上发布全新工具及游戏 2016年,在一些新兴市场,比如印度.巴西以及印度尼西亚,大概有3亿新用户使用Android设备.这帮助Google Play上的移动游戏获得了加好的现金流. 去年,超过1亿新用户可以能够使用各种形式的支付手段来购买Google Play上的app应用和游戏.谷歌在今年的GDC上举办了其一年一度的开发者大会,揭露了更多关于关于Google Play的消息. Android Nougat版本拥有Vulkan应用程序界面,能够支持实

新华三成立大会上,赵伟国、于英涛都说了什么?

2016年5月1日,"新华三"公司在中国香港正式注册成立.5月4日,紫光股份与Hewlett Packard Enterprise(以下简称HPE)作为新公司的股东相继发出交易完成公告.5月6日,新华三集团(以下简称新华三)在北京钓鱼台宣布正式成立,成为一家由紫光股份控股51%,HPE占股49%的中美合资国有控股企业. 新华三由杭州华三通信有限公司(以下简称华三通信)和紫光华山科技有限公司(HPE在中国的服务器.存储和技术服务业务)组成.新华三不仅拥有华三通信的业务,还包括了HPE服务

云栖大会上宣布即将开源的手淘Atlas什么来头?

在刚刚过去的云栖大会上,手淘宣布其移动容器化框架Atlas将于2017年年初开源,对这个框架,在过去团队对外部做过一些分享,外界也一直对其十分关注,到现在它终于即将开源了. 本文将介绍Atlas的设计思路和手淘对容器化.组件化和动态化上的思考,主要内容来自阿里巴巴资深技术专家倪生华(玄黎)在云栖大会上的分享. Atlas是什么 2013年,手淘航母战略的制定,带来了业务和开发人员的翻倍膨胀.从不到100人猛增四五倍,同时业务数量大增,整个客户端的架构和发版节奏受到极大挑战,Atlas作为之前手淘

在纪念中国人民抗日战争暨世界反法西斯战争胜利70周年大会上的讲话

9月3日,纪念中国人民抗日战争暨世界反法西斯战争胜利70周年大会在北京天安门广场隆重举行.这是中共中央总书记.国家主席.中央军委主席习 近 平在大会上发表重要讲话.新华社记者兰红光摄 新华社北京9月3日电 在纪念中国人民抗日战争暨世界反法西斯战争胜利70周年大会上的讲话 (2015年9月3日) 习 近 平 全国同胞们, 尊敬的各位国家元首.政府首脑和联合国等国际组织代表, 尊敬的各位来宾, 全体受阅将士们, 女士们.先生们,同志们.朋友们: 今天,是一个值得世界人民永远纪念的日子.70年前的今天

补全开发大会上缺失的Apple和Google应用商店数据和比拼结果

Apple和Google通常会在它们夏天的开发大会上以头条的方式公布它们的应用商店方面的数据,以证明它们的商店是发展的如何的好.这些数据往往是在特定活动下采集的且是一个取整的大概数目,而数据之间往往也没有多大的可比性,但是这些数据往往能让我们感知到整个趋势是怎么一回事. 在上一年夏天,在它们各自的开发者大会上,Apple和Google都给出了他们在各自的应用商店中支付给开发人员的金额:在该年各自开发者大会之前的12个月内, Google Play共支付了50亿美元,而Apple Store共支付

阿里云在LC3大会上透露未来要做的两件事

摘要: 阿里云研究员褚霸在LC3大会上同多位业界资深大咖同台交流表示,阿里云发展到今天,把过去应对淘宝.天猫大规模计算以及双11的计算能力转换成普惠的能力放在云上,这是一个非常大的挑战,也是其他厂商没有经历过的.这些挑战不断帮助阿里云积累经验,提升技术能力. 6月25日,由LFAsia, LLC主办的全球开源顶级盛会LinuxCon + ContainerCon + CloudOpen(LC3)在北京国家会议中心召开.来自国内外的开发人员.架构师.系统管理员.DevOps专家.商业领袖等数千名专