PHPとMySQLの間で奇妙なクエリタイムアウトが発生する原因は何ですか?


11

私は、さまざまな顧客が使用するSoftware-as-a-Serviceアプリケーションの上級開発者です。私たちのソフトウェアは、MySQLバックエンドを搭載したApache / PHPアプリケーションサーバーのクラスター上で実行されます。上の1つのソフトウェアの特定のインスタンス、カテゴリ名のリストを照会するためのPHPコードがタイムアウトしている顧客が29個のを超えるカテゴリがある場合。私はこれが意味をなさないことを知っています。これを打破する30について特別なことは何もありません。他の顧客は30を超えるカテゴリを持っていますが、この1つのインストールに30以上のカテゴリがある場合、問題は100%再現可能であり、30未満のカテゴリがある場合、問題は解消します。

問題の表は次のとおりです。

CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(64) NOT NULL,
  `title` varchar(128) NOT NULL,
  `parent` int(10) unsigned NOT NULL,
  `keywords` varchar(255) NOT NULL,
  `description` text NOT NULL,
  `status` enum('Active','Inactive','_Deleted','_New') NOT NULL default 'Active',
  `style` enum('_Unknown') default NULL COMMENT 'Autoenum;',
  `order` smallint(5) unsigned NOT NULL,
  `created_at` datetime NOT NULL,
  `modified_at` datetime default NULL,
  PRIMARY KEY  (`id`),
  KEY `name` (`name`),
  KEY `parent` (`parent`),
  KEY `created_at` (`created_at`),
  KEY `modified_at` (`modified_at`),
  KEY `status` (`status`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COMMENT='R2' AUTO_INCREMENT=33 ;

問題のコードは、テーブルを再帰的にクエリして、すべてのカテゴリをフェッチします。それを発行します

SELECT * FROM `categories` WHERE `parent`=0 ORDER BY `order`,`name`

そして、返された各行に対してこのクエリを繰り返しますが、毎回使用しWHERE parent=$category_idます。(この手順は改善できると確信していますが、それはおそらく別の質問です)

私の知る限り、次のクエリは永久にハングしています。

SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`

このクエリをサーバーのmysqlクライアントで完全に実行でき、PHPMyAdminでも問題なく実行できます。

問題となっているのはその特定のクエリではないことに注意してください。私の場合DELETE FROM categories WHERE id=22、その後異なるその後、ハングします上記のものと同様のクエリ。また、上記のクエリを手動で実行すると、行が返されません

私はテーブルが壊れているのではないかと疑いましたが、これらの報告された問題のいずれかを試しましたがREPAIR TABLEOPTIMIZE TABLEその問題も解決しませんでした。テーブルを削除して再作成しましたが、問題が再発しました。これは完全に同じテーブル構造であり、30以上のカテゴリを持つ顧客を含め、他の顧客が問題なく使用しているPHPコードです。

PHPコードが永遠に再帰するわけではありません。(これは無限ループではありません

MySQLサーバーは、i686上のpc-linux-gnuのmysqld Ver 5.0.92-community(MySQL Community Edition(GPL))でCentOS linuxを実行しています

MySQLサーバーの負荷が低い:負荷平均:0.58、0.75、0.73、CPU:4.6%us、2.9%sy、0.0%ni、92.2%id、0.0%wa、0.0%hi、0.3%si、 0.0%st。使用されているごくわずかなスワップ(448k)

この問題のトラブルシューティング方法を教えてください。何が起こっているのかについての提案はありますか?

更新:TRUNCEテーブルを編集し、30行のダミーデータを挿入しました。

INSERT INTO `categories` (`id`, `name`, `title`, `parent`, `keywords`, `description`, `status`, `style`, `order`, `created_at`, `modified_at`) VALUES
(1, 'New Category', '', 0, '', '', 'Inactive', NULL, 1, '2011-10-25 12:06:30', '2011-10-25 12:06:34'),
(2, 'New Category', '', 0, '', '', 'Inactive', NULL, 2, '2011-10-25 12:06:39', '2011-10-25 12:06:40'),
(3, 'New Category', '', 0, '', '', 'Inactive', NULL, 3, '2011-10-25 12:06:41', '2011-10-25 12:06:42'),
(4, 'New Category', '', 0, '', '', 'Inactive', NULL, 4, '2011-10-25 12:06:46', '2011-10-25 12:06:47'),
(5, 'New Category', '', 0, '', '', 'Inactive', NULL, 5, '2011-10-25 12:06:49', NULL),
(6, 'New Category', '', 0, '', '', 'Inactive', NULL, 6, '2011-10-25 12:06:51', '2011-10-25 12:06:52'),
(7, 'New Category', '', 0, '', '', 'Inactive', NULL, 7, '2011-10-25 12:06:53', '2011-10-25 12:06:54'),
(8, 'New Category', '', 0, '', '', 'Inactive', NULL, 8, '2011-10-25 12:06:56', '2011-10-25 12:06:57'),
(9, 'New Category', '', 0, '', '', 'Inactive', NULL, 9, '2011-10-25 12:06:59', '2011-10-25 12:06:59'),
(10, 'New Category', '', 0, '', '', 'Inactive', NULL, 10, '2011-10-25 12:07:01', '2011-10-25 12:07:01'),
(11, 'New Category', '', 0, '', '', 'Inactive', NULL, 11, '2011-10-25 12:07:03', '2011-10-25 12:07:03'),
(12, 'New Category', '', 0, '', '', 'Inactive', NULL, 12, '2011-10-25 12:07:05', '2011-10-25 12:07:05'),
(13, 'New Category', '', 0, '', '', 'Inactive', NULL, 13, '2011-10-25 12:07:06', '2011-10-25 12:07:07'),
(14, 'New Category', '', 0, '', '', 'Inactive', NULL, 14, '2011-10-25 12:07:08', '2011-10-25 12:07:09'),
(15, 'New Category', '', 0, '', '', 'Inactive', NULL, 15, '2011-10-25 12:07:11', '2011-10-25 12:07:12'),
(16, 'New Category', '', 0, '', '', 'Inactive', NULL, 16, '2011-10-25 12:07:13', '2011-10-25 12:07:14'),
(17, 'New Category', '', 0, '', '', 'Inactive', NULL, 17, '2011-10-25 12:09:41', '2011-10-25 12:09:42'),
(18, 'New Category', '', 0, '', '', 'Inactive', NULL, 18, '2011-10-25 12:09:47', NULL),
(19, 'New Category', '', 0, '', '', 'Inactive', NULL, 19, '2011-10-25 12:09:48', NULL),
(20, 'New Category', '', 0, '', '', 'Inactive', NULL, 20, '2011-10-25 12:09:48', NULL),
(21, 'New Category', '', 0, '', '', 'Inactive', NULL, 21, '2011-10-25 12:09:49', NULL),
(22, 'New Category', '', 0, '', '', 'Inactive', NULL, 22, '2011-10-25 12:09:50', NULL),
(23, 'New Category', '', 0, '', '', 'Inactive', NULL, 23, '2011-10-25 12:09:51', NULL),
(24, 'New Category', '', 0, '', '', 'Inactive', NULL, 24, '2011-10-25 12:09:51', NULL),
(25, 'New Category', '', 0, '', '', 'Inactive', NULL, 25, '2011-10-25 12:09:52', NULL),
(26, 'New Category', '', 0, '', '', 'Inactive', NULL, 26, '2011-10-25 12:09:53', NULL),
(27, 'New Category', '', 0, '', '', 'Inactive', NULL, 27, '2011-10-25 12:09:54', NULL),
(28, 'New Category', '', 0, '', '', 'Inactive', NULL, 28, '2011-10-25 12:09:55', NULL),
(29, 'New Category', '', 0, '', '', 'Inactive', NULL, 29, '2011-10-25 12:09:56', NULL),
(30, 'New Category', '', 0, '', '', 'Inactive', NULL, 30, '2011-10-25 12:09:57', NULL);

親はいません。すべてのカテゴリが最上位にあります。問題はまだあります。PHPによって実行される次のクエリは失敗します。

SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`

ここにありEXPLAINます:

mysql> EXPLAIN SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`;
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
| id | select_type | table      | type | possible_keys | key    | key_len | ref   | rows | Extra                       |
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
|  1 | SIMPLE      | categories | ref  | parent        | parent | 4       | const |    1 | Using where; Using filesort | 
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
1 row in set (0.00 sec)

アップデート#2:私は今、以下のすべてを試しました:

  1. この表とデータを同じソフトウェアを使用して別のサイトにコピーしました。問題は表に従っていませんでした。このデータベースに限定されているようです。
  2. GBNの答えが示唆したように、私はインデックスを変更しました。問題は残った。
  3. テーブルをドロップしてテーブルとして再作成し、InnoDB上記と同じ30テスト行を挿入しました。問題は残った。

私はそれがこのデータベースの何かであるに違いないと思います...

更新#3:データベースを完全に削除し、新しい名前で再作成して、データをインポートしました。問題は残っています。

ハングする実際のPHPステートメントはへの呼び出しであることがわかりましたmysql_query()。これ以降のステートメントは実行されません。

その呼び出しがハングしている間、 MySQLはスレッドをスリープ状態としてリストします

mysql> show full processlist;
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
| Id    | User             | Host                        | db                   | Command | Time | State | Info                  |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
|  5560 | root             | localhost                   | problem_db           | Query   |    0 | NULL  | show full processlist |  
                          ----- many rows which have no relevancy; only rows from this customer's app are shown ------
| 16341 | shared_db        | oak01.sitepalette.com:53237 | shared_db            | Sleep   |  308 |       | NULL                  | 
| 16342 | problem_db       | oak01.sitepalette.com:60716 | problem_db           | Sleep   |  307 |       | NULL                  | 
| 16344 | shared_db        | oak01.sitepalette.com:53241 | shared_db            | Sleep   |  308 |       | NULL                  | 
| 16346 | problem_db       | oak01.sitepalette.com:60720 | problem_db           | Sleep   |  308 |       | NULL                  |  
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+

更新#4:上記のテーブルと556行のテーブルの2つのテーブルの組み合わせに絞り込みました。場合は、テーブルが未満556行が含まれているか、テーブルが30の未満の行が含まれている、問題が消えます。それは私がここで打っているある種のMySQLの制限のようなものです...categoriesmedia_imagesmedia_imagescategories

更新#5:データベースを別のMySQLサーバーに完全に移動しようとしたところ、問題は解決しました...それで、本番データベースサーバーに関連しています...

アップデート#6:毎回ハングする関連するPHPコードは次のとおりです。

    public function find($type,$conditions='',$order='',$limit='')
    {
            if($this->_link == self::AUTO_LINK)
                    $this->_link = DFStdLib::database_connect();

            if(is_resource($this->_link))
            {
                    $q = "SELECT ".($type==_COUNT?'COUNT(*)':'*')." FROM `{$this->_table}`";
                    if($conditions)
                    {
                            $q .= " WHERE $conditions";
                    }
                    if($order)
                    {
                            $q .= " ORDER BY $order";
                    }
                    if($limit)
                    {
                            $q .= " LIMIT $limit";
                    }

                    switch($type)
                    {
                            case _ALL:
                                    DFSkel::log(DFSkel::LOG_DEBUG,"mysql_query($q,$this->_link);");
                                    $res = @mysql_query($q,$this->_link);
                                    DFSkel::log(DFSkel::LOG_DEBUG,"res = $res");

このコードは製品版であり、他のすべてのインストールで正常に動作します。一度のインストールで、それはでハングし$res = @mysql_query($q,$this->_link);ます。私mysql_queryはでなくデバッグログ res =にを表示しstrace、PHPプロセスを実行するとハングアップするため、read(

更新#どんな-それが-ある-I-憎悪-this-& (#^& -issue!これは今まで起こって開始されました2人の。私の顧客に私は解雇tcpdump、それはのように見えるのMySQLからの応答が完全に送信されることはありません。完全なMySQL応答を送信する前に、TCPストリームがハングしたように見えます(ただし、まだ調査中です)。

更新#I-have-gone-completely-crazy-but-it-works-now-kinda: OK、これは意味がありませんが、解決策を見つけました。MySQLサーバーのeth2インターフェースに2番目のIPアドレスを割り当て、1つのIPをNFSトラフィックに使用し、2番目のIPをMySQLに使用すると、問題は解消します。NFSとMySQLの両方のトラフィックがそのIPに向かう場合、どういうわけか... IPアドレスをオーバーロードしているようです。ただし、IPアドレスを「オーバーロード」することはできないため、これは意味がありません。インターフェースを飽和させることは確かですが、それは同じインターフェースです。

ここで何が起こっているのでしょうか?これはおそらくこの時点でのunix.SEまたはServerFaultの質問です...(少なくとも今は動作しています...)

更新#why-oh-why:この問題はまだ発生しています。2つの異なるIPを使用しても発生し始めます。新しいプライベートIPを作成し続けることはできますが、明らかに何かが間違っています。


さて、これはmysql内ですべて再帰的な階層クエリを実行する際の潜在的な「その他の質問」へのリンクです。
Derek Downey

@DTest確かに、すぐに追加します。他のリンクをありがとう!
Josh、


こんにちはジョシュ。クエリはMySQLクライアント内とPHPMyAdminで正常に実行されると言いましたか?PHPアプリケーションだけがハングアップしますか?
marcio

@marcioAlmadaはい、そうです。私はこの状況全体に非常に混乱しています。
Josh

回答:


5

クエリプランで正確に行われていることの一般的なプロファイリングについては、PROFILINGを試すことができます。

基本的に、ハングアップの場所を特定するのに役立ちます。

もちろん、MySQLをでコンパイルした場合にのみ機能しenable-profilingます。


3

アイデア(MyISAMに当てはまるかどうかは不明ですが、私はInnoDBを使用しています)

インデックスを「親」に変更して、3つの列(親、順序、名前)になるようにします。これはWHERE .. ORDER BYと一致します。

を削除しSELECT *ます。必要な列だけを取得します。他の列をインデックス「親」に追加します

これにより、オプティマイザは現在カバーしているため、インデックスのみ を使用できます。現状では、インデックスはそのクエリには役に立たないため、テーブル全体を読み取る必要があります。


parentインデックスを変更した後も問題が解決しない(parent, order, name)
Josh

3

Production DBサーバーでいくつかのことをチェックします

  • チェック#1:/ var / lib / mysqlがマウントされているデータボリュームに不良ブロックがないことを確認します。これには、fsck(ファイルシステムチェック)を実行するためのダウンタイムが必要な場合があります。
  • チェック#2:テーブルがDML(INSERT / UPDATE / DELETE)またはSELECTで重くないことを確認する
  • チェック#3:PHPがmysql_close()を適切に発行していること、およびアプリがDB接続を閉じるためにApacheに依存していないことを確認します。そうしないと、PHPがMySQLによって事実上閉じられたDB接続リソースを使用しようとするときに、ある種の競合状態が発生する可能性があります。
  • チェック#4:DBサーバーのOSに、PHPとMySQLの観点から閉じられた接続のnetstatリストにあるTIME_WAITの在庫がないことを確認しますが、OSはまだハングしています。あなたはこれを見ることができますnetstat | grep -i mysql | grep TIME_WAIT
  • チェック#5:mysql_pconnectを使用しいないことを確認します。永続的な接続が適切に閉じないという未解決のバグレポートがまだあります。それらの接続にアクセスしようとすることを想像するのは嫌いです。
  • チェック#6:ロードバランサー、スイッチ、ファイアウォール、DNSサーバーを介したDBトラフィックスループットが、本番DBサーバーと他の外部サーバーで同じであることを確認します。個人的には、mysql.userおよびmysql.dbのホスト列でDNS名を使用するのは嫌いです。私は通常、クライアントにそれらを取り除き、ハードIPに置き換えさせます。また、mysqldのDNSの使用をバイパスskip-host-cacheおよび追加しskip-name-resolveます。したがって、@ marcioAlmadaの回答をチェックポイントとして関連付けることができます。

これらのチェックのいずれも役に立たないと思われる場合は、できるだけ早くコメントして、回答を削除できるようにお知らせください。


これは役に立つ答えだと思います!私はいないことを確認、私はすべての接続を閉じていますので、私はそれを試すことができます。私はそれが不良ブロックを持っている思いませんが/var(RAID10にあります)、私は簡単に間違っている可能性があります。netstatを確認します。私は使用していませんmysql_pconnectが、network / dns / etcをチェックします。
Josh

@ジョシュ:不良ブロックが表示されている場合は、それらについて多くのメッセージが表示されdmesgます。ハードウェアRAIDがない場合は、ハードウェアRAIDモニタープログラムを確認してください。
2011年

これが発生すると、TIME_WAITMySQL接続が1つだけ表示されることがあります(常にではありません)。決して数が多いわけではない…活動的にテーブルは重くない。
ジョシュ

2

a)こんにちは、ジョシュ。クエリはMySQLクライアント内とPHPMyAdminで正常に実行されると言いましたか?PHPアプリケーションだけがハングアップしますか?
b)@marcioAlmadaはい、そうです

あなたはschrödinbugをヒットしたと思います。あなたはしようとする可能性がありdie()、あなたのクエリの後または前にのためにあなたのコードを参照しようif statementsめったに起こりませんいるし。コードがない場合、何がハングするかを言うのは困難です。

編集:私は現在、この行かもしれないと言います

$this->_link = DFStdLib::database_connect();

これは(私が想定している)関数が呼び出されるたびに接続を作成します。それが問題かもしれません。my.cnfのmax_connectionsは何ですか?


私はそれがハングする場所を正確に知っています:それは呼び出しを通り過ぎることは決してありませんmysql_query()
Josh

1
コードの+-10行を投稿できますか?
創世記

完了しました。tcpdump 今後数日でこれをデバッグする予定です。これが本当にPHPの問題である場合、SOに新しい質問を投稿する必要があります。
Josh、

@ジョシュ:私の回答を更新
創世記

@genesisに感謝します...しかし、2つの理由でそれはそれではありません。1.このコードは、「データベースリンクを自動的に確立する」機能を使用している場合にのみ呼び出されます。これは$this->_link、定数に設定することによって行われますself::AUTO_LINK2.私がそうであったとしても、そのコードはif:if($this->_link == self::AUTO_LINK)にあり、次の行$this->_link = DFStdLib::database_connect();はの値を変更する$this->_linkため、ifは再度実行されません。スレッドごとにデータベースへの接続は1つしかないと確信しています。(プロセスリストをご覧ください)
Josh

1

これはMySQLの問題ではなくPHPの問題であるとほぼ確信していますが、MySQLサーバーを切り替えるとなぜ機能するのですか?

いくつかの試み:

  • ファイアウォール?? アプリケーションをブロックし、本番データベースサーバーへのリクエストやその逆のアクセスを妨げるファイアウォールはありますか?

  • 接続構成でドメイン名を使用していますか、それともIPアドレスを使用していますか?ドメイン名を使用すると、データベースの相互作用が少し遅くなる可能性があり、これと短いPHPの最大スクリプト実行時間との組み合わせにより、永久にハングアウトが発生します

この最後の提案は、データベースサーバーを切り替えるときの奇妙な変数の動作を説明しているようです。1つは他よりもはるかに速く応答する可能性があり、見つかったすべてのレコードには2次クエリがあるため、その仮説は、アプリケーションが一定量のクエリ結果(> 30)だけで遅延する理由を説明します。

少なくとも私たちは主要な結論に達しました。間違いなく、MySQLサーバーistelfに問題はありません。ドキュメンテーションを見て、あなたの特定の状況に適した機能制限はないようです。また、再帰的なテーブルと特定の量のエントリに問題があったことは一度もありません。

お役に立てば幸いです。


0

mysql_query()コマンドをネイティブのPHP5ドライバーに更新してみましたか?mysqli :: query()?これで何ができるかはわかりませんが、試してみる価値はあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.