Я лишь строю догадки, но вот моя теория из фольги:
Я запускал Reindex одновременно дважды (сначала ccnew, затем ccnew1),
и оба раза возникали ошибки.
Возможно, при сбое он просто не откатывает изменения, а оставляет мусор и завершает работу.
Я лишь строю догадки, но вот моя теория из фольги:
Я запускал Reindex одновременно дважды (сначала ccnew, затем ccnew1),
и оба раза возникали ошибки.
Возможно, при сбое он просто не откатывает изменения, а оставляет мусор и завершает работу.
Это звучит очень, очень, очень вероятно.
Добавлю свои два цента.
Индексы TOAST нельзя удалять таким способом.
Мне пришлось сделать следующее:
su postgres
psql
\connect discourse
drop index pg_toast.pg_toast_309322_index_ccnew;
drop index pg_toast.pg_toast_309322_index_ccnew1;
Вышеуказанное относится только к pg_toast, так как пользователь discourse не имеет доступа к этому индексу.
PG::InsufficientPrivilege: ERROR: permission denied for schema pg_toast
Удаление индексов для тостов — это очень страшно. Но, похоже, у вас нет другого выбора. Да, это нужно делать через psql.
И вот наступает счастливый реиндекс:
discourse=# REINDEX SCHEMA CONCURRENTLY public;
REINDEX
![]()
Отличная находка! Я нашел все индексы, содержащие «ccnew» в имени, с помощью этого запроса.
psql
\connect discourse
select tablename,indexname,indexdef from pg_indexes where indexname LIKE '%ccnew%';
incoming_referer.csv (7.2 KB)
Оказалось, что у меня целых 30 таких индексов, и все они находились в таблице incoming_referers. Поэтому я проверил, что все индексы ccnew действительно являются дубликатами, используя колонку indexdef в этом запросе.
select indexname,indexdef from pg_indexes where tablename = 'incoming_referers';
ccnew.csv (6.6 KB)
Затем я успешно удалил все из них.
DROP INDEX incoming_referers_pkey_ccnew;
DROP INDEX incoming_referers_pkey_ccnew_ccnew;
DROP INDEX incoming_referers_pkey_ccnew1;
...и так далее для всех 30
После этого я снова переиндексировал всю схему, но снова не смог восстановить два одинаковых индекса incoming_referers ccnew, а также обнаружил три индекса pg_toast. Я удалил их и снова переиндексировал всю схему, но снова возникли ошибки: я нашел еще несколько индексов ccnew в схеме discourse. Я переиндексировал в третий раз…
Мне не удается завершить полную переиндексацию без ошибок: система постоянно создает новые индексы ccnew, а затем не может их восстановить. После четырех полных перестроек я удалил индексы ccnew и отказался от дальнейших попыток. Я мог бы попробовать восстановить их без режима CONCURRENTLY, но это привело бы к значительным простоям.
В любом случае, я предполагаю, что у большинства пользователей, обновлявшихся с PG10 до PG12 и пытавшихся затем выполнить переиндексацию, есть эти лишние индексы ccnew, и их следует удалить. Они лишь занимают место и увеличивают нагрузку на дисковый ввод-вывод без какой-либо пользы.
Я запустил его аналог в Data-Explorer. Так было немного проще управлять.
Что я сделал: взял все индексы ccnew/ccnew1/ccnew2…ccnewn, удалил их и выполнил перестроение индексов заново. Это помогло мне.
ccnew — это особенность PostgreSQL, связанная с пометкой индексов, и, думаю, это какая-то неэффективность в процессе, из-за которой такие индексы остаются, если перестроение индексов по какой-то причине не удаётся.
Я могу лишь посоветовать вам сначала устранить коренную причину проблемы. У меня были дублирующиеся теги, которые мне пришлось удалить перед удалением индексов. Если бы у меня остался хотя бы один дублирующийся тег, индексация прошла бы неправильно, создался бы новый индекс ccnew'n', и процесс завершился бы ошибкой.
Вы правы, у меня всё ещё есть дубликаты — невозможно перестроить индексы, не связанные с ccnew, без параллельного выполнения. Мне нужно удалить эти дублирующиеся строки.
discourse=# reindex index index_incoming_referers_on_path_and_incoming_domain_id;
ERROR: could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id"
DETAIL: Key (path, incoming_domain_id)=(/search/, 3433) is duplicated.
discourse=# reindex index index_incoming_referers_on_path_and_incoming_domain_id;
ERROR: could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id"
DETAIL: Key (path, incoming_domain_id)=(/search/, 1861) is duplicated.
Очень странно, что в таблице incoming_referers я вижу только одну строку с каждым из этих значений incoming_domain_id. Так почему же они считаются дубликатами?
discourse=# select * from incoming_referers where path='/search/' AND incoming_domain_id IN (1861,3433);
id | path | incoming_domain_id
-------+----------+--------------------
42845 | /search/ | 1861
40763 | /search/ | 3433
(2 rows)
@sam или @riking, стоит ли мне удалить эти две строки следующим образом:
DELETE FROM incoming_referers WHERE path='/search/' AND incoming_domain_id IN (1861,3433);
… Кажется, я всё-таки учу Postgres, хах.
Нет, это удалит оба записи — вам нужно использовать WHERE, чтобы найти два id, и выбрать только один из id для удаления.
Ваш запрос использует повреждённый индекс, поэтому вы видите только одну строку для каждого. Попробуйте это:
... where path LIKE '%/search/' ...
Хорошо, это выводит 43 строки.
discourse=# select * from incoming_referers where path LIKE '%/search/' ORDER BY incoming_domain_id;
id | path | incoming_domain_id
-------+-------------+--------------------
878 | /search/ | 63
33457 | /do/search/ | 567
1580 | /search/ | 602
1888 | /search/ | 663
42983 | /search/ | 1259
4896 | /search/ | 1788
42845 | /search/ | 1861
5162 | /search/ | 1861
5176 | /search/ | 1866
43350 | /search/ | 1905
17238 | /search/ | 1905
20689 | /search/ | 1982
5781 | /hg/search/ | 1987
8031 | /search/ | 2665
10325 | /search/ | 3192
11289 | /search/ | 3414
40763 | /search/ | 3433
42849 | /search/ | 3433
13087 | /search/ | 3895
13159 | /search/ | 3949
13802 | /do/search/ | 4051
14407 | /search/ | 4209
14507 | /search/ | 4211
15394 | /search/ | 4230
15533 | /search/ | 4258
45274 | /search/ | 5303
20923 | /search/ | 5400
21317 | /search/ | 5534
22928 | /search/ | 5918
22956 | /search/ | 5926
37448 | /search/ | 6393
25094 | /search/ | 6412
25594 | /search/ | 6547
39655 | /search/ | 6596
27371 | /search/ | 6986
27452 | /a/search/ | 7003
27623 | /search/ | 7041
31041 | /search/ | 7767
36943 | /search/ | 8622
37381 | /search/ | 8711
37411 | /search/ | 8716
40424 | /search/ | 9124
44451 | /search/ | 9525
(43 rows)
Так что, мне стоит выполнить следующее?
DELETE FROM incoming_referers WHERE path LIKE '%/search/' AND id IN (42845,43350,42849);
Да, этот DELETE выглядит правильно и позволит вам сделать чистую резервную копию.
Поверите или нет, но снова сбои. В первом прогоне я обнаружил ещё один дубликат ключа в /m/search, который по какой-то причине не был перехвачен маской %/search/, и удалил его. Затем я снова проиндексировал базу (каждый раз это занимало почти час!), и снова появились сбои, указывающие на дубликат ключа в users(username_lower).
discourse=# reindex index index_users_on_username_lower;
ERROR: could not create unique index "index_users_on_username_lower"
DETAIL: Key (username_lower)=(john_smith) is duplicated.
Но дело в том, что в таблице была только одна строка с username_lower=john_smith! Пришлось надеть шляпу детектива.
Посмотрев в административный интерфейс форума, я обнаружил двух разных пользователей с именами “john_smith” и “John_Smith” — обратите внимание на разное написание заглавных букв. Поэтому я удалил пользователя с именем в нижнем регистре на уровне форума, так как он не был активен уже 4 года, и после этого индекс успешно перестроился.
Я запустил ещё одну полную перестройку (каждая из которых занимала почти час), и, наконец, думаю, мы в порядке — только одна ошибка, но без дубликатов, просто pg_toast ccnew. Я удалил его.
discourse=# REINDEX SCHEMA CONCURRENTLY public;
WARNING: cannot reindex invalid index "pg_toast.pg_toast_2783329_index_ccnew" concurrently, skipping
REINDEX
Это было настоящее приключение.
Можно ли и будут ли эти исправления базы данных автоматизированы в будущем обновлении Discourse?
Скорее всего, проблема с повреждением индексов PostgreSQL прекратится в будущем обновлении PostgreSQL, если Питеру удастся воспроизвести её стабильно.
Если параллельная операция REINDEX завершается неудачей из-за некорректных данных в таблице, необходимо:
Исправить некорректные данные, как это было сделано здесь.
Получить список некорректных индексов с помощью запроса SELECT pg_class.relname FROM pg_class, pg_index WHERE pg_index.indisvalid = false AND pg_index.indexrelid = pg_class.oid;
Удалить каждый некорректный индекс из полученного списка с помощью команды DROP INDEX <indexname>;
Повторить попытку выполнения REINDEX.
После обновления до версии 2.5.0beta5 и следования рекомендациям по переиндексации базы данных я получил следующее:
discourse=# REINDEX SCHEMA CONCURRENTLY public;
ERROR: could not create unique index "index_plugin_store_rows_on_plugin_name_and_key_ccnew"
DETAIL: Key (plugin_name, key)=(discourse-data-explorer, q:-10) is duplicated.
Я бы предпочел не экспериментировать с этим… так как же безопасно удалить дубликат?
Нет. Обновление не повреждает ваш индекс, оно лишь указывает на то, что индекс уже повреждён. Вы можете проверить это, попытавшись восстановить вашу резервную копию на другом сервере (даже на том, где запущен pg10). Или попробуйте перестроить индекс в вашей текущей установке. Пока неясно, что вызывает повреждение индексов, но есть надежда, что в pg12 вероятность такого сбоя будет ниже.
Обновление даёт некоторые преимущества в производительности, но отложить его — не плохая идея.
Сейчас уже поздно, поэтому я всё ещё ищу рекомендации относительно следующих шагов.
Что-то вроде
select id, plugin_name, key from plugin_store_rows where plugin_name like '%discourse-data-explorer%'
должно вернуть нужные строки. Думаю, их можно смело удалить.
Думаю, понадобится ilike