PostgreSQLでEUCJPとUTF8のTEXT中間一致検索速度を比較


動機

ASCIIは容量少なくて羨ましい・・。日本語の文章ならUTF8よりEUCJPとかSJISの方が低容量だ!もしかしてデータベースの検索速度も上がるのかな?

使用データ

Wikipediaを使います。
wp2txtでwikipediaのコーパスを作るまでの道のりを参考に本文を取得します。
wp2txt --input-file jawiki-latest-pages-articles.xml --num-threads=8 --no-list --no-heading --no-title --no-marker
でテキストを出力し、その後はRubyで↓のような前処理とエンコードをします。

txt = File.read("元テキストファイル")
# 色々消す
# tsvとして読み込ませるため \t   念の為 \r
# SQLの挿入で困らないように \\   空白行 \n{2,}
txt.delete!("\t\r\\")
txt.gsub!(/\n{2,}/, "\n")

opts =  {:undef=>:replace, :invalid=>:replace, :replace=>""}
utf8_txt = txt.encode(Encoding::EUC_JP, opts).encode(Encoding::UTF_8, opts)
eucjp_txtx = utf8_txt.encode(Encoding::EUC_JP, opts)

環境

  • OS: Ubuntu 18.04.1 LTS
  • PostgreSQL: 10.5 (Ubuntu 10.5-0ubuntu0.18.04)
    • 一切チューニングをしない
    • インデックスなにそれ?

準備

SJISが入出力時の文字コードとして指定できるけど、内部データには使用できないので、UTF8とEUCJPを使用する。

$ sudo  localedef -f EUC-JP -i ja_JP ja_JP.EUC-JP
postgres=# CREATE DATABASE utf8 WITH ENCODING 'UTF8';
CREATE DATABASE
postgres=# CREATE DATABASE "eucjp" WITH TEMPLATE="template0" ENCODING='EUC_JP' LC_COLLATE='C' LC_CTYPE='C';
CREATE DATABASE
postgres=# \l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+----------+----------+-------------+-------------+-----------------------
 eucjp     | postgres | EUC_JP   | C           | C           | 
 postgres  | postgres | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | 
 template0 | postgres | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 utf8      | postgres | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | 
(5 rows)

postgres=# \c utf8
You are now connected to database "utf8" as user "postgres".
utf8=# CREATE TABLE utf8text ( text text );
CREATE TABLE
eucjp=# \encoding
UTF8
utf8=# COPY utf8text FROM '/path/to/utf8.txt';
COPY 10191461
utf8=# \c eucjp
You are now connected to database "eucjp" as user "postgres".
eucjp=# CREATE TABLE eucjptext ( text text );
CREATE TABLE
eucjp=# \encoding EUC-JP
eucjp=# \encoding
EUC_JP
eucjp=# COPY eucjptext FROM '/path/to/eucjp.txt';
COPY 10191461

-- なんとなくデータベースの容量を確認
eucjp=# SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database;
  datname  | pg_size_pretty 
-----------+----------------
 postgres  | 7629 kB
 template1 | 7497 kB
 template0 | 7497 kB
 utf8      | 2961 MB
 eucjp     | 2142 MB
(5 rows)

計測

postgres=# \timing
Timing is on.
postgres=# \c utf8 
You are now connected to database "utf8" as user "postgres".
-- 下記コマンドをそれぞれ5回
utf8=# SELECT count(text) FROM utf8text WHERE text LIKE '%に%';
utf8=# SELECT count(text) FROM utf8text WHERE text LIKE '%ナレッジコミュニティ%';

utf8=# \c eucjp 
You are now connected to database "eucjp" as user "postgres".
-- 下記コマンドをそれぞれ5回
eucjp=# SELECT count(text) FROM eucjptext WHERE text LIKE '%に%';
eucjp=# SELECT count(text) FROM eucjptext WHERE text LIKE '%ナレッジコミュニティ%';

結果

単位は [ms]

  • '%に%'
UTF8 EUCJP
1回目 1880.681 1532.657
2回目 1876.743 1588.650
3回目 1942.774 1549.886
4回目 1883.708 1576.893
5回目 1904.838 1603.320
平均 1897.7488 1570.2812
  • '%ナレッジコミュニティ%'
UTF8 EUCJP
1回目 4053.864 2562.225
2回目 3983.899 2510.445
3回目 3926.061 2475.307
4回目 3952.371 2457.570
5回目 3946.132 2465.966
平均 3972.4654 2494.3026

コメント

まあまあ差が出てしまった。
入出力で検索語の文字コードを変換するロスとかを考えたら、やっぱりUTF8安定かなあ。

色々調べていたらKEEPONLYALNUMとかあったけど、これ多分ONのまま計測してるよな~。