Python调用subprocess.Popen执行MySQL迁移时wait方法长期阻塞无法退出
问题修复方案
问题根源
- 进程等待顺序错误:当前代码先调用
mysql.communicate()再等待mysqldump退出,管道上下游等待顺序颠倒,导致下游mysql进程一直等待上游输入,无法正常退出。 - 管道文件描述符未正确关闭:mysqldump执行完成输出所有内容后,中间sed进程的stdin没有收到EOF,sed不会主动退出,进而mysql的stdin也一直处于打开状态,mysql进程会永久等待输入,触发死锁。
- 强制中断触发的SQL语法报错是终止时mysql接收到了不完整的SQL片段导致的,不属于代码逻辑本身的问题。
修复代码
import sys import os from subprocess import Popen, PIPE, STDOUT import shlex # Transfer Database print('Transferring from ' + mysql_source_database) # 改用shlex.split避免账号、库名含特殊字符时拆分错误 mysql = Popen(shlex.split(f"mysql -h {mysql_dest_host} -P 3306 -u {mysql_dest_username} -p{mysql_dest_pw} {mysql_dest_database}"), stdin=PIPE, stdout=PIPE, stderr=STDOUT) dbnamerewrite = Popen(shlex.split(f"sed s/{mysql_source_database}/{mysql_dest_database}/g"), stdin=PIPE, stdout=mysql.stdin) mysqldump = Popen(shlex.split(f"mysqldump --set-gtid-purged=OFF --column-statistics=0 -h {mysql_source_host} -P 3306 -u {mysql_source_username} -p{mysql_source_pw} {mysql_source_database}"), stdout=dbnamerewrite.stdin) # 按上游到下游顺序等待进程退出,主动关闭对应管道避免死锁 mysqldump.wait() dbnamerewrite.stdin.close() dbnamerewrite.wait() mysql.stdin.close() mysql_stdout = mysql.communicate()[0] # 可选:增加退出码检查判断执行结果 if mysqldump.returncode != 0: print(f"mysqldump执行失败,退出码:{mysqldump.returncode}") if dbnamerewrite.returncode !=0: print(f"sed执行失败,退出码:{dbnamerewrite.returncode}") if mysql.returncode !=0: print(f"mysql导入失败,退出码:{mysql.returncode},输出:{mysql_stdout.decode('utf-8', errors='ignore')}")
修复逻辑说明
- 调整等待顺序:先等最上游的mysqldump执行完成,确认所有备份内容已经输出到sed的输入管道
- 主动关闭管道:mysqldump退出后主动关闭sed的stdin,让sed收到EOF后处理完剩余内容退出,之后关闭mysql的stdin,让mysql收到EOF后开始执行所有导入逻辑
- 最后调用mysql的communicate读取所有输出,等待mysql进程正常退出
内容的提问来源于stack exchange,提问作者Krobar
相关产品推荐
相关产品推荐

