ホームページ  >  記事  >  バックエンド開発  >  MySQL の数千万のデータを最適化する方法

MySQL の数千万のデータを最適化する方法

WBOY
WBOYオリジナル
2016-06-23 13:39:54893ブラウズ

function insert(){		global $m;		if (!isset($_REQUEST['strs']) || !isset($_REQUEST['type'])			 || !isset($_REQUEST['hash'])){			echo 'param error';			return;		}		//strs为所有字符串		$poststr = $_REQUEST['strs'];        		$xstrs = json_decode(stripslashes($poststr), true);        		$type = $_REQUEST['type'];		$hash = $_REQUEST['hash'];				if (count($xstrs) <= 0){			$msg = 'str error';			DsLog::errLog($msg.$poststr);			echo $msg;			return;		}		if ($type == '0'){			$table = 'white';		}		else if($type == '1'){			$table = 'black';		}		else{			$msg = 'type error';			DsLog::errLog($msg);			echo $msg;			return;		}        $strs = array();        		for($i = 0; $i < count($xstrs); $i++) {            $strtmp = $xstrs[$i];            $strtmp = trim($strtmp);            $strtmp = strtolower($strtmp);	    	$strtmp = addslashes($strtmp);            if (strlen($strtmp) > 256){				$strtmp = substr($strtmp, 0, 256);			}            if (strlen($strtmp) >= 7)            {                array_push($strs, $strtmp);            }	    }                		//拼接所有字符串		$tmp = '("'.implode('","', $strs).'")';		//获取已存在的字符串		$sql = "select * from $table where str in $tmp";		$ret = mysql_query($sql, $m);		if (!$ret){			$msg = 'exec error:'.mysql_error($m).','.$sql;			DsLog::errLog($msg);			echo $msg;			return;		}		$exists = array();		$notexists = array();		$count = mysql_num_rows($ret);		for ($i = 0; $i < $count; $i++)		{			$item = mysql_fetch_assoc($ret);			if (!$item){				break;			}			array_push($exists, $item['str']);		}				for ($i = 0; $i < count($strs); $i++){			if (in_array($strs[$i], $exists)){				continue;			}			array_push($notexists, $strs[$i]);		}		for($i = 0; $i < count($exists); $i++) {	    	$exists[$i] = addslashes($exists[$i]);	    }	    for($i = 0; $i < count($notexists); $i++) {	    	$notexists[$i] = addslashes($notexists[$i]);	    }				if (count($exists) > 0){			//更新已存在字符串的count字段			$tmp = '("'.implode('","', $exists).'")';			$time = date('YmdHi');			$sql = "update $table set count=count+1 where str in $tmp";			$ret = mysql_query($sql, $m);			if (!$ret){				$msg = 'exec error:'.mysql_error($m).','.$sql;				DsLog::errLog($msg);				echo $msg;				return;			}			//更新已存在字符串的upd字段			$sql = "update $table set upd='$time' where str in $tmp";			$ret = mysql_query($sql, $m);			if (!$ret){				$msg = 'exec error:'.mysql_error($m).','.$sql;				DsLog::errLog($msg);				echo $msg;				return;			}		}						//插入新信息		if (count($notexists) > 0){			$time = date('YmdHi');			$sql = "insert ignore into $table (str,hash,count, upd) values";			for ($i = 0; $i < count($notexists); $i++){				$str = $notexists[$i];				$crc = sprintf("%u", crc32($str));				$sql .= "('$str','$crc','1', '$time'),";			}			$sql = substr($sql, 0, strlen($sql) - 1);			$ret = mysql_query($sql, $m);			if (!$ret){				$msg = 'insert error:'.mysql_error($m).','.$sql;				DsLog::errLog($msg);				echo $msg;				return;			}		}				echo !!$ret;	}


次に、各文字列の数をカウントします。これはマップに似ています。もちろん、データベースには文字内の crc およびその他の情報も保存されます。 insert 関数が受け取る ポストされる文字列は文字列配列です。毎回ポストされる文字列の平均数は 1500 です
現在、データベースには 1,200 万件のレコードがあり、各挿入には平均 20 秒かかります (30 秒かかります)。タイムアウトが頻繁に発生します)
最適化する方法を皆さんに聞きたいのですが、まだデータの一部しか数えていません。統計後のデータ量は約 10 億から 10 億であると推定されています

返信ディスカッション(解決策)へ

ちなみに、strフィールドは一意のIndexにしています、他のフィールドは何もしていません、mysqlはMyISAMです

ボトルネックがデータベース内のクエリと変更の3つのステートメントにあることを確認できますか?

毎回投稿される文字列の平均数は1500とのことですが

1500ワードということでしょうか?そうでない場合でも (文字列の長さです)、単語ごとの平均 20 文字に基づくと、まだ 75 単語あります
フィルター条件は str in $tmp (str in ('xxx','xxx'. ...)) つまり、テーブル内のレコードごとに 1500 (75) 回の文字列比較が必要で、ヒット率は最大 1/1500 (1/75) になると思いますか?
データベースの強みはレコード間の比較にありますが、実行しているのは列間の弱い比較です
受信データを使用して一時テーブル (行ごとに 1 ワード) を構築し、メイン テーブルで関連する操作を実行できます。この方法でのみデータベースを活用できます

また、2 つの連続する update ステートメントをマージすることはできませんか?

ボトルネックがデータベース内のクエリと変更の 3 つのステートメントにあることを確認できますか?

毎回投稿される文字列の平均数は1500とのことですが

1500ワードということでしょうか?そうでない場合でも (文字列の長さです)、単語ごとの平均 20 文字に基づくと、まだ 75 単語あります
フィルター条件は str in $tmp (str in ('xxx','xxx'. ...)) つまり、テーブル内のレコードごとに 1500 (75) 回の文字列比較が必要で、ヒット率は最大 1/1500 (1/75) になると思いますか?
データベースの強みはレコード間の比較にありますが、実行しているのは列間の弱い比較です
受信データを使用して一時テーブル (行ごとに 1 ワード) を構築し、メイン テーブルで関連する操作を実行できます。この方法でのみデータベースを活用できます

また、2 つの連続する update ステートメントをマージすることはできませんか?

1500 は文字数を指します。文字数は約 5W である必要があります

つまり、最初に 1500 レコードを一時テーブル tmp_table に挿入し、次に
次に table のテーブル内部結合 tmp_talbe から select* するということです。 str = tmp_table.str そうですか?


ボトルネックがデータベース内のクエリと変更の 3 つのステートメントにあることを確認できますか?
毎回投稿される文字列の平均数は1500とのことですが

1500ワードということでしょうか?そうでない場合でも (文字列の長さです)、単語ごとの平均 20 文字に基づくと、まだ 75 単語あります
フィルター条件は str in $tmp (str in ('xxx','xxx'. ...)) つまり、テーブル内のレコードごとに 1500 (75) 回の文字列比較が必要で、ヒット率は最大 1/1500 (1/75) になると思いますか?
データベースの強みはレコード間の比較にありますが、実行しているのは列間の弱い比較です
受信データを使用して一時テーブル (行ごとに 1 ワード) を構築し、メイン テーブルで関連する操作を実行できます。この方法でのみデータベースを活用できます

また、2 つの連続する update ステートメントをマージすることはできませんか?

1500 は文字列の数で、約 3W 文字です
一時テーブル tmp に 1500 文字を挿入し、white.str=tmp.str の白い内部結合 tmp から select* するという意味ですか?
テストしました
select* from white inner join tmp onwhite.str=tmp.str 実行時間 10S
id 	select_type 	table 	type 	possible_keys 	key 	key_len 	ref 	rows 	Extra1 	SIMPLE 	tmp 	index 	str 	str 	770 	NULL 	2531 	Using index1 	SIMPLE 	white 	eq_ref 	str 	str 	770 	bayes.tmp.str 	1 	 


select * from white where str in(''xxx','xxx'... ) を直接実行 実行time 9S
id 	select_type 	table 	type 	possible_keys 	key 	key_len 	ref 	rows 	Extra1 	SIMPLE 	white 	range 	str 	str 	770 	NULL 	2531 	Using where


効果はほぼ同じです

1. mysql にはメモリテーブルがありますが、それを使用していません 2. 2 つの更新をマージすると 10 秒節約できるはずです


mysql_error のようなライブラリ関数も非効率的です私が PHP を学習していたときは、PDO を直接起動したことはほとんどありませんでした。

また、 でキーワードを使用しましたが、インデックスされていないようです。

効果はほぼ同じだと感じます


テスト環境に問題があるはずです
テスト結果は以下の理由によるものと考えられます
1. データ。クエリテーブルの量が特定のレベルに達していない
2. 2 つのクエリメソッドで見つかった結果セットは異なります
3. クエリフィールドのインデックスが作成されていないか、正しく追加されていません
4. 他のインデックスの影響が多すぎます
5. その他理由
より明らかなのは 2 番目の理由だと思います。 これが理由です
結合テーブルをクエリするときにクエリ フィールドを指定しませんでした。 結合テーブルによって生成されるフィールドは、結合されていないテーブルのフィールドより多くなければなりません
そこにあります。結合テーブルクエリでも 1 対多の状況になるため、1 つのレコードが複数になり、グループ化 を追加します。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。