LoginSignup
0
0

More than 1 year has passed since last update.

Colabで全文検索(その4:Mroonga編)

Last updated at Posted at 2022-03-28

Github Open In Colab

ご覧いただきありがとうございます。
Google Colaboratoryにアカウントをお持ちの方は、上の「Open in Colab」と書かれた青いボタンを押せば直接notebookをColabで開けます。ぜひ動かしてみてください。
過去の記事も含め、全てのコードをGithubで公開しています。
  

各種全文検索ツールをColabで動かしてみるシリーズです。全7回の予定です。今回はMroongaです。MroongaはGroongaをMySQLで使えるようにした拡張機能です。MySQLのネイティブな全文検索に対してどれぐらい優位性があるのでしょうか。

処理時間の計測はストレージのキャッシュとの兼ね合いがあるので、2回測ります。2回目は全てがメモリに載った状態での性能評価になります。ただ1回目もデータを投入した直後なので、メモリに載ってしまっている可能性があります。

準備

まずは検索対象のテキストを日本語wikiから取得して、Google Driveに保存します。

Google Driveに約1GBの空き容量が必要です。以前のデータが残っている場合は取得せず再利用します。

Google Driveのマウント

from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive

jawikiの取得とjson形式に変換。90分ほど時間がかかります。

他の全文検索シリーズでも同じデータを使うので、他の記事も試す場合は wiki.json.bz2 を捨てずに残しておくことをおすすめします。

%%time
%cd /content/
import os
if not os.path.exists('/content/drive/MyDrive/wiki.json.bz2'):
    !wget https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2
    !pip install wikiextractor
    !python -m wikiextractor.WikiExtractor --no-templates --processes 4 --json -b 10G -o - jawiki-latest-pages-articles.xml.bz2 | bzip2 -c > /content/drive/MyDrive/wiki.json.bz2
/content
CPU times: user 1.06 ms, sys: 964 µs, total: 2.02 ms
Wall time: 8.16 ms

json形式に変換されたデータを確認

import json
import bz2

with bz2.open('/content/drive/MyDrive/wiki.json.bz2', 'rt', encoding='utf-8') as fin:
    for n, line in enumerate(fin):
        data = json.loads(line)
        print(data['title'].strip(), data['text'].replace('\n', '')[:40], sep='\t')
        if n == 5:
            break
アンパサンド	アンパサンド(&, )は、並立助詞「…と…」を意味する記号である。ラテン
言語	言語(げんご)は、広辞苑や大辞泉には次のように解説されている。『日本大百科事典』
日本語	 日本語(にほんご、にっぽんご)は、日本国内や、かつての日本領だった国、そして日
地理学	地理学(ちりがく、、、伊:geografia、)は、。地域や空間、場所、自然環境
EU (曖昧さ回避)	EU
国の一覧	国の一覧(くにのいちらん)は、世界の独立国の一覧。対象.国際法上国家と言えるか否

Mroongaのインストール

!sudo apt-get install -y -V software-properties-common lsb-release
!sudo add-apt-repository -y universe
!sudo add-apt-repository "deb http://security.ubuntu.com/ubuntu $(lsb_release --short --codename)-security main restricted"
!sudo add-apt-repository -y ppa:groonga/ppa
!sudo apt-get update
!sudo apt-get install -y -V mysql-server-mroonga
Reading package lists... Done
Building dependency tree       
Reading state information... Done
lsb-release is already the newest version (9.20170808ubuntu1).
・
・
・
W: Target Packages (restricted/binary-amd64/Packages) is configured multiple times in /etc/apt/sources.list:44 and /etc/apt/sources.list:51
W: Target Packages (restricted/binary-all/Packages) is configured multiple times in /etc/apt/sources.list:44 and /etc/apt/sources.list:51

MySQLの起動

サーバーを起動します。

!service mysql start
 * Starting MySQL database server mysqld
No directory, logging in with HOME=/
   ...done.

Mroongaを導入します。

!mysql < /usr/share/mroonga/install.sql

Mroongaが導入されていることを確認します。

!echo "show engines;" | mysql
Engine	Support	Comment	Transactions	XA	Savepoints
InnoDB	DEFAULT	Supports transactions, row-level locking, and foreign keys	YES	YES	YES
MRG_MYISAM	YES	Collection of identical MyISAM tables	NO	NO	NO
MEMORY	YES	Hash based, stored in memory, useful for temporary tables	NO	NO	NO
BLACKHOLE	YES	/dev/null storage engine (anything you write to it disappears)	NO	NO	NO
MyISAM	YES	MyISAM storage engine	NO	NO	NO
CSV	YES	CSV storage engine	NO	NO	NO
Mroonga	YES	CJK-ready fulltext search, column store	NO	NO	NO
PERFORMANCE_SCHEMA	YES	Performance Schema	NO	NO	NO
FEDERATED	NO	Federated MySQL storage engine	NULL	NULL	NULL
ARCHIVE	YES	Archive storage engine	NO	NO	NO

DBを作成します。

!echo "create database db" | mysql

データのインポート

pythonからMySQLを操作できるmysqlclientをインストールします。

!pip install mysqlclient
Collecting mysqlclient
  Downloading mysqlclient-2.1.0.tar.gz (87 kB)
[K     |████████████████████████████████| 87 kB 6.0 MB/s 
[?25hBuilding wheels for collected packages: mysqlclient
  Building wheel for mysqlclient (setup.py) ... [?25l[?25hdone
  Created wheel for mysqlclient: filename=mysqlclient-2.1.0-cp37-cp37m-linux_x86_64.whl size=99969 sha256=32eb347460dc8959efcca474f36e167fbdb8de730bc5818cf0c4e74e6ee4fec3
  Stored in directory: /root/.cache/pip/wheels/97/d4/df/08cd6e1fa4a8691b268ab254bd0fa589827ab5b65638c010b4
Successfully built mysqlclient
Installing collected packages: mysqlclient
Successfully installed mysqlclient-2.1.0

データを解凍して50万件のデータをインポートします。全文検索用のインデックスを作りながら読み込むので、1時間以上かかります。

import MySQLdb
import json
import bz2
from tqdm.notebook import tqdm

db = MySQLdb.connect(host='localhost', user='root', db='db', charset='utf8mb4')
cursor = db.cursor()

cursor.execute('drop table if exists wiki_jp')
cursor.execute('create table wiki_jp('
 'id bigint unsigned not null auto_increment primary key,'
 'title tinytext collate utf8mb4_unicode_ci storage memory,'
 'body mediumtext collate utf8mb4_unicode_ci storage memory,'
 'fulltext index (body)'
 ') engine = Mroonga default charset utf8')

limit = 500000
insert_wiki = 'insert into wiki_jp (title, body) values (%s, %s);'

with bz2.open('/content/drive/MyDrive/wiki.json.bz2', 'rt', encoding='utf-8') as fin:
    n = 0
    for line in tqdm(fin, total=limit*1.5):
        data = json.loads(line)
        title = data['title'].strip()
        body = data['text'].replace('\n', '')
        if len(title) > 0 and len(body) > 0:
            cursor.execute(insert_wiki, (title, body))
            n += 1
        if n == limit:
            break
db.commit()
db.close()
  0%|          | 0/750000.0 [00:00<?, ?it/s]

データが書き込まれたテーブルを確認します。

!echo "show columns from db.wiki_jp" | mysql
Field	Type	Null	Key	Default	Extra
id	bigint(20) unsigned	NO	PRI	NULL	auto_increment
title	tinytext	YES		NULL	
body	mediumtext	YES	MUL	NULL	

登録件数を確認します。

!echo "select count(*) from db.wiki_jp" | mysql
count(*)
500000

インデックスを使わない検索

bodyに「日本語」を含むレコードの数を取得します。

%%time
!echo "select sql_no_cache count(*) from db.wiki_jp where body like '%日本語%'" | mysql -vvv
--------------
select sql_no_cache count(*) from db.wiki_jp where body like '%日本語%'
--------------

+----------+
| count(*) |
+----------+
|    17006 |
+----------+
1 row in set, 1 warning (7.84 sec)

Bye
CPU times: user 76.2 ms, sys: 13.7 ms, total: 89.8 ms
Wall time: 7.96 s

bodyに「日本語」を含むレコードを取得します。

%%time
!echo "select sql_no_cache * from db.wiki_jp where body like '%日本語%'" | mysql -vvv | tail -3 | head -1
17006 rows in set, 1 warning (8.91 sec)
CPU times: user 112 ms, sys: 18.5 ms, total: 130 ms
Wall time: 12.7 s

2回目

%%time
!echo "select sql_no_cache * from db.wiki_jp where body like '%日本語%'" | mysql -vvv | tail -3 | head -1
17006 rows in set, 1 warning (8.83 sec)
CPU times: user 119 ms, sys: 12.8 ms, total: 132 ms
Wall time: 12.6 s

インデックスを使った検索

bodyに「日本語」を含むレコードの数を取得します。

%%time
!echo "select sql_no_cache count(*) from db.wiki_jp where match (body) against ('日本語' in boolean mode)" | mysql -vvv
--------------
select sql_no_cache count(*) from db.wiki_jp where match (body) against ('日本語' in boolean mode)
--------------

+----------+
| count(*) |
+----------+
|    17006 |
+----------+
1 row in set, 1 warning (0.08 sec)

Bye
CPU times: user 7.8 ms, sys: 3.96 ms, total: 11.8 ms
Wall time: 118 ms

MySQLが2.5秒、Groongaが0.3秒ですから、相当速いです。なんらかの並列検索が働いている感じです。

bodyに「日本語」を含むレコードを取得します。

%%time
!echo "select sql_no_cache * from db.wiki_jp where match (body) against ('日本語' in boolean mode)" | mysql -vvv | tail -3 | head -1
17006 rows in set, 1 warning (1.17 sec)
CPU times: user 57 ms, sys: 11 ms, total: 68 ms
Wall time: 5.03 s

2回目

%%time
!echo "select sql_no_cache * from db.wiki_jp where match (body) against ('日本語' in boolean mode)" | mysql -vvv | tail -3 | head -1
17006 rows in set, 1 warning (1.17 sec)
CPU times: user 47.5 ms, sys: 13.3 ms, total: 60.8 ms
Wall time: 4.93 s

MySQL単体では8秒程度でしたから、5秒で少し速くなっています。ただ、Groonga単体では2秒程度でした。

参考として、idとtitleだけを取得してみます。

%%time
!echo "select sql_no_cache id title from db.wiki_jp where match (body) against ('日本語' in boolean mode)" | mysql -vvv | tail -3 | head -1
17006 rows in set, 1 warning (0.14 sec)
CPU times: user 10.8 ms, sys: 7.1 ms, total: 17.9 ms
Wall time: 219 ms

やはりbodyの読み込みの時間が大半を占めているようです。

MySQLの停止

!service mysql stop
 * Stopping MySQL database server mysqld
   ...done.
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0