Агенты реплики могут использоваться взаимозаменяемо при обработке поискового запроса. Экземпляр Manticore, на котором размещена распределённая таблица с определёнными реплицированными агентами, отслеживает статус реплик (работают или нет) и время отклика, а также выполняет автоматическое переключение при сбое и балансировку нагрузки на основе этой информации.
agent = node1|node2|node3:9312:shard2
В приведённом выше примере объявляется, что node1:9312, node2:9312 и node3:9312 все имеют таблицу с именем shard2 и могут использоваться как взаимозаменяемые реплики. Если любой из этих серверов выйдет из строя, запросы будут распределены между оставшимися. Когда сервер вернётся в онлайн, мастер обнаружит это и снова начнёт направлять запросы ко всем репликам.
Зеркало также может включать индивидуальный список таблиц, как показано ниже:
agent = node1:9312:node1shard2|node2:9312:node2shard2
Это работает аналогично предыдущему примеру, но при запросе к разным серверам будут использоваться разные имена таблиц. Например, node1shard2 будет использоваться при запросе к node1:9312, а node2shard2 — при запросе к node2:9312.
По умолчанию выбор реплики использует глобальную или табличную стратегию ha_strategy. Если вы не зададите её явно, стратегия по умолчанию — random. Мастер хранит метрики, такие как общее количество запросов, количество ошибок и время отклика для каждого агента, и группирует их во временные интервалы, контролируемые параметром ha_period_karma. Затем эти статистические данные используются стратегиями балансировки, описанными в разделе Балансировка нагрузки.
Период кармы указывается в секундах и по умолчанию равен 60 секундам. Мастер хранит до 15 интервалов кармы со статистикой по агентам для целей инструментирования (см. SHOW AGENT STATUS). Однако для логики HA/LB используются только последние два интервала.
Когда запросов нет, мастер отправляет регулярную команду ping каждые ha_ping_interval для сбора статистики и проверки, жив ли ещё удалённый хост. ha_ping_interval по умолчанию равен 1000 мс. Установка значения 0 отключает пинги, и статистика будет накапливаться только на основе фактических запросов. Вместе с ha_period_karma это управляет тем, насколько быстро изменения в состоянии и задержке реплик влияют на балансировку нагрузки.
Это различие важно:
- Одна запись
agent='host1|host2:table'означает один удалённый шард с реплицированными бэкендами. - Несколько записей
agent='...'означают несколько удалённых шардов.
Например:
# one shard, two mirrors
agent = node1|node2:9312:products
# two shards
agent = node1:9312:products_a
agent = node2:9312:products_b
Та же концепция репликации может быть настроена либо в конфигурационном файле, либо с помощью CREATE TABLE. Для TCP-соединений agent= должен использовать удалённый порт агента/API (обычно 9312), а не порт MySQL (9306). Если вы хотите конкретную политику балансировки только для одной распределённой таблицы, установите ha_strategy для этой таблицы, а не полагайтесь только на глобальные настройки по умолчанию.
- Config
- SQL
table products_dist {
type = distributed
agent = 127.0.0.1:9312|127.0.0.1:9313:products
ha_strategy = roundrobin
}CREATE TABLE products_dist type='distributed'
agent='127.0.0.1:9312:products|127.0.0.1:9313:products'
ha_strategy='roundrobin';Предположим, что реплицированные таблицы уже существуют, например:
- SQL
CREATE TABLE products_dist type='distributed'
agent='127.0.0.1:9312:products|127.0.0.1:9313:products'
ha_strategy='roundrobin';Используйте распределённую таблицу как обычно с мастера, затем проверьте, как она была сохранена и как ведут себя реплики.
- SQL
SELECT id, title, node FROM products_dist;
SHOW CREATE TABLE products_dist;
SHOW AGENT STATUS;+------+------------+-------+
| id | title | node |
+------+------------+-------+
| 1 | same title | node1 |
+------+------------+-------+
+---------------+----------------------------------------------------------------------------------+
| Table | Create Table |
+---------------+----------------------------------------------------------------------------------+
| products_dist | CREATE TABLE products_dist type='distributed' agent='127.0.0.1:9312:products|... |
+---------------+----------------------------------------------------------------------------------+Пример шардирования таблицы на 4 сервера в общей сложности, в 2 шарда с 2 репликами для каждого шарда.
- Config
# node1, node2 carry shard1 as local
# node3, node4 carry shard2 as local
# config on node1, node2
agent = node3:9312|node4:9312:shard2
# config on node3, node4
agent = node1:9312|node2:9312:shard1С Manticore транзакции записи (такие как INSERT, REPLACE, DELETE, TRUNCATE, UPDATE, COMMIT) могут реплицироваться на другие узлы кластера до полного применения транзакции на текущем узле. В настоящее время репликация поддерживается для таблиц percolate, rt и distributed в Linux и macOS.
Нативные бинарные файлы Windows для Manticore не поддерживают репликацию. Мы рекомендуем устанавливать Manticore через WSL (Подсистема Windows для Linux).
На macOS репликация имеет ограниченную поддержку и рекомендуется только для целей разработки.
Репликация Manticore базируется на библиотеке Galera и обладает несколькими впечатляющими возможностями:
- Истинный multi-master: чтение и запись на любой узел в любое время.
- Почти синхронная репликация — отсутствие задержек ведомых и потери данных после сбоя узла.
- Горячий резерв: отсутствие времени простоя при переключении (так как переключения нет).
- Плотно связанные узлы: все узлы содержат одно и то же состояние, расхождений данных между узлами не допускается.
- Автоматическое добавление узлов: нет необходимости вручную создавать резервные копии базы и восстанавливать их на новом узле.
- Простота использования и развёртывания.
- Обнаружение и автоматическое удаление ненадёжных узлов.
- Репликация на основе сертификации.
Для настройки репликации в Manticore Search:
- Опция data_dir должна быть задана в разделе "searchd" конфигурационного файла. Репликация не поддерживается в plain-режиме.
- Директива listen должна содержать IP-адрес, доступный для других узлов, либо должен быть указан node_address с доступным IP-адресом.
- По желанию, можно задать уникальные значения для server_id на каждом узле кластера. Если значение не задано, узел попытается использовать MAC-адрес или случайное число для генерации
server_id.
Если включены аутентификация и авторизация, для операций с кластером требуется разрешение replication. Выдайте его пользователю, который должен владеть операциями репликации для кластера:
GRANT replication ON 'posts' TO 'repl_user';
CREATE CLUSTER и JOIN CLUSTER могут указывать такого пользователя через '<user>' AS user. Пользователь должен существовать с совпадающими сохраненными данными аутентификации на узлах, участвующих в операции. Просто создать одно и то же имя пользователя и пароль независимо на каждом узле недостаточно, потому что сохраненные данные аутентификации могут отличаться. Если вы изменили данные аутентификации вне демона, выполните RELOAD AUTH на затронутых узлах перед использованием операции с кластером.
Позднее ALTER CLUSTER ... ADD, ALTER CLUSTER ... DROP, ALTER CLUSTER ... UPDATE nodes и DELETE CLUSTER используют сохраненного пользовательского кластера. Когда аутентификация включена, успешный JOIN CLUSTER заменяет все локальные данные аутентификации на присоединяющемся узле данными аутентификации кластера-источника.
Если директива replication listen не задана, Manticore использует первые два свободных порта из диапазона в 200 портов после порта прослушивания протокола по умолчанию для каждого созданного кластера. Для ручного задания портов репликации необходимо определить диапазон портов в директиве listen (типа replication), при этом пары адрес/диапазон портов не должны пересекаться между разными узлами на одном сервере. Как правило, диапазон портов должен содержать как минимум два порта на кластер. Когда вы определяете слушатель репликации с диапазоном портов (например, listen = 192.168.0.1:9320-9328:replication), Manticore не начинает прослушивать эти порты сразу. Вместо этого система будет выбирать случайные свободные порты из указанного диапазона только при начале использования репликации.
Репликационный кластер — это группа узлов, в которой реплицируются транзакции записи. Репликация настраивается на уровне таблицы, то есть одна таблица может принадлежать только одному кластеру. Нет ограничений на количество таблиц в кластере. Все операции INSERT, REPLACE, DELETE, TRUNCATE для любой percolate или real-time таблицы, входящей в кластер, реплицируются на все другие узлы этого кластера. В репликацию также могут быть включены distributed таблицы. Репликация является multi-master, поэтому записи на любой узел или несколько узлов одновременно работают корректно.
Для создания кластера обычно используется команда create cluster с CREATE CLUSTER <имя кластера>, а для присоединения к кластеру — команда join cluster с JOIN CLUSTER <имя кластера> at 'host:port'. Однако в некоторых редких случаях может понадобиться тонкая настройка поведения CREATE/JOIN CLUSTER. Доступные опции:
Эта опция задаёт имя кластера. Оно должно быть уникальным среди всех кластеров в системе.
Примечание: Максимальная длина имени хоста для команды
JOINсоставляет 253 символа. Если предел превышен, searchd выдаст ошибку.
Параметр path задаёт каталог данных для репликации кэша write-set и других файлов провайдера кластера. Он не влияет на место хранения реплицируемых таблиц. Входящие реплицируемые таблицы сохраняются в обычном каталоге таблицы внутри data_dir. Это значение должно быть уникальным среди всех кластеров в системе и задаваться как относительный путь к каталогу data_dir. По умолчанию оно равно значению data_dir.
Критическое изменение: В более старых версиях входящие файлы реплицируемых таблиц тоже сохранялись по пути кластера. Если вы использовали собственный
pathкластера, обновляйтесь осторожно, потому что реплицируемые таблицы, полученные старыми версиями, может потребоваться переместить или повторно синхронизировать в обычную структуруdata_dir/<table>.
Опция nodes — это список адресов и портов всех узлов в кластере, разделённых запятыми. Этот список должен быть получен через API узла и может включать адрес текущего узла. Он используется для присоединения узла к кластеру и для повторного присоединения после перезапуска.
Опция options позволяет передавать дополнительные параметры непосредственно плагину репликации Galera, как описано в Galera Documentation Parameters
При работе с репликационным кластером все операторы записи, такие как INSERT, REPLACE, DELETE, TRUNCATE, UPDATE, которые изменяют содержимое таблицы кластера, должны использовать выражение cluster_name:table_name вместо имени таблицы. Это гарантирует, что изменения будут распространены на все реплики в кластере. Если использовать неправильное выражение, будет выдана ошибка.
В JSON-интерфейсе свойство cluster должно быть установлено вместе с именем table для всех операций записи в таблицу кластера. Если свойство cluster не установлено, это приведет к ошибке.
Автоматический ID для таблицы в кластере будет действителен, если server_id настроен правильно.
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
INSERT INTO posts:weekly_table VALUES ( 'iphone case' )
TRUNCATE TABLE click_query:weekly_table
UPDATE INTO posts:rt_tags SET tags=(101, 302, 304) WHERE MATCH ('use') AND id IN (1,101,201)
DELETE FROM clicks:rt WHERE MATCH ('dumy') AND gid>206POST /insert -d '
{
"cluster":"posts",
"table":"weekly_table",
"doc":
{
"title" : "iphone case",
"price" : 19.85
}
}'
POST /delete -d '
{
"cluster":"posts",
"table": "weekly_table",
"id":1
}'$table->addDocuments([
1, ['title' => 'iphone case', 'price' => 19.85]
]);
$table->deleteDocument(1);indexApi.insert({"cluster":"posts","table":"weekly_table","doc":{"title":"iphone case","price":19.85}})
indexApi.delete({"cluster":"posts","table":"weekly_table","id":1})await indexApi.insert({"cluster":"posts","table":"weekly_table","doc":{"title":"iphone case","price":19.85}})
await indexApi.delete({"cluster":"posts","table":"weekly_table","id":1})res = await indexApi.insert({"cluster":"posts","table":"weekly_table","doc":{"title":"iphone case","price":19.85}});
res = await indexApi.delete({"cluster":"posts","table":"weekly_table","id":1});InsertDocumentRequest newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","Crossbody Bag with Tassel");
put("price",19.85);
}};
newdoc.table("weekly_table").cluster("posts").id(1L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);
DeleteDocumentRequest deleteRequest = new DeleteDocumentRequest();
deleteRequest.table("weekly_table").cluster("posts").setId(1L);
indexApi.delete(deleteRequest);Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title", "Crossbody Bag with Tassel");
doc.Add("price", 19.85);
InsertDocumentRequest newdoc = new InsertDocumentRequest(table: "weekly_table", cluster:posts, id: 1, doc: doc);
var sqlresult = indexApi.Insert(newdoc);
DeleteDocumentRequest deleteDocumentRequest = new DeleteDocumentRequest(table: "weekly_table", cluster: "posts", id: 1);
indexApi.Delete(deleteDocumentRequest);let mut doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("Crossbody Bag with Tassel"));
doc.insert("price".to_string(), serde_json::json!(19.85));
let insert_req = InsertDocumentRequest {
table: serde_json::json!("weekly_table"),
doc: serde_json::json!(doc),
cluster: serde_json::json!("posts"),
id: serde_json::json!(1),
};
let insert_res = index_api.insert(insert_req).await;
let delete_req = DeleteDocumentRequest {
table: serde_json::json!("weekly_table"),
cluster: serde_json::json!("posts"),
id: serde_json::json!(1),
};
index_api.delete(delete_req).await;Операции чтения, такие как SELECT, CALL PQ, DESCRIBE, могут использовать обычные имена таблиц без префикса имени кластера или могут использовать формат cluster_name:table_name. Если используется последний, компонент cluster_name игнорируется.
При использовании HTTP-эндпоинта json/search свойство cluster можно указать при желании, но его также можно опустить.
- SQL
- JSON
SELECT * FROM weekly_table
CALL PQ('posts:weekly_table', 'document is here')POST /search -d '
{
"cluster":"posts",
"table":"weekly_table",
"query":{"match":{"title":"keyword"}}
}'
POST /search -d '
{
"table":"weekly_table",
"query":{"match":{"title":"keyword"}}
}'Параметры плагина репликации можно настроить с помощью оператора SET.
Список доступных опций можно найти в Galera Documentation Parameters.
- SQL
- JSON
SET CLUSTER click_query GLOBAL 'pc.bootstrap' = 1POST /cli -d "
SET CLUSTER click_query GLOBAL 'pc.bootstrap' = 1
"Возможна ситуация, когда реплицированные узлы расходятся друг от друга, что приводит к состоянию, когда все узлы помечены как non-primary. Это может произойти в результате сетевого разделения между узлами, сбоя кластера или если плагин репликации столкнется с исключением при определении primary component. В таком сценарии необходимо выбрать узел и повысить его до роли primary component.
Чтобы определить узел, который нужно повысить, следует сравнить значение переменной состояния кластера last_committed на всех узлах. Если все серверы в настоящее время работают, нет необходимости перезапускать кластер. Вместо этого можно просто повысить узел с наибольшим значением last_committed до primary component с помощью оператора SET (как показано в примере).
Остальные узлы затем переподключатся к основному компоненту и повторно синхронизируют свои данные на основе этого узла.
- SQL
- JSON
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1POST /cli -d "
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1
"Чтобы использовать репликацию, в конфигурационном файле нужно определить один порт listen для протокола SphinxAPI и один listen для адреса репликации и диапазона портов. Также укажите каталог data_dir для хранения входящих реплицируемых таблиц.
- ini
searchd {
listen = 9312
listen = 192.168.1.101:9360-9370:replication
data_dir = /var/lib/manticore/
...
}Для репликации таблиц необходимо создать кластер на сервере, который содержит локальные таблицы для репликации.
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
CREATE CLUSTER postsPOST /cli -d "
CREATE CLUSTER posts
"$params = [
'cluster' => 'posts'
]
];
$response = $client->cluster()->create($params);utilsApi.sql('CREATE CLUSTER posts')await utilsApi.sql('CREATE CLUSTER posts')res = await utilsApi.sql('CREATE CLUSTER posts');utilsApi.sql("CREATE CLUSTER posts");utilsApi.Sql("CREATE CLUSTER posts");utils_api.sql("CREATE CLUSTER posts", Some(true)).await;Добавьте эти локальные таблицы в кластер
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
ALTER CLUSTER posts ADD pq_title
ALTER CLUSTER posts ADD pq_clicksPOST /cli -d "
ALTER CLUSTER posts ADD pq_title
"
POST /cli -d "
ALTER CLUSTER posts ADD pq_clicks
"$params = [
'cluster' => 'posts',
'body' => [
'operation' => 'add',
'table' => 'pq_title'
]
];
$response = $client->cluster()->alter($params);
$params = [
'cluster' => 'posts',
'body' => [
'operation' => 'add',
'table' => 'pq_clicks'
]
];
$response = $client->cluster()->alter($params);utilsApi.sql('ALTER CLUSTER posts ADD pq_title')
utilsApi.sql('ALTER CLUSTER posts ADD pq_clicks')await utilsApi.sql('ALTER CLUSTER posts ADD pq_title')
await utilsApi.sql('ALTER CLUSTER posts ADD pq_clicks')res = await utilsApi.sql('ALTER CLUSTER posts ADD pq_title');
res = await utilsApi.sql('ALTER CLUSTER posts ADD pq_clicks');utilsApi.sql("ALTER CLUSTER posts ADD pq_title");
utilsApi.sql("ALTER CLUSTER posts ADD pq_clicks");utilsApi.Sql("ALTER CLUSTER posts ADD pq_title");
utilsApi.Sql("ALTER CLUSTER posts ADD pq_clicks");utils_api.sql("ALTER CLUSTER posts ADD pq_title", Some(true)).await;
utils_api.sql("ALTER CLUSTER posts ADD pq_clicks", Some(true)).await;Все остальные узлы, которые хотят получить реплику таблиц кластера, должны присоединиться к кластеру следующим образом:
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
JOIN CLUSTER posts AT '192.168.1.101:9312'POST /cli -d "
JOIN CLUSTER posts AT '192.168.1.101:9312'
"$params = [
'cluster' => 'posts',
'body' => [
'192.168.1.101:9312'
]
];
$response = $client->cluster->join($params);utilsApi.sql('JOIN CLUSTER posts AT \'192.168.1.101:9312\'')await utilsApi.sql('JOIN CLUSTER posts AT \'192.168.1.101:9312\'')res = await utilsApi.sql('JOIN CLUSTER posts AT \'192.168.1.101:9312\'');utilsApi.sql("JOIN CLUSTER posts AT '192.168.1.101:9312'");utilsApi.Sql("JOIN CLUSTER posts AT '192.168.1.101:9312'");utils_api.sql("JOIN CLUSTER posts AT '192.168.1.101:9312'", Some(true)).await;При выполнении запросов добавьте к имени таблицы префикс имени кластера posts: или используйте свойство cluster для объекта HTTP-запроса.
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- Javascript
- Java
- C#
- Rust
INSERT INTO posts:pq_title VALUES ( 3, 'test me' )POST /insert -d '
{
"cluster":"posts",
"table":"pq_title",
"id": 3
"doc":
{
"title" : "test me"
}
}'$table->addDocuments([
3, ['title' => 'test me']
]);indexApi.insert({"cluster":"posts","table":"pq_title","id":3"doc":{"title":"test me"}})await indexApi.insert({"cluster":"posts","table":"pq_title","id":3"doc":{"title":"test me"}})res = await indexApi.insert({"cluster":"posts","table":"pq_title","id":3"doc":{"title":"test me"}});InsertDocumentRequest newdoc = new InsertDocumentRequest();
HashMap<String,Object> doc = new HashMap<String,Object>(){{
put("title","test me");
}};
newdoc.table("pq_title").cluster("posts").id(3L).setDoc(doc);
sqlresult = indexApi.insert(newdoc);Dictionary<string, Object> doc = new Dictionary<string, Object>();
doc.Add("title", "test me");
InsertDocumentRequest newdoc = new InsertDocumentRequest(table: "pq_title", cluster: "posts", id: 3, doc: doc);
var sqlresult = indexApi.Insert(newdoc);let mut doc = HashMap::new();
doc.insert("title".to_string(), serde_json::json!("test me"));
let insert_req = InsertDocumentRequest {
table: serde_json::json!("pq_title"),
doc: serde_json::json!(doc),
cluster: serde_json::json!("posts"),
id: serde_json::json!(3),
};
let insert_res = index_api.insert(insert_req).await;Все запросы, которые изменяют таблицы в кластере, теперь реплицируются на все узлы в кластере.
Чтобы создать репликационный кластер, необходимо как минимум задать его имя.
Если вы создаете один кластер или первый кластер, вы можете опустить опцию path. В этом случае в качестве пути кластера будет использоваться опция data_dir. Однако для всех последующих кластеров вы должны указать path, и путь должен быть доступен. Также может быть установлена опция nodes для перечисления всех узлов в кластере.
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- Java
- C#
- Rust
CREATE CLUSTER posts
CREATE CLUSTER click_query '/var/data/click_query/' as path
CREATE CLUSTER click_query '/var/data/click_query/' as path, 'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312' as nodesPOST /cli -d "
CREATE CLUSTER posts
"
POST /cli -d "
CREATE CLUSTER click_query '/var/data/click_query/' as path
"
POST /cli -d "
CREATE CLUSTER click_query '/var/data/click_query/' as path, 'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312' as nodes
"$params = [
'cluster' => 'posts',
]
];
$response = $client->cluster()->create($params);
$params = [
'cluster' => 'click_query',
'body' => [
'path' => '/var/data/click_query/'
]
]
];
$response = $client->cluster()->create($params);
$params = [
'cluster' => 'click_query',
'body' => [
'path' => '/var/data/click_query/',
'nodes' => 'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312'
]
]
];
$response = $client->cluster()->create($params);utilsApi.sql('CREATE CLUSTER posts')
utilsApi.sql('CREATE CLUSTER click_query \'/var/data/click_query/\' as path')
utilsApi.sql('CREATE CLUSTER click_query \'/var/data/click_query/\' as path, \'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312\' as nodes')await utilsApi.sql('CREATE CLUSTER posts')
await utilsApi.sql('CREATE CLUSTER click_query \'/var/data/click_query/\' as path')
await utilsApi.sql('CREATE CLUSTER click_query \'/var/data/click_query/\' as path, \'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312\' as nodes')res = await utilsApi.sql('CREATE CLUSTER posts');
res = await utilsApi.sql('CREATE CLUSTER click_query \'/var/data/click_query/\' as path');
res = await utilsApi.sql('CREATE CLUSTER click_query \'/var/data/click_query/\' as path, \'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312\' as nodes');utilsApi.sql("CREATE CLUSTER posts");
utilsApi.sql("CREATE CLUSTER click_query '/var/data/click_query/' as path");
utilsApi.sql("CREATE CLUSTER click_query '/var/data/click_query/' as path, 'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312' as nodes");utilsApi.Sql("CREATE CLUSTER posts");
utilsApi.Sql("CREATE CLUSTER click_query '/var/data/click_query/' as path");
utilsApi.Sql("CREATE CLUSTER click_query '/var/data/click_query/' as path, 'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312' as nodes");utils_api.sql("CREATE CLUSTER posts", Some(true)).await;
utils_api.sql("CREATE CLUSTER click_query '/var/data/click_query/' as path", Some(true)).await;
utils_api.sql("CREATE CLUSTER click_query '/var/data/click_query/' as path, 'clicks_mirror1:9312,clicks_mirror2:9312,clicks_mirror3:9312' as nodes", Some(true)).await;Если аутентификация и авторизация включены, фактический пользователь репликации должен иметь право replication для целевого кластера. Этого пользователя можно указать в операторе CREATE CLUSTER:
GRANT replication ON 'posts' TO 'repl_user';
CREATE CLUSTER posts 'repl_user' AS user;
Если пользователь не указан, для оператора используется текущий пользователь сеанса, а после успешного создания он сохраняется как пользователь кластера.
Перед тем как другие узлы с включенной авторизацией присоединятся к кластеру или начнут им управлять, настройте сохраненного пользователя кластера с соответствующими учетными данными и правом replication на этих узлах.
Если опция nodes не указана при создании кластера, первый узел, присоединившийся к кластеру, будет сохранен как опция nodes.
Чтобы присоединиться к существующему кластеру, вы должны указать как минимум:
- имя кластера
host:portдругого узла в кластере, к которому вы присоединяетесь
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- Java
- C#
- Rust
JOIN CLUSTER posts AT '10.12.1.35:9312'POST /cli -d "
JOIN CLUSTER posts AT '10.12.1.35:9312'
"$params = [
'cluster' => 'posts',
'body' => [
'10.12.1.35:9312'
]
];
$response = $client->cluster->join($params);utilsApi.sql('JOIN CLUSTER posts AT \'10.12.1.35:9312\'')await utilsApi.sql('JOIN CLUSTER posts AT \'10.12.1.35:9312\'')res = await utilsApi.sql('JOIN CLUSTER posts AT \'10.12.1.35:9312\'');utilsApi.sql("JOIN CLUSTER posts AT '10.12.1.35:9312'");utilsApi.Sql("JOIN CLUSTER posts AT '10.12.1.35:9312'");utils_api.sql("JOIN CLUSTER posts AT '10.12.1.35:9312'", Some(true)).await;{u'error': u'', u'total': 0, u'warning': u''}{u'error': u'', u'total': 0, u'warning': u''}{"total":0,"error":"","warning":""}Если включены аутентификация и авторизация, эффективный пользователь репликации должен иметь право replication. Указать этого пользователя можно в операторе JOIN CLUSTER:
GRANT replication ON 'posts' TO 'repl_user';
JOIN CLUSTER posts AT '10.12.1.35:9312' 'repl_user' AS user;
У присоединяющегося узла и узлов-источников должен быть один и тот же пользователь с совпадающими сохраненными данными аутентификации. Недостаточно независимо создать одинаковые имя пользователя и пароль на каждом узле, потому что сохраненные данные аутентификации могут отличаться.
Если пользователь не указан, для операции присоединения используется текущий пользователь сеанса. После успешного присоединения сохраненный пользователь кластера берется из метаданных кластера-источника.
ПРИМЕЧАНИЕ: Когда аутентификация включена, успешный
JOIN CLUSTERзаменяет все локальные данные аутентификации на присоединяющемся узле данными аутентификации кластера-источника. Если уровень журнала аутентификацииinfoили выше, Manticore перед заменой записывает прежние локальные данные auth вsearchd.log.authв виде JSON-резервной копии. В этой резервной копии есть соли и хэши учетных данных, поэтому храните журнал auth в секрете и обезличивайте его перед передачей. ЕслиJOIN CLUSTERне может получить пользователя кластера-источника, проверьте пользователя репликации и совпадение данных auth на узлах-источниках, а также ошибки аутентификации API вsearchd.log.authна узлах-источниках.
В большинстве случаев вышеуказанного достаточно, когда существует один кластер репликации. Однако, если вы создаете несколько кластеров репликации, вы также должны установить путь и убедиться, что каталог доступен.
- SQL
- JSON
JOIN CLUSTER c2 at '127.0.0.1:10201' 'c2' as pathPOST /sql?mode=raw -d "JOIN CLUSTER c2 at '127.0.0.1:10201' 'c2' as path"Узел присоединяется к кластеру, получая данные от указанного узла и, если это успешно, обновляет списки узлов на всех других узлах кластера так же, как если бы это было сделано вручную через ALTER CLUSTER ... UPDATE nodes. Этот список используется для повторного присоединения узлов к кластеру при перезапуске.
Существует два списка узлов:
1.cluster_<name>_nodes_set: используется для повторного присоединения узлов к кластеру при перезапуске. Он обновляется на всех узлах так же, как это делает ALTER CLUSTER ... UPDATE nodes. Команда JOIN CLUSTER выполняет это обновление автоматически. Статус кластера отображает этот список как cluster_<name>_nodes_set.
2. cluster_<name>_nodes_view: этот список содержит все активные узлы, используемые для репликации, и не требует ручного управления. ALTER CLUSTER ... UPDATE nodes фактически копирует этот список узлов в список узлов, используемых для повторного присоединения при перезапуске. Статус кластера отображает этот список как cluster_<name>_nodes_view.
Когда узлы расположены в разных сетевых сегментах или центрах обработки данных, опция nodes может быть установлена явно. Это минимизирует трафик между узлами и использует узлы-шлюзы для межцентровой связи. Следующий код присоединяет существующий кластер, используя опцию nodes.
Примечание: Список кластера
cluster_<name>_nodes_setне обновляется автоматически, когда используется этот синтаксис. Чтобы обновить его, используйте ALTER CLUSTER ... UPDATE nodes.
- SQL
- JSON
- PHP
- Python
- Python-asyncio
- javascript
- Java
- C#
- Rust
JOIN CLUSTER click_query 'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312' as nodesPOST /cli -d "
JOIN CLUSTER click_query 'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312' as nodes
"$params = [
'cluster' => 'posts',
'body' => [
'nodes' => 'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312'
]
];
$response = $client->cluster->join($params);utilsApi.sql('JOIN CLUSTER click_query \'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312\' as nodes')await utilsApi.sql('JOIN CLUSTER click_query \'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312\' as nodes')res = await utilsApi.sql('JOIN CLUSTER click_query \'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312\' as nodes');utilsApi.sql("JOIN CLUSTER click_query 'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312' as nodes");utilsApi.Sql("JOIN CLUSTER click_query 'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312' as nodes");utils_api.sql("JOIN CLUSTER click_query 'clicks_mirror1:9312;clicks_mirror2:9312;clicks_mirror3:9312' as nodes", Some(true)).await;{u'error': u'', u'total': 0, u'warning': u''}{u'error': u'', u'total': 0, u'warning': u''}{"total":0,"error":"","warning":""}Команда JOIN CLUSTER работает синхронно и завершается, как только узел получает все данные от других узлов в кластере и синхронизируется с ними.
Операция JOIN CLUSTER может завершиться с ошибкой, указывающей на дублирующийся server_id. Это происходит, когда присоединяющийся узел имеет тот же server_id, что и существующий узел в кластере. Чтобы решить эту проблему, убедитесь, что каждый узел в кластере репликации имеет уникальный server_id. Вы можете изменить значение по умолчанию server_id в разделе "searchd" вашего конфигурационного файла на уникальное значение перед попыткой присоединиться к кластеру.