Skip to content
yqekzb edited this page Dec 29, 2020 · 6 revisions

原文见:实验指导

实验指导


实验系统介绍

架构介绍

系统架构如图所示,主要包括两大模块:

  1. 客户端实例,作为事务的发起者。客户端中的每个线程负责发起事务请求,并将发起的事务请求放到消息队列中,按序发送给服务端具体执行。客户端与服务端实例是全连接的拓扑结构,一般部署在不同的机器上;
  2. 服务端实例,具体执行事务中的各类操作。不同服务端实例中存有数据,数据使用一致性哈希进行索引,从而形成服务端IP与所存数据的全局分区映射表,通过控制分区映射在测试期间不会修改的方法,保证该映射表是所有节点都可以准确获取的。客户端与服务端实例、服务端与服务端实例间的通信使用TCP/IP协议。每个服务端实例内部可被细分为四个模块:
    1. 输入消息队列,暂存由客户端/其他服务端发来的消息;
    2. 执行引擎,分配多个工作线程来对消息队列中的消息进行解析并实际执行,采用一个核绑定一个线程的资源调度方式;
    3. 并发控制模块,工作线程在执行事务操作的过程中,要维护特定并发控制协议要求的信息,并执行协议规定的流程,从而保证所指定的并发控制协议生效;
    4. 数据存储模块,负责管理本实例中的数据,将数据都放在内存中。

事务执行流程

一个事务的执行流程为:

  1. 客户端首先发起一个事务请求(由多个操作组成),并将事务放到ClientQueryQueue中。ClientThread会从队列中取出事务请求存入消息队列MsgQueue。之后,消息发送线程会从MsgQueue中取出某一事务的操作集合,封装为一个Request,发送到某一服务端(通过第一个操作所访问的数据确定),作为这个事务的协调节点;
  2. Request到达服务器后,服务器先对请求进行解析,并把这一事务的所有操作作为一个元素,放到工作队列(TaskQueue)中。工作队列中放置的有来自客户端的新事务和已经开始执行的事务的远程操作,后者在队列中比前者优先级更高。工作线程池中的线程轮询工作队列并处理事务中的操作。当某一工作线程处理当前事务的操作时,其首先对事务进行初始化,然后按顺序执行读写操作,并最终提交或者回滚;
    1. 执行事务的过程中有两种情况会导致事务进入等待,一是等待某一资源上的排他锁释放;二是需要访问远程服务端中的数据。当远程访问其他服务端中的数据需要的等待时,远程服务端将返回WAIT指令给当前协调节点,协调节点会暂存当前事务的等待状态并调度当前工作线程执行其他事务的操作,从而避免了工作线程的阻塞。当某一等待事务可以继续执行时,基于工作队列的优先级调度策略,它将由第一个可用的工作线程继续执行;
    2. 并发控制协议所要求的额外操作会嵌入在事务执行过程中,包括读写操作、验证操作、提交/回滚操作等。
  3. 当协调节点完成某一事务的操作后,他会将事务执行结果放入消息队列,然后由消息发送线程通知客户端当前事务的执行结果。

实验环境搭建和测试

实验环境

Linux centos 7.2
需要安装boost-devel和nanomsg
yum install nanomsg, nanomsg-devel, boost, boost-devel,

测试

修改config.h文件的ALGO参数,选择某种算法进行测试

#define ALGO MVCC

选择要进行的测试类型,修改config.h文件的WORKLOAD参数,用TEST跑正确性测试YCSB跑性能测试

#define WORKLOAD TEST //正确性测试
#define WORKLOAD YCSB //性能测试

在代码根目录下,编译程序进行测试

make deps
make -j 4
# 打开一个终端执行
./rundb -nid0
# 打开另一个终端执行下一句
./runcl -nid1

即可进行测试。

正确性测试说明

当进行正确性测试时,如果发现输出消息中存在Abort,则正确性测试通过。

补充正确性测试

修改config.h文件的如下参数

#define NODE_CNT 1
#define THREAD_CNT 1 
#define REM_THREAD_CNT 1
#define SEND_THREAD_CNT 1

#define CLIENT_NODE_CNT 1
#define CLIENT_THREAD_CNT 1
#define CLIENT_REM_THREAD_CNT 1
#define CLIENT_SEND_THREAD_CNT 1

#define WORKLOAD DA   //修改为DA测试负载

此外ifconfig.txt文件中,需要写明进行测试的机器ip。例子如下,同学们在148上进行测试时,需要修改ifconfig.txt为如下内容

10.77.110.148
10.77.110.148

同学们在修改完毕上config.h和ifconfig.txt后即可进行测试。 在代码根目录下,编译程序进行测试

make deps
make -j 4
# 打开一个终端执行
./rundb -nid0
# 打开另一个终端执行下一句
./runcl -nid1

该正确性测试负载的输入是input.txt文件,其中是规定好要执行的事务操作序列,将实际的执行序列打印到commit_history.txt文件中。通过对比commit_history.txt和给出的正确执行结果commit_history_2pl.txt/commit_history_occ.txt/commit_history_to.txt进行对比来验证正确性。


需要实现的接口和模块

在本系统中实现一个并发控制算法,需要实现一套并发控制算法的数据结构和事务操作时的操作接口。 并发控制算法的数据结构可以分为两种:

  1. 维护在事务中的数据,比如TO算法的开始时间戳。
  2. 维护在数据项的数据,比如TO算法需要维护数据的写时间戳和读时间戳。

将一个事务分为四个阶段,每个阶段都需要实现一部分接口。

  1. 事务初始化
  2. 执行读写操作
  3. 2PC的prepare阶段
  4. 2PC的commit阶段。

并发控制算法的数据结构

事务上的数据

见txn.h的TxnMgr数据结构,即事务的数据结构。

数据项上的数据

需要实现row_xxx.h,里面用来存储某个并发控制算法在数据项上维护的信息。可以参考row_occ_template.h

关于读写集 本系统中已经为所有算法提供了读写集的接口,见txn.h文件的Access类:

class Access {
		type:操作类型,读操作或者是写操作
		orig_row:读写到数据项的原地址
		data:读操作时为读取到的值,写操作时为预写入的值
		orig_data:读写操作前数据项的原始值,用于事务回滚后的回放。
}

TxnMgr类中也提供了几个函数,用来获取读写集。见178-182行。

access_get_original_row函数:获取读写集中某个操作的数据项原地址
access_get_type:获取读写集中某个操作的类型
access_get_cnt:获取读写集中的总数量

事务初始化操作

需要实现的内容 事务初始化操作中,需要实现各个并发控制算法数据结构的初始化。比如TO算法需要初始化并获取一个开始时间戳,系统自带的occ_template算法需要初始化事务时间戳范围hash表。

初始化操作所在位置以及修改方法 task_thread.cpp文件里是事务在工作线程中的执行逻辑,其中rtxn_process函数是线程开始执行事务部分的逻辑。 这里需要在第500行之后增加并发控制算法初始化的逻辑,可以参考occ_template算法的写法。

image.png-74.8kB


事务读写数据操作

需要实现的内容 事务读写操作中,需要实现获取数据并且进行并发控制处理的接口函数access。 读写操作所在位置以及修改方法 实现读写操作,首先需要实现row_xxx.cpp文件,其中需要实现如下几个接口:

Row_xxx::access(access_t type, TxnMgr * txn); //用于真正执行读写操作,并且进行并发控制操作

之后需要在row.cpp文件中的get_row函数中增加并发控制算法的读写操作。可以参考occ_template算法的实现:

  1. 初始化row结构体,用来存储数据库中读写到的数据(图中红框)
  2. 实现row_xxx.cpp以及其中的access接口,真正执行对数据的读写和并发控制处理,比如2PL的加锁,TO算法的时间验证等等(图中黄框)
  3. 将access函数中获取的数据copy到row结构中输出。(图中蓝框)

image.png-62.8kB


事务验证操作

需要实现的内容 实现OCC等含有验证操作的算法时,需要额外实现事务验证的逻辑。 验证所在位置以及修改方法 事务验证的入口见下图,在txn.cpp:467行

image.png-21.9kB

validate函数中,需要参考occ_template(红框)的算法实现OCC的验证方法。图中黄框是occ_template的验证接口函数。

image.png-47.9kB

这里推荐实现occ.cpp和occ.h两个文件用来处理全局的OCC验证。

事务提交和回滚操作

需要实现的内容 这里需要实现事务的提交和回滚两种接口。 每种接口都需要实现两个部分:提交或回滚数据项,以及对事物数据结构的清理。 读写操作所在位置以及修改方法 事务提交或回滚的函数入口见下图

image.png-22.3kB

提交/回滚数据项

在row.cpp的return_row函数中,实现对应的提交/回滚数据项操作。 可以参考系统自带的occ_template算法

image.png-73.2kB

这里推荐在row_xxx.cpp中实现commitabort两种接口:

Row_xxx::commit(access_t type, TxnMgr * txn, RowData * data);
Row_xxx::abort(access_t type, TxnMgr * txn);

数据结构清理部分

需要在上图的commitabort函数中完成清理并发控制算法某些数据结构的工作。 例如occ_template算法在413行完成了对事物时间戳范围的清理。

image.png-22.9kB

以及abort函数中的445行

image.png-66.2kB

MVCC接口

代码最新提交中已经增加了MVCC的接口,可以参考新添加的row_mvcc.h和row_mvcc.cpp。接口的具体介绍见row_mvcc.h的注释。