2012-03-21 45 views
6

私はビジネスのためのウェブベースのフォームを友人に手伝っています。私はそれを複数のユーザーを処理する準備をしようとしています。レコードを編集用に表示する直前に、次のコードでレコードをロックするように設定しました。PHPを使ったmysql行のロック

$query = "START TRANSACTION;"; 
mysql_query($query); 
$query = "SELECT field FROM table WHERE ID = \"$value\" FOR UPDATE;"; 
mysql_query($query); 

(大丈夫それが大幅に簡素化されますが、それは、MySQLの本質である)

それは動作しているように表示されません。私は、同じユーザーでログイン、コマンドラインからMySQLへ直接行くと

START TRANSACTION; 
SELECT field FROM table WHERE ID = "40" FOR UPDATE; 

を実行したときしかし、私は効果的に、レコード「40」にアクセスしてからWebフォームをブロックし、タイムアウトの警告を得ることができます。

START TRANSACTIONの代わりにBEGINを使用しようとしました。私はSET AUTOCOMMIT = 0を最初にやろうとし、ロック後にトランザクションを開始しようとしましたが、PHPコードから行をロックできないようです。コマンドラインから行をロックすることができるので、データベースの設定方法に問題はないと思います。私は私の読書に欠けているシンプルなものがあることを本当に望んでいます。

私はApache 2.2.14、MySQL 5.1.41、PHP 5.3.1を持つXAMPPバージョン1.7.3を開発中です。

ありがとうございます。これは私の初めての投稿ですが、私は過去にこのサイトからたくさんの知識を集めました。

+0

2番目のmysql_query($ query)の後に何をしましたか? ?あなたのPHPスクリプトが終了したら、ロックを解放してください – PasteBT

答えて

0

このため使用PDO(と、すべてのデータベース操作):

$value = 40; 

try { 
    $dbh = new PDO("mysql:host=localhost;dbname=dbname", 'username', 'password'); 
} catch (PDOException $e) { 
    die('Could not connect to database.'); 
} 

$dbh->beginTransaction(); 

try { 
    $stm = $dbh->prepare("SELECT field FROM table WHERE ID = ? FOR UPDATE"); 
    $stm->execute(array($value)); 
    $dbh->commit(); 
} catch (PDOException $e) { 
    $dbh->rollBack(); 
} 

あなたは時代遅れのmysql_を使用する必要がある場合*関数は、あなたのような何かをすることができます:

mysql_query('SET AUTOCOMMIT=0'); 
mysql_query('START TRANSACTION'); 
mysql_query($sql); 
mysql_query('SET AUTOCOMMIT=1'); 
+2

沈黙の中でdownvoteをしないでください。より良い答えを得るためにコメントを残してください。 – webbiedave

+2

私はこの提案に問題は見られませんが、それは前向きな考えです! – Paul

0

問題は、MySQLのコマンドです。あなたはこの

http://nz.php.net/manual/en/class.mysqli.php

やPDOのためのmysqliのを使用することができます。ここで説明:

PHP + MySQL transactions examples

HTH

+0

私はwebbiedaveに同意します。なぜあなたがそれをしたのか、改善する方法を教えてください。それがあなた、私、そしてコミュニティに役立つでしょう。ありがとう! – Andreas

0

それは、SQL-注射などを可能に間違いを犯すのは簡単だ、と同様に、それはいくつかの機能が欠けているので、あなたは、MySQL APIを使用しないでください。私はトランザクションがこれらの1つだと思う。もし私が間違っていなければ、すべてのクエリはより大きな文脈ではなく "それ自身で"送られるからだ。

しかし、解決策はいくつかの他のAPIを使用することです、私はmysqlと似ているのでmysqliが好きで広くサポートされています。代わりにmysqliを使用するようにコードを簡単に書き換えることができます。

トランザクション機能では、自動コミットをfalseに設定し、必要なときに自分自身をコミットします。これは、トランザクションの開始および停止と同じです。

の基準一見のために:

http://www.php.net/manual/en/mysqli.autocommit.php

http://www.php.net/manual/en/mysqli.commit.php

9

問題は、あなたのコードの構文が、あなたがそれを使用しようとしている方法ではありません。

レコードは、私は次のコードを、私はあなたが選択していることを想定しています。このことから

を持つレコードをロックしています編集用に表示される直前に行を「ロック」、その後にその編集ページを表示変更をサブミットすると、テーブルを保存してロックを解除します。ここに基本的な問題があります。ページのロードが完了すると、PHPは終了してMySQL接続を閉じます。これが起こると、すべてのロックがすぐに解放されます。これは、コンソールがPHPとは異なる動作をしているように見える理由です。コンソールの同等の機能は、プログラムを終了することです。

長期間編集するためにテーブル行をロックすることはできません。これは彼らのデザインではありません。編集のためにレコードをロックするには、これらのロックを別のテーブルで追跡する必要があります。 "edit_locks"という新しいテーブルを作成し、ロックされているレコードID、ユーザIDの編集、ロックされた時間を保存します。編集のためにレコードを開く場合は、edit_locksテーブル全体をロックして、そのレコードが他の誰かによってロックされているかどうかを調べます。そうでない場合は、ロック・レコードを挿入し、ロックされている場合はロックされたエラーを表示します。ユーザーが保存または取り消すと、edit_locksからロックレコードが削除されます。簡単にしたい場合は、プログラムでいつでもこのテーブルをロックすることができます。これは競争状態を避けるのに役立ちます。

もう1つ問題が発生する可能性があります。ユーザーが編集用のレコードを開き、保存またはキャンセルせずにブラウザを閉じた場合、編集ロックは永遠にそこにとどまります。これがロックされた時間を店に記憶していた理由です。エディタ自体は2分ぐらいごとにAJAXコールを行い、「まだロックが必要です!」と言わなければなりません。 PHPプログラムがこの "再ロック"要求を受け取ると、ロックを検索し、タイムスタンプを現在のものに更新する必要があります。このようにして、ロックのタイムスタンプは常に2分以内に更新されます。また古い古いロックを削除する別のプログラムを作成する必要があります。これは数分ごとにcronジョブで実行する必要があります。 5分以上経過したタイムスタンプのロックを検索して削除する必要があります。タイムスタンプがそれよりも古い場合は、エディタが2分以内にどのように閉じるか、タイムスタンプが最新のものになっていることが明らかです。

他にも言及したように、mysqliを使用するようにしてください。これは "MySQL Improved"の略で、古いインターフェイスの代わりです。

+0

この方法にはいくつかの利点がありますが、いくつかの欠点もあります。ロック/アンロックテーブルのクリーンアップが必要になります。これは、クラッシュや予期せぬ障害によって引き起こされます。私は個人的にそれを使用しませんでした。 – Andreas

+0

Okay JMack、あなたはあなたの答えを今まで拡張して、2番目の部分の短所を説明しました。返信+1 – Andreas

+0

@Andreas私の説明には、ロック・テーブルをクリーンアップしてブラウザのクラッシュが数分以内にレコードのロックアウトを引き起こすようにする方法が含まれています。 –

0

これは古い議論ですが、おそらく人々はそれに続いています。私はJMackと似た方法を使いますが、行ロックしたいテーブルにロック情報を含めてください。私の新しい列はLockTimeとLockedByです。ロックを試みるために、私が行います

UPDATE table 
SET LockTime='$now',LockedBy='$Userid' 
WHERE Key='$value' AND (LockTime IS NULL OR LockTime<'$past' OR LockedBy='$Userid') 

これが失敗した場合、他の誰かがロックを持っている

($の過去が4分前です)。私は明示的に次のようにロックを解除するか、私のロックが期限切れにさせることができます:

UPDATE table 
SET LockTime=NULL,LockedBy='' 
WHERE Key='$value' AND LockedBy='$Userid' 

cronジョブは、古いロックを削除することもできますが、それは本当に必要はありません。