在Oracle中编写SQL语句时,我们都强调使用共享SQL的重要性,换种说法是:在适当的情况下使用绑定变量,我们大多数人可能都已经看过关于这个原则的标准演示。经典的演示是比较两个类似的脚本,其中一个使用生成大量包含字面量的 SQL 语句,另一个使用绑定变量重新重写相同的脚本以获得显著的改进。这是我编写的一个简单例子:
SQL> create table t ( x int primary key) organization index; Table created. SQL> insert into t 2 select rownum from dual 3 connect by level <= 100000; 100000 rows created. SQL> exec dbms_stats.gather_table_stats('','T'); PL/SQL procedure successfully completed. SQL> set timing onSQL> declare 2 v int; 3 begin 4 for i in 1 .. 100000 loop 5 select x into v from t where x = i; 6 end loop; 7 end; 8 / PL/SQL procedure successfully completed. Elapsed: 00:00:01.56 SQL> set timing onSQL> declare 2 v int; 3 begin 4 for i in 1 .. 100000 loop 5 execute immediate 'select x from t where x = '||i into v; 6 end loop; 7 end; 8 / PL/SQL procedure successfully completed. Elapsed: 00:01:04.92
所以通常接下来是手指着花费的时间“看看绑定变量有多棒,看看使用字面量的SQL有多糟糕!”
但最近,有人乐于反驳,即“那又怎样?”他们的观点是:在“糟糕”的示例中,我们每秒执行大约 1600 个语句。这和绑定变量一样好吗?当然不是...但是谁在乎呢?有很多系统每秒 16 个语句就可以了,更不用说 1600 个了。那么,有必要对绑定变量的性能提升大惊小怪吗?虽然这样的观点在其他方面站不住脚——比如多用户场景下的latch的争用,还有SQL注入的风险,但我不会重新强调这些,让我们看看“性能已经够用了”的观点能不能站住脚。
我们不要忘记第一次“绑定变量与字面量”脚本出来的时代。对我个人而言,在90年代初,我的Oracle数据库(版本7)运行在Sparcstation 10上,它具有非常强大且非常昂贵的50 Mhz处理器!😀 (大家可到维基百科上重温一下回忆:https://en.wikipedia.org/wiki/SPARCstation_10 )
因此,在那些日子里,绑定变量和字面量之间的差异是巨大的,因为CPU处理器的周期非常有价值。但我们往往会忘记,正如处理器的发展一样,我们对访问数据的需求也在发展。是的,现代处理器确实可以轻松地处理那些基于字面量的简单主键查找SQL。但现在的SQL并不总是这么简单,如今,SQL就像
处理器一样不断发展——它们可能要复杂得多。那么解析成本会怎样呢?因此,让我们引入一些复杂性,并使用一些更复杂的SQL重新运行我们的测试。
显然,复杂的SQL可能具有很长的执行时间,因此我们将通过使用DBMS_SQL仅解析,而不执行SQL。我们在SQL中将包括几个字典视图,这些视图本身就包含多个字典视图,因此即使SQL只是一个简单的联接,也存在很大的复杂性。
SQL> set timing onSQL> declare 2 c int; 3 begin 4 c := dbms_sql.open_cursor; 5 for i in 1 .. 100000 loop 6 dbms_sql.parse(c, 7 q'{ 8 select o.object_name, o.last_ddl_time, sum(s.bytes) 9 from all_objects o, 10 dba_segments s 11 where o.owner = s.owner 12 and o.object_name = s.segment_name 13 and o.object_type = 'TABLE' 14 and o.object_id = :1 15 group by o.object_name, o.last_ddl_time 16 }', 17 dbms_sql.native ); 18 end loop; 19 dbms_sql.close_cursor(c); 20 end; 21 / PL/SQL procedure successfully completed. Elapsed: 00:00:01.14 SQL> declare 2 c int; 3 begin 4 c := dbms_sql.open_cursor; 5 for i in 1 .. 100000 loop 6 dbms_sql.parse(c, 7 q'{ 8 select o.object_name, o.last_ddl_time, sum(s.bytes) 9 from all_objects o, 10 dba_segments s 11 where o.owner = s.owner 12 and o.object_name = s.segment_name 13 and o.object_type = 'TABLE' 14 and o.object_id = }'||i|| 15 q'{ 16 group by o.object_name, o.last_ddl_time 17 }', 18 dbms_sql.native ); 19 end loop; 20 dbms_sql.close_cursor(c); 21 end; 22 / PL/SQL procedure successfully completed. Elapsed: 10:07:12.71
是的...你没看错!超过 10 个小时来完成所有这些SQL的解析。
所以不能因为CPU变得更加强大和聪明,就忽视了不带绑定变量的SQL对系统的伤害!
京东购物,,,
欢迎购买号主的专著