概要描述
本文主要介绍,分布式KUNDB下,如何保证元数据操作,杀掉前置事务。
场景构造
session1:
KUNDB对DML默认是自动提交的,因此每条DML语句都是独立事务,当语句执行完,元数据锁就释放了,这里通过begin
显式开启事务,让select
语句执行完后,事务依然存在
create table lkw.t1 (id int primary key auto_increment);
begin;
select * from lkw.t1;
session2:
另启动一个会话2,执行下面DDL语句,可以发现其被阻塞(会话迟迟不返回):
DDL在执行前会隐式提交事务并释放元数据锁,这就是为什么要另一个会话发起DDL。
alter table lkw.t1 add name varchar(16);
session3:
启动会话3,执行show processlist;
或者 select * from information_schema.processlist where command <>'Sleep' order by time desc;
命令,即可看到会话2在等待元数据锁(Waiting for table metadata lock
):
解决方案
KUNDB提供了performance_schema.metadata_locks
用来查询具体元数据锁信息,且默认就打开了元数据锁的信息收集,直接查询即可。表中包含了持有,等待及其他中间状态的MDL数据,当锁释放时,会从表中删除。
通过如下 sql 构造kill命令
select
concat('KILL ',PROCESSLIST_ID,';') as kill_sql
from performance_schema.threads
where thread_id in(select OWNER_THREAD_ID from performance_schema.metadata_locks)
and PROCESSLIST_COMMAND="Sleep";
复制出来,准备在每个shard master 底层执行
通过kungate页面,获取每个shard的master节点(鼠标悬停到MASTER的103节点可以看到hostname)
进入到对应shard1的pod内,通过socket连接mysqld,执行kill命令
如果有多个shard,继续执行上述操作(确认每个shardN的shard master的hostname,进入kuntablet-shardN pod,执行sql)
操作执行完之后,name列被成功添加,session1的事务会被杀掉。
FAQ
进一步的,我们可以在容器映射的本地目录(比如 /mnt/disk1/kundb3/shard1/kundbdata/,对应容器内的/vdir/mnt/disk1/kundb3/shard1/kundbdata ),部署如下脚本,后面遇到该问题,进入每个shard master的 pod内 执行该脚本即可。
可以在每个shard的本地化目录都部署一份,防止因shard master切换导致需要再次部署
亦或者走直连底层的方式 在外面执行
#!/bin/bash
# MySQL socket file path
SOCKET="/vdir/mnt/disk1/kundb3/shard1/kundbdata/mysql.sock"
# Step 1: Generate the KILL SQL statements
KILL_SQL=$(mysql -S "$SOCKET" -N -e "
SELECT CONCAT('KILL ', PROCESSLIST_ID, ';') AS kill_sql
FROM performance_schema.threads
WHERE thread_id IN (
SELECT OWNER_THREAD_ID
FROM performance_schema.metadata_locks
) AND PROCESSLIST_COMMAND='Sleep';
")
# Step 2: Execute the generated KILL SQL statements
if [ -n "$KILL_SQL" ]; then
echo "Executing KILL statements:"
echo "$KILL_SQL" | mysql -S "$SOCKET"
echo "KILLED SUCCESS !"
else
echo "No KILL statements generated."
fi