web-dev-qa-db-ja.com

MyISAMテーブルは、MySQLの実行中にコピーするとロックされますか? (免責事項を読む)

免責事項:これは想定されていないことは承知していますが、テーブル間の時間の整合性はここではコンサートではありません。より堅牢でスケジュールされたバックアップを補完するバックアップを要求します。

MyISAMテーブルファイル(.frm、.MYD、MYI)をコピーしてトランザクションを取得している間に読み取りの問題が発生するかどうかを知りたいと思います。

ほとんどのテーブルは小さいので、リスクを冒すだけで十分ですが、サイズが気になるので心配になるカップルがいくつかあります。トランザクションが完了するのを待ってもかまいません。心配なのは、読み取りエラーが発生し、クエリからの応答が得られないことです。

それはそれについてです、あなたが何かを知っているなら私に知らせてください

5
Hito_kun

@RolandoMySQLDBAへの+1はもちろん、別の勇敢な答えです。しかし、あなたの質問のポイントにもっと:

... MyISAMテーブルファイル(.frm、.MYD、MYI)をコピーし、[書き込み]トランザクションを取得している間、読み取りの問題が発生します。

はい。

書き込みを防ぐために何らかのロックを行わない限り、単一のMyISAMテーブルでも一貫したバックアップを取得することはできません。 Rolandoは、ロックに使用できるオプションを使用してかなり完全な答えを出しました。

MyISAMデータのバックアップに使用するもう1つのオプションは、LVMスナップショットです。これを支援する優れたツールについては、 http://www.lenzg.net/mylvmbackup/ を参照してください。

最後の推奨事項は、MyISAMの使用を停止し、代わりにInnoDBを使用することです。次に、 Percona XtraBackup を使用して、高速で非ロックの物理バックアップを実行できます。


あなたのコメントを再:

大きなファイルを読み取るのは、瞬時的でもアトミックでもないためです。バックアップがテーブルを介して進行している間、他の同時更新により、バックアップが既に読み取った行と、バックアップがまだ到達していない行の両方が変更される可能性があります。

トランザクションの動作についてテキストの例を見てみましょう。私は自分の銀行口座から引き落とし、あなたの銀行口座に入金することによって銀行振込を行います。私の銀行口座はファイルの物理的に早い行に保存されており、あなたの銀行口座はファイルの後の行に保存されています。

これが行われている間、バックアップはファイル全体を読み取っており、トランザクションが発生した時点で途中まで読み取っています。復元すると、借方が適用されていないoriginalのアカウント残高が得られます。戻ることはできません)。ただし、残高にはupdatedのアカウント残高が含まれています。これは、残高を増やした後、バックアップがファイルのそのポイントに到達したためです。

エルゴ、無料のお金! ;-)

MyISAMは、読み取りを行っている間、更新に対してテーブルをロックすることを要求することで、これを解決します。

InnoDBは、データベースの一貫したビューのために読み取りトランザクションがそれらを表示する必要がある限り、行の複数のバージョンを保持することによってこれを解決します。そのため、バックアップが進行中でも、誰もが待たずにデータを更新できます。

Percona XtraBackupはこれをわずかに異なる方法で解決します。ある方法で逆方向にできます。データファイルを読み取っている間、トランザクションログを継続的にチェックして、含める必要がある最新の変更がないかどうかを確認します。これらの変更は、Percona XtraBackupがすでに読み取ったデータファイルの一部に適用される場合があります。ただし、データファイルと、バックアップの開始以降に記録された変更を取得している限り、データベース全体を再構築できます。

ただし、これは、信頼性の高いトランザクションログを作成するInnoDBなどのストレージエンジンでのみ機能します。 Percona XtraBackupはMyISAMもバックアップできますが、他のバックアップツールと同様に、ロックを使用するだけです。

5
Bill Karwin

MyISAMの主な弱点のため、.MYDおよび.MYIのコピーで問題が発生する場合があります。データの変更(.MYDファイルへの変更)がOSにキャッシュされます。 (もちろん、これはWindowsでMySQLを使用すると100倍悪くなるため、Windowsはこの回答から除外します)。

MySQL 5.6を使用している場合、ちょっと良いニュースがあります。

コマンドFLUSH TABLES WITH READ LOCKは、すべてのMyISAMテーブルを単一のロックの下で調べます。 MySQL 5.6の構文はFLUSH TABLES db.tb,db.tb ... WITH READ LOCKになりました。

MySQL 5.6ドキュメント は言う

FLUSH TABLES tbl_name [、tbl_name] ...読み取りロックあり

このステートメントは、指定されたテーブルの読み取りロックをフラッシュして取得します。ステートメントは最初にテーブルの排他的メタデータロックを取得するため、これらのテーブルを開いているトランザクションが完了するのを待ちます。次に、ステートメントはテーブルキャッシュからテーブルをフラッシュし、テーブルを再度開き、テーブルロック(LOCK TABLES ... READなど)を取得し、メタデータロックを排他的から共有にダウングレードします。ステートメントがロックを取得してメタデータロックをダウングレードした後、他のセッションはテーブルを読み取ることはできますが、変更することはできません。

このステートメントはテーブルロックを取得するため、FLUSHステートメントを使用するために必要なRELOAD特権に加えて、各テーブルに対するLOCK TABLES特権が必要です。

このステートメントは、既存のベーステーブルにのみ適用されます。名前がベーステーブルを参照している場合は、そのテーブルが使用されます。 TEMPORARYテーブルを参照する場合は無視されます。名前がビューに適用される場合、ER_WRONG_OBJECTエラーが発生します。そうしないと、ER_NO_SUCH_TABLEエラーが発生します。

UNLOCK TABLESを使用してロックを解除し、LOCK TABLESを使用してロックを解除して他のロックを取得するか、START TRANSACTIONを使用してロックを解除して新しいトランザクションを開始します。

このFLUSHのバリアントにより、1回の操作でテーブルをフラッシュおよびロックできます。これは、アクティブなLOCK TABLES ... READがある場合、FLUSH TABLESが許可されないというMySQL 5.6の制限に対する回避策を提供します。

このステートメントは暗黙的なUNLOCK TABLESを実行しないため、アクティブなLOCK TABLESがあるときにこのステートメントを使用するか、最初に取得したロックを解放せずに2回使用するとエラーが発生します。

フラッシュされたテーブルがHANDLERで開かれた場合、ハンドラーは暗黙的にフラッシュされ、その位置を失います。

[〜#〜]警告[〜#〜]:InnoDBの人々のために、同じドキュメントにシステム内でメタデータロックを含むFLUSH TABLES tbl_name [, tbl_name] ... FOR EXPORTがありますテーブル。

データベース内のすべてのMyISAMテーブルをロックし、そのDB接続をスリープさせ、OSコピーを実行し、ロックを解放するスクリプトを作成できます。

4019のパーティションを持つMyISAMテーブルで作業しているので、これは私が今やっていることです。これにより、.MYDsのすべてが強制的にフラッシュされます。このようなロックを発行して接続をスリープ状態にすると、MYDとMYIを他のデータセンターにFTPで転送できます。

Percona Server 5.6を使用している場合、良いニュースがあります。 Perconaが考案したばかり バックアップ用のロックテーブル 。これにより、テーブルの一貫性にある程度の信頼を置いて、ロックテーブルから推測作業と不快な作業を取り除きます。

http://www.percona.com/doc/percona-server/5.6/management/backup_locks.html

あなたの実際の質問

データベースごとにMyISAMのロックをスクリプト化するように努力する必要があります。

バックアップの前にデータベース内のすべてのMyISAMテーブルにロックを作成するために使用するスクリプトは次のとおりです

MYSQL_HOSTADDR=${1}
DB_SUFFIX=${2}
RUNLOG=${3}

MYSQL_DATABASE=DB_${DB_SUFFIX}

MYSQL_CONN="-h${MYSQL_HOSTADDR} -D${MYSQL_DATABASE}"

PRODUCT_LIST="prod1 prod2 prod3"

#
# Acquire Locks
#
for PRODUCT in `echo "${PRODUCT_LIST}"`
do
        PREFIX=sum_${PRODUCT}
        LOCK_SLEEP_KILL_SCRIPT=/tmp/Kill_Summary_Table_Locks_For_${PRODUCT}_${DB_SUFFIX}.sql
        SQL="SELECT GROUP_CONCAT(table_name) FROM information_schema.tables"
        SQL="${SQL} WHERE table_schema=DATABASE() AND table_name LIKE '${PREFIX}%'"
        TABLES_TO_LOCK=`mysql -h${MYSQL_HOSTADDR} -D${MYSQL_DATABASE} -ANe"${SQL}"`
        LOCK_PATTERN="LockProcessFor${PRODUCT}"
        FLUSH_COMMAND="FLUSH TABLES ${TABLES_TO_LOCK} WITH READ LOCK; SELECT SLEEP(86400) INTO @${LOCK_PATTERN}"
        mysql ${MYSQL_CONN} -ANe"${FLUSH_COMMAND}" &
        SQL="SELECT MAX(id) FROM (SELECT 0 id UNION"
        SQL="${SQL} SELECT id FROM information_schema.processlist"
        SQL="${SQL} WHERE LOCATE('${LOCK_PATTERN}',Info)>0"
        SQL="${SQL} AND id <> CONNECTION_ID()) A"
        SLEEP_ID=0
        while [ ${SLEEP_ID} -eq 0 ]
        do
                sleep 5
                SLEEP_ID=`mysql -h${MYSQL_HOSTADDR} -ANe"${SQL}"`
        done
        echo "KILL QUERY ${SLEEP_ID};" > ${LOCK_SLEEP_KILL_SCRIPT}
done

バックアップ後にこれらのロックを解除するために使用するスクリプトは次のとおりです

MYSQL_HOSTADDR=${1}

cd /tmp
ls Kill_Summary_Table_Locks_For*.sql > KillScriptList.txt
for KILLSQL in `cat KillScriptList.txt` ; do mysql -h${MYSQL_HOSTADDR} < ${KILLSQL} ; rm -f ${KILLSQL}; done

試してみる !!!

4
RolandoMySQLDBA