(在下边之前会将 plan 和 status 信息写入某个row key,之后每次更新status 所以不同sql 应该用不同 rowkey 避免并发引起的冲突)
首先看看创建partition的过程 4步
step0.检查表在不在,可以通过元数据
step1.创建pangu (创建前会检查下meta存在否,但由于pt创建链路较长,所以这里即使meta不存在到 下面那步2的时候 还是可能存在的)
a check下有没有 partition的pangu目录(注意:即使分区不存在,很多业务场景partition的pangu目录也会存在的,eg 跨集群复制,复制完后去掉某个集群,critical问题也会遗留)
b 如果不存在则新建一个 pt dir,如果存在 先backup那个已经存在的 在rollback的时候恢复(mv origindir backdir_timestamp),在新建一个 pt dir
step2 再次checkmeta记录,在这里会检查pt meta在不在 不在则继续写meta记录,在的话则做rollback
step3.记录一些回滚log
step4.发送event 通知其他机制,如 跨集群复制等
坑,如何丢数据的,场景重现
hive ddl 内三个线程,A 创建分区 B也创建分区 C对该分区写数据(通过TT 或 insert等)
时间点1. 在A走完 step1后未到step2,此时B也可以通过step0 进入step1创建Adir的过程
时间点2. A走完了step2 写完了meta数据,此时B走完step1即开始backup Adir目录再创建自己的目录Bdir,但还未开始step2的写meta。 此时线程C出场写数据,先验证了meta在因为meta已由A线程写完,再对线程Bdir创建的目录开始写数据 假设写了两行。(也有可能C出场的时候,B只是经过step1验证还没有创建自己的Bdir,2条数据写入Adir,之后Adir 被backup了,如果B线程之后即使正常完成,也会丢掉写进Adir的数据)
时间点3. B开始进入step2 发现meta已经有了,于是开始回滚(删除自己建的pt dir,mv backup目录回来),此时C之前写的两行数据就丢了,而C可以因看到ptdir存在 而继续写数据。
复现完毕
如何解决问题
在写pangu 目前前 利用ots 或zk 或第三方库 加锁 (加锁 加的是 ddltask 即 对数据目录的操作和ddl操作 将这两个操作绑定成 原子操作)
我们引入对每个表 的pangu的具体操作 record化,导入ots 同一行的一个cell,记录 操作类型 和 时间戳 ,还有一个是否 开始ddltask了的flag
这样可以对sql task进行管理,eg
1.对同一pangu目录的操作record ,可以对ddl task加锁,使ddl 操作(数据(目录) + 元数据)原子化
2. 可以分析 sql 类型,如果 先是一个insert into 再是一个 overwrite ,判断到有overwrite 之后,直接可以取消之前的into 操作,如果有drop操作,便可以先取消overwrite操作
引入 ddl task 服务化 收拢接口
注:一个insert into 会先做mr job 将数据写到一个临时文件, 然后再做 ddl task (将数据放到 表对应的目录,还有写meta数据)