FullText Search Innodb Fails, MyIsam возвращает результаты - PullRequest
1 голос
/ 09 апреля 2019

Я обновил таблицу с myisam до innodb, но у меня не такая производительность. innodb возвращает оценку 0, когда должно быть какое-то отношение. Таблица myisam возвращает совпадение для того же термина (я сохранил копию старой таблицы, чтобы можно было выполнить тот же запрос).

SELECT MATCH (COLUMNS) AGAINST ('+"Term Ex"' IN BOOLEAN MODE) as score
FROM table_myisam
where id = 1;

Возвращает:

+-------+
| score |
+-------+
|     1 |
+-------+

но:

SELECT MATCH (COLUMNS) AGAINST ('+"Term Ex"' IN BOOLEAN MODE) as score
FROM table
where id = 1;

возвращается:

+-------+
| score |
+-------+
|     0 |
+-------+

Я думал, что ex, возможно, не был проиндексирован, потому что innodb_ft_min_token_size был установлен в 3. Я понизил это до 1 и оптимизировал таблицу, но это никак не повлияло. Содержимое столбца имеет длину 99 символов, поэтому я предположил, что весь столбец не был проиндексирован из-за innodb_ft_max_token_size. Я также увеличил это значение до 150 и снова запустил оптимизацию, но снова получил тот же результат.

Единственная разница между этими таблицами - это двигатель и набор символов. Эта таблица использует utf8, таблица myisam использует latin1.

Кто-нибудь видел это поведение, или у вас есть совет, как его решить?

UPDATE: Я добавил ft_stopword_file="" к своему my.cnf и снова набрал OPTIMIZE TABLE table. На этот раз я получил

оптимизировать | примечание | Таблица не поддерживает оптимизацию, вместо этого выполняется воссоздание + анализ

Запрос сработал после этого изменения. Ex - не стоп-слово, хотя не уверен, почему это будет иметь значение.

Новый запрос, который не выполнен, хотя:

SELECT MATCH (Columns) AGAINST ('+Term +Ex +in' IN BOOLEAN MODE) as score FROM Table where id = 1;

+-------+
| score |
+-------+
|     0 |
+-------+

in приводит к сбою, но это следующее слово в моей таблице.

SELECT MATCH (Columns) AGAINST ('+Term +Ex' IN BOOLEAN MODE) as score FROM Table where id = 1;

+--------------------+
| score              |
+--------------------+
| 219.30206298828125 |
+--------------------+

Я также пытался CREATE TABLE my_stopwords(value VARCHAR(30)) ENGINE = INNODB;, затем обновил my.cnf с innodb_ft_server_stopword_table='db/my_stopwords'. Я перезапустил и побежал:

show variables like 'innodb_ft_server_stopword_table';

который вернул:

+---------------------------------+---------------------------+
| Variable_name                   | Value                     |
+---------------------------------+---------------------------+
| innodb_ft_server_stopword_table | 'db/my_stopwords'; |
+---------------------------------+---------------------------+

поэтому я подумал, что in не приведет к сбою запроса сейчас, но он продолжается. Я также снова попробовал OPTIMIZE TABLE table и даже ALTER TABLE table DROP INDEX ... и ALTER TABLE table ADD FULLTEXT KEY ..., ни один из которых не оказал влияния.

Второе обновление Проблема со стоп-словами.

$userinput = preg_replace('/\b(a|about|an|are|as|at|be|by|com|de|en|for|from|how|i|in|is|it|la|of|on|or|that|the|this|to|was|what|when|where|who|will|with|und|the|www)\b/', '', $userinput);

решает проблему, но это не кажется мне хорошим решением. Мне бы хотелось, чтобы решение, которое не использовало стоп-слова, нарушало бы это в mysql.

Данные таблицы стоп-слов:

CREATE TABLE `my_stopwords` (
  `value` varchar(30) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1

и

Name: my_stopwords
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 0
 Avg_row_length: 0
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2019-04-09 17:39:55
    Update_time: NULL
     Check_time: NULL
      Collation: latin1_swedish_ci
       Checksum: NULL
 Create_options: 
        Comment: 

Ответы [ 3 ]

3 голосов
/ 16 апреля 2019

Вот пошаговая процедура, которая должна была воспроизвести вашу проблему. (Это именно то, как вы должны были написать свой вопрос.) Среда представляет собой недавно установленную виртуальную машину с Debian 9.8 и Percona Server Ver 5.6.43-84.3 .

  1. Создание таблицы InnoDB с полнотекстовым индексом и некоторыми фиктивными данными:

    create table test.ft_innodb (
        txt text,
        fulltext index (txt)
    ) engine=innodb charset=utf8 collate=utf8_unicode_ci;
    
    insert into test.ft_innodb (txt) values
        ('Some dummy text'),
        ('Text with a long and short stop words in it ex');
    
  2. Выполните тестовый запрос, чтобы убедиться, что он еще не работает, как нам нужно:

    select txt
        , match(t.txt) against ('+some' in boolean mode) as score0
        , match(t.txt) against ('+with' in boolean mode) as score1
        , match(t.txt) against ('+in'   in boolean mode) as score2
        , match(t.txt) against ('+ex'   in boolean mode) as score3
    from test.ft_innodb t;
    

    Результат (округлено):

    txt                                            | score0 | score1 | score2 | score3
    -----------------------------------------------|--------|--------|--------|-------
    Some dummy text                                | 0.0906 | 0      | 0      | 0
    Text with a long and short stop words in it ex | 0      | 0      | 0      | 0
    

    Как видите, он не работает со стоп-словами ("+ с") или с короткими словами ("+ ex").

  3. Создать пустую InnoDB таблицу для пользовательских стоп-слов:

    create table test.my_stopwords (value varchar(30)) engine=innodb;
    
  4. Редактировать /etc/mysql/my.cnf и добавить следующие две строки в блок [mysqld]:

    [mysqld]
    # other settings
    innodb_ft_server_stopword_table = "test/my_stopwords"
    innodb_ft_min_token_size = 1
    
  5. Перезапустите MySQL с помощью service mysql restart

  6. Запустите запрос из (2.) еще раз (результат должен быть таким же)

  7. Перестройка полнотекстового индекса с

    optimize table test.ft_innodb;
    

    Фактически будет перестроена вся таблица, включая все индексы.

  8. Еще раз выполнить тестовый запрос из (2.). Теперь результат:

    txt                                            | score1 | score1 | score2 | score3
    -----------------------------------------------|--------|--------|--------|-------
    Some dummy text                                | 0.0906 | 0      | 0      | 0
    Text with a long and short stop words in it ex | 0      | 0.0906 | 0.0906 | 0.0906
    

Вы видите, это прекрасно работает для меня. И это довольно просто воспроизвести. (Опять же - это то, как вы должны были написать свой вопрос.)

Поскольку ваша процедура скорее хаотична, чем детальна, трудно сказать, что может пойти вам не так. Например:

CREATE TABLE my_stopwords(value VARCHAR(30)) ENGINE = INNODB;

Не содержит информации, в какой базе данных вы определили эту таблицу. Обратите внимание, что все мои таблицы имеют префикс соответствующей базы данных. Теперь рассмотрим следующее: я изменяю my.cnf и устанавливаю innodb_ft_server_stopword_table = "db/my_stopwords". Примечание. На моем сервере такой таблицы нет (даже схемы db не существует). Перезагрузите сервер MySQL. И проверьте новые настройки с помощью

show variables like 'innodb_ft_server_stopword_table';

Возвращает:

    Variable_name                   | Value
    --------------------------------|----------------
    innodb_ft_server_stopword_table | db/my_stopwords

И после optimize table test.ft_innodb; тестовый запрос возвращает это:

    txt                                            | score0 | score1 | score2 | score3
    -----------------------------------------------|--------|--------|--------|-------
    Some dummy text                                | 0.0906 | 0      | 0      | 0
    Text with a long and short stop words in it ex | 0      | 0      | 0      | 0.0906

Видишь? Он больше не работает со стоп-словами. Но это работает с короткими безостановочными словами, такими как "+ ex". Поэтому убедитесь, что таблица, которую вы определили в innodb_ft_server_stopword_table, действительно существует.

3 голосов
/ 14 апреля 2019

Есть несколько различий между FULLTEXT MyISAM и InnoDB.Я думаю, что вы были пойманы обработкой «коротких» слов и / или стоп-слов.MyISAM будет показывать строки, но InnoDB не сможет.

Что я сделал при использовании FT (и после переключения на InnoDB), чтобы отфильтровать ввод пользователя, чтобы избежать коротких слов.Это требует дополнительных усилий, но дает мне желаемые строки.Мой случай немного отличается, так как полученный запрос выглядит примерно так.Обратите внимание, что я добавил +, чтобы требовать слова, но не для слов короче 3 (мой ft_min_token_size равен 3).Эти поиски были для build a table и build the table:

WHERE match(description) AGAINST('+build* a +table*' IN BOOLEAN MODE)
WHERE match(description) AGAINST('+build* +the* +table*' IN BOOLEAN MODE)

(Конечный * может быть избыточным; я не исследовал это.)

Другой подход

Так как FT очень эффективен для коротких слов без остановки, выполните поиск в два этапа, каждый из которых является необязательным. Для поиска «длинного слова» выполните

WHERE MATCH(d) AGAINST ('+long +word' IN BOOLEAN MODE)
  AND d REGEXP '[[:<:]]a[[:>:]]'

Первая часть быстро сокращает возможные строки, ища «long» и «word» (как words ).Вторая часть гарантирует, что в строке тоже есть слово a.REGEXP стоит дорого, но будет применяться только к тем строкам, которые проходят первый тест.

Для поиска просто для "длинного слова":

WHERE MATCH(d) AGAINST ('+long +word' IN BOOLEAN MODE)

Для поиска просто по слову "a":

WHERE d REGEXP '[[:<:]]a[[:>:]]'

Предупреждение: этот случай будет медленным.

Примечание: мои примеры позволяют использовать слова вв любом порядке и в любом месте строки.То есть эта строка будет соответствовать во всех моих примерах: «Она жаждала слова от него».

0 голосов
/ 19 апреля 2019

Обычный метод поиска - создание дополнительного столбца с очищенной строкой для поиска. Затем добавьте индекс FULLTEXT к этому столбцу вместо исходного столбца.

В вашем случае удаление стоп-слов является основным отличием. Но также может быть пунктуация, которая может (должна?) Быть удалена. Иногда переносимые слова или слова или сокращения или номера деталей или номера моделей вызывают проблемы. Они могут быть изменены, чтобы изменить пунктуацию или интервал, чтобы сделать его более совместимым с требованиями FT и / или вкусом ввода пользователя. Другое дело, чтобы добавить слова в столбец строки поиска, которые являются общими ошибками написания слов в столбце.

Конечно, это больше работы, чем вы хотели бы сделать. Но я думаю, что это дает жизнеспособное решение.

...