• 締切済み

MySQLでの128次元ベクトルの距離計算高速化

mpro-gramの回答

  • mpro-gram
  • ベストアンサー率74% (170/228)
回答No.4

まずは、質問文の SQL で気付く点 1.2乗するだけなら、power(x,2) よりも、x*x の方がずーっと早いのは、どのプログラミング言語もおなじなはず。 2.group by してるけど、どのみち全件で計算がおこなわれるから、よけいな作業が入って遅くなるだけでは?最終的に最短の1件取り出してるだけの様だし。 3.いずれにしても、150万件全部計算してはいられないから、絞る必要がありますね。 一回の SQL文では難しいと思います。 近そうなものを割り出すには、No.3 ki073 さん の方法を、さらに mysql での最適化をするなら、 between を使った方が確実に index を利用します。 とはいえ、128カラム全部の複合index では、本体となんら代わりがない可能性が高いので、1カラムごと128個かな、実際の検索に使われるindexは1個だし、複合indexにするか悩みどころかも。 count(*) の場合には、一番効率のよさそうなindex が使われるらしいです(これは、MySQL 5 以降でinnodb の場合についてだったかな、以下参照) http://nippondanji.blogspot.jp/2010/03/innodbcount.html あと、insert,updateが頻繁にあるなら OPTIMIZE TABLE は、行っておいた方がよさそうです。 で、本題、まずはあたりをつけるSQL文 pt1,pt2 はカラム名、 dat1,dat2 は座標値、 d はその座標からの距離値 select count(*) from `testDB` where ( `pt1` between dat1-d and dat1+d ) and (`pt2` between dat2-d and dat2+d) and ... ; これで、dを変更しつつ、適当な件数(ま、100件くらいは許容範囲として、もし1件だったら、正方形の対角線距離より近いけど正方形の外というのと同じ状況がありうるので、d*2 で絞り直す,次元数が大きいので2倍でよいかは不明)に絞れたら、 この条件をつけたもので距離計算する。power は遅いので、地道に かけ算をする select `id1`, (`pt1`-dat1)*(`pt1`-dat1) + (`pt2`-dat2)*(`pt2`-dat2) + ()*() + ... as `nearest` from `testDB` where (`pt1` between dat1-d and dat1+d) and (`pt2` between dat2-d and dat2+d) and () and ... order by `nearest` limit 1 ; 128カラムもあるとすごく長くなるけど、SQL文の最大長は何バイトだったかな?? 識別子に予約語を使っていなければ、 `` は省略可能、これで770バイトも違ってくる。 http://nippondanji.blogspot.jp/2009/05/mysql.html によれば、「MySQLサーバーが実行出来るSQL文の最大長は、max_allowed_packetシステム変数で表され、max_allowed_packetの最大値は1GB」 そのシステムでの制限値を調べる必要はあるけど、そうとう長くても、全然問題ないようですね。データの桁数にもよるけど、1カラム用の計算式と条件文で100byte 近く必要になったとして、全文で 13KB くらい。 それでもちょっとやそっとでない時間もかかりそうだけど、15万件全部計算するのに比べれば、タイムアウトはしないんじゃないかな?

nmtkn
質問者

お礼

ご回答ありがとうございます。 インデックスはあまり使ったことがなかったので、大変勉強になります。 どの程度の速度が実際に出るものなのか教えていただいた方法を試してみたいと思います。

nmtkn
質問者

補足

補足です。 updateは基本的にはありません。 (deleteもあまりありません) が、insertについてはそれなりに発生します。 抽象的な言葉での申し訳ありません。 なにぶんまだシステムの検討段階でして、技術的な検証を進めている状態です。 ご容赦ください。

関連するQ&A

  • 高次元整数ベクトルの高速計算

    VC++2010でwindowsアプリケーション(CLI)を作成しています。 その中で1000次元の整数ベクトルのパワーを求めるために概略次のような計算をしています。 int v[1000]; //v[0]~v[999]に整数値を代入後 long long int power = 0; for(int i = 0; i < 1000; i++){ power += v[i] * v[i]; } この計算を数万回繰り返す必要があるため、是非高速化を図りたいのですが、CPUのベクトル計算機能等を利用して高速化することができるでしょうか?

  • PHP+MySQLで高速化

    mysql_query()が返したresourceの行数は得られませんか? 例をあげると データが id p (フィールド) 42 651 23 357 67 123 28 385 このように4レコード有ったとして $res = mysql_query('select p from tbl where p="123"'); これで、$resから3行目というのを得て、3を使ってidの67を得たいのですが、 $res = mysql_query('select id,p from tbl where p="123"'); のようにして$resのidを見るようにする以外の方法は無理なんでしょうか? もし前者が可能ならそちらの方が速いと思うのですが。

    • ベストアンサー
    • PHP
  • MySQL「10行を超えたら削除する」について

    初心者と言うより、全く全くわからない状態で質問することご容赦下さい。ブログ調の掲示板で、MySQLを使っておりますが、掲示板内の投稿が10投稿を超えると、古いものから順に削除されていく状態のようで、そのリミットを完全に解除したいと思い、ファイルを探していたら、 //10行超えたら削除していく $result = mysql_query("select * from bbs_data where bbs_user_id='{$urow[id]}' order by id desc limit 9,1") or die(mysql_error()); if(mysql_num_rows($result) == 1){ $row = mysql_fetch_assoc($result); $result = mysql_query("delete from bbs_data where bbs_user_id='{$urow[id]}' and id < '{$row[id]}'") or die(mysql_error()); } という部分がありました。このリミットを解除するにはここのプログラムを変えればいいのでしょうか? その場合、どのようにしたらいいか教えて下さい。

    • ベストアンサー
    • MySQL
  • phpとmysqlでデータの一覧表示をしたいと思っています。

    phpとmysqlでデータの一覧表示をしたいと思っています。 ずらーっと並べるだけでなく、1ページに100件した場合には 10行毎に<hr>や<br>などのタグを挿入して間隔を空けたいのですが どのようにしたらいいのか悩んでいます。 現在は下記のようにLIMITで何度もqueryを発行しています。 できれば1回のqueryで処理したいのですが 他にどのような方法があるでしょうか。 $rs = mysql_query("select * from data order by id desc LIMIT 0,10 ;",$conn); while($rec = mysql_fetch_array($rs, MYSQL_ASSOC)){ $site = $rec['site']; $url = $rec['url']; echo '<a href="'.$url.'">'.$site.'</a>'; } $rs = mysql_query("select * from data order by id desc LIMIT 10,10 ;",$conn); while($rec = mysql_fetch_array($rs, MYSQL_ASSOC)){ $site = $rec['site']; $url = $rec['url']; echo '<a href="'.$url.'">'.$site.'</a>'; } $rs = mysql_query("select * from data order by id desc LIMIT 20,10 ;",$conn); while($rec = mysql_fetch_array($rs, MYSQL_ASSOC)){ $site = $rec['site']; $url = $rec['url']; echo '<a href="'.$url.'">'.$site.'</a>'; } よろしくお願い致します。

    • ベストアンサー
    • MySQL
  • phpでmysqlからデータを取り出して一覧表示

    phpでmysqlからデータを取り出して一覧表示させるプログラムを造りたいのですが、どうもうまくいきません。まずはデータの取り出し表示の仕方を教えてください。 $sqlstr="select * from webdiary where username=$id order by topicid asc"; $result = mysql_query( $sqlstr ); この後どのようにしたらよいでしょうか?

    • ベストアンサー
    • PHP
  • PHPでMySQLのデータを2次元配列に格納する

    PHPの本に $r = mysql_query("SELECT * FROM tb;"); while ($row = mysql_fetch_array($r)){ print "{$row['id']}{$row['title']}<BR>"; } とあったのですが1行ずつしか保存できないので 2次元配列row[][]で while ($row[] = mysql_fetch_array($r)){ } foreach($row as $v){ print "{$v['id']}{$v['title']}<BR>"; } このようにコーディングしたところ 一応うまく表示されたのですが、問題はないですか? もし普通はこういう風にするみたいなやり方があれば 教えて欲しいです。

    • ベストアンサー
    • PHP
  • mysql_fetch_arrayとテンプレートの使い方

    データベースからランダムに取り出した3つのデータがあります。 取り出したデータにはそれぞれid、name、ageのデータが入っています。 $rs = mysql_query("select * FROM table order by Rand() LIMIT 0,3;",$con); while($rec = mysql_fetch_array($rs, MYSQL_ASSOC)){ $tpl->assign(array( id => $rec[id], name => $rec['name'], age => $rec[age] )); } これだと1種類のデータが3つ連続して表示されてしまいました。 3種類のデータを全部表示するにはどのようにしたらよいのでしょうか? テンプレート(?)の使い方がよく分かっていないので検討違いのことをしている気がしますがよろしくお願いします。

    • ベストアンサー
    • MySQL
  • 2次元配列の作り方

    データ型 name varchar(30), item1 char(8), item2 int(6) として下記のテーブルがあります。 | name | item1 | item2 | | taro | abcd | 28 | | taro | efghk | 33| | taro | lmnp | 05 | これから2次元配列形式でデータを取得したいのですが、うまく並んでくれません。 まず、 (前略) $query ="SELECT item1,item2 from *** where name='taro'"; $result = mysql_query($query,$conn_id) or die($query.'failed('.mysql_error().')'); $data[][] = mysql_fetch_assoc($result);  とすると(当然ながら)、 Array ( [0] => Array ( [0] => Array ( [item1] => abcd [item2] =>28 ) ) ) と最初のレコードだけが取得されます。 while($data[][] = mysql_fetch_assoc($result)){ print_r($data); echo "<br/>\n"; } とすると、 Array ( [0] => Array ( [0] => Array ( [item1] =>abcd [item2] => 28 ) ) ) Array ( [0] => Array ( [0] => Array ( [item1] => abcd [item2] => 28 ) ) [1] => Array ( [0] => Array ( [item1] => efghk [item2] => 33 ) ) ) Array ( [0] => Array ( [0] => Array ( [item1] => abcd [item2] => 28 ) )      [1] => Array ( [0] => Array ( [item1] => efghk [item2] => 33 ) )      [2] => Array ( [0] => Array ( [item1] => lmnp [item2] => 05 ) ) ) となってしまうのですが、 Array ( [0] => Array ( [0] => Array ( [item1] => abcd [item2] => 28 ) )      [1] => Array ( [0] => Array ( [item1] => efghk [item2] => 33 ) )      [2] => Array ( [0] => Array ( [item1] => lmnp [item2] => 05 ) ) ) だけを取得させるにはどのように書けば良いでしょうか?

    • ベストアンサー
    • PHP
  • MySQLのデータをPHPで多次元連想配列にしたい

    MySQLのデータを多次元連想配列にする方法を教えて下さい。 下記のような多次元連想配列のデータがあります。 これと同様のMySQLに登録されたデータから多次元連想配列を作りたいと思います。 $test= array( array("id" => "1","kamoku" => "算数","tensu" => "70"), array("id" => "2","kamoku" => "理科","tensu" => "88"), array("id" => "3","kamoku" => "国語","tensu" => "90"), ); print_r($test); ---print_r($test)の結果--------------------------------- Array ( [0] => Array ( [id] => 1 [kamoku] => 算数 [tensu] => 70 ) [1] => Array ( [id] => 2 [kamoku] => 理科 [tensu] => 88 ) [2] => Array ( [id] => 3 [kamoku] => 国語 [tensu] => 90 ) ) -------------------------------------------------------- これと同様のデータをMySQLに作成します。 テーブル名:test 列名:id,kamoku,tensu MySQLからデータを取得 try { $dbh = new PDO($DSN , $DBUSER , $DBPASS); $query = select * from test $stmt = $dbh->prepare($query); $stmt->execute(); while($result = $stmt->fetch(PDO::FETCH_ASSOC)) { $id = ($result['id']); $kamoku = ($result['kamoku']); $tensu = ($result['tensu']); } } catch(PDOException $e) { print "Error!: " . $e->getMessage() . "<br>"; die(); } このソースの中で何らかの処理をして print_r($test); を実行したときに ---print_r($test)の結果--------------------------------- Array ( [0] => Array ( [id] => 1 [kamoku] => 算数 [tensu] => 70 ) [1] => Array ( [id] => 2 [kamoku] => 理科 [tensu] => 88 ) [2] => Array ( [id] => 3 [kamoku] => 国語 [tensu] => 90 ) ) -------------------------------------------------------- というような、文頭で記載したものと同じ結果を得たいと思います。 while内で print_r($result);を行うと1行ずつ下記のような連想配列 Array ( [id] => 1 [kamoku] => 算数 [tensu] => 70 ) が取得できているのでこれを連結させて $test_sample=<<<EOF array("id" => "1","kamoku" => "算数","tensu" => "70"), array("id" => "2","kamoku" => "理科","tensu" => "88"), array("id" => "3","kamoku" => "国語","tensu" => "90") EOF; という配列の中身は作ることができました。 (1)ケース1 $test=array($test_sample); print_r($test); としてもダメで、 Array ( [0] => array("id" => "1","kamoku" => "算数","tensu" => "70"), array("id" => "2","kamoku" => "理科","tensu" => "88"), array("id" => "3","kamoku" => "国語","tensu" => "90") ) というようにうまく多次元連想配列になっていません。 (2)ケース2 $test='array('.$test_sample.')'; print_r(test); としてみたところ、 array( array("id" => "1","kamoku" => "算数","tensu" => "70"), array("id" => "2","kamoku" => "理科","tensu" => "88"), array("id" => "3","kamoku" => "国語","tensu" => "90")) というように配列ではなく単なる文字列として表示されてしまいます。 配列の中身を変数で扱う時には特別な記述法などがあるのでしょうか?

    • ベストアンサー
    • PHP
  • mysql のテーブルの名前は数字ではだめですか

    phpmyadmin で数字名前のテーブル上手く作りましたが。。なぜかmysqlで $query = "CREATE TABLE 2234( id int AUTO_INCREMENT key, 入力日 VARCHAR(10 ) $squery1 = mysql_query($query); (create table "2234"( ...)もcreat table '2234'(..)もだめ) なぜかエラーが出ました?