Я программист php, но захотел расширить горизонты, узнать что ни будь новое. Поэтому решил поучить другие языки и технологии. Выбор пал пока на perl, python и mysql.
Был взят замечательный пакет pymorphy , библиотека Флибуста (только .fb2), sedna для хранения fb2, mysql percona 5.1 для хранения статистики и маленький напильник. Была создана примитивная myisam табличка куда записывалась сколько встречалось предложение, и описание частей речи этого предложения.
По описанию сделал уникальный текстовый индекс, а по числовому полю индекс сделать забыл (думал не пригодится).
Fb2 с флибустры поместил в базу sedan, получилось база где то в 90 GB.
Первым этапом я собрал все уникальные слова, встречающие в библиотеке. Получилось примерно 14 миллионов слов. Это были как уникальные слова, так и их формы, и прочий мусор.
Опытным путем было выявлено, что pymorphy в режиме pickle на инициализацию тратит довольно много времени, и просто его дерзать из php через shell будет очень долго. Так как python я только начал изучать, а для получения части речи нужно было использовать pymorphy, то я принял решение последний запустить как сервер. Это сняло проблему долгой инициализации, и вот что примерно получилось:#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pymorphy import get_morph
import re,sys,socket,pprint,json,chardet,ConfigParser
def do_word(data):
if(data == False or data == ''): print "error incoming data !!!"
tmpWord = re.sub(r"rn",'',data)
word = tmpWord.decode('utf-8').upper()
if(DEBUG == 1): print word
info = morph._decline(word)
if(DEBUG == 1): pprint.pprint(info)
sjSon = json.dumps(info)
return sjSon
config = ConfigParser.ConfigParser()
config.read('pymorphy_conf.ini')
DEBUG = config.getint('decline','DEBUG')
HOST = config.get('decline','HOST')
PORT = config.getint('decline','PORT')
morph = get_morph("/pymorphy/dicts/converted/ru/morphs.pickle",'pickle')
srv = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
srv.bind((HOST,PORT))
while 1:
if(DEBUG == 1): print "Слушаю порт ",PORT
srv.listen(1)
sock,addr = srv.accept()
while 1:
pal = sock.recv(1024)
if not pal:
break
lap = do_word(pal)
sock.send(lap)
sock.close()
После чего обращаясь к этому импровизированному серверу проставил части речи для ранее найденных слов. Все это было сделано за вечер, и не вызвало особых затруднений.
После чего осталось самое простое – собрать статистику. Собирал я ее 2 месяца, после чего надоело. Что стало узким местом сказать трудно. Поиск части речи слова ~ 0.0046 sec, остальные операции тоже вменяемые, коль просто слова получилось быстро собрать. Sedna тоже на прошлых этапах была протестирована, и конечно не летала, но слова ж с нее получилось собрать за пару часов, следовательно ее производительности было достаточно что бы и статистику по предложениям собрать.
В итоге получились следующие данные:
И т.д.
Всего было обработано 90 611 059 предложений.
Забыв сразу сделать индекс по числовому полю я столкнулся с серьезной проблемой. После того, как получилась таблица в 58 миллионов записей и размером в 12 GB индекс строился по ней больше суток и так и не построился. Выручил myisam_sort_buffer_size установленный в 1 GB, индекс построился за часы.
Ps. Конфигурация сервера: AMD Athlon(tm) II X4 635 Processor, 16 gb DDR3,WDC WD7500AACS-00D6B1