分享一次意外的ORACLE数据库主机性能测试结果

 

      最近一直在做一些ORACLE SQL性能量化方面的研究,要实现较准确的SQL性能量化很复杂,现在一直在小心推进,周末做了一下一些机器的两个参数测试:

      一个是oracle数据库1毫秒可执行多少次内存hash运算操作,这个主要用于量化hash join的时间;

      一个是oracle数据库1毫秒可执行多少次内存排序的比较运算,这个主要用于量化order by的时间。

 

以下是测试结果,让我非常意外。

 

 

 

 

以下是测试代码,测试采用单会话sqlplus执行,因此只能衡量主机CPU单核的性能。

 

下面这个是hash次数的测试代码:

declare
  i             integer;
  int1          integer;
  t1            number;
  t2            number;
  t_all         number;
  t_select      number;
  execute_count number := 10;
  function getnumfromtimestamp(iTime timestamp) return number is
    Result        number;
    v_hour        number;
    v_minute      number;
    v_second      number;
    v_millisecond number;
  begin
    v_hour   := EXTRACT(hour from iTime);
    v_minute := EXTRACT(minute from iTime);
    v_second := EXTRACT(second from iTime);
    Result   := (v_hour + 1) * 3600 + (v_minute + 1) * 60 + v_second;
    return(Result);
  end;
begin
  select /*+use_hash(a,b)*/
   count(*)
    into int1
    from (select rownum + 20000000 rn from dual connect by rownum <= 1000000) a,
         (select rownum + 10000000 rn from dual connect by rownum <= 1000000) b
   where a.rn = b.rn;
  t1 := getnumfromtimestamp(systimestamp);
  for i in 1 .. execute_count loop
    select /*+use_hash(a,b)*/
     count(*)
      into int1
      from (select rownum + 20000000 rn
              from dual
            connect by rownum <= 1000000) a,
           (select rownum + 10000000 rn
              from dual
            connect by rownum <= 1000000) b
     where a.rn = b.rn;
  end loop;
  t2    := getnumfromtimestamp(systimestamp);
  t_all := t2 - t1;
  select count(*)
    into int1
    from (select rownum + 20000000 rn from dual connect by rownum <= 1000000);
  t1 := getnumfromtimestamp(systimestamp);
  for i in 1 .. execute_count loop
    select count(*)
      into int1
      from (select rownum + 20000000 rn
              from dual
            connect by rownum <= 1000000);
  end loop;
  t2       := getnumfromtimestamp(systimestamp);
  t_select := t2 - t1;
  dbms_output.put_line('hash_times:' ||
                       trunc(1000 /
                             ((t_all - t_select) / (2 * execute_count))));
end;
/

 

下面这个是sort次数的测试代码:

 

declare
  i             integer;
  int1          integer;
  t1            number;
  t2            number;
  t_all         number;
  t_select      number;
  execute_count number := 10;
  function getnumfromtimestamp(iTime timestamp) return number is
    Result        number;
    v_hour        number;
    v_minute      number;
    v_second      number;
    v_millisecond number;
  begin
    v_hour   := EXTRACT(hour from iTime);
    v_minute := EXTRACT(minute from iTime);
    v_second := EXTRACT(second from iTime);
    Result   := (v_hour + 1) * 3600 + (v_minute + 1) * 60 + v_second;
    return(Result);
  end;
begin
  select count(*)
    into int1
    from (select rn
            from (select (mod(rownum, 3) * 3 - 1) * 1000000 + rownum rn
                    from dual
                  connect by rownum <= 1000000
                   order by rn) c
           where rownum + 1 < 10000000);
  t1 := getnumfromtimestamp(systimestamp);
  for i in 1 .. execute_count loop
    select count(*)
      into int1
      from (select rn
              from (select (mod(rownum, 3) * 3 - 1) * 1000000 + rownum rn
                      from dual
                    connect by rownum <= 1000000
                     order by rn) c
             where rownum + 1 < 10000000);
  end loop;
  t2    := getnumfromtimestamp(systimestamp);
  t_all := t2 - t1;
  select count(*)
    into int1
    from (select rn
            from (select (mod(rownum, 3) * 3 - 1) * 1000000 + rownum rn
                    from dual
                  connect by rownum <= 1000000) c
           where rownum + 1 < 10000000);
  t1 := getnumfromtimestamp(systimestamp);
  for i in 1 .. execute_count loop
    select count(*)
      into int1
      from (select rn
              from (select (mod(rownum, 3) * 3 - 1) * 1000000 + rownum rn
                      from dual
                    connect by rownum <= 1000000) c
             where rownum + 1 < 10000000);
  end loop;
  t2       := getnumfromtimestamp(systimestamp);
  t_select := t2 - t1;
  dbms_output.put_line('sort_times:' ||
                       trunc(18792428 / 1000 / ((t_all - t_select))));
end;
/

体会如下:

1、测试结果只是反应CPU单核的性能,并不体现主机的整体性能,主机负载对测试有一定的影响,但比较小。

2、现在PC服务器上采用的INTEL或AMD CPU单核非常强劲,已经远远超过几年前的小型机POWER5,Itanium CPU单核性能,由于没有最新的POWER6,POWER7,所以也不好评论POWER6和POWER7的性能。

3、台式机的CPU INTEL E8400(3G)比 PC服务器上的XEON E5520(2.26G)还强很多,这个得益于CPU主频的提高。现在PC服务器CPU的单核性能与台式机CPU差不多,并不像有些文章说的服务器性能强很多,我理解是服务器CPU稳定性更好,可扩展性更强,用普通台式机服务器一般最多可装2颗,但是服务器上可装4颗,甚至更多,这一方面是服务器本身架构的设计支持,另一方面是服务器CPU在多路并行处理方面考虑了更多。

4、AMD的CPU性能也非常强劲,AMD Opteron 2378(45nm) 与AMD Opteron 2356(65nm) 主频差不多,但Cache差很多,性能高一倍,可以看出AMD的CPU性能与制造工艺和缓存关系密切。

5、Oracle9i排序算法的复杂度约为nlog(n),Oracle10g以后排序算法有非常大的改进,算法的复杂度已经远远小于nlog(n)。

6、小型机CPU性能已经没有任何优势,小型机的优势只是

稳定性更好(我们的小型机还没发生过异常宕机,PC服务器有时会发生);

可维护性更好(小型机很多组件都可在线维护与升级,PC服务器可做的很少);

可扩展性更强(小型机的CPU经常看到扩展到64颗以上,PC服务器一般很少能上16颗)。

 

个人观点:

      如果你是银行、证券类核心数据库主机,那小型机还是有意义的,如果只是一般企业信息化主机,那PC服务器性价比更好,如果只是公司内部开发用服务器,那选择当前高性能的台式机INTEL CPU Core i7会更实用。

 

      Oracle10g与Oracle9i相比,除了RAC外,数据库IO管理方面没有大的提升,但是在一些细节方面做得很优秀,如本次测试中的hash运算及排序算法,所以如果你的主机性能瓶颈是CPU运算(非IO等待),那从Oracle9i升级至Oracle10g或11g会有一些帮助。

 

      测试结果只是反应CPU单核的性能,并不体现主机的整体性能。以上测试代码没有参考标准,也没有理论基础,只是个人随便写的,所以不严谨,但是测试结果还是有一些参考意义,拿出来与大家分享,欢迎讨论。