こんにちは、ハブロフスクの住民。 開発者が遭遇する製品のほとんどは、通常、互いに独立して動作する複数のマシンに展開する必要があります。 これは、典型的な問題の1つを引き起こします-異なるサーバー上のデータベースの相違、参照テーブルの識別子の不一致、そしてもちろん、特定のマシンでデータベースを更新する際の不注意とパッチの欠落による不均一性。 場合によっては、これは「列を削除することはありません-列を追加するだけ」などの野生の(私の単純な意見では)概念に変換されます。
その他では、他のサイトからのゴミでベースが完全に詰まってしまい、「単純なマージ」後にエラーが発生します。
そのような状況、批評家に精通し、私が自転車を発明したことを確かに知っている-私はあなたを猫に招待します。
私はいくつかの会社で同様のストレージの原則を満たしましたが、何らかの理由でネットワーク上のその説明が
隠されています。
一般的な概念はオレンジのように単純で、2つの設定が必要です。
1.データベースは、適用されたパッチを知っている必要があります。
2.レコードに関係を作成する場合、識別子の値は決して使用されません。 (計算で得られたものを除く)。
これらの条件に従って、製品はどのマシンでも動作し、客観的に安定した結果が得られます。 はい、2番目の条件は実行できないように思えるかもしれませんが、すべてのデータベースが同じスクリプトで作成されている場合、識別子間の矛盾はすでに異常です。
回線を作成するプロセスでの反復。
「列を作成したのは誰ですか?」
「ここに何を保管すべきですか?」 これらの数字はどこから来たのですか? 少なくともコメントを書いてください!”
「私たちはこれを100年間使用していません。 ここからどこにありますか?」
おなじみですか? 多くの人は、データベースが「そのまま」存在するという事実を公理として受け入れます。 これは、作者のいないヒョードルおじさんからの手紙です。 しかし、これはすべて部分的にそうです。 各変更、列、およびレコードには独自の作成者がいます。 すべての変更はタイムラインで行われます。 git / svnなどのように聞こえますか? 私たちは皆、バージョン管理システムを積極的に使用しており、このアプローチが私たちに与えるすべてのボーナスと素晴らしい友達を作りました。 ここで適用してみましょう。
それでは、実践に移り、プロジェクト内の無数のフォルダーを見てみましょう。
最初に、パッチ情報を含むテーブルを作成します。 ここで、どのパッチが機能したか(名前+タイプ)およびどの結果であるか(結果)を思い出します。
dc.sqlCREATE TABLE IF NOT EXISTS dc ( id INTEGER(11) AUTO_INCREMENT NOT NULL, code VARCHAR(100) NOT NULL, type VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, result VARCHAR(1) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, m_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) );
すべてが希望どおりに機能するように、最も重要なファイルを元の名前で追加します。
start.sh #!/ usr / bin / env bash
ユーザー名= "habr"
パスワード= "habr"
データベース=「mydatabase」
cd共通
mysql --user $ {username} --password = $ {password} -D $ {database} <dc.sql
[$? -eq "1"]; それから
出口$?
fi
エコー ''
echo '>>> TABLES'
エコー ''
cd TABLE
ファイル= *
$ {FILES}のf
する
scriptName = `expr" $ f ": '\([a-z _] * \)' '
var = $(mysql --user $ {username} --password = $ {password} -D $ {database} <<< "tとしてinformation_schema.tablesからカウント(*)を選択します。ここでt.TABLE_NAME = '$ {scriptName } '"-s)
if [$ {var} -ne '1']; それから
echo "$ fファイルを処理しています..."
mysql --user $ {username} --password = $ {password} -D $ {database} <$ {f}
mysql --user $ {username} --password = $ {password} -D $ {database} <<< "INSERT INTO dc(code、type、result)values( '$ {f}'、 'CREATE TABLE'、 「$?」)」
[$? -ne 0]; それから
出口$?
fi
他に
echo '--- Skip' $ {f} '---'
fi
やった
エコー ''
echo '>>> FOREIGN KEYS'
エコー ''
cd ../F_KEY
ファイル= *
$ {FILES}のf
する
scriptName = `expr" $ f ": '\([a-z_A-Z] * \)' '
var = $(mysql --user $ {username} --password = $ {password} -D $ {database} <<< "information_schema.table_constraintsからselect count(*)where t.constraint_name = '$ {scriptName } '"-s)
if [$ {var} -ne '1']; それから
echo "$ fファイルを処理しています..."
mysql --user $ {username} --password = $ {password} -D $ {database} <$ {f}
mysql --user $ {username} --password = $ {password} -D $ {database} <<< "INSERT INTO dc(code、type、result)values( '$ {f}'、 'CREATE FK'、 「$?」)」
[$? -ne 0]; それから
出口$?
fi
他に
echo '--- Skip' $ {f} '---'
fi
やった
エコー ''
エコー ''
echo '>>> LOAD DATA SCRIPTS'
エコー ''
cd ../DATA
ファイル= *
$ {FILES}のf
する
scriptName = `expr" $ f ": '\([a-z0-9] * \)' '
var = $(mysql --user $ {username} --password = $ {password} -D $ {database} <<< "$ {database}からselect count(*).dc as t where t.code = ' $ {f} 'and result =' 0 '"-s)
if [$ {var} -ne '1']; それから
echo "$ fファイルを処理しています..."
mysql --user $ {username} --password = $ {password} -D $ {database} <$ {f}
mysql --user $ {username} --password = $ {password} -D $ {database} <<< "INSERT INTO dc(code、type、result)values( '$ {f}'、 'LOAD DATA'、 「$?」)」
[$? -ne 0]; それから
出口$?
fi
他に
echo '--- Skip' $ {f} '---'
fi
やった
エコー ''
エコー ''
echo '>>>トリガーのロード'
エコー ''
cd ../TRIGGER
ファイル= *
$ {FILES}のf
する
scriptName = `expr" $ f ": '\([a-z_0-9] * \)' '
var = $(mysql --user $ {username} --password = $ {password} -D $ {database} <<< "tとしてinformation_schema.triggersからカウント(*)を選択します。ここでt.trigger_name = '$ {f } '"-s)
if [$ {var} -ne '1']; それから
echo "$ fファイルを処理しています..."
mysql --user $ {username} --password = $ {password} -D $ {database} <$ {f}
mysql --user $ {username} --password = $ {password} -D $ {database} <<< "INSERT INTO dc(code、type、result)values( '$ {f}'、 'LOAD TRIGGER'、 「$?」)」
[$? -ne 0]; それから
出口$?
fi
他に
echo '--- Skip' $ {f} '---'
fi
やった
エコー ''
出口$?
原則は最も明白なものとして採用されました-パッチ名は同じファイル名です。 トリガー、テーブル、および外部キーの場合、そのような名前のオブジェクトの存在は
information_schemaによってチェックされ
ます 。 結果が負の場合、対応するスクリプトが実行されます。
アイデンティティの衝突はありません
カタログをデータで埋めることは、はるかに興味深いです。 / COMMON / DATAディレクトリでgo-scriptsを実行することにより実行されます。
スクリプトを実行するという事実は、テーブルdc(データコンテナー)で修正されています。 すべてが問題なければ-次の実行でファイルをスキップします。
実行はアルファベット順なので、最も一般的なのは名前にタイムスタンプを使用することです。
最も単純な実行では、これらは通常のINSERTクエリであり、自動インクリメントの安定性と予測可能性に大きな期待を寄せています。
しかし、より複雑なデータはどうでしょうか? ネストされたクエリを使用します。
INSERT INTO reso_speed (resoId, popId, speed) VALUES ( (SELECT ht.id FROM human_type ht WHERE ht.name = ''), (SELECT rt.id FROM reso_type rt WHERE rt.name = ''), 30);
または、補助関数を作成します。
go-scriptで次のように記述します。
SELECT installTaskGroup('TEST_GROUP') into @groupId; SELECT installTaskType('TEST_TASK', ' HABR', @groupId, '');
そのため、例としてgit + mysqlを使用して統合データベース管理システムを開発できるアプローチを取得しました。
受け取った利益として:
-作成者+場合によっては、タスクをタスクトラッカーに割り当てます。
-データロールのシーケンス。
-ジェンキンスの新しいプロジェクト。
-落ち着いた神経。
この記事を読んでくださった皆さんに心から感謝します。 ご挨拶と批判を待っています)。 一部の地域の製品は未加工であることに同意しますが、国内のニーズに完全に適合します。
スリッパをあまり投げないでください-これはHabréで公開する最初の試みです(女の子はできます)。