1 统计信息概述
统计信息是指优化器统计信息(Optimizer Statistics),描述数据库中表和列信息的数据集合。
优化器代价模型(Optimizer Cost Model)依赖于查询中涉及到的表、列、谓词等对象的统计信息来选取计划,优化器可以利用统计信息来优化计划的选择,所以统计信息是代价模型中选取最优执行计划的关键。
在 OceanBase 数据库优化器中,统计信息以普通数据的形式存储在内部表中,并且会在本地维护统计信息的缓存,以提高优化器对统计信息的访问速度。
统计信息包含表统计信息(Table Level Statistics)和列统计信息(Column Level Statistics)两种类型。
表的统计信息主要包含以下信息:
1.表的基本信息(包括 tenant_id、table_id、partition_id 等)
2.表的统计信息类型(信息级别分为 GLOBAL、PARTITION 和 SUBPARTITION)
3.表的行数
4.表所占用的宏块数
5.表所占用的微块数
6.表的平均行长
7.表的收集统计信息时间
8.表的统计信息是否锁定
列的统计信息主要包含以下信息:
1.列的基本信息(包括 tenant_id、table_id、partition_id、column_id)
2.列的统计信息类型(信息级别分为 GLOBAL、PARTITION 和 SUBPARTITION)
3.列中不同的值的数量 NDV(Number of Distinct Values)
4.列中 NULL 值的数量
5.列的最大值和最小值
6.列的采样数据量大小
7.列的直方图的稠密度
8.列的直方图桶个数
9.直方图类型(频率直方图/ TopK 直方图/混合直方图)
OceanBase 数据库 MySQL 模式下用于查询相关统计信息的视图如下:
1.OCEANBASE.DBA_TAB_STATISTICS:用于查询表级的统计信息。
2.OCEANBASE.DBA_TAB_COL_STATISTICS:用于查询 GLOBAL 级别的列级统计信息。
3.OCEANBASE.DBA_PART_COL_STATISTICS:用于查询 PARTITON 级别的列级统计信息。
4.OCEANBASE.DBA_SUBPART_COL_STATISTICS:用于查询 SUBPARTITON 级别的列级统计信息。
5.OCEANBASE.DBA_TAB_HISTOGRAMS:用于查询 GLOBAL 级别的列级直方图统计信息。
6.OCEANBASE.DBA_PART_HISTOGRAMS:用于查询 PARTITON 级别的列级直方图统计信息。
7.OCEANBASE.DBA_SUBPART_HISTOGRAMS:用于查询 SUBPARTITON 级别的列级直方图统计信息。
8.OCEANBASE.DBA_IND_STATISTICS:用于查询索引统计信息。
查看表的统计信息:
obclient [oceanbase]> select owner,TABLE_NAME ,partition_name,NUM_ROWS ,AVG_ROW_LEN ,LAST_ANALYZED from OCEANBASE.DBA_TAB_STATISTICS where owner='cndba';
+-----------+--------------------------------+----------------+----------+-------------+----------------------------+
| owner | TABLE_NAME | partition_name | NUM_ROWS | AVG_ROW_LEN | LAST_ANALYZED |
+-----------+--------------------------------+----------------+----------+-------------+----------------------------+
| cndba | chat_req_records_transit | NULL | 0 | 0 | 2023-07-10 17:07:38.295836 |
| cndba | chat_record_tree | NULL | 29 | 156 | 2023-07-10 17:07:38.389382 |
| cndba | chat_score | NULL | 119013 | 100 | 2023-07-10 17:07:38.569771 |
| cndba | freeze_user_record | NULL | 457272 | 80 | 2023-07-10 17:07:38.983850 |
| cndba | chat_violation | NULL | 1698 | 100 | 2023-07-10 17:07:39.032010 |
| cndba | test_data_industry_copy1 | NULL | 0 | 0 | 2023-07-10 17:07:39.067542 |
| cndba | chat_complaint | NULL | 0 | 0 | 2023-07-10 17:07:39.104315 |
| cndba | chat_resp_records_transit | NULL | 0 | 0 | 2023-07-10 17:07:39.137004 |
| cndba | ow_survey | NULL | 10648 | 100 | 2023-07-10 17:07:39.212023 |
| cndba | chat_special_uid | NULL | 392 | 133 | 2023-07-10 17:07:39.244743 |
| cndba | ow_register | NULL | 1 | 298 | 2023-07-10 17:07:39.307249 |
| cndba | sparkdesk_industry | NULL | 11 | 77 | 2023-07-10 17:07:39.344826 |
| cndba | chat_bot_market | NULL | 2078 | 714 | 2023-07-10 17:07:39.427636 |
| cndba | bot_type_list | NULL | 18 | 151 | 2023-07-10 17:07:39.513939 |
| cndba | call_out_record | NULL | 449476 | 351 | 2023-07-10 17:07:40.788247 |
| cndba | chat_properties | NULL | 2 | 129 | 2023-07-10 17:07:40.850132 |
| cndba | screen | NULL | 0 | 0 | 2023-07-10 17:07:40.899464 |
| cndba | test_data_industry | NULL | 11 | 77 | 2023-07-10 17:07:40.932092 |
| cndba | chat_token_records | NULL | 37031506 | 183 | 2023-07-10 17:07:42.644534 |
| cndba | manage_user | NULL | 0 | 0 | 2023-07-10 17:07:42.690298 |
| cndba | chat_bot_list | NULL | 420923 | 498 | 2023-07-10 17:07:43.185654 |
| cndba | chat_vip_users | NULL | 33118 | 212 | 2023-07-10 17:07:43.352218 |
| cndba | chat_appointment_log | NULL | 4126 | 110 | 2023-07-10 17:07:43.408195 |
| cndba | prompt_type | NULL | 6 | 61 | 2023-07-10 17:07:43.443032 |
| cndba | test_data_pv_uv | NULL | 7 | 76 | 2023-07-10 17:07:43.470428 |
| cndba | chat_bot_remove | NULL | 151 | 615 | 2023-07-10 17:07:43.511825 |
| cndba | chat_resp_records | NULL | 17942553 | 957 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230410 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230417 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230424 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230501 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230508 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230515 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230522 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230529 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230605 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230612 | 3980882 | 991 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230619 | 4479632 | 940 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230626 | 3134480 | 943 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230703 | 3193543 | 953 | 2023-07-10 17:08:37.146617 |
……
查看索引的统计信息:
obclient [oceanbase]> select owner,index_name,TABLE_NAME ,partition_name,NUM_ROWS ,LAST_ANALYZED from OCEANBASE.DBA_IND_STATISTICS where owner='cndba';
+-----------+---------------------------+---------------------------+----------------+----------+----------------------------+
| owner | index_name | TABLE_NAME | partition_name | NUM_ROWS | LAST_ANALYZED |
+-----------+---------------------------+---------------------------+----------------+----------+----------------------------+
| cndba | uid_unique_index | chat_score | NULL | 119013 | 2023-07-10 17:07:38.589751 |
| cndba | uid_unique_index | chat_violation | NULL | 1698 | 2023-07-10 17:07:39.044642 |
| cndba | uid_unique_index | chat_user | NULL | 828760 | 2023-07-10 17:08:45.162723 |
| cndba | idx_req_id | chat_req_model | NULL | 18982 | 2023-07-10 17:08:45.278139 |
| cndba | uni_uid_url-key | share_chat | NULL | 0 | 2023-07-10 17:08:45.353340 |
| cndba | idx_act | chat_bot_list | NULL | 420923 | 2023-07-10 17:07:43.205647 |
| cndba | idx_create_time | chat_bot_base | NULL | 5054 | 2023-07-10 17:08:41.096770 |
| cndba | idx_create_time1 | chat_bot_base | NULL | 5054 | 2023-07-10 17:08:41.103599 |
| cndba | idx_create_time2 | chat_bot_list | NULL | 420923 | 2023-07-10 17:07:43.212634 |
| cndba | idx_support_context | chat_bot_base | NULL | 5054 | 2023-07-10 17:08:41.110763 |
| cndba | idx_real_bot_id | chat_bot_list | NULL | 420923 | 2023-07-10 17:07:43.219261 |
| cndba | idx_uid | chat_bot_base | NULL | 5054 | 2023-07-10 17:08:41.117820 |
| cndba | idx_uid | chat_bot_list | NULL | 420923 | 2023-07-10 17:07:43.225805 |
| cndba | idx_create_time | call_out_record | NULL | 449476 | 2023-07-10 17:07:40.802805 |
2 统计信息收集
2.1 直方图说明
直方图是一种特殊类型的列统计信息,通过将数据保存到一系列有序的桶中来描述其列的数据分布特征,优化器可以依据直方图来估算出更准确的行数。
在默认情况下,优化器会认为列的数据是分布均匀的,之后会根据这一特征来进行行数估计,但是在真实的场景中,大多数表的数据分布都是不均匀的,这种情况就需要使用直方图。
在 OceanBase 数据库优化器中,列的直方图信息存储在视图 ALL_TAB_HISTOGRAMS、DBA_TAB_HISTOGRAMS 和 USER_TAB_HISTOGRAMS 中,包含以下信息:
1.直方图的基本信息(包括 tenant_id、table_id、partition_id、column_id)
2.直方图的统计信息类型(信息级别分为 GLOBAL、PARTITION 和 SUBPARTITION)
3.直方图中每个桶累积的数据量(包含当前桶及其之前的桶的总和)
4.直方图中每个桶里面的最大的 Value 值
5.直方图中每个桶里面的最大 Value 值的频次
OceanBase 社区版暂不支持 ALL_TAB_HISTOGRAMS 和 USER_TAB_HISTOGRAMS 视图。
2.1.1 直方图类型
OceanBase 数据库优化器支持三种直方图:频率直方图、Topk 直方图和混合直方图。
1.频率直方图:每个不同的列值对应于直方图的单个桶,要求指定的桶的个数不低于列的 NDV 值(列中不同值)。
2.Topk 直方图:是频率直方图的变体,基于 Lossy Counting 算法,通过取部分数据特征来估算整体的数据分布,要求它所记录的数据数与总数据数的比例不低于 1-(1/bucket_size)。
3.混合直方图:主要通过采集指定的的数据量进行直方图构建,是对频率直方图和 Topk 直方图的功能补充。
2.1.1.1 频率直方图
在频率直方图中,每个不同的列值对应于直方图的单个桶。因为每个值都有自己的专用桶,所以一些桶可能有很多值,而另一些则很少。
频率直方图的类比是对硬币进行分类,例如一个钱袋里面一共有 4 种不同面值(0.1 元、0.2 元、0.5 元、1 元)的 20 枚硬币,我们按照分类将所有 0.1 元的硬币放在第一个桶,所有 0.2 元的硬币放在第二个桶,所有 0.5 元的硬币放在第三个桶,所有 1 元的硬币放在第四个桶,综合频率直方图的特性,要求指定的桶的个数不低于列的 NDV 值。
2.1.1.2 Topk 直方图
Topk 直方图是频率直方图的变体,当我们指定的桶个数不足以装下所有 NDV 时,就会考虑选择使用 Topk 直方图,Topk 直方图本质是忽略频次低的数据,主要考虑频次高的数据分布。
例如,一个钱袋里面一共有 4 种不同面值(0.1 元、0.2 元、0.5 元、1 元)的 100 枚硬币,其中 0.1 元的硬币仅仅只有 1 枚,同时我们只有 3 个桶来装硬币,因此就可以忽略 0.1 元的硬币,只考虑剩下三种硬币的分布。
由于 Topk 直方图是通过取部分数据特征来估算整体的数据分布,因此为了保证误差不会太大,要求 Topk 直方图记录的数据数与总数据数的比例不低于 1–(1/bucket_size);比如上述场景中,指定桶个数为 3,一共有 100 枚硬币,Topk 直方图记录了 99 枚,那么显然 99/100 > 2/3,满足要求。目前 OceanBase 数据库优化器主要通过 Lossy Counting 算法来实现 Topk 直方图。
2.1.1.3 混合直方图
针对于一种数据量很大的大表场景,指定的直方图桶个数低于 NDV 值,同时 Topk 直方图也无法满足最低的数据占比,这个时候就需要一种更加均衡的直方图来描述数据分布的特征,由此引入了混合直方图。
混合直方图主要通过采集指定的的数据量进行直方图构建,与频率直方图和 Topk 直方图的不同的是,一个桶里面可能装多个不同的 Value 值,将采集到的数据量按照桶个数分段,将每一分段内的所有数据都放到对应的一个桶中,达到用更少的桶来描述更大数据量的数据分布,其中桶内的最大的 Value 值将作为 endpoint_value,同时会多一个 endpoint_repeat_cnt 来记录 endpoint_value 的频次。
例如,同样的有 100 枚硬币:0.1 元有 10 枚、0.2 元有 10 枚、0.5 元有 15 枚、1 元有 15 枚、2 元有 25 枚、5 元有 10 枚、10 元有 15 枚,由此计算得出 Topk 直方图覆盖的数据占比是 (25+15+15+15)/100=0.7,而 Topk 覆盖数据占比的最小阈值是 1-1/N =3/4=0.75,未达到该阈值,不满足 Topk 直方图的条件。
2.1.2 直方图的选择策略
OceanBase 数据库优化器根据 NDV、bucket_size 和 p 指标选择直方图。直方图的选择策略如下图所示。
指标含义如下:
1.NDV:指定一个列上不同 Value 值的个数。
2.bucket_size:指定的直方图桶个数,默认为 254。
3.p:Topk 直方图期望的最低百分比阈值,计算公式为 (1—(1/bucket_size)) * 100。如果使用默认值 254,则对应的最低百分比阈值为 99.6。
2.2 手动收集统计信息
OceanBase 数据库优化器主要通过 DBMS_STATS 包 和 ANALYZE 语句两种方式手动收集统计信息。
2.2.1 通过 DBMS_STATS 包
2.2.1.1 参数说明
涉及如下 3 个包:
GATHER_INDEX_STATS 收集索引统计信息。
GATHER_TABLE_STATS 收集表和列的统计信息。
GATHER_SCHEMA_STATS 收集 Schema 中所有对象的统计信息。
关于包具体字段的说明直接参考官网:
https://www.oceanbase.com/docs/common-oceanbase-database-cn-10000000001699698
在使用时重点关注如下几个参数:
1.ownname:Oracle 模式:用户名。MySQL 模式:表所在数据库名称。
2.tabname 表名。
3.indname 索引名。
4.partname 分区名。默认为 NULL。
5.estimate_percent指定使用多少比例的数据计算其分布特征,范围为 [0.000001,100]。 如果指定为 NULL,则使用所有数据。默认是 AUTO_SAMPLE_SIZE,即由优化器内部决定使用多少比例的数据。
6.degree 统计信息收集时的并行度,默认为 NULL。
7.cascade 是否同时收集表的索引统计信息,默认为 TRUE。
8.method_opt 设置列级别的统计信息收集方式(控制直方图)。
直方图的收集通过method_opt 选项来控制,其语法如下:
FOR ALL [INDEXED | HIDDEN] COLUMNS [size_clause]
| FOR COLUMNS [size clause] column [size_clause] [,column [size_clause]...]
size_clause:
SIZE integer
| SIZE REPEAT
| SIZE AUTO
| SIZE SKEWONLY
column:
column_name
| (column_name [, column_name])
SIZE integer:指定收集列的直方图桶的个数,取值范围为 [1, 2048]。
REPEAT:仅仅只收集被收集过直方图的列的直方图。使用之前收集直方图设置的桶个数。
AUTO:OceanBase 数据库优化器根据列的使用情况,来决定是否收集列的直方图。直方图桶个数使用默认值 254。
SKEWONLY:仅仅只收集数据分布不均匀的列的直方图。直方图桶个数使用默认值 254。
因为收集直方图比较耗时,所以一般都不收集,但如果执行计划不准确,那么则需要收集。
比如method_opt=>’for all columns size 1’ 表示不收集直方图。
2.2.1.2 使用示例
非分区表的统计信息收集
#不收集直方图的场景
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't1', method_opt=>'for all columns size 1');
#收集直方图的场景并使用默认策略
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't1');
收集用户 user 的表 tbl1 的全局级别的统计信息。
CALL dbms_stats.gather_table_stats('user', 'tbl1', granularity=>'GLOBAL', method_opt=>'FOR ALL COLUMNS SIZE AUTO');
收集用户 user 的表 t_part1 的分区级别的统计信息,并行度为 64,只收集数据分布不均匀的列的直方图。
CALL dbms_stats.gather_table_stats('user', 't_part1', degree=>'64', granularity=>'PARTITION', method_opt=>'FOR ALL COLUMNS SIZE SKEWONLY');
收集用户 user 的表 t_subpart1 所有的统计信息,并行度为 128,只收集 50% 的数据,所有的列的直方图都由优化器内部决定。
CALL dbms_stats.gather_table_stats('user', 't_subpart1', degree=>'128', estimate_percent=> '50', granularity=>'ALL', method_opt=>'FOR ALL COLUMNS SIZE AUTO');
分区表的统计信息收集
# 不收集直方图的场景
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't_part', granularity=>'APPROX_GLOBAL AND PARTITION', method_opt=>'for all columns size 1');
# 收集直方图的场景并使用默认策略
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 't_part', granularity=>'APPROX_GLOBAL AND PARTITION');
Schema(数据库)级别的统计信息收集
没有大表(亿级):
# 不收集直方图的场景
CALL DBMS_STATS.GATHER_SCHEMA_STATS('test', degree=>'16', method_opt=>'for all columns size 1');
# 收集直方图的场景并使用默认策略
CALL DBMS_STATS.GATHER_SCHEMA_STATS('test', degree=>'16');
如果用户下存在超大表(行数超过 1 亿)时,可以选择针对超大表开大并行单独收集,然后锁定超大表的统计信息再使用上述命令收集整个用户的,收集完成后在解锁超大表的统计信息,后续按照增量模式收集,示例如下:
CALL DBMS_STATS.GATHER_TABLE_STATS('test', 'big_table', degree=>128, method_opt=>'for all columns size 1');
CALL DBMS_STATS.LOCK_TABLE_STATS('test','big_table');
CALL DBMS_STATS.GATHER_SCHEMA_STATS('TEST', degree=>'16', method_opt=>'for all columns size 1');
CALL DBMS_STATS.UNLOCK_TABLE_STATS('test','big_table');
2.2.2 通过 ANALYZE 语句
OB Oracle 模式下的 analyze 语法更丰富一下,以为开源只支持 mysql。
我们这里只看MySQL 模式下的 ANALYZE 语法:
ANALYZE TABLE table_name UPDATE HISTOGRAM ON column_name_list WITH INTNUM BUCKETS
收集表 tbl1 的统计信息,列的桶个数为 30 个。
ANALYZE TABLE tbl1 UPDATE HISTOGRAM ON a, b, c, d WITH 30 BUCKETS;
2.3 自动收集统计信息
2.3.1 自动收集说明
OceanBase 数据库中表的统计信息过期判断标准为,如果当前表增量的 DML 次数(上一次收集统计信息时 DML 次数到本次收集统计信息期间发生的增/删/改总次数)超过设置的阈值时就会过期。阈值的默认值是 10%,阈值可以通过 peres 调整设置。
系统表和非分区用户表的自动收集策略:
1.如果表没有 GLOBAL 级别的统计信息,自动收集统计信息。
2.如果表有 GLOBAL 级别的统计信息,但是统计信息已经过期,则自动收集统计信息。否则,OceanBase 数据库优化器不会自动收集统计信息。
分区用户表的自动收集策略:
1.如果表没有任何统计信息,自动收集所有的统计信息。
2.如果表有分区级别的统计信息,但是没有 GLOBAL 级别统计信息,则采用增量的方式自动收集统计信息。
3.如果表有 GLOBAL 级别统计信息,但是已经过期,则自动收集所有的统计信息。
4.如果表有 GLOBAL 级别统计信息,但是没有全部过期,只是部分分区的统计信息过期,则自动收集统计信息过期的分区统计信息,同时采用增量的方式推导 GLOBAL 级别统计信息。
2.3.2 查看自动收集任务
OceanBase 优化器通过基于 DBMS_SCHEDULER 系统包实现的 MAINTENANCE WINDOW 来实现每日的自动统计信息收集,从而保证统计信息的不断迭代更新。
OceanBase 数据库定义 MAINTENANCE WINDOW 为周一到周日有 7 个自动统计信息收集任务,周一到周五的任务开始时间为 22:00,最大收集时长为 4 小时,周六和周日的开始时间为 6:00,最大收集时长为 20 小时。
obclient [oceanbase]> select JOB_NAME ,START_DATE ,REPEAT_INTERVAL ,MAX_RUN_DURATION from OCEANBASE.DBA_SCHEDULER_JOBS where JOB_NAME like '%WINDOW';
+------------------+----------------------------+-------------------------+------------------+
| JOB_NAME | START_DATE | REPEAT_INTERVAL | MAX_RUN_DURATION |
+------------------+----------------------------+-------------------------+------------------+
| FRIDAY_WINDOW | 2023-06-16 22:00:00.000000 | FREQ=WEEKLY; INTERVAL=1 | 14400 |
| MONDAY_WINDOW | 2023-06-19 22:00:00.000000 | FREQ=WEEKLY; INTERVAL=1 | 14400 |
| SATURDAY_WINDOW | 2023-06-17 06:00:00.000000 | FREQ=WEEKLY; INTERVAL=1 | 72000 |
| SUNDAY_WINDOW | 2023-06-18 06:00:00.000000 | FREQ=WEEKLY; INTERVAL=1 | 72000 |
| THURSDAY_WINDOW | 2023-06-15 22:00:00.000000 | FREQ=WEEKLY; INTERVAL=1 | 14400 |
| TUESDAY_WINDOW | 2023-06-20 22:00:00.000000 | FREQ=WEEKLY; INTERVAL=1 | 14400 |
| WEDNESDAY_WINDOW | 2023-06-14 22:00:00.000000 | FREQ=WEEKLY; INTERVAL=1 | 14400 |
+------------------+----------------------------+-------------------------+------------------+
7 rows in set (0.007 sec)
可以通过如下函数管理和配置 JOB:
DBMS_SCHEDULER.DISABLE($window_name);
DBMS_SCHEDULER.ENABLE($window_name);
DBMS_SCHEDULER.SET_ATTRIBUTE();
2.3.3 DML STATS
DML STATS 用于记录一张表做增、删、改的次数。
表的 DML STATS 对于自动统计信息收集有着至关重要的作用,它决定了一张表是否需要再次收集统计信息。数据库中的表的数据信息并不是每天都会更新,因此对于不存在数据变化或者数据变化在预期范围内的表,不需要再次收集统计信息,这样可以提升自动统计信息收集任务的执行效率。
DML STATS 信息的相关视图和表信息:
1.OCEANBASE.GV$DML_STATS、OCEANBASE.V$DML_STATS:查询虚表中记录的表的DML STATS
2.OCEANBASE.DBA_TAB_MODIFICATIONS:查询表中所有的 DML STATS
2.4 在线统计信息收集
在线统计信息收集指在执行 INSERT 插入时,数据库优化器就可以收集统计信息,不用手动调用系统包。在批量导入数据的过程中,可以实时收集统计信息,不需要额外的发起收集任务,减少了运维操作,提高统计信息收集性能。
OceanBase 使用 GATHER_OPTIMIZER_STATISTICS/NO_GATHER_OPTIMIZER_STATISTICS Hint 和系统变量 _optimizer_gather_stats_on_load(默认开启)进行在线统计信息收集,同时也可以使用旁路导入功能的 APPEND Hint 实现在线统计信息收集。
obclient [oceanbase]> show variables like '%_optimizer_gather_stats_on_load%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| _optimizer_gather_stats_on_load | ON |
+---------------------------------+-------+
1 row in set (0.002 sec)
2.4.1 CREATE TABLE…AS SELECT(即 CTAS)
在系统变量 _optimizer_gather_stats_on_load 为 True 时,CTAS 默认启动在线收集统计信息功能。
如果需要禁用该功能,可以使用 NO_GATHER_OPTIMIZER_STATISTICS Hint,示例如下:
CREATE TABLE table_name AS SELECT /+NO_GATHER_OPTIMIZER_STATISTICS/* FROM…
2.4.2 INSERT INTO
在系统变量 _optimizer_gather_stats_on_load 为 True 时,使用 GATHER_OPTIMIZER_STATISTICS Hint 或者 APPEND Hint 启动在线收集统计信息功能。
示例如下:
INSERT /+GATHER_OPTIMIZER_STATISTICS/ INTO table_name SELECT * FROM table_name
2.4.3 PDML
通过 ENABLE_PARALLEL_DML Hint 或者通过 ALTER SESSION ENABLE PARALLEL DML; 设置系统并行模式的方式让插入操作以并行的方式进行。
示例如下:
CREATE TABLE t1(c1 INT) PARTITION BY HASH(c1) PARTITIONS 4;
CREATE TABLE t2(c1 INT) PARTITION BY HASH(c1) PARTITIONS 4;
INSERT INTO t1 VALUES (1),(2),(3),(4);
obclient> EXPLAIN INSERT /*+ENABLE_PARALLEL_DML PARALLEL(4)*/ INTO t2 SELECT * FROM t1;
查看统计信息:
obclient [oceanbase]> select owner,TABLE_NAME ,partition_name,NUM_ROWS ,AVG_ROW_LEN ,LAST_ANALYZED from OCEANBASE.DBA_TAB_STATISTICS where owner='cndba';
+-----------+--------------------------------+----------------+----------+-------------+----------------------------+
| owner | TABLE_NAME | partition_name | NUM_ROWS | AVG_ROW_LEN | LAST_ANALYZED |
+-----------+--------------------------------+----------------+----------+-------------+----------------------------+
| cndba | chat_req_records_transit | NULL | 0 | 0 | 2023-07-10 17:07:38.295836 |
| cndba | chat_record_tree | NULL | 29 | 156 | 2023-07-10 17:07:38.389382 |
| cndba | chat_score | NULL | 119013 | 100 | 2023-07-10 17:07:38.569771 |
| cndba | freeze_user_record | NULL | 457272 | 80 | 2023-07-10 17:07:38.983850 |
| cndba | chat_violation | NULL | 1698 | 100 | 2023-07-10 17:07:39.032010 |
| cndba | test_data_industry_copy1 | NULL | 0 | 0 | 2023-07-10 17:07:39.067542 |
| cndba | chat_complaint | NULL | 0 | 0 | 2023-07-10 17:07:39.104315 |
| cndba | chat_resp_records_transit | NULL | 0 | 0 | 2023-07-10 17:07:39.137004 |
| cndba | ow_survey | NULL | 10648 | 100 | 2023-07-10 17:07:39.212023 |
| cndba | chat_special_uid | NULL | 392 | 133 | 2023-07-10 17:07:39.244743 |
| cndba | ow_register | NULL | 1 | 298 | 2023-07-10 17:07:39.307249 |
| cndba | sparkdesk_industry | NULL | 11 | 77 | 2023-07-10 17:07:39.344826 |
| cndba | chat_bot_market | NULL | 2078 | 714 | 2023-07-10 17:07:39.427636 |
| cndba | bot_type_list | NULL | 18 | 151 | 2023-07-10 17:07:39.513939 |
| cndba | call_out_record | NULL | 449476 | 351 | 2023-07-10 17:07:40.788247 |
| cndba | chat_properties | NULL | 2 | 129 | 2023-07-10 17:07:40.850132 |
| cndba | screen | NULL | 0 | 0 | 2023-07-10 17:07:40.899464 |
| cndba | test_data_industry | NULL | 11 | 77 | 2023-07-10 17:07:40.932092 |
| cndba | chat_token_records | NULL | 37031506 | 183 | 2023-07-10 17:07:42.644534 |
| cndba | manage_user | NULL | 0 | 0 | 2023-07-10 17:07:42.690298 |
| cndba | chat_bot_list | NULL | 420923 | 498 | 2023-07-10 17:07:43.185654 |
| cndba | chat_vip_users | NULL | 33118 | 212 | 2023-07-10 17:07:43.352218 |
| cndba | chat_appointment_log | NULL | 4126 | 110 | 2023-07-10 17:07:43.408195 |
| cndba | prompt_type | NULL | 6 | 61 | 2023-07-10 17:07:43.443032 |
| cndba | test_data_pv_uv | NULL | 7 | 76 | 2023-07-10 17:07:43.470428 |
| cndba | chat_bot_remove | NULL | 151 | 615 | 2023-07-10 17:07:43.511825 |
| cndba | chat_resp_records | NULL | 17942553 | 957 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230410 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230417 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230424 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230501 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230508 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230515 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230522 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230529 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230605 | 0 | 0 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230612 | 3980882 | 991 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230619 | 4479632 | 940 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230626 | 3134480 | 943 | 2023-07-10 17:08:37.146617 |
| cndba | chat_resp_records | p20230703 | 3193543 | 953 | 2023-07-10 17:08:37.146617 |
……
3 统计信息管理
统计信息的管理功能是 OceanBase 的优化器兼容 Oracle 的 DBMS_STATS 包相关统计信息管理函数而实现的相关功能,主要包括统计信息删除、统计信息设置、统计信息导入导出、统计信息锁定、统计信息历史版本管理及统计信息收集策略 Prefs 配置等功能模块。
我们这里只看 2 个常用的功能删除和锁定。
3.1 删除统计信息
OceanBase 数据库调用 DBMS_STATS 包相关的存储过程 PROCEDURE 去删除保存在内部表中收集的统计信息。
目前支持如下三种删除统计信息的方式:
1.使用存储过程 delete_table_stats 删除表级的统计信息。
2.使用存储过程 delete_column_stats 删除列级的统计信息。
3.使用存储过程 delete_schema_stats 删除 Schema 下所有表的统计信息。
4.使用存储过程 delete_index_stats 删除索引统计信息.
注:在 OB 的 mysql 模式里,owner 和 scheme 就是数据库名称。
操作示例:删除表的所有统计信息。
obclient [oceanbase]> select owner,TABLE_NAME ,partition_name,NUM_ROWS ,AVG_ROW_LEN ,LAST_ANALYZED from OCEANBASE.DBA_TAB_STATISTICS where table_name='visit_log';
+-----------+------------+----------------+----------+-------------+----------------------------+
| owner | TABLE_NAME | partition_name | NUM_ROWS | AVG_ROW_LEN | LAST_ANALYZED |
+-----------+------------+----------------+----------+-------------+----------------------------+
| cndba | visit_log | NULL | 7 | 144 | 2023-07-10 17:09:18.793960 |
+-----------+------------+----------------+----------+-------------+----------------------------+
1 row in set (0.044 sec)
obclient [oceanbase]> CALL dbms_stats.delete_table_stats('cndba', 'visit_log');
Query OK, 0 rows affected (0.371 sec)
obclient [oceanbase]> select owner,TABLE_NAME ,partition_name,NUM_ROWS ,AVG_ROW_LEN ,LAST_ANALYZED from OCEANBASE.DBA_TAB_STATISTICS where table_name='visit_log';
+-----------+------------+----------------+----------+-------------+---------------+
| owner | TABLE_NAME | partition_name | NUM_ROWS | AVG_ROW_LEN | LAST_ANALYZED |
+-----------+------------+----------------+----------+-------------+---------------+
| cndba | visit_log | NULL | NULL | NULL | NULL |
+-----------+------------+----------------+----------+-------------+---------------+
1 row in set (0.005 sec)
obclient [oceanbase]> CALL DBMS_STATS.GATHER_TABLE_STATS('cndba', 'visit_log', method_opt=>'for all columns size 1');
Query OK, 0 rows affected (0.095 sec)
obclient [oceanbase]> select owner,TABLE_NAME ,partition_name,NUM_ROWS ,AVG_ROW_LEN ,LAST_ANALYZED from OCEANBASE.DBA_TAB_STATISTICS where table_name='visit_log';
+-----------+------------+----------------+----------+-------------+----------------------------+
| owner | TABLE_NAME | partition_name | NUM_ROWS | AVG_ROW_LEN | LAST_ANALYZED |
+-----------+------------+----------------+----------+-------------+----------------------------+
| cndba | visit_log | NULL | 7 | 144 | 2023-07-12 19:58:52.080771 |
+-----------+------------+----------------+----------+-------------+----------------------------+
1 row in set (0.005 sec)
obclient [oceanbase]>
3.2 锁定 / 解锁统计信息
统计信息的锁定功能主要用于锁死统计信息,防止统计信息更新,以保证统计信息的稳定性。
例如,如果想指定某一张表不收集统计信息,就可以提前上锁,这样计划生成的时候就会采取其他方式获取统计信息(动态采样等)。又如,对于一张数据稳定的表,我们不需要经常去收集它的统计信息,因此就可以提前上锁。
三种锁定统计信息的方式:
1.使用存储过程 lock_table_stats 锁定某个表的统计信息。
2.使用存储过程 lock_partition_stats 锁定某个分区的统计信息。
3.使用存储过程 lock_schema_stats 锁定某个 Schema 下所有表的统计信息。
三种解锁统计信息的方式:
1.使用存储过程 unlock_table_stats 解锁某个表的统计信息。
2.使用存储过程 unlock_partition_stats 解锁某个分区的统计信息。
3.使用存储过程 unlock_schema_stats 解锁某个 Schema 下所有表的统计信息。
操作示例:
obclient [oceanbase]> select owner,TABLE_NAME ,partition_name,NUM_ROWS ,AVG_ROW_LEN ,LAST_ANALYZED,stattype_locked from OCEANBASE.DBA_TAB_STATISTICS where table_name='visit_log';
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
| owner | TABLE_NAME | partition_name | NUM_ROWS | AVG_ROW_LEN | LAST_ANALYZED | stattype_locked |
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
| cndba | visit_log | NULL | 7 | 144 | 2023-07-12 19:58:52.080771 | NULL |
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
1 row in set (0.043 sec)
obclient [oceanbase]> CALL dbms_stats.lock_table_stats('cndba', 'visit_log');
Query OK, 0 rows affected (0.009 sec)
obclient [oceanbase]> select owner,TABLE_NAME ,partition_name,NUM_ROWS ,AVG_ROW_LEN ,LAST_ANALYZED,stattype_locked from OCEANBASE.DBA_TAB_STATISTICS where table_name='visit_log';
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
| owner | TABLE_NAME | partition_name | NUM_ROWS | AVG_ROW_LEN | LAST_ANALYZED | stattype_locked |
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
| cndba | visit_log | NULL | 7 | 144 | 2023-07-12 19:58:52.080771 | ALL |
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
1 row in set (0.005 sec)
obclient [oceanbase]> CALL dbms_stats.unlock_table_stats('cndba', 'visit_log');
Query OK, 0 rows affected (0.009 sec)
obclient [oceanbase]> select owner,TABLE_NAME ,partition_name,NUM_ROWS ,AVG_ROW_LEN ,LAST_ANALYZED,stattype_locked from OCEANBASE.DBA_TAB_STATISTICS where table_name='visit_log';
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
| owner | TABLE_NAME | partition_name | NUM_ROWS | AVG_ROW_LEN | LAST_ANALYZED | stattype_locked |
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
| cndba | visit_log | NULL | 7 | 144 | 2023-07-12 19:58:52.080771 | NULL |
+-----------+------------+----------------+----------+-------------+----------------------------+-----------------+
1 row in set (0.005 sec)
obclient [oceanbase]>
注:本文根据官网手册整理
版权声明:本文为博主原创文章,未经博主允许不得转载。