Timely Rain    About    Archive

YCSB官方文档-实现新工作负载(中译)


本文是YCSB-wiki-Implementing-New-Workloads一文的中文翻译。


1. 概述

一个工作负载代表一个给定的应用将加给数据库系统的负载。对于基准测试来说,相比真实应用,我们必须将工作负载定义得相对简单,这样我们才能更好地评估我们得到的基准测试的结果。然而,一个工作负载应该足够具体,才能让我们一旦测量了数据库的性能,就知道何种应用可能会有相似的性能表现。

YCSB中,一个工作负载定义了一个数据集和一个事务集,数据集是会被装载进数据库的一系列记录,事务集是一系列对数据库的读写操作。创建事务需要理解记录的结构,这也是为什么数据和事务必须在工作负载中被定义。

对于一个完整的基准测试,多个重要(但不重复)的工作负载可能被一起打包到一个workload package中。YCSB客户端中的CoreWorkload包就是这样一个工作负载集合的例子。

典型地,一个工作负载包含两个文件:

  • 一个Java类,包含创建数据记录并生成对记录的事务的代码
  • 一个参数文件,可以调节工作负载的属性

例如,一个工作负载类文件可能生成某个对数据库读和更新的操作集合。参数文件可能指定了读操作和更新操作的比例是50/50,80/20,等等。 有两种方法来创建一个新的工作负载或者工作负载的包。

2. 选择1:新的参数文件

YCSB所包含的核心工作负载是由一系列参数文件定义的(workloada,workloadb,等等)。你可以赋予读/写混合比例、请求分布等参数以新的值,由此来创建你自己的参数文件。例如,workloada文件拥有以下内容:

workload=com.yahoo.ycsb.workloads.CoreWorkload
readallfields=false
readproportion=0.5
updateproportion=0.5
scanproportion=0
insertproportion=0
requestdistribution=zipfian

创建一个更改了其中任意一个参数的新文件将生成一个新的拥有不同特性的工作负载。可以被指定的属性集在[核心属性]中描述。

3. 选择2:新的Java类

工作负载Java类在运行时会被YCSB客户端创建,并将使用一个数据库接口层的实例来生成实际的对数据库的操作。因此,Java类只需要决定(取决于参数文件中的设置)数据集要创建什么样的记录,以及事务阶段要生成什么读、更新等操作。YCSB客户端将关注创建工作负载Java类,将它传递给一个工作者线程去执行,并决定创建多少记录或者生成多少要执行的操作,以及测量性能结果。

如果CoreWorkload(或者其他已有的包)无法生成你所要的工作负载,你可以创建一个新的工作负载Java类。这由以下几步完成:

步骤1:扩展com.yahoo.ycsb.Workload

所有工作负载类的基类是com.yahoo.ycsb,Workload。这是一个抽象类,因此你要创建一个从这个基类扩展的新的工作负载。你的类必须拥有一个公共的无参数构造函数。YCSB客户端将为每一个工作者线程创建一个工作负载对象,因此如果你用多线程运行客户端,多个工作负载对象将被创建。

步骤2:写代码来初始化你的工作负载类

在构造函数被调用之后参数将被传递给工作负载对象,因此如果你正在使用任何参数属性,你必须用它们来初始化你的工作负载,这可以使用init()或者initThread()方法。

  • init()——为所有工作负载实例只调用一次。被用于初始化任何被所有线程共享的对象。
  • initThread()——在工作者线程上下文中,每个工作负载实例都会调用一次。被用于初始化任何被单个工作负载实例或者单个工作者线程所指定的对象。

在这两个例子中,你可以使用Properties对象来传递参数属性到这两个方法中。这些属性将包括被传递给YCSB客户端的任何属性文件中的所有属性,以及在客户端命令行中定义的属性。

步骤3:写清空代码

cleanup()方法会为所有工作负载实例调用一次,在工作负载被完成之后。

步骤4:定义被插入的记录

YCSB客户端将调用doInsert()方法来插入一条记录到数据库中。因此你应该实现这个方法来创建并插入单条记录。你可以用来实现插入的数据库对象将被传递给doInsert()方法。

步骤5:定义事务

YCSB客户端将调用doTransaction()方法来执行一次事务。因此你应该实现这个方法来执行单个事务,可以通过使用被传递的数据库对象来访问数据库。你对这个方法的实现可以选择不同类型的事务,也可以对数据库接口层进行多次调用。然而,每次对这个方法的调用应该是一个逻辑上的事务。特别的,当你运行客户端时,你将指定要执行的操作的数量;如果你请求了1000个操作,那么doTransaction()将被执行1000次。

注意,你不需要为了达到目标吞吐量而对你的事务(或者记录的插入)做任何限制。YCSB客户端会为你做这些限制。

还要注意的是,在doTransaction()方法中插入记录是被允许的。如果你希望数据库在执行工作负载期间记录增长你可能会这么做。在这种情况下,初始的数据集会使用doInsert()方法来生成,而额外的记录将由doTransaction()方法来插入。

步骤6:测量时延,如果必要的话

YCSB客户端会自动地测量时延和数据库操作的吞吐量,甚至是你自己定义的工作负载。然而,客户端只会测量每一个对数据库的调用的时延,而不是更复杂的事务。考虑一个例子,一个工作负载读取一条记录,修改它,并将它写回数据库。YCSB客户端将自动测量对数据库的读操作的时延;与此独立地,还将自动测量更新操作的时延。然而,如果你想要测量整个读-修改-写事务的时延,你将需要添加额外的计时步骤到你的代码里。

Measurements.measure()被调用于聚集测量结果。有一个单独的Measurements实例,它可以通过静态方法Measurements.getMeasurements()来获取。对于你所测量的每个指标,你需要分配一个字符串标签;这个标签将用于标识在工作负载结束后由工具所输出的均值、最大值、最小值、直方图等测量结果。例如,考虑以下代码:

long st=System.currentTimeMillis();
db.read(TABLENAME,keyname,fields,new HashMap<String,String>());
db.update(TABLENAME,keyname,values);
long en=System.currentTimeMillis();
Measurements.getMeasurements().measure(“READ-MODIFY-WRITE”, (int)(en-st));

在这个代码中,对System.currentTimeMillis()的调用被用于对读写事务的计时。然后,对measure()的调用则给测量模块报告时延。 通过这种模式,你定制的测量将被收集并聚合,与收集单个的读、更新等操作的测量结果使用一样的机制。

步骤7:通过YCSB客户端来使用它

确保你的实现的类(或者一个包含那些类的jar包),以及你的实现所使用到的库/jar文件都在你的CLASSPATH中。现在,当你运行YCSB客户端时,使用你的数据库类的全名来指定“workload”属性。例如:

workload=com.foo.YourWorkloadClass
comments powered by Disqus