mysql_upgrade を実行する

昨日MySQLをアップグレードしたんですが、いろいろすったもんだした挙句、無事に動いていると思っていました。

しかし、ログをよく確認すると以下のようなログが。。。

110420 21:20:14 [ERROR] Column count of mysql.
proc is wrong. Expected 20, found 16.
Created with MySQL 50044, now running 50511.
Please use mysql_upgrade to fix this error.

どうやら問題があるので mysql_upgrade をしてね。ということらしい。

mysql_upgrade とは?

MySQLのリファレンスに以下のようなページがありました。

MySQL のアップグレードでは、その度に、mysql_upgrade を実行します。これにより、データベース内のテーブルにおける最新の MySQL Server との互換性をチェックすることができます。該当テーブルが非互換の場合は、チェックの対象になり、問題があれば、そのテーブルを修正します。mysql_upgrade コマンドは、システム テーブルのアップグレードも行うため、新たな権限と追加機能を使用できるようになります。

MySQLをアップグレードしたら、mysql_upgradeをしてね。だそうです。知らんかった。

mysql_upgrade のやり方

mysqld が起動している状態で以下のコマンドを実行する。

mysql_upgrade -u root -p

-p はパスワードプロンプトを出してねという意味なので、環境に応じて。

というわけで、エラーログもでなくなりました。

MySQL5.5.3にアップグレードしたら再起動に失敗した。

本日、yumでアップデートを行ったところMySQLが起動しなくなって、そりゃーもうあせりました。

  • OSはCentOS5.x。
  • アップデート後のMySQLのバージョンは、mysql-server-5.5.11-1.el5

エラーログ

すぐにログを確認したところ、以下ようなログが残っていました。

/var/log/mysqld.log

[ERROR] /usr/libexec/mysqld: unknown variable 'default-character-set=utf8'

取り急ぎ/etc/my.cnfから該当する行をコメントアウトして再起動したら無事に復活。

原因

いつものとおりGoogle先生に聞いたら以下のような記述がありました。

[url2link url="http://www.mysql.gr.jp/frame/modules/news/article.php?storyid=175" summary="- いくつかの非推奨だった変数やコマンド、オプションなどが廃止になっています。特に TYPE を廃止して今後は ENGINE を使用することや、サーバの default-character-set オプションを廃止して今後は character-set-server を使用すること(クライアントの default-character-set はそのまま)など影響を受ける方も多いでしょう。"]

というわけで、[mysqld] ディテクティブの default-char-set を character-set-server に変更したら無事に再起動ができました。

ただし、[client] ディレクティブの default-charset は従来通りなので間違えないようにしましょう。

その他の廃止項目

他にも廃止となったり過去のバージョンですでに非推奨となっている項目があるようです。

アップデートする予定がなくても、今のうちに書き換えておいたほうがベターなようです。

http://d.hatena.ne.jp/sakaik/20100414/mysql533obsol

MySQLで任意の緯度・経度から最も近いデータを取得するSQL

MySQLには位置情報を扱う機能があるが、コツが必要なようなのでメモ

テーブルを作成

テーブルを作成する際に注意するべきなのは以下の2点。

  • 緯度・経度を保存するフィールドのデータ型は、 “geometry” とする。
  • インデックスに SPATIAL を使用する。

自分の環境の、phpMyAdmin では SPATIAL インデックスが作成できなかったので、以下のようなSQLを発行した。

ALTER TABLE geom ADD SPATIAL INDEX(geo);

テーブルのスキーマは以下のような感じ。

CREATE TABLE `points` (
  `loc` varchar(20) NOT NULL,
  `point` varchar(50) NOT NULL,
  `geo` geometry NOT NULL,
  PRIMARY KEY  (`loc`),
  UNIQUE KEY `point` (`point`),
  SPATIAL KEY `geo` (`geo`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

実際のSQL

以下のようなSQLで、指定した緯度・経度に最も近い1件を取り出すことができる。

SELECT
    loc,
    GLength(
        GeomFromText(
            CONCAT(
                'LineString(135.8 33.48,',
                X(geo),
                ' ',
                Y(geo),
                ')'
            )
        )
    ) AS len
FROM points
WHERE
    MBRContains(
        GeomFromText(
            Concat(
                'LineString(',
                135.8 + 1,
                ' ',
                33.48 + 1,
                ',',
                135.8 - 1,
                ' ',
                33.48 - 1,
                ')'
            )
        ),
        geo
    )
ORDER BY len limit 0,1

ここで注意!

単純に最も近い1件を取り出すだけならWHERE句は必要ないが、MySQLでは空間インデックス(SPATIAL)をORDER BYで使用することはできなようだ。(たぶん。。。)

そのため、WHERE句で対象となるデータを減らした上で、ORDER BYしたらパフォーマンスが向上したので、やむなくWHERE句を挿入した。

WHERE句内で使用している “+ 1″ とか “- 1″ のあたりは指定した緯度経度を基準にした約100kmに相当する矩形をつくるという意味であるが、対象となるデータの件数や密度によって、増やしたり減らしたりする必要がある。

これに関しては、以下のサイトで詳しく検証されている。

MySQL TIPS 3 空間情報(geometry)を使って経度・緯度の検索を高速化する – イノベートな非日常

が、explain の結果にfilesort とあるので、ORDER BYではやはりインデックスが使用されていないことに注意する必要がある。

というわけで。。。

MySQLで位置情報を取り扱う際にORDER BYを使用するには注意が必要。

Google MAPを使用する等ユーザーインターフェースで工夫して逃げる方が得策かもしれない。

MySQL用郵便番号データをupdate

1月末に父が他界したりして、なんとなくダラダラと何もする気がしない日が続きまして、しばらく振りに更新しました。

2月27日にゆうびんホームページの郵便番号データが更新されました。
それに伴い、MySQL用郵便番号データも更新しました。

ダウンロード

最新版のダウンロードはこちらのページからどうぞ。

MySQLによる日本語全文検索

php+MySQL+mecabで日本語全文検索システムを作成した。

弊社で提供しているデジタルカタログサービスで提供するデジタルカタログに全文検索機能を実装するためで、実装したサンプルは以下の通り。

JULIA OPEN WATER MANUAL(お客様のご好意によりここで紹介しています。)

MySQLによる日本語全文検索とは?

MySQLにはもともとFULLTEXT型というインデックス機能がある。

これを使用するとGoogleやYahooのような全文検索機能を利用することができるのだが、残念ながら元となる文章が英文のように単語の間にスペースが入っている必要があり、そのままでは日本語では使用できない。

そこで、MySQLのFULLTEXT検索を行うために以下のような方法が使用される。

  1. mecabなどの分かち書きシステムを利用してインデックスを構築する。
  2. n-gramを使用してインデックスを構築する。

それぞれの技術的な詳細はここでは紹介しないが、上記の2種類の方法には以下のようなメリット/デメリットがある。

mecabを使用した全文検索のメリット/デメリット

メリット

  • LIKEを使用した検索に比べて非常に高速に動作する。
  • 関連性が高いものから低いものへ自動的にソートされる。
  • 文字セットをutf8_unicode_ciにすれば大文字小文字や全角半角等を同じものとして検索できる。

デメリット

  • データのinsertやupdateに時間がかかる。
  • 分かち書きの精度により、検出漏れがあり得る。

n-gramを使用した際のメリット/デメリット

メリット

  • LIKEを使用した検索に比べて非常に高速に動作する。
  • アルゴリズムが単純で特別なライブラリのインストールを必要としない。
  • mecabを使用した場合に比べて検索漏れが生じない。

デメリット

  • 誤検出が発生しうる。(「日本人」と検索した場合に「本人は日本」もマッチする)
  • 関連性によるソートがうまく動作しない。
  • insertやupdateに時間がかかる。

今回使用した方法

以上の結果をふまえていろいろテストした結果、mecabを使用して検索を行うこととした。

事例で紹介したサンプルでは、pdfから抽出したテキストをmecabで分かち書きしてMySQLにinsertしFULLTEXTインデックスを適用した。

mecabを用いた際の最も大きなデメリットとなる検出漏れの問題については、検索に用いるキーワードもmecabにより分かち書きして、さらにmatch() 〜 against()を使用することで分かち書きされた単語によるOR条件で検索されるため、ほとんど問題にならないことがわかった。

ORで検索されると(正確にはmatch()〜against())、例えば「最大水深」という単語が「最大」と「水深」に分割され、それらのどちらか一方の単語しか無いページも検索されてしまう。

しかし関連度順にソートされるため「最大水深」とある文書がきちんと上位表示されるため、結果的に使い勝手が非常にいいものとなった。

はまったこと

主にMySQL側の設定であるが、いくつかはまった。

  1. MySQLデフォルトでは、4文字以下の単語のインデックスが作成されないため日本語には不向きである。そのためmy.cnfのft_min_word_len=1という行をmysqldディレクティブに追加する必要がある。(この値の変更をした場合はインデックスを再構築すること。これを忘れた!)
  2. against()にIN BOOLEAN MODEを指定すると検索時にANDやOR、ワイルドカードなどを使用できるが、関連度順にソートされないため注意する必要がある。