「YashanDB个人版体验」YashanDB的大脑优化器

前言


众所周知,数据库的优化器是核心技术中关键的的一环,由于yashandb使用了数据块、区、段、表空间的逻辑存储方式,按理很多调优方面类似Oracle。所以下面的测试用例在yashandb做一轮,同样也会在Oracle上也做一轮测试。从客户端操作使用感受可用性,从服务端提供的工程规则感受丝滑性。

测试思路

  • 用例一,单表插入批量数据,不加索引MAX/MIN查询以及 加索引后进行MAX/MIN查询

  • 用例二, 单表建立分区及索引 ,并插入批量数据, 进行分区及索引的查找

  • 用例三,索引查询单表,观察回表与不回表的差别

  • 用例四,插入空值后索引是否快,对比全表扫描的差别。

  • 用例五,插入8个表数据,查看表索引大小,以及索引表的高度,查看表的统计数据,并对比两个大表体验。

  • 用例六,两表关联,通过不同顺序和指定,查看优化器分配的表连接用的算法。

准备语句

sql 复制代码
create table single_t as select * from dba_objects;  

insert into single_t  select * from single_t;    
insert into single_t  select * from single_t;    

select max(object_id)  from single_t;  
select min(object_id)  from single_t;  

create  index singlet_objectid on single_t(object_id);  

create table  part_t(id  int,col2  int,  col3 int)  
  partition by range(id)  
  (  
    partition  p1 values  less than(10000),  
    partition  p2 values  less than(20000),  
    partition  p3 values  less than(30000),  
    partition  p4 values  less than(40000),  
    partition  p5 values  less than(50000),  
    partition  p6 values  less than(60000),  
    partition  p7 values  less than(70000),  
    partition  p8 values  less than(80000),  
    partition  p9 values  less than(90000),  
    partition  p10 values  less than(100000),  
    partition  p11 values  less than(110000),  
    partition  p12 values  less than(120000),  
    partition  p13 values  less than(130000),  
    partition  p14 values  less than(140000),  
    partition  p15 values  less than(150000),  
    partition  p16 values  less than(MAXVALUE)  
  );  


  insert into  part_t select rownum,rownum+1,rownum+2  from dual connect  by rownum<=2100000;  

  create  index  idx_par_t_col2  on  part_t(col2) local;  
  create  index  idx_par_t_col3  on  part_t(col3) ;  
    
 ALTER SESSION SET statistics_level=ALL;

set autotrace  traceonly;  
select * from part_t where col2=8;  
select * from part_tab where col2=8 and  id=7;  

insert into part_t values(1,null,null);  


select * from  part_t where col3=500000;  

select col3 from  part_t where col3=500000;  

create table  part_t2  as select *  from part_t;

用例一

select max(object_id) from single_t;未加索引前,整个表走了全表索引,约花费了96个db blocks get

加了索引后,表往索引的方向进行,只用了2个db block

用例二

select * from part_t where col2=8 虽然有普通 索引 ,但是分区上走了所有的分区的查找,从分区1到分区15都走遍了,34个物理读 ,34个db block。

select * from part_t where col2=8 and id=7 因为有分区索引 ,所以路径上走了分区的查找,只花费了3个db blocks gets。

用例三

select * from part_t where col3=50000很明显因为*走了回表。3个物理读 ,4个逻辑读 。

select col3 from part_t where col3=50000我们把*改成索引对应值,回表就消失了,只有2个db block

用例四

目前part_t表没有空值,count等聚集函数还会走索引,26035个物理读,4213845个逻辑读 。

insert into part_t values(1,null,null); 插入空值后,预期会这个空值,没有走索引,走了全盘索引

select max(col2),min(col3) from part_t;发现还是走了索引,物理读和逻辑读 差不多。

select /*+ no_index(part_t) */ max(col2),min(col3) from part_t; 通过HINT没有走索引,终于走了全盘扫描,物理读和逻辑读 比原来好很多了。

用例五

csharp 复制代码
drop table  t1 purge;
drop table  t2 purge;
drop table  t3 purge;
drop table  t4 purge;
drop table  t5 purge;
drop table  t6 purge;
drop table  t7 purge;
drop table  t8 purge;


create table t1  as select rownum as  id,rownum+1 as id2 from dual  connect by level<=1;
create table t2  as select rownum as  id,rownum+1 as id2 from dual  connect by level<=10;
create table t3  as select rownum as  id,rownum+1 as id2 from dual  connect by level<=100;
create table t4  as select rownum as  id,rownum+1 as id2 from dual  connect by level<=1000;
create table t5  as select rownum as  id,rownum+1 as id2 from dual  connect by level<=10000;
create table t6  as select rownum as  id,rownum+1 as id2 from dual  connect by level<=100000;
create table t7  as select rownum as  id,rownum+1 as id2 from dual  connect by level<=1000000;
create table t8  as select rownum as  id,rownum+1 as id2 from dual  connect by level<=10000000;


create index idx_id_t1 on t1(id);
create index idx_id_t2 on t2(id);
create index idx_id_t3 on t3(id);
create index idx_id_t4 on t4(id);
create index idx_id_t5 on t5(id);
create index idx_id_t6 on t6(id);
create index idx_id_t7 on t7(id);
create index idx_id_t8 on t8(id);


BEGIN
    DBMS_STATS.GATHER_INDEX_STATS('sys', 'idx_id_t1', '', 0.2, 2,'ALL');
    DBMS_STATS.GATHER_INDEX_STATS('sys', 'idx_id_t2', '', 0.2, 2,'ALL');
    DBMS_STATS.GATHER_INDEX_STATS('sys', 'idx_id_t3', '', 0.2, 2,'ALL');
    DBMS_STATS.GATHER_INDEX_STATS('sys', 'idx_id_t4', '', 0.2, 2,'ALL');
    DBMS_STATS.GATHER_INDEX_STATS('sys', 'idx_id_t5', '', 0.2, 2,'ALL');
    DBMS_STATS.GATHER_INDEX_STATS('sys', 'idx_id_t6', '', 0.2, 2,'ALL');
    DBMS_STATS.GATHER_INDEX_STATS('sys', 'idx_id_t7', '', 0.2, 2,'ALL');
    DBMS_STATS.GATHER_INDEX_STATS('sys', 'idx_id_t8', '', 0.2, 2,'ALL');

END;
/


select index_name,blevel,leaf_blocks,num_rows,distinct_keys,clustering_factor  
from user_ind_statistics  where table_name  
in('T1','T2','T3','T4','T5','T6','T7','T8');


select   segment_name, segment_type,tablespace_name,bytes,blocks,extents from   user_segments  where  segment_name  in( 'IDX_ID_T1', 'IDX_ID_T2', 'IDX_ID_T3', 'IDX_ID_T4', 'IDX_ID_T5', 'IDX_ID_T6', 'IDX_ID_T7','IDX_ID_T7');

USER_SEGMENTS 用于查询每个表拥有的段,列出idx_id_t1到idx_id_t8一共8个索引的占用段大小。最小索引占用的段64KB。

user_tab_statistics表包括很重要的统计信息,主要看BLEVEL和LEAF_BLOCKS,BLEVEL把它当成一个树结构的层次组来,第一层就是根页,根页是占用一个block,Yashandb默认的DB_BLOCK_SIZE 是8092,估计是4K。T4要插入1000行数据就要进入第2层了。更多的数据就要进入第三层,T8的数据也是第三层。从调优来看,无论多大的数据都要保障在三层的范围,这样的话索引的效果才会好。

select id from t5 where id=9999; T5一万行的数据,索引查找花了2个db block

select id from t8 where id=9999; t8总共有1000万的数据也只是花2个物理读与3个逻辑读 ,与一万的数据差1000陪,但是索引提升的效果是惊人的。

用例六

sql 复制代码
基于part_t表派生part_t2表,part_t2表插入1倍多的数据,part_t表是小表,part_t2表是大表。   查看顺序和不同优先级、不同HINT指定下的扫描算子、块消耗、行消耗、行扫描。

create table  part_t2  as select *  from part_t;

insert into  part_t2 select rownum,rownum+1,rownum+2  from dual connect  by rownum<=2100000;  

 ALTER SESSION SET statistics_level=ALL;
 set autotrace  traceonly;  


 select /*+ leading(part_t)  use_nl(part_t2) */  part_t.id  from   part_t,part_t2  where part_t.col2=part_t2.col2   and  part_t.col3=20000;


select /*+ leading(part_t2)  use_nl(part_t2) */  part_t.id  from   part_t,part_t2  where part_t.col2=part_t2.col2   and  part_t.col3=20000;



drop  table  part_t2  purge;
drop  table  part_t  purge;

select /*+ leading(part_t2)  use_nl(part_t1) */  part_t.id  from   part_t,part_t2  where part_t.col2=part_t2.col2   and  part_t.col3=20000;

select /*+ leading(part_t)  use_nl(part_t1) */  part_t.id  from   part_t,part_t2  where part_t.col2=part_t2.col2   and  part_t.col3=20000;


select  part_t.id  from   part_t,part_t2  where part_t.col2=part_t2.col2   and  part_t.col3=20000;

select  part_t.id  from   part_t2,part_t  where part_t.col2=part_t2.col2   and  part_t.col3=20000;

测试实验中出现YashDB的一个锁,记录YashDB的锁破解如下

SQL> drop  table  part_t  purge;

YAS-02024 lock wait timeout, wait time 0 milliseconds

SQL> select * from  v$LOCKED_OBJECT;

    XEXT    XNODE          XSN             OBJECT_ID SESSION_ID LMODE
-------- -------- ------------ --------------------- ---------- ---------
      53     9603            1                  2257         22 TS

SQL> SELECT sid,serial# FROM V$SESSION WHERE sid IN (22);

     SID      SERIAL#
-------- ------------
      22         4963

1 row fetched.


SQL> ALTER SYSTEM KILL SESSION '22,4963';

Succeed.

主要采用了嵌套循环来做两表的测试,不用leading和不同nl的呈现不同的走向,性能调优重点看Statistics以及A-Row、A-Time

通过8图对比,leading是否选择小表对性能很关系,默认不加HINT也能跑出一个不错的性能,把大表和小表优先权倒置,优化器也能识别区分。

最后4图都是一样的Statistics,但是不一样的A-Row和操作连接,图2是最差的路径选择,消耗了最多的物理读和逻辑读,图8是最优路径的结果,原因是A-ROW是最小的。

原始SQL什么也不定指定,接近图8的性能。

总结

不能说YashanDB吊打Oracle,也不能说YashanDB离Oracle还有十万八千里,Oracle能实现的功能,YashanDB都具备。

  1. YashanDB的优化器与Oracle的大同小异,性能判断来于 从硬盘里读【physical read】,从缓冲区里读 【db block get | CONSTSTENT GET】。
  2. 如果你是ORACLE DBA,SQL语句整体优化按照Oracle风格去调整优化,没有很大的出入。
  3. YashanDB还加了一些物理参数例如Memory 以及Disk,目前没有这方面的输出,仍在TODo进程中,相信后期会有很大的改善。
  4. 为了启用Statistics,YashanDB需要输入 ALTER SESSION SET statistics_level=ALL;
  5. user_ind_statistics表的统计更新,需要人为执行某包下的存储过程DBMS_STATS.GATHER_INDEX_STATS去触发,这点对个别工程师可能体验不好,因为Oracle12c不需要执行这一步就已经更新统计数据了,YashanDB这方面应该与Oracle 12c对齐,统计数据自适应,搜集对象的统计数据应该是后台某个钩子函数干的事,加上有对比,建议跟上。
  6. 简单的两表的测试,YashanDB对比Oracle12c,YashanDB优化器路径与Oracle12C大致相同。

结论,每个数据库的优化器都是不一样的,更复杂的语句和更复杂的环境,呈现多条路径选择,测试中我们看到YashanDB是基于CBO的优化,以后TODO的路线还会把CPU消耗和内存放进来,增加优化器的选择,我们拭目以待。

相关推荐
不剪发的Tony老师40 分钟前
Mathesar:一款基于PostgreSQL的在线电子表格
数据库·postgresql·电子表格
万邦科技Lafite4 小时前
京东按图搜索京东商品(拍立淘) API (.jd.item_search_img)快速抓取数据
开发语言·前端·数据库·python·电商开放平台·京东开放平台
金仓拾光集4 小时前
__金仓数据库平替MongoDB实战:从多模兼容到高可用落地__
数据库·mongodb·数据库平替用金仓·金仓数据库
北邮-吴怀玉4 小时前
6.1.2.2 大数据方法论与实践指南-离线任务SQL 任务开发规范
大数据·数据库·sql
流烟默4 小时前
MongoDB索引创建语法分析
数据库·mongodb
金仓拾光集4 小时前
__国产化转型实战:制造业供应链物流系统从MongoDB至金仓数据库迁移全指南__
数据库·mongodb·数据库平替用金仓·金仓数据库
初学者_xuan4 小时前
零基础新手小白快速了解掌握服务集群与自动化运维(十五)Redis模块-Redis数据库基础
运维·数据库·自动化
小马哥编程4 小时前
【软考架构】案例分析:MongoDB 如何存储非结构化数据以及其矢量化存储的优点。
数据库·mongodb·架构
默 语5 小时前
MySQL中的数据去重,该用DISTINCT还是GROUP BY?
java·数据库·mysql·distinct·group by·1024程序员节·数据去重
哲Zheᗜe༘6 小时前
了解学习Redis主从复制
数据库·redis·学习