背景
对于单机数据库而言[如postgres],死锁检测机制相对于分布式数据库如greenplum来说相对简单,其本质是通过相关约束构造等待图,如果出现死锁情况,则采用拓扑排序重排序列规避死锁,若无解法则回滚等待环中的某一事务/或若干事务来打破死锁。但是对于share nothing 的MPP架构数据库而言,采用上述方法无法获取全局等待视图,死锁的攻克显得力不从心.另辟蹊径,greenplum设计的方法是:通过在 master 节点设置全局死锁检测进程,不断地轮询计算节点上的本地等待图graph,在master上汇总形成全局等待图,利用相应的规则和算法来打破死锁。接下来一起学习,探索源码:
关键数据结构
1 数据结构流转图
2 概念介绍
3 数据结构分析
3.1 GddCtx:全局死锁上下文结构体,该结构体记录了全局等待图的相关信息,包括所有节点[分布式事务]的出/入度、segid与节点间映射关系、segid与本地等待图间的映射关系信息
struct GddCtx
{
GddStat topstat; /* overall in/out degrees of all verts on all graphs */
GddMap globals; /* Map<vid, Stat>, global in/out degrees */
GddMap graphs; /* Map<segid, Graph>, the local graphs */
};
3.2 GddStat:记录某个分布式事务节点的出入度信息
/*
* Accounting information of a vert.
*/
struct GddStat
{
int id; /* vert id */ // 节点号 ===》 分布式事务号
int indeg; /* in degree */ // 该节点的入度
int outdeg; /* out degree */ // 该节点的出度
};
3.3 GddEdge:该结构记录等待图中边信息:包括是否为硬边、出节点、入节点信息和存放的数据
/*
* A directed edge.
*/
struct GddEdge
{
bool solid; /* a solid edge? */
GddVert *from; /* the from vert */
GddVert *to; /* the to vert */
void *data;
};
3.4 GddVert:该结构为计算segment上某个节点的相关信息,包括该节点在全局视图中的出入度,出边和入边等信息
/*
* A vertex (vert) on a segment.
*/
struct GddVert
{
int id; /* vert id */
/*
* Pointer to ctx->topstat, all the verts on all the globals maintain
* the overall in/out degrees count together.
*/
GddStat *topstat;
/*
* Pointer to the `global` struct with the same vert id, the global
* in/out degrees of vert.
*/
GddStat *global;
List *edgesIn; /* List<Edge>, directed edges to vert */
List *edgesOut; /* List<Edge>, directed edges from vert */
/*
* The data set and used only by the caller, GDD does not touch or access
* it.
*/
void *data;
};
3.5 GddMap:本质GddPair数组,保存的是K-V映射关系
/*
* A <int, pointer> pair.
*/
struct GddPair
{
int key;
void *ptr;
};
/*
* A simple int->ptr map, implemented with array.
*/
struct GddMap
{
int length; /* length of the <k,v> array */
int capacity; /* capacity of the <k,v> array */
GddPair *pairs; /* array of <k,v> pairs */
};
3 GDD进程介绍和工作总流程
该进程是greenplum内置的辅助进程,postmaster正式接受客户端请求前在后台启动
{"global deadlock detector process", "global deadlock detector process",
BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION,
BgWorkerStart_RecoveryFinished,
0, /* restart immediately if gdd exits with non-zero code */
"postgres", "GlobalDeadLockDetectorMain", 0, {0}, 0,
GlobalDeadLockDetectorStartRule},
运行规则:
bool GlobalDeadLockDetectorStartRule(Datum main_arg)
{
if (Gp_role == GP_ROLE_DISPATCH &&
gp_enable_global_deadlock_detector)
return true;
return false;
}
入口函数:GlobalDeadLockDetectorMain
/*
* GlobalDeadLockDetectorMain
*/
void
GlobalDeadLockDetectorMain(Datum main_arg)
{
am_global_deadlock_detector = true; // 更新标识
pqsignal(SIGHUP, sigHupHandler); // 注册信号处理函数
/* We're now ready to receive signals */
BackgroundWorkerUnblockSignals(); // 打开信号
/* Connect to our database */
BackgroundWorkerInitializeConnection(DB_FOR_COMMON_ACCESS, NULL, 0); // 连接数据库
/* disable orca here */
extern bool optimizer;
optimizer = false;
GlobalDeadLockDetectorLoop(); // 死锁检测,轮询,下节重点讲解
/* One iteration done, go away */
proc_exit(0);
}