2

外部ディスクにデータをコピーするアプリケーションがあります。コピー リクエストは MySQL データベースに保存され、複数のコピー マシンから読み取られます。コピー マシンは、bash スクリプトを実行してリクエストを取得します。リクエストが取得されると、データベースで「進行中」として設定されます。ただし、複数のマシンがリクエストを近くで読み取り、同じデータのコピーを開始する状況を回避しようとしています。

これを行うためにテーブルロックを使用するつもりでしたが、セッションの有効期限が切れるとテーブルロックが期限切れになるため苦労しています。

mysql="mysql -h dbhost -u user -pPassword diskcopydb"
echo "LOCK tables diskcopy WRITE;" | $mysql
echo "SELECT SQL_NO_CACHE * FROM diskcopy WHERE status=\"request\"" | $mysql

リクエストを取得するまでにロックが実際に期限切れになるため、競合状態が持続します。ここに質問があります:

コマンド ラインからの MySQL - 実際に LOCK を使用できますか?

SQL コマンドはブロックにパッケージ化され、MySQL に一緒にパイプされますが、要求を取得するために途中で MySQL から出力を取得する必要があります。誰かがこれのレシピを持っていますか?かなり一般的なユースケースであるべきだと思われます...

4

3 に答える 3

1

不可能は可能です!

>しかし、リクエストを取得するために途中で MySQL から出力を取得する必要があります。誰かがこれのレシピを持っていますか?

これが私のソリューションのデモです:

#!/bin/bash
################################################################################
#                  Bash synchronous client-server                              #
#                              or                                              #
#     Bash parallel processes with synchronized rendezvous points              #
#                              or                                              #
#          Bash interprocess communication with named pipes                    #
################################################################################
#Declare your session 'globals'
args=    #command line arguments are stored here
DIR=     #script's current path is stored here

#Early store your bash args before you lose them
for arg in "$@"; do
   args[i]=$arg
   (( i+=1 ))
done
################################################################################
## Initialize constants                                                       ##
#------------------------------------------------------------------------------#
# Explain more...                                                              #
################################################################################
function init_vars() {
   #absolute path of this script
   DIR=$( cd "$( dirname "$0" )" && pwd )
   #global MYSQL_PASS is defined in ~/.profile
   #this is the file where we store MySQL root password
   if [[ ! $MYSQL_PASS ]]; then
      MYSQL_PASS='.mysqlpass'
   fi
   #do some other stuff here...
}

################################################################################
## Rotate over passed arguments                                               ##
#------------------------------------------------------------------------------#
#Parse array 'args' based on the logic of your command line argument syntax    #
#Say, '--db <database>' denotes the processing of one database, '--db all' of  #
#all databases etc.                                                            #
#Linux utilities have contradicting rules for such kind of argument processing #
#(full of crap!), i.e. see tar: tar -xvf or tar --xfv or tar xvf, mysql -p     #
#<pass> or mysql -p=<pass> or mysql -p'<pass>' (???)                           #
################################################################################
function parse_args() {
   #your logic goes here
}

################################################################################
## Opens a MySQL client session                                               ##
#------------------------------------------------------------------------------#
# For security reasons password is kept in a file given by MYSQL_PASS.         #
################################################################################
function mysql_client() {
   mysql --host=127.0.0.1 --port=3306 --default-character-set=utf8 -u root -p"$( cd $DIR; cat $MYSQL_PASS )"
}

################################################################################
## A job dispatcher                                                           ##
#------------------------------------------------------------------------------#
# This thread initiates the workers and also reads intermediate results from   #
# the spawned jobs. We don't want to parallelize the whole part of a job but   #
# rather to put some parts to run in parallel with the dispatcher and some     #
# parts in a sequential order determined by what we call rendezvous points.    #
# See the diagram: A, B, C, D, E and F are events or actions during the life of#
# a program.                                                                   #
#                                                                              #
#  dispatcher                                                                  #
#     |         start                                                          #
#     A     ---------------->    worker                                        #
#     |                            |                                           #
#     B (read blocks)              C                                           #
#     |                   signals  |                                           #
#     B (unblocks)<--------------- D (write blocks)                            #
#     |                            |                                           #
#     E (1st set of results)       F                                           #
#     |                            |                                           #
# We can only guarantee that E comes after D or, D->E in time but E->F or F->E #
# and B->C or C->B (we don't care)                                             #
################################################################################
function dispatcher() {
   #go to the directory of this script
   cd $DIR
   #make a temporary directory secured in 'time & space'
   TMPDIR=$( mktemp -d XXXXXXXXXX )
   #catch exit of this script and delete temporary folder
   trap 'cd $DIR && rm -rf "$TMPDIR"' EXIT
   #move into the temporary folder
   cd ${TMPDIR}
   #create named pipes-global to all functions of this script
   TMPSQL=mysql-$RANDOM.$RANDOM.$RANDOM.$$
   TMPSCRIPT=myscript-$RANDOM.$RANDOM.$RANDOM.$$
   mkfifo $TMPSQL
   mkfifo $TMPSCRIPT
   #exec 3<> ${DIR}/${TMPDIR}/$TMPSQL || (echo 'error'; exit 1)
   #exec 4<> ${DIR}/${TMPDIR}/$TMPSCRIPT || ( echo 'error'; exit 1)
   echo '===1.PARENT=== Starting background jobs...'
   echo 'We can guarantee the succession only of 2->3,4 and 5->6,7 but NOT 3->4 or 6->7!'
   worker &
   ##################################
   cat $TMPSQL #make a blocking read!
   ##################################
   echo '===4.PARENT=== ...1st query has been read'
   ##################################
   cat $TMPSQL #make a blocking read!
   ##################################
   echo '===7.PARENT=== ...2nd query has been read'
   #don't you dare to exit!
   ##################################
   cat $TMPSQL #make a blocking read!
   ##################################
}

################################################################################
## A job spawned by the dispatcher                                            ##
#------------------------------------------------------------------------------#
################################################################################
function worker() {
   echo "===2.CHILD=== Executing 1st query......writing to $TMPSQL"
   #the dash symbol '-' makes tabs at the beginning of line to be
   #ignored inside the here-doc but improves formation!
   mysql_client <<- QUERY
\! tee $TMPSQL
use test;
select * from customers;
\! echo '===3.CHILD=== End 1st query'
#####################################
#\. $TMPSCRIPT
#####################################
QUERY

   echo "===5.CHILD=== Executing 2nd query......writing to $TMPSQL"
   mysql_client <<- QUERY
\! tee $TMPSQL
use test;
select * from cars;
\! echo '===6.CHILD=== End 2nd query'
#####################################
#\. $TMPSCRIPT
#####################################
QUERY

   echo '===8.CHILD=== End of background jobs'
   echo > $TMPSQL
}

   #parse command line arguments
   parse_args

   #initialize variables
   init_vars

   #call dispatcher
   dispatcher

「テスト」データベースがあると仮定すると、次の出力が表示されます。

===1.PARENT=== Starting background jobs...
We can guarantee the succession only of 2->3,4 and 5->6,7 but NOT 3->4 or 6->7!
===2.CHILD=== Executing 1st query......writing to mysql-30627.1495.5394.7533
===4.PARENT=== ...1st query has been read
id_customer firstname   secondname
1   John    Pincolo
2   Mark    Denonto
3   Ann Curtis
4   Jeny    Wirth
===3.CHILD=== End 1st query
===5.CHILD=== Executing 2nd query......writing to mysql-30627.1495.5394.7533
===7.PARENT=== ...2nd query has been read
id_car  type    plate   date_rent   date_returned
1   fiat    BG-457  2012-07-18 00:00:00 2012-07-20 00:00:00
2   renault AS-1234 2012-07-20 00:00:00 2012-07-25 00:00:00
3   fiat    JYB-2856    2012-06-23 00:00:00 2012-06-24 00:00:00
===6.CHILD=== End 2nd query
===8.CHILD=== End of background jobs

ご覧のとおり、2 から 5 の間には、最初のデータベースの結果セットがあります。クエリと「メイン」プログラムの間の中間結果が必要でした。

「ねえ、データベース ロックを取得したいのですが、私の「メイン」プログラムが何か他のことをしている間、フリーズという点でセッションを停止してください!」まあ、答えもあります。関数 'worker' には 2 つのコメント付きコマンドがあります。

#####################################
#\. $TMPSCRIPT
#####################################

それらを有効にすると、実行がハングします!!! それでおしまい!これで、ディスパッチャで作業を開始できます。完了したら、次のように「エコー」を送信します。

echo > $TMPSCRIPT

このコード行をワーカーに追加することで、ディスパッチャーとワーカーの両方をブロック スレッドに変換しました。これで、両方が相互にリッスンします。概略的に:

  dispatcher
     |         start
     A     ---------------->    worker
     |                            |
     B (read blocks)              C
     |                   signals  |
     B (unblocks)<--------------- D (write blocks)
     |                            |
     E (1st set of results)       F
     |                            |
     |                            G (read blocks)
     |  signals                   |
     H ---------------->          G (unblocks)
     | (write blocks)             |

もっと興奮が必要なら、送信はどうですか

echo 'set @x=@x+1;' > $TMPSCRIPT

'@x' はユーザー定義変数です! ここには何がありますか?さて、SQL ジョブにライブ コードを「注入」しただけです。素晴らしい以上!!!

これで、データベースの LOCK を取得し、論理バックアップを作成し、mysql クライアントを停止して続行し、データベースの LOCK がまだアクティブな間、好きなことを行うことができます!

バイナリ バックアップの作成についてはどうでしょうか。(年間 2,000 ~ 10,000 米ドル節約できました。https ://shop.oracle.com を参照してください)

10 個のデータベースをロックしてバックアップを開始できます。最初に完了したデータベースはディスパッチャに確認を送信し、ディスパッチャは空になるまでリストに別のデータベースを追加します。すべてのワーカーが双方向ロックのために 2 つのロックを必要とすることだけを覚えておいてください。

3 つのディスパッチャを同時に開始することもできます。それらの名前付きパイプは空間内で一意であるため、それらの名前付きパイプの衝突に直面することは不可能だからです。

それが私の2,000ドルのアドバイスです

甘い!

(百人隊長)

于 2012-11-27T23:38:40.417 に答える
1

この質問は実際にここで答えられます:

bash からの MySQL 関連テーブルの一括挿入

言い換えると、MySQL との双方向通信は Bash では扱いにくく、接続を開いて保持できる Perl や Python などの「適切な」プログラミング言語を使用する方が簡単です。

于 2012-09-18T08:37:35.403 に答える
0

同じ接続で SQL を実行します。

mysql="mysql -h dbhost -u user -pPassword diskcopydb"
echo "LOCK tables diskcopy WRITE;SELECT SQL_NO_CACHE * FROM diskcopy WHERE status=\"request\"" | $mysql

それ以外の場合、ロックは、mysql を 2 回目に実行したときにすでに死んでいるセッション用です。

于 2012-09-17T14:29:36.173 に答える