【问题标题】:MySQL from the command line - can I practically use LOCKs?命令行中的 MySQL - 我实际上可以使用 LOCK 吗?
【发布时间】:2010-11-22 10:06:03
【问题描述】:

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

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

我问的原因,是因为表锁只为会话保留,所以不会在mysql程序完成后立即释放锁吗?

【问题讨论】:

    标签: mysql bash shell locking


    【解决方案1】:

    现有答案中的问题和限制

    • NVRAMnosxer0x 的回答

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

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

    • Amr Mostafa回答

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

    修改后的解决方案

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

    #!/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 以处理不同的错误情况。

    【讨论】:

      【解决方案2】:

      没有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 立即运行,然后它将等待睡眠计时器。

      【讨论】:

        【解决方案3】:

        我在自己研究这个问题时发现的一个非常有趣的方法是使用 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

        【讨论】:

          【解决方案4】:

          [编辑]

          nos有基本的想法——只运行“mysql”一次,提供的解决方案nos应该可以工作,但是它把FIFO留在了磁盘上。

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

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

          #!/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或其他技巧,如果你做 echo ... >${FIFO} mysql 将在 1. 命令之后退出,因为写入结束关闭管道
          • 你说得对,我的脚本无法运行,而你的脚本应该运行(但它会将 FIFO 留在磁盘上)。
          【解决方案5】:

          这是一种方法,但我相信还有一种更简单的方法..

          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
          

          【讨论】:

          • 这不会按预期工作。 echo "LOCK TABLES mytable READ ;" 1&gt;&amp;6 是非阻塞的。所以echo "Doing something " 将在它之后执行,无论是否获得锁。
          猜你喜欢
          • 1970-01-01
          • 2011-01-23
          • 2013-07-14
          • 1970-01-01
          • 2021-04-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多