MySQL来自命令行 - 我几乎可以使用LOCK吗?

时间:2009-08-24 20:50:08

标签: mysql bash shell locking

我正在使用mysql命令行程序执行与MySQL数据库交互的bash脚本。我想在我的SQL中使用表锁。我可以这样做吗?

mysql -e "LOCK TABLES mytable"
# do some bash stuff
mysql -u "UNLOCK TABLES"

我问的原因是因为只为会话保留了表锁,所以一旦mysql程序完成就不会释放锁吗?

5 个答案:

答案 0 :(得分:8)

[编辑]

nos 有一个基本的想法 - 只运行一次“mysql”,并且提供的解决方案nos应该可以工作,但它将FIFO保留在磁盘上。

nos 也是正确的,我搞砸了:一个简单的“echo X >FIFO”将关闭FIFO;我记错了。我的(删除)评论w.r.t.时间不适用,抱歉。

那就是说,你不需要FIFO,你可以使用进程间管道。通过我的旧MySQL脚本查看,有些工作类似于此,但你不能让任何命令写入stdout(没有一些“exec”技巧)。

#!/bin/bash
(
  echo "LOCK TABLES mytable READ ;"
  echo "Doing something..." >&2
  echo "describe mytable;" 
  sleep 5
  echo "UNLOCK  tables;" 
) | mysql ${ARGUMENTS}

另一种选择可能是将文件描述符分配给FIFO,然后让它在后台运行。这非常 nos 类似,但“exec”选项不需要子shell来运行bash命令;因此可以让你在“其他东西”中设置“RC”:

#!/bin/bash
# Use the PID ($$) in the FIFO and remove it on exit:
FIFO="/tmp/mysql-pipe.$$"
mkfifo ${FIFO} || exit $?
RC=0

# Tie FD3 to the FIFO (only for writing), then start MySQL in the u
# background with its input from the FIFO:
exec 3<>${FIFO}

mysql ${ARGUMENTS} <${FIFO} &
MYSQL=$!
trap "rm -f ${FIFO};kill -1 ${MYSQL} 2>&-" 0

# Now lock the table...
echo "LOCK TABLES mytable WRITE;" >&3

# ... do your other stuff here, set RC ...
echo "DESCRIBE mytable;" >&3
sleep 5
RC=3
# ...

echo "UNLOCK TABLES;" >&3
exec 3>&-

# You probably wish to sleep for a bit, or wait on ${MYSQL} before you exit
exit ${RC}

请注意,存在一些控制问题:

  • 此代码对于无法锁定(或任何SQL命令)没有错误检查 在“其他东西”)。那肯定非平凡。
  • 由于在第一个例子中,“其他东西”在子shell中,你不能轻易 从该上下文设置脚本的返回码。

答案 1 :(得分:4)

这是一种方式,我确信有一种更简单的方法..

mkfifo /tmp/mysql-pipe
mysql mydb </tmp/mysql-pipe &
(
  echo "LOCK TABLES mytable READ ;" 1>&6 
  echo "Doing something "
  echo "UNLOCK  tables;" 1>&6
) 6> /tmp/mysql-pipe

答案 2 :(得分:2)

我在研究这个问题时发现的一个非常有趣的方法是使用MySQL的SYSTEM命令。我不确定究竟有什么缺点,如果有的话,但它肯定会适用于很多情况:

示例:

mysql <<END_HEREDOC
LOCK TABLES mytable;
SYSTEM /path/to/script.sh
UNLOCK TABLES;
END_HEREDOC

值得注意的是,这只适用于* nix,显然是as does the SYSTEM command

感谢Daniel Kadosh:http://dev.mysql.com/doc/refman/5.5/en/lock-tables.html#c10447

答案 3 :(得分:0)

没有mkfifo命令的另一种方法:

cat <(echo "LOCK TABLES mytable;") <(sleep 3600) | mysql &
LOCK_PID=$!
# BASH STUFF
kill $LOCK_PID

我认为Amr的答案是最简单的。但是我想分享这个,因为其他人可能也需要稍微不同的答案。

sleep 3600暂停输入1小时。您可以在此处找到其他命令以暂停:https://unix.stackexchange.com/questions/42901/how-to-do-nothing-forever-in-an-elegant-way

lock tables SQL立即运行,然后等待睡眠计时器。

答案 4 :(得分:0)

现有答案中的问题和局限性

  • NVRAMnosxer0x的答案

    如果LOCK TABLESUNLOCK TABLES之间的命令都是SQL查询,则应该没问题。 但是,在这种情况下,为什么不简单地构造一个SQL文件并将其通过管道传递到mysql命令呢?

    如果关键部分中除了发出SQL查询外还有其他命令,则可能会遇到麻烦。 将lock语句发送到文件描述符的echo命令不会阻塞,并等待mysql响应。 因此,可以在实际获得锁定之前执行后续命令。无法保证同步。

  • Amr Mostafa回答

    SYSTEM命令在MySQL服务器上执行。因此,要执行的脚本或命令必须存在于同一MySQL服务器上。 您将需要终端访问托管服务器的机器/ VM /容器(或者至少是一种将脚本传输到服务器主机的方法)。 SYSTEM命令also works on Windows as of MySQL 8.0.19,但是在Windows服务器上运行它当然意味着您将运行Windows命令(例如批处理文件或PowerShell脚本)。

修改后的解决方案

以下是基于NVRAM和否的答案的示例解决方案,但需要等待锁定:

#!/bin/bash

# creates named pipes for attaching to stdin and stdout of mysql
mkfifo /tmp/mysql.stdin.pipe /tmp/mysql.stdout.pipe

# unbuffered option to ensure mysql doesn't buffer the output, so we can read immediately
# batch and skip-column-names options are for ease of parsing the output
mysql --unbuffered --batch --skip-column-names $OTHER_MYSQL_OPTIONS < /tmp/mysql.stdin.pipe > /tmp/mysql.stdout.pipe &
PID_MYSQL=$!

# make sure to stop mysql and remove the pipes before leaving
cleanup_proc_pipe() {
    kill $PID_MYSQL
    rm -rf /tmp/mysql.stdin.pipe /tmp/mysql.stdout.pipe
}
trap cleanup_proc_pipe EXIT

# open file descriptors for writing and reading
exec 10>/tmp/mysql.stdin.pipe
exec 11</tmp/mysql.stdout.pipe

# update the cleanup procedure to close the file descriptors
cleanup_fd() {
    exec 10>&-
    exec 11>&-
    cleanup_proc_pipe
}
trap cleanup_fd EXIT

# try to obtain lock with 5 seconds of timeout
echo 'SELECT GET_LOCK("my_lock", 5);' >&10

# read stdout of mysql with 6 seconds of timeout
if ! read -t 6 line <&11; then
    echo "Timeout reading from mysql"
elif [[ $line == 1 ]]; then
    echo "Lock acquired successfully"
    echo "Doing some critical stuff..."
    echo 'DO RELEASE_LOCK("my_lock");' >&10
else
    echo "Timeout waiting for lock"
fi

以上示例使用SELECT GET_LOCK()进入关键部分。它为我们产生输出以分析结果并决定下一步要做什么。 如果您需要执行不会产生输出的语句(例如LOCK TABLESSTART TRANSACTION),则可以在该语句之后执行伪SELECT 1;并以合理的超时时间从标准输出中读取。例如:

# ...
echo 'LOCK TABLES my_table WRITE;' >&10
echo 'SELECT 1;' >&10
if ! read -t 10 line <&11; then
    echo "Timeout reading from mysql"
elif [[ $line == 1 ]]; then
    echo "Table lock acquired"
    # ...
else
    echo "Unexpected output?!"
fi

您可能还想将第三个命名管道附加到mysql的stderr上,以处理不同的错误情况。