Когда весь репликационный кластер не работает, сначала необходимо запустить один узел, чтобы остальные узлы знали, к какой копии кластера присоединиться.
Решение о том, какой узел запустить первым, основывается на файле grastate.dat — небольшом файле состояния репликации, хранящемся в каталоге данных кластера. Наиболее важные поля:
seqno— номер последней известной этому узлу транзакцииsafe_to_bootstrap— помечен ли этот узел как безопасный для первого запуска после чистого завершения работы
Пример того, как может выглядеть grastate.dat после чистого завершения работы:
# saved replication state
version: 2.1
uuid: <cluster-uuid>
seqno: 12345
safe_to_bootstrap: 1
В этом примере:
seqno: 12345означает, что этот узел знает о транзакциях вплоть до порядкового номера 12345safe_to_bootstrap: -1означает, что этот узел помечен как безопасный для первого запуска
Если весь кластер был корректно остановлен, запустите узел, который был остановлен последним. На практике это обычно узел с:
- наибольшим значением
seqno safe_to_bootstrap: 1
Запустите этот узел первым. Это укажет Manticore начать новую копию кластера с этого узла. После этого запустите оставшиеся узлы обычным образом, чтобы они могли повторно присоединиться.
Используйте это после чистого полного отключения кластера.
- Bash
- Systemd
searchd --new-clustermanticore_new_clusterЕсли другой узел будет запущен первым без требуемого состояния чистого завершения работы, запуск будет отклонен для защиты кластера от восстановления из более старой копии.
Если все узлы аварийно завершили работу или были остановлены некорректно, файл grastate.dat может больше не быть надежным для обычного выбора начальной загрузки. В этом случае найдите узел с самыми свежими данными, обычно тот, у которого наибольший seqno, и запустите его с опцией --new-cluster-force. Это отменяет обычную защиту и принудительно запускает кластер с выбранного узла.
Используйте это после аварийного или некорректного полного отключения кластера.
- Bash
- Systemd
searchd --new-cluster-forcemanticore_new_cluster --forceЕсли репликационный узел или весь кластер становится недоступным, правильная процедура восстановления зависит от того, сколько узлов остаются доступными и был ли останов чистым или внезапным.
Репликационный кластер следует рассматривать как одну логическую систему, а не набор независимых серверов. Это обеспечивает многомастерные записи и согласованность данных, но также означает, что вы должны осторожно восстанавливать кворум. В частности, не запускайте команду ручного восстановления, которая восстанавливает записи на сохранившейся стороне, пока не убедитесь, что пропавшие узлы действительно отсутствуют. Эта команда показана далее на этой странице как SET CLUSTER <name> GLOBAL 'pc.bootstrap' = 1. Если запустить её слишком рано, можно создать расщепление мозга и получить два независимых кластера.
Для примеров ниже предполагается кластер с узлами A, B и C, если не указано иначе.
Сначала определите, в какой ситуации вы находитесь:
- Есть хотя бы один узел онлайн?
- Узел был остановлен чисто или он упал? Чистая остановка означает, что
searchdбыл остановлен нормально и имел время сохранить состояние репликации перед выходом. Падение, потеря питания илиkill -9не являются чистой остановкой. - Сохранившаяся часть кластера ещё имеет кворум? Кворум означает, что достаточно узлов могут видеть друг друга, чтобы безопасно оставаться кластером с возможностью записи.
- Если все узлы отключены, какой узел следует запустить первым для восстановления кластера?
Полезные проверки:
SHOW STATUS LIKE 'cluster_<name>_status'SHOW STATUS LIKE 'cluster_<name>_size'SHOW STATUS LIKE 'cluster_<name>_node_state'- если все узлы отключены, проверьте
grastate.dat, небольшой файл состояния репликации, хранящийся в директории данных кластера. Особенно обратите внимание наseqnoиsafe_to_bootstrap: при чистой остановке лучший узел для запуска первым обычно тот, с наиболее продвинутымseqnoиsafe_to_bootstrap: 1. Для полной процедуры bootstrap см. Перезапуск кластера.
Пример того, как grastate.dat может выглядеть после чистой остановки:
# saved replication state
version: 2.1
uuid: <cluster-uuid>
seqno: 12345
safe_to_bootstrap: 1
В этом примере:
seqno: 12345означает, что этот узел знает о транзакциях до порядкового номер 12345safe_to_bootstrap: 1означает, что этот узел помечен как безопасный для запуска первым
При чистом восстановлении после остановки всех узлов, это обычно тот тип узла, который вы запускаете первым с --new-cluster для восстановления кластера.
После восстановления, дождитесь, пока перезапущенный узел сообщает cluster_<name>_status=primary и cluster_<name>_node_state=synced, прежде чем считать его полностью доступным для записи. Это можно проверить с помощью SHOW STATUS LIKE 'cluster_<name>_status' и SHOW STATUS LIKE 'cluster_<name>_node_state'. В локальных тестах перезапущенные узлы иногда некоторое время находились в состоянии cluster_<name>_node_state=joining и cluster_<name>_status=disconnected перед достижением synced/primary.
Если узел A остановлен нормально, узлы B и C продолжают обслуживать записи. Вы можете подтвердить, что кластер всё ещё здоров на этих узлах с помощью SHOW STATUS LIKE 'cluster_<name>_status' и SHOW STATUS LIKE 'cluster_<name>_size'.
Когда узел A запускается снова, он автоматически присоединяется к кластеру. До завершения синхронизации не отправляйте записи на этот узел. Проверьте SHOW STATUS LIKE 'cluster_<name>_status' и SHOW STATUS LIKE 'cluster_<name>_node_state' и дождитесь primary / synced.
Если узлы-доноры B или C ещё имеют все транзакции, которые узел A пропустил, в своём репликационном кэше, узел A может догнать их с помощью инкрементного переноса состояния (IST). IST означает инкрементный перенос состояния. Это означает, что узел получает только транзакции, которые он пропустил, поэтому восстановление обычно быстрее и легче. В противном случае потребуется перенос состояния через снимок (SST). SST означает перенос состояния через снимок. Это означает копирование файлов таблиц из другого узла вместо простого воспроизведения пропущенных транзакций. SST более тяжелый: обычно он медленнее, перемещает больше данных и может сделать восстановление более разрушительным на больших кластерах.
Если узлы A и B остановлены чисто и узел C остаётся онлайн, узел C может продолжать принимать записи. Проверьте SHOW STATUS LIKE 'cluster_<name>_status' и SHOW STATUS LIKE 'cluster_<name>_size' на узле C, если хотите подтвердить, что он теперь единственный активный узел.
Когда узлы A и B запускаются снова, они автоматически присоединяются и синхронизируются с узлом C. Во время присоединения проверьте SHOW STATUS LIKE 'cluster_<name>_status', SHOW STATUS LIKE 'cluster_<name>_node_state', и SHOW STATUS LIKE 'cluster_<name>_size'. Дождитесь, пока все узлы показывают primary / synced и ожидаемый размер кластера, прежде чем считать восстановление завершенным.
Если все узлы были остановлены нормально, кластер полностью отключен и должен быть запущен снова специальным образом, чтобы он мог стать первым работающим узлом кластера.
При чистой остановке каждый узел записывает свой последний номер транзакции в grastate.dat. Узел, который был остановлен последним, является самым безопасным узлом для запуска первым:
- он имеет наиболее продвинутый
seqno - он имеет
safe_to_bootstrap: 1
Запустите этот узел с --new-cluster. Это указывает Manticore запустить новую копию кластера с этого узла. Если вы запускаете Manticore через systemd в Linux, используйте manticore_new_cluster. Он запускает Manticore в режиме --new-cluster за вас.
После этого запустите остальные узлы нормально и позвольте им присоединиться. Проверьте восстановление с помощью SHOW STATUS LIKE 'cluster_<name>_status', SHOW STATUS LIKE 'cluster_<name>_node_state', и SHOW STATUS LIKE 'cluster_<name>_size'.
Если вы запустите менее продвинутый узел первым, более продвинутый узел позже присоединится к нему и получит полный SST из более старого состояния, что может отбросить транзакции, которые существовали только на более продвинутом узле. Поэтому узел с safe_to_bootstrap: 1 должен быть вашим первым выбором.
Если узел A исчезает из-за сбоя или сетевой проблемы, узлы B и C сначала попытаются переподключиться к нему. Если это не удается, они удаляют его из кластера, пересчитывают кворум и продолжают работать как основной кластер.
В локальных тестах это удаление узла не было мгновенным: оставшиеся узлы сохраняли старый размер кластера в течение нескольких секунд, прежде чем отбросить отказавший узел и переключиться на меньший основной кластер.
Когда узел A снова запускается, он автоматически присоединяется и наверстывает упущенное так же, как после чистого завершения работы одного узла. Снова используйте SHOW STATUS LIKE 'cluster_<name>_status', SHOW STATUS LIKE 'cluster_<name>_node_state' и SHOW STATUS LIKE 'cluster_<name>_size', чтобы подтвердить завершение восстановления.
Если узлы A и B потеряны, и работает только узел C, узел C больше не имеет кворума в трехузловом кластере. Он переключается в состояние non-primary и отклоняет запись.
Ошибка записи явная:
ERROR 1064 (42000): cluster '<name>' is not ready, not primary state (synced)
Если узлы A и B только временно отключены, но все еще могут видеть друг друга, они могут продолжать принимать записи, в то время как узел C остается изолированным. Используйте SHOW STATUS LIKE 'cluster_<name>_status' на каждой стороне, если вам нужно увидеть, какая сторона все еще доступна для записи.
Если узлы A и B действительно аварийно завершили работу, а узел C — единственная сохранившаяся копия, с которой вы хотите продолжать работать, выполните эту команду на узле C, чтобы снова сделать его доступным для записи:
Если вы подтвердили, что другие узлы действительно отключены, выполните:
- SQL
- JSON
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1POST /cli -d "
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1
"Важно:
- выполняйте это только после того, как убедитесь, что другие узлы недостижимы
- выполняйте это только на стороне, которая должна выжить
- после начальной загрузки узел снова может принимать записи, и другие узлы позже смогут к нему повторно присоединиться
Если каждый узел аварийно завершил работу, grastate.dat, как правило, больше не заслуживает доверия для обычного выбора начальной загрузки. В локальных тестах все узлы показывали:
seqno: -1safe_to_bootstrap: 0
В этой ситуации выберите узел с самыми свежими данными и запустите его с --new-cluster-force. Это заставляет Manticore запустить новую копию кластера с этого узла, даже несмотря на то, что обычные метаданные чистого завершения работы не заслуживают доверия. Если вы запускаете Manticore через systemd в Linux, используйте manticore_new_cluster --force. Он запускает Manticore в режиме --new-cluster-force для вас.
Затем запустите оставшиеся узлы в обычном режиме и позвольте им повторно присоединиться. Проверьте восстановление с помощью SHOW STATUS LIKE 'cluster_<name>_status', SHOW STATUS LIKE 'cluster_<name>_node_state' и SHOW STATUS LIKE 'cluster_<name>_size'.
Риск разделения мозга наиболее высок в кластерах с четным числом узлов. Например, представьте четыре узла, разделенных на две изолированные пары в двух центрах обработки данных. Каждая сторона имеет ровно половину от исходного числа участников, поэтому ни одна из сторон не имеет кворума, и обе стороны прекращают принимать записи.
Если вам необходимо восстановить запись до устранения проблемы с подключением, выберите только одну сторону разделения и выполните ту же команду восстановления там, чтобы эта сторона снова стала доступной для записи. Перед этим проверьте SHOW STATUS LIKE 'cluster_<name>_status' на обеих сторонах, чтобы знать, какая сторона в настоящее время является неосновной.
Выберите сторону, которая должна остаться доступным для записи кластером, затем выполните:
- SQL
- JSON
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1POST /cli -d "
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1
"Никогда не выполняйте этот оператор на обеих сторонах. Если вы это сделаете, вы создадите два отдельных основных кластера, и они не объединятся автоматически при восстановлении сети.
В локальном тестировании внезапная потеря половины четырехузлового кластера воспроизвела то же поведение non-primary, и та же команда восстановления вернула одну выжившую половину в состояние primary.
Со стандартной конфигурацией, Manticore ожидает ваши подключения на:
- порт 9306 для клиентов MySQL
- порт 9308 для HTTP/HTTPS подключений
- порт 9312 для HTTP/HTTPS, а также подключений от других узлов Manticore и клиентов, основанных на бинарном API Manticore
- SQL
- HTTP
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
- docker
mysql -h0 -P9306HTTP — это безсессионный протокол, поэтому не требует специальной фазы подключения:
curl -s "http://localhost:9308/search"require_once __DIR__ . '/vendor/autoload.php';
$config = ['host'=>'127.0.0.1','port'=>9308];
$client = new \Manticoresearch\Client($config);import manticoresearch
config = manticoresearch.Configuration(
host = "http://127.0.0.1:9308"
)
client = manticoresearch.ApiClient(config)
indexApi = manticoresearch.IndexApi(client)
searchApi = manticoresearch.searchApi(client)
utilsApi = manticoresearch.UtilsApi(client)import manticoresearch
config = manticoresearch.Configuration(
host = "http://127.0.0.1:9308"
)
async with manticoresearch.ApiClient(config) as client:
indexApi = manticoresearch.IndexApi(client)
searchApi = manticoresearch.searchApi(client)
utilsApi = manticoresearch.UtilsApi(client)var Manticoresearch = require('manticoresearch');
var client= new Manticoresearch.ApiClient()
client.basePath="http://127.0.0.1:9308";
indexApi = new Manticoresearch.IndexApi(client);
searchApi = new Manticoresearch.SearchApi(client);
utilsApi = new Manticoresearch.UtilsApi(client);import com.manticoresearch.client.ApiClient;
import com.manticoresearch.client.ApiException;
import com.manticoresearch.client.Configuration;
import com.manticoresearch.client.model.*;
import com.manticoresearch.client.api.IndexApi;
import com.manticoresearch.client.api.UtilsApi;
import com.manticoresearch.client.api.SearchApi;
ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("http://127.0.0.1:9308");
IndexApi indexApi = new IndexApi(client);
SearchApi searchApi = new UtilsApi(client);
UtilsApi utilsApi = new UtilsApi(client);using ManticoreSearch.Client;
using ManticoreSearch.Api;
using ManticoreSearch.Model;
string basePath = "http://127.0.0.1:9308";
IndexApi indexApi = new IndexApi(basePath);
SearchApi searchApi = new UtilsApi(basePath);
UtilsApi utilsApi = new UtilsApi(basePath);use std::sync::Arc;
use manticoresearch::{
apis::{
{configuration::Configuration,IndexApi,IndexApiClient,SearchApi,SearchApiClient,UtilsApi,UtilsApiClient}
},
};
async fn maticore_connect {
let configuration = Configuration {
base_path: "http://127.0.0.1:9308".to_owned(),
..Default::default(),
};
let api_config = Arc::new(configuration);
let utils_api = UtilsApiClient::new(api_config.clone());
let index_api = IndexApiClient::new(api_config.clone());
let search_api = SearchApiClient::new(api_config.clone());Запустите контейнер Manticore и используйте встроенный клиент MySQL для подключения к узлу.
docker run --name manticore -d manticoresearch/manticore && docker exec -it manticore mysqlManticore Search реализует SQL-интерфейс с использованием протокола MySQL, что позволяет использовать любую библиотеку или коннектор MySQL, а также многие MySQL-клиенты для подключения к Manticore Search и работы с ним так, как если бы это был сервер MySQL, а не Manticore.
Однако диалект SQL отличается и реализует только подмножество SQL-команд или функций, доступных в MySQL. Кроме того, существуют команды и функции, специфичные для Manticore Search, такие как клауза MATCH() для полнотекстового поиска.
Manticore Search поддерживает подготовленные выражения на стороне сервера через протокол MySQL. Также можно использовать подготовленные выражения на стороне клиента. Важно отметить, что Manticore реализует типы данных multi-value (MVA) и float_vector, которые не имеют эквивалентов в MySQL или библиотеках, реализующих подготовленные выражения. В этих случаях значения должны быть сформированы в исходном запросе в виде списка чисел, разделённых запятыми.
Некоторые клиенты/коннекторы MySQL требуют значения для пользователя/пароля и/или имени базы данных. Поскольку Manticore Search не имеет концепции баз данных и в нем не реализован контроль доступа пользователей, эти значения могут быть заданы произвольно, так как Manticore просто проигнорирует их.
Порт по умолчанию для SQL-интерфейса — 9306, и он включен по умолчанию.
Вы можете настроить порт MySQL в разделе searchd конфигурационного файла, используя директиву listen следующим образом:
searchd {
...
listen = 127.0.0.1:9306:mysql
...
}
Имейте в виду, что в Manticore нет аутентификации пользователей, поэтому убедитесь, что порт MySQL недоступен никому за пределами вашей сети.
Для выполнения "VIP" подключений можно использовать отдельный порт MySQL. При подключении к этому порту пул потоков обходится, и всегда создается новый выделенный поток. Это полезно в случае серьезной перегрузки, когда сервер либо зависает, либо препятствует подключению через обычный порт.
searchd {
...
listen = 127.0.0.1:9306:mysql
listen = 127.0.0.1:9307:mysql_vip
...
}
Самый простой способ подключения к Manticore — использование стандартного MySQL клиента:
mysql -P9306 -h0
Протокол MySQL поддерживает шифрование SSL. Безопасные подключения можно установить на том же порту mysql.
Сжатие можно использовать при подключениях MySQL, и оно доступно клиентам по умолчанию. Клиенту просто нужно указать, что соединение должно использовать сжатие.
Пример использования MySQL-клиента:
mysql -P9306 -h0 -C
Сжатие можно использовать как в защищённых, так и в незашищённых подключениях.
Официальные коннекторы MySQL можно использовать для подключения к Manticore Search, однако им могут потребоваться определённые настройки, передаваемые в строке DSN, поскольку коннектор может попытаться выполнить SQL-команды, которые ещё не реализованы в Manticore.
JDBC коннектор 6.x и выше требует Manticore Search версии 2.8.2 или выше, и строка DSN должна содержать следующие опции:
jdbc:mysql://IP:PORT/DB/?characterEncoding=utf8&maxAllowedPacket=512000&serverTimezone=XXX
По умолчанию Manticore Search сообщает коннектору свою собственную версию, однако это может вызвать некоторые проблемы. Для их решения директива mysql_version_string в разделе searchd конфигурационного файла должна быть установлена на версию ниже 5.1.1:
searchd {
...
mysql_version_string = 5.0.37
...
}
.NET коннектор MySQL по умолчанию использует пулы подключений. Для правильного получения статистики SHOW META запросы вместе с командой SHOW META должны отправляться как единое многокомандное выражение (SELECT ...;SHOW META). Если пулы включены, в строку подключения необходимо добавить опцию Allow Batch=True, чтобы разрешить многокомандность:
Server=127.0.0.1;Port=9306;Database=somevalue;Uid=somevalue;Pwd=;Allow Batch=True;
К Manticore можно получить доступ с помощью ODBC. Рекомендуется устанавливать charset=UTF8 в строке ODBC. Некоторые драйверы ODBC не понравится версия, сообщаемая сервером Manticore, так как они увидят её как очень старую версию сервера MySQL. Это можно переопределить с помощью опции mysql_version_string.
SQL Manticore через MySQL поддерживает синтаксис комментариев в стиле C. Всё от начальной последовательности /* до конечной последовательности */ игнорируется. Комментарии могут занимать несколько строк, не могут вкладываться и не должны записываться в лог. Специфичные для MySQL комментарии /*! ... */ также в настоящее время игнорируются. (Поддержка комментариев была добавлена скорее для лучшей совместимости с дампами, созданными mysqldump, а не для улучшения общей интероперабельности запросов между Manticore и MySQL.)
SELECT /*! SQL_CALC_FOUND_ROWS */ col1 FROM table1 WHERE ...
Manticore поддерживает подготовленные выражения через протокол MySQL.
В базах данных подготовленные выражения — это способ выполнения запросов, при котором SQL-код отделён от входных данных. Вместо написания полного SQL-запроса со значениями, включёнными напрямую в него, вы пишете запрос один раз, используя плейсхолдеры, а затем отдельно передаёте значения.
Некоторые клиентские библиотеки (например, sqlx) взаимодействуют с базами данных только таким образом.
-
Безопасность (предотвращение SQL-инъекций): Это самая важная причина. SQL-инъекция — распространённая уязвимость веб-приложений, при которой злоумышленники вставляют вредоносный SQL в запросы. Подготовленные выражения гарантируют, что пользовательский ввод обрабатывается строго как данные, а не как исполняемый код, предотвращая его интерпретацию как часть SQL-команды.
-
Читаемость и поддерживаемость: Подготовленные выражения улучшают ясность кода, отделяя SQL-логику от входных данных, что делает код более удобным для чтения и поддержки.
-
Производительность: База данных может кэшировать планы запросов. Если один и тот же параметризованный запрос выполняется многократно с разными значениями, база данных может повторно использовать кэшированный план выполнения вместо того, чтобы каждый раз разбирать и оптимизировать запрос, что может повысить производительность.
- Подготовка запроса: Отправьте SQL-запрос с заполнителями (такими как
?или?VEC?) в Manticore. Manticore разбирает и компилирует запрос, сохраняет его и возвращает уникальный идентификатор, который можно использовать для последующего выполнения. - Привязка параметров: Отправьте значения для заполнителей отдельно. Manticore рассматривает эти значения строго как данные (не как SQL-код) и безопасно вставляет их в сохранённый запрос.
- Выполнение запроса: Manticore выполняет запрос, используя предварительно скомпилированный план вместе с предоставленными значениями параметров.
- SQL
- PHP
INSERT INTO table (id, floatvec) VALUES (?, (?VEC?))$stmt = $mysqli->prepare("INSERT INTO tbl (id, str, floatvec) VALUES (0, ?, (?VEC?))");
$str = "I'm a string";
$vec = "0.1,0.2,0.3";
$stmt->bind_param("ss", $str, $vec);
$stmt->execute();Будет выполнено:
INSERT INTO tbl (id, str, floatvec) VALUES (0, 'I\'m a string', (0.1,0.2,0.3))?(вопросительный знак): Представляет один параметр (целое число, число с плавающей точкой или строка). Строковые значения обрабатываются автоматически — например, специальные символы, такие как одинарные кавычки, экранируются, а значение заключается в кавычки. Логические значения можно передавать в виде строк ('true','false').?VEC?: Представляет список числовых значений, предоставленных в виде строки (например,1,2,3или0.3,15E+3, 20 ,INF). Разрешены только числа, запятые и пробелы. После проверки значения вставляются точно так, как предоставлены (без дополнительного экранирования или кавычек).
В стандартном SQL-синтаксисе Manticore MVA или векторы с плавающей точкой записываются как список значений в круглых скобках, например (1, 2, 3).
При использовании подготовленных запросов включите круглые скобки непосредственно в запрос и используйте заполнитель ?VEC? только для значений внутри них. Например:
$stmt = $mysqli->prepare("INSERT INTO tbl (id, floatvec) VALUES (0, (?VEC?))");
$vec = "0.1,0.2,0.3";
$stmt->bind_param("s", $vec);
- Разрешён только один SQL-запрос на подготовленный запрос. Мультизапросы (например,
SELECT ...; SHOW META) не поддерживаются. Если вам нужно выполнить несколько запросов, подготовьте отдельный запрос для каждого и выполните их последовательно в одной сессии. - Некоторые драйверы отправляют числовые параметры как DOUBLE (например,
mysql2для Node.js). Если вам требуется строгое целочисленное поведение (например, отклонение отрицательных ID), отправляйте целые числа в виде строк или используйте специфичные для драйвера целочисленные типы (например,BigInt). - Rust sqlx: При чтении строк результирующего набора используйте индексы столбцов, а не их имена. Имена столбцов присутствуют в результирующем наборе, но sqlx не использует их для сопоставления. Например, используйте
row.try_get(0)?вместоrow.try_get("id")?.