科学戳方法,或者如何使用基准和优化算法选择数据库配置

你好

我决定分享我的发现——思考、尝试和错误的成果。
总的来说:这当然不是一个发现——对于那些参与应用统计数据处理和任何系统优化的人来说,所有这些都应该已经知道很长时间了,不一定是专门针对 DBMS 的人。
而且:是的,他们知道,他们写了关于他们的研究的有趣文章, 例子 (更新:在评论中他们指出了一个非常有趣的项目: 奥特图纳 )
另一方面:我没有看到 IT 专家、DBA 在互联网上广泛提及或传播这种方法。

所以,切中要点。

假设我们有一个任务:建立一定的服务系统来服务某项工作。

知道这个作品:它是什么,这个作品的质量如何衡量,衡量这个质量的标准是什么。

我们还假设它或多或少是已知和理解的:工作是如何在该服务系统中(或使用)执行的。

“或多或少”-这意味着可以准备(或从某处获取)某种工具、实用程序、服务,这些工具、实用程序、服务可以合成并应用于系统,测试负载足以满足生产中的需要,在足够适合生产工作的条件下。

好吧,我们假设该服务系统的一组调整参数是已知的,这些参数可用于根据系统的工作效率来配置该系统。

问题是什么 - 对该服务系统没有足够完整的了解,该系统允许您熟练地配置该系统的设置以适应给定平台上的未来负载并获得系统所需的生产力。

出色地。 情况几乎总是如此。

你在这里能做什么?

嗯,首先想到的是查看该系统的文档。 了解调整参数值的可接受范围。 并且,例如,使用坐标下降法,在测试中为系统参数选择值。

那些。 以一组特定的配置参数值的形式为系统提供某种配置。

使用这个工具实用程序负载生成器对其应用测试负载。
并查看价值 - 响应或系统质量的度量。

第二个想法可能会得出这样的结论:这已经是很长的时间了。

好吧,那就是:如果有很多调整参数,如果它们覆盖的值范围很大,如果每个单独的负载测试需要很多时间才能完成,那么:是的,这一切可能会花费一个不可接受的量的时间。

好吧,这就是您可以理解和记住的内容。

你可以发现,在服务系统设置参数的值集中有一个向量,作为一些值的序列。

在其他条件相同的情况下(因为它不受该向量的影响),每个这样的向量都对应于一个完全确定的度量值——测试负载下系统运行质量的指标。

让我们将系统配置向量表示为 科学戳方法,或者如何使用基准和优化算法选择数据库配置哪里 科学戳方法,或者如何使用基准和优化算法选择数据库配置; 在哪里 科学戳方法,或者如何使用基准和优化算法选择数据库配置 — 系统配置参数的数量,这些参数有多少个。

以及与此对应的metric的值 科学戳方法,或者如何使用基准和优化算法选择数据库配置 让我们将其表示为
科学戳方法,或者如何使用基准和优化算法选择数据库配置,那么我们得到一个函数: 科学戳方法,或者如何使用基准和优化算法选择数据库配置

好吧,那么:就我而言,一切都立即归结为:在我的学生时代几乎被遗忘的搜索函数极值的算法。

好的,但是这里出现了一个组织和应用的问题:使用哪种算法。

  1. 从某种意义上说——这样你就可以减少手工编码。
  2. 并让它发挥作用,即找到极值(如果有的话),好吧,至少比坐标下降更快。

第一点暗示我们需要寻找一些已经实现此类算法的环境,并且以某种形式准备在代码中使用。
嗯,我知道 python и cran-r

第二点意味着你需要了解算法本身,它们是什么,它们的要求是什么,以及它们工作的特点。

它们给出的可能是有用的副作用——结果,或者直接来自算法本身。

或者它们可以从算法的结果中获得。

很大程度上取决于输入条件。

例如,如果出于某种原因,您需要更快地获得结果,那么您需要考虑梯度下降算法并选择其中一种。

或者,如果时间不是那么重要,您可以使用随机优化方法,例如遗传算法。

我建议在接下来的实验室工作中考虑这种方法的工作,选择系统配置,使用遗传算法。

资源:

  1. 让它成为一个服务系统: oracle xe 18c
  2. 让它服务于事务活动和目标:获得子数据库的最高可能吞吐量(以事务/秒为单位)。
  3. 事务处理数据的性质和工作环境可能有很大不同。
    我们同意这些事务不处理大量表格数据。
    从某种意义上说,它们不会生成比重做更多的撤消数据,并且不会处理大量的行和大型表。

这些事务会更改或多或少较大的表中的一行,并且该表上有少量索引。

在这种情况下:处理事务的子数据库的生产率将有保留地由氧化还原数据库的处理质量决定。

免责声明 - 如果我们具体谈论子数据库设置。

因为,在一般情况下,由于用户使用表格数据和/或表格模型的设计,在 SQL 会话之间可能存在事务锁。

当然,这会对 TPS 指标产生抑制作用,并且相对于子数据库而言,这将是一个外生因素:嗯,这就是表格模型的设计方式以及如何处理其中发生阻塞的数据。

因此,为了实验的纯度,我们会排除这个因素,下面我会具体说明如何排除。

  1. 为了明确起见,我们假设提交给数据库的 SQL 命令 100% 都是 DML 命令。
    测试时让用户操作子数据库的特性相同。
    即:skl 会话的数量、表格数据、skl 会话如何使用它们。
  2. Subd 工作于 FORCE LOGGING, ARCHIVELOG 模组。 闪回数据库模式在子级别关闭。
  3. 重做日志:位于单独的文件系统、单独的“磁盘”上;
    数据库的其余物理组件:在另一个单独的文件系统中,在单独的“磁盘”上:

有关物理设备的更多详细信息。 实验室数据库组件

SQL> select status||' '||name from v$controlfile;
 /db/u14/oradata/XE/control01.ctl
SQL> select GROUP#||' '||MEMBER from v$logfile;
1 /db/u02/oradata/XE/redo01_01.log
2 /db/u02/oradata/XE/redo02_01.log
SQL> select FILE_ID||' '||TABLESPACE_NAME||' '||round(BYTES/1024/1024,2)||' '||FILE_NAME as col from dba_data_files;
4 UNDOTBS1 2208 /db/u14/oradata/XE/undotbs1_01.dbf
2 SLOB 128 /db/u14/oradata/XE/slob01.dbf
7 USERS 5 /db/u14/oradata/XE/users01.dbf
1 SYSTEM 860 /db/u14/oradata/XE/system01.dbf
3 SYSAUX 550 /db/u14/oradata/XE/sysaux01.dbf
5 MONITOR 128 /db/u14/oradata/XE/monitor.dbf
SQL> !cat /proc/mounts | egrep "/db/u[0-2]"
/dev/vda1 /db/u14 ext4 rw,noatime,nodiratime,data=ordered 0 0
/dev/mapper/vgsys-ora_redo /db/u02 xfs rw,noatime,nodiratime,attr2,nobarrier,inode64,logbsize=256k,noquota 0 0

最初,在这些负载条件下,我想使用事务 subd SLOB 实用程序
它有这么棒的功能,我引用一下作者的话:

SLOB 的核心是“SLOB 方法”。 SLOB 方法旨在测试平台
没有应用程序争用。 无法驱动最大的硬件性能
使用应用程序代码,例如,受应用程序锁定或什至约束的应用程序代码
共享 Oracle 数据库块。 没错,共享数据时会产生开销
在数据块中! 但 SLOB(在其默认部署中)不受此类争用的影响。

此声明:对应,确实如此。
方便调节cl会话的并行度,这个是关键 -t 启动实用程序 runit.sh 来自 SLOB
DML 命令的百分比受到控制,包括发送到 subd 的文本消息数量、每个文本会话、参数 UPDATE_PCT
单独且非常方便: SLOB 本身,在加载会话之前和之后 - 准备一个 statspack 或 awr-snapshots(设置为准备的内容)。

然而,事实证明 SLOB 不支持持续时间小于 30 秒的 SQL 会话。
因此,我首先编写了自己的工农版装载机,然后继续运行。

为了清楚起见,让我澄清一下加载程序的作用以及它是如何工作的。
本质上,加载器看起来像这样:

工人代码

function dotx()
{
local v_period="$2"
[ -z "v_period" ] && v_period="0"
source "/home/oracle/testingredotracе/config.conf"

$ORACLE_HOME/bin/sqlplus -S system/${v_system_pwd} << __EOF__
whenever sqlerror exit failure
set verify off
set echo off
set feedback off

define wnum="$1"
define period="$v_period"
set appinfo worker_&&wnum

declare
 v_upto number;
 v_key  number;
 v_tots number;
 v_cts  number;
begin
 select max(col1) into v_upto from system.testtab_&&wnum;
 SELECT (( SYSDATE - DATE '1970-01-01' ) * 86400 ) into v_cts FROM DUAL;
 v_tots := &&period + v_cts;
 while v_cts <= v_tots
 loop
  v_key:=abs(mod(dbms_random.random,v_upto));
  if v_key=0 then
   v_key:=1;
  end if;
  update system.testtab_&&wnum t
  set t.object_name=translate(dbms_random.string('a', 120), 'abcXYZ', '158249')
  where t.col1=v_key
  ;
  commit;
  SELECT (( SYSDATE - DATE '1970-01-01' ) * 86400 ) into v_cts FROM DUAL;
 end loop;
end;
/

exit
__EOF__
}
export -f dotx

Worker 是这样启动的:

运行工人

echo "starting test, duration: ${TEST_DURATION}" >> "$v_logfile"
for((i=1;i<="$SQLSESS_COUNT";i++))
do
 echo "sql-session: ${i}" >> "$v_logfile"
 dotx "$i" "${TEST_DURATION}" &
done
echo "waiting..." >> "$v_logfile"
wait

工人的桌子是这样准备的:

创建表

function createtable() {
source "/home/oracle/testingredotracе/config.conf"
$ORACLE_HOME/bin/sqlplus -S system/${v_system_pwd} << __EOF__
whenever sqlerror continue
set verify off
set echo off
set feedback off

define wnum="$1"
define ts_name="slob"

begin
 execute immediate 'drop table system.testtab_&&wnum';
exception when others then null;
end;
/

create table system.testtab_&&wnum tablespace &&ts_name as
select rownum as col1, t.*
from sys.dba_objects t
where rownum<1000
;
create index testtab_&&wnum._idx on system.testtab_&&wnum (col1);
--alter table system.testtab_&&wnum nologging;
--alter index system.testtab_&&wnum._idx nologging;
exit
__EOF__
}
export -f createtable

seq 1 1 "$SQLSESS_COUNT" | xargs -n 1 -P 4 -I {} -t bash -c "createtable "{}"" | tee -a "$v_logfile"
echo "createtable done" >> "$v_logfile"

那些。 对于每个工作人员(实际上:数据库中的一个单独的 SQL 会话),都会创建一个单独的表,工作人员使用该表进行工作。

这确保了工作会话之间不存在事务锁。
每个工人:做同样的事情,有自己的桌子,桌子都是一样的。
所有工人的工作时间相同。
而且,在足够长的时间内,例如日志切换肯定会发生,而且不止一次。
嗯,相应地,相关的成本和影响就出现了。
在我的例子中,我将工人的工作时间配置为 8 分钟。

一段 statspack 报告描述了负载下 subd 的操作

Database    DB Id    Instance     Inst Num  Startup Time   Release     RAC
~~~~~~~~ ----------- ------------ -------- --------------- ----------- ---
          2929910313 XE                  1 07-Sep-20 23:12 18.0.0.0.0  NO

Host Name             Platform                CPUs Cores Sockets   Memory (G)
~~~~ ---------------- ---------------------- ----- ----- ------- ------------
     billing.izhevsk1 Linux x86 64-bit           2     2       1         15.6

Snapshot       Snap Id     Snap Time      Sessions Curs/Sess Comment
~~~~~~~~    ---------- ------------------ -------- --------- ------------------
Begin Snap:       1630 07-Sep-20 23:12:27       55        .7
  End Snap:       1631 07-Sep-20 23:20:29       62        .6
   Elapsed:       8.03 (mins) Av Act Sess:       8.4
   DB time:      67.31 (mins)      DB CPU:      15.01 (mins)

Cache Sizes            Begin        End
~~~~~~~~~~~       ---------- ----------
    Buffer Cache:     1,392M              Std Block Size:         8K
     Shared Pool:       288M                  Log Buffer:   103,424K

Load Profile              Per Second    Per Transaction    Per Exec    Per Call
~~~~~~~~~~~~      ------------------  ----------------- ----------- -----------
      DB time(s):                8.4                0.0        0.00        0.20
       DB CPU(s):                1.9                0.0        0.00        0.04
       Redo size:        7,685,765.6              978.4
   Logical reads:           60,447.0                7.7
   Block changes:           47,167.3                6.0
  Physical reads:                8.3                0.0
 Physical writes:              253.4                0.0
      User calls:               42.6                0.0
          Parses:               23.2                0.0
     Hard parses:                1.2                0.0
W/A MB processed:                1.0                0.0
          Logons:                0.5                0.0
        Executes:           15,756.5                2.0
       Rollbacks:                0.0                0.0
    Transactions:            7,855.1

回到实验室工作。
在其他条件相同的情况下,我们将改变实验室子数据库的以下参数的值:

  1. 数据库日志组的大小。 值范围:[32, 1024] MB;
  2. 数据库中日记组的数量。 取值范围:[2,32];
  3. log_archive_max_processes 取值范围:[1,8];
  4. commit_logging 允许两个值: batch|immediate;
  5. commit_wait 允许两个值: wait|nowait;
  6. log_buffer 值范围:[2,128] MB。
  7. log_checkpoint_timeout 取值范围:[60,1200]秒
  8. db_writer_processes 取值范围:[1,4]
  9. undo_retention 值范围:[30;300]秒
  10. transactions_per_rollback_segment 取值范围:[1,8]
  11. disk_asynch_io 允许两个值: true|false;
  12. filesystemio_options 允许使用以下值: none|setall|directIO|asynch;
  13. db_block_checking 允许使用以下值: OFF|LOW|MEDIUM|FULL;
  14. db_block_checksum 允许使用以下值: OFF|TYPICAL|FULL;

具有维护 Oracle 数据库经验的人当然已经可以从指定的参数及其可接受的值中说出应该设置什么以及应该设置什么值,以便获得数据库更高的生产力来处理由应用程序代码,在上面。

不过。

实验室工作的目的是表明优化算法本身将相对较快地为我们澄清这一点。

对于我们来说,剩下的就是通过可定制的系统查看文档,足以找出要更改哪些参数以及在什么范围内更改。
并且:编写将用于与所选优化算法的自定义系统一起工作的代码。

那么,现在关于代码。
我在上面谈到 cran-r,即:定制系统的所有操作都以 R 脚本的形式编排。

实际任务、分析、按度量值选择、系统状态向量:这是一个包 GA (文件)
在这种情况下,该包不是很合适,因为它期望向量(染色体,如果就包而言)以带有小数部分的数字字符串的形式指定。

而我的向量,从设置参数的值来看:这些是 14 个数量 - 整数和字符串值。

当然,通过将一些特定数字分配给字符串值可以轻松避免该问题。

因此,最终 R 脚本的主要部分如下所示:

调用 GA::ga

cat( "", file=v_logfile, sep="n", append=F)

pSize = 10
elitism_value=1
pmutation_coef=0.8
pcrossover_coef=0.1
iterations=50

gam=GA::ga(type="real-valued", fitness=evaluate,
lower=c(32,2, 1,1,1,2,60,1,30,1,0,0, 0,0), upper=c(1024,32, 8,10,10,128,800,4,300,8,10,40, 40,30),
popSize=pSize,
pcrossover = pcrossover_coef,
pmutation = pmutation_coef,
maxiter=iterations,
run=4,
keepBest=T)
cat( "GA-session is done" , file=v_logfile, sep="n", append=T)
gam@solution

在这里,在帮助下 lower и upper 子程序属性 ga 本质上,指定搜索空间的一个区域,在该区域内将搜索一个或多个向量,以获得适应度函数的最大值。

ga 子例程执行最大化适应度函数的搜索。

那么,事实证明,在这种情况下,适应度函数必须将向量理解为 subd 某些参数的一组值,并从 subd 接收度量。

也就是说:在给定的子程序设置和子程序上给定的负载下有多少:子程序每秒处理事务。

也就是说,展开时,适应度函数内部必须执行以下多步:

  1. 处理数字的输入向量 - 将其转换为子数据参数的值。
  2. 尝试创建给定数量、给定大小的重做组。 而且,这种尝试可能会失败。
    为了实验的纯粹性,已经在 subd 中存在一定数量和一定规模的杂志组 - d.b. 已删除。
  3. 如果上一点成功:向数据库指定配置参数的值(同样:可能会失败)
  4. 如果上一步成功:停止subd,启动subd,使新指定的参数值生效。 (再次强调:可能有问题)
  5. 如果上一步成功:执行负载测试。 从 subd 获取指标。
  6. 将 subd 返回到其原始状态,即删除额外的日志组,使原始子数据库配置恢复工作。

健身功能代码

evaluate=function(p_par) {
v_module="evaluate"
v_metric=0
opn=NULL
opn$rg_size=round(p_par[1],digit=0)
opn$rg_count=round(p_par[2],digit=0)
opn$log_archive_max_processes=round(p_par[3],digit=0)
opn$commit_logging="BATCH"
if ( round(p_par[4],digit=0) > 5 ) {
 opn$commit_logging="IMMEDIATE"
}
opn$commit_logging=paste("'", opn$commit_logging, "'",sep="")

opn$commit_wait="WAIT"
if ( round(p_par[5],digit=0) > 5 ) {
 opn$commit_wait="NOWAIT"
}
opn$commit_wait=paste("'", opn$commit_wait, "'",sep="")

opn$log_buffer=paste(round(p_par[6],digit=0),"m",sep="")
opn$log_checkpoint_timeout=round(p_par[7],digit=0)
opn$db_writer_processes=round(p_par[8],digit=0)
opn$undo_retention=round(p_par[9],digit=0)
opn$transactions_per_rollback_segment=round(p_par[10],digit=0)
opn$disk_asynch_io="true"
if ( round(p_par[11],digit=0) > 5 ) {
 opn$disk_asynch_io="false"
} 

opn$filesystemio_options="none"
if ( round(p_par[12],digit=0) > 10 && round(p_par[12],digit=0) <= 20 ) {
 opn$filesystemio_options="setall"
}
if ( round(p_par[12],digit=0) > 20 && round(p_par[12],digit=0) <= 30 ) {
 opn$filesystemio_options="directIO"
}
if ( round(p_par[12],digit=0) > 30 ) {
 opn$filesystemio_options="asynch"
}

opn$db_block_checking="OFF"
if ( round(p_par[13],digit=0) > 10 && round(p_par[13],digit=0) <= 20 ) {
 opn$db_block_checking="LOW"
}
if ( round(p_par[13],digit=0) > 20 && round(p_par[13],digit=0) <= 30 ) {
 opn$db_block_checking="MEDIUM"
}
if ( round(p_par[13],digit=0) > 30 ) {
 opn$db_block_checking="FULL"
}

opn$db_block_checksum="OFF"
if ( round(p_par[14],digit=0) > 10 && round(p_par[14],digit=0) <= 20 ) {
 opn$db_block_checksum="TYPICAL"
}
if ( round(p_par[14],digit=0) > 20 ) {
 opn$db_block_checksum="FULL"
}

v_vector=paste(round(p_par[1],digit=0),round(p_par[2],digit=0),round(p_par[3],digit=0),round(p_par[4],digit=0),round(p_par[5],digit=0),round(p_par[6],digit=0),round(p_par[7],digit=0),round(p_par[8],digit=0),round(p_par[9],digit=0),round(p_par[10],digit=0),round(p_par[11],digit=0),round(p_par[12],digit=0),round(p_par[13],digit=0),round(p_par[14],digit=0),sep=";")
cat( paste(v_module," try to evaluate vector: ", v_vector,sep="") , file=v_logfile, sep="n", append=T)

rc=make_additional_rgroups(opn)
if ( rc!=0 ) {
 cat( paste(v_module,"make_additional_rgroups failed",sep="") , file=v_logfile, sep="n", append=T)
 return (0)
}

v_rc=0
rc=set_db_parameter("log_archive_max_processes", opn$log_archive_max_processes)
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("commit_logging", opn$commit_logging )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("commit_wait", opn$commit_wait )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("log_buffer", opn$log_buffer )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("log_checkpoint_timeout", opn$log_checkpoint_timeout )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("db_writer_processes", opn$db_writer_processes )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("undo_retention", opn$undo_retention )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("transactions_per_rollback_segment", opn$transactions_per_rollback_segment )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("disk_asynch_io", opn$disk_asynch_io )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("filesystemio_options", opn$filesystemio_options )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("db_block_checking", opn$db_block_checking )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("db_block_checksum", opn$db_block_checksum )
if ( rc != 0 ) {  v_rc=1 }

if ( rc!=0 ) {
 cat( paste(v_module," can not startup db with that vector of settings",sep="") , file=v_logfile, sep="n", append=T)
 rc=stop_db("immediate")
 rc=create_spfile()
 rc=start_db("")
 rc=remove_additional_rgroups(opn)
 return (0)
}

rc=stop_db("immediate")
rc=start_db("")
if ( rc!=0 ) {
 cat( paste(v_module," can not startup db with that vector of settings",sep="") , file=v_logfile, sep="n", append=T)
 rc=stop_db("abort")
 rc=create_spfile()
 rc=start_db("")
 rc=remove_additional_rgroups(opn)
 return (0)
}

rc=run_test()
v_metric=getmetric()

rc=stop_db("immediate")
rc=create_spfile()
rc=start_db("")
rc=remove_additional_rgroups(opn)

cat( paste("result: ",v_metric," ",v_vector,sep="") , file=v_logfile, sep="n", append=T)
return (v_metric)
}

那。 所有工作:在适应度函数中执行。

ga 子例程处理向量,或更准确地说,处理染色体。
其中,对我们来说最重要的是选择具有适应度函数产生较大值的基因的染色体。

这本质上是在N维搜索空间中使用向量搜索最优染色体组的过程。

非常清楚、详细 一个解释,以 R 代码为例,遗传算法的工作。

我想分别说明两个技术点。

来自函数的辅助调用 evaluate,例如stop-start,设置subd参数的值,都是根据 cran-r 功能 system2

借助它: 调用一些 bash 脚本或命令。

例如:

设置数据库参数

set_db_parameter=function(p1, p2) {
v_module="set_db_parameter"
v_cmd="/home/oracle/testingredotracе/set_db_parameter.sh"
v_args=paste(p1," ",p2,sep="")

x=system2(v_cmd, args=v_args, stdout=T, stderr=T, wait=T)
if ( length(attributes(x)) > 0 ) {
 cat(paste(v_module," failed with: ",attributes(x)$status," ",v_cmd," ",v_args,sep=""), file=v_logfile, sep="n", append=T)
 return (attributes(x)$status)
}
else {
 cat(paste(v_module," ok: ",v_cmd," ",v_args,sep=""), file=v_logfile, sep="n", append=T)
 return (0)
}
}

第二个点是线, evaluate 函数,将特定的度量值及其相应的调整向量保存到日志文件中:

cat( paste("result: ",v_metric," ",v_vector,sep="") , file=v_logfile, sep="n", append=T)

这很重要,因为从该数据阵列中,可以获得关于调整向量的哪些分量对度量值具有更大或更小的影响的附加信息。

即:可以进行属性重要性分析。

那么会发生什么呢?

以图表形式,如果按公制升序对测试进行排序,则图片如下:

科学戳方法,或者如何使用基准和优化算法选择数据库配置

指标极值对应的一些数据:
科学戳方法,或者如何使用基准和优化算法选择数据库配置
在这里,在带有结果的屏幕截图中,我将澄清:调整向量的值是根据适应度函数代码给出的,而不是根据参数/参数值范围的数量列表给出的,这是用公式表示的上面的文字。

出色地。 是多还是少,~8 tps:一个单独的问题。
在实验室工作的框架内,这个数字并不重要,重要的是动态,这个值如何变化。

这里的动态很好。
显然,至少有一个因素显着影响度量值,即 ga 算法,对染色体向量进行排序:覆盖。
从曲线值相当活跃的动态来看,至少还有一个因素虽然小得多,但也有影响。

这就是你需要的地方 attribute-importance 分析以了解哪些属性(在本例中是调整向量的组成部分)以及它们对度量值的影响程度。
并从这些信息中:了解哪些因素受到重要属性变化的影响。

运行 attribute-importance 可以有不同的方式。

出于这些目的,我喜欢该算法 randomForest 同名的 R 包(文件)
randomForest据我了解他的工作总体情况以及他评估属性重要性的方法,建立了响应变量对属性的依赖关系的特定模型。

在我们的例子中,响应变量是在负载测试中从数据库获取的指标: tps;
属性是调整向量的组成部分。

所以 randomForest 用两个数字评估每个模型属性的重要性: %IncMSE — 模型中该属性的存在/不存在如何改变该模型的 MSE 质量(均方误差);

IncNodePurity 是一个数字,它反映了根据该属性的值,可以将具有观察值的数据集划分得有多好,以便在一部分中存在具有所解释的度量值的数据,而在另一部分中存在具有被解释的度量值的数据度量的另一个值。
嗯,那就是:这在多大程度上是一个分类属性(我在RandomForest上看到了最清晰的俄语解释 这里).

用于处理具有负载测试结果的数据集的工农 R 代码:

x=NULL
v_data_file=paste('/tmp/data1.dat',sep="")
x=read.table(v_data_file, header = TRUE, sep = ";", dec=",", quote = ""'", stringsAsFactors=FALSE)
colnames(x)=c('metric','rgsize','rgcount','lamp','cmtl','cmtw','lgbffr','lct','dbwrp','undo_retention','tprs','disk_async_io','filesystemio_options','db_block_checking','db_block_checksum')

idxTrain=sample(nrow(x),as.integer(nrow(x)*0.7))
idxNotTrain=which(! 1:nrow(x) %in% idxTrain )
TrainDS=x[idxTrain,]
ValidateDS=x[idxNotTrain,]

library(randomForest)
#mtry=as.integer( sqrt(dim(x)[2]-1) )
rf=randomForest(metric ~ ., data=TrainDS, ntree=40, mtry=3, replace=T, nodesize=2, importance=T, do.trace=10, localImp=F)
ValidateDS$predicted=predict(rf, newdata=ValidateDS[,colnames(ValidateDS)!="metric"], type="response")
sum((ValidateDS$metric-ValidateDS$predicted)^2)
rf$importance

您可以直接用手选择算法的超参数,并关注模型的质量,选择更准确地满足验证数据集预测的模型。
您可以为此工作编写某种函数(顺便说一下,再次使用某种优化算法)。

您可以使用 R 包 caret,不是重点。

因此,在这种情况下,可以得到以下结果来评估属性的重要程度:

科学戳方法,或者如何使用基准和优化算法选择数据库配置

出色地。 因此,我们可以开始全局反思:

  1. 事实证明,在这些测试条件下,最重要的是参数 commit_wait
    从技术上讲,它指定了将redo数据从subdb日志缓冲区写入当前日志组的io操作的执行模式:同步或异步。
    nowait 这导致 tps 指标的值几乎垂直地成倍增加:这是在重做组中包含异步 io 模式。
    另一个问题是是否应该在食品数据库中执行此操作。 在这里我只想说:这是一个重要因素。
  2. subd 的日志缓冲区大小是一个重要因素,这是合乎逻辑的。
    日志缓冲区的大小越小,其缓冲能力越小,其溢出和/或无法在其中分配空闲区域用于一部分新的氧化还原数据的情况就越频繁。
    这意味着:与在日志缓冲区中分配空间和/或将重做数据从日志缓冲区转储到重做组相关的延迟。
    当然,这些延迟应该而且确实会影响数据库的事务吞吐量。
  3. 参数 db_block_checksum:嗯,总的来说,很明显 - 事务处理会导致子数据库的缓冲区高速缓存中形成 darty 块。
    其中,当启用检查数据块校验和时,数据库必须处理 - 从数据块主体计算这些校验和,用数据块标头中写入的内容检查它们:匹配/不匹配。
    同样,这样的工作不能不延迟数据处理,因此,参数和设置该参数的机制变得很重要。
    这就是为什么供应商在该参数的文档中为其(参数)提供了不同的值,并指出是的,会产生影响,但是,您可以选择不同的值,最高可达“关闭”和不同的影响。

好吧,一个全球性的结论。

总的来说,这种方法非常有效。

他非常允许自己,在某个业务系统的负载测试的早期阶段,为了选择其(系统)针对负载的最佳配置,而不是过多地钻研为负载设置系统的细节。

但它并没有完全排除它——至少在理解的层面上:系统必须了解“调节旋钮”以及这些旋钮的允许旋转范围。

该方法可以相对快速地找到最佳系统配置。
并且根据测试结果,可以获得有关系统性能指标与系统设置参数值之间关系性质的信息。

当然,这应该有助于对系统及其操作(至少在给定负载下)的这种非常深入的理解。

实际上,这是理解定制系统的成本与准备系统测试的成本的交换。

我想单独指出:在这种方法中,系统测试对其商业运行中的运行条件的充分程度至关重要。

感谢您的关注和时间。

来源: habr.com

添加评论