[comment]: # ({e26480a0-e26480a0})
# 3. Обновление базы данных для использования первичных ключей

[comment]: # ({/e26480a0-e26480a0})

[comment]: # ({c4558d36-53564572})
### Обзор

В этом разделе приведены инструкции по ручному обновлению таблиц в существующих установках до первичных ключей.

Переход на первичные ключи оптимизирует индексирование и доступ к данным, что может ускорить выполнение запросов и сэкономить место.
Это также улучшает управление данными и синхронизацию в кластерных конфигурациях, помогая масштабированию и обеспечивая надежную работу системы даже при отказе некоторых серверов.

::: noteimportant
Инструкции, приведенные на этой странице, предназначены для опытных пользователей и могут потребовать адаптации под вашу конкретную конфигурацию.
Переход на первичные ключи может быть длительным и ресурсоемким.
Убедитесь, что доступно достаточно свободного места на диске; в зависимости от размера базы данных и хранимых данных процесс может потребовать до 2,5 раза больше места, чем сейчас занимают таблицы истории.
:::

Первичные ключи используются для всех таблиц в новых установках начиная с Zabbix 6.0.

Автоматического обновления базы данных до первичных ключей нет; однако существующие установки можно обновить вручную **после** обновления Zabbix server до 6.0 или более новой версии.

::: noteimportant
Начиная с Zabbix 7.0, при обновлении таблиц до первичных ключей таблицы также переводятся на использование типов данных double precision.
<br><br>
Если у вас Zabbix 7.0 (или новее), таблицы уже используют double precision.
Однако инструкции на этой странице по-прежнему можно использовать для обновления таблиц до первичных ключей без влияния на таблицы, которые уже используют double precision.
<br><br>
Если у вас Zabbix 6.4 (или более ранняя версия), рекомендуется сначала обновить таблицы до double precision.
Дополнительную информацию см. в разделе [Обновление до числовых значений расширенного диапазона](https://www.zabbix.com/documentation/7.0/en/manual/appendix/install/db_float_range) в документации Zabbix 7.0.
:::

Доступны инструкции для:

* [MySQL](#mysql)
* [PostgreSQL](#postgresql)
* [TimescaleDB](#postgresql-timescaledb)

[comment]: # ({/c4558d36-53564572})

[comment]: # ({5c5adaf8-980f7329})
### Важные примечания

Чтобы выполнить обновление базы данных:

1. Остановите сервер Zabbix.

Настоятельно рекомендуется останавливать сервер Zabbix на время обновления.
Однако, если это абсолютно необходимо, вы можете выполнить обновление, пока сервер работает (только для MySQL, MariaDB и PostgreSQL без TimescaleDB).

2. Создайте резервную копию базы данных.
3. Установите последнюю версию пакета zabbix-sql-scripts, совместимую с вашей версией Zabbix (например, для RHEL: `dnf install zabbix-sql-scripts`).
4. Запустите скрипты для вашей базы данных.
5. Запустите сервер Zabbix.

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

Если база данных использует разделы, обратитесь за помощью к администратору БД или в службу поддержки Zabbix.

CSV-файлы можно удалить после успешного обновления до первичных ключей.

При необходимости веб-интерфейс Zabbix можно перевести в [режим обслуживания](/manual/web_interface/maintenance_mode).

[comment]: # ({/5c5adaf8-980f7329})

[comment]: # ({4f99bcf1-1551eb55})
### MySQL

Экспорт и импорт необходимо выполнять в tmux/screen, чтобы гарантировать, что сеанс не будет прерван.

Смотрите также: [Важные примечания](#важные-примечания)

[comment]: # ({/4f99bcf1-1551eb55})

[comment]: # ({ddfd920a-dc89d749})
#### MySQL 8.0+ с mysqlsh

Этот метод можно использовать при работающем сервере Zabbix, но рекомендуется остановить сервер на время обновления.
MySQL Shell (*mysqlsh*) должен быть [установлен](https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-install-linux-quick.html) и иметь возможность подключаться к БД.

* Войдите в консоль MySQL как root (рекомендуется) или как любой пользователь с привилегиями FILE.

* Запустите MySQL с включенной переменной [local_infile](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_local_infile).

* Переименуйте старые таблицы и создайте новые, выполнив `history_upgrade_prepare.sql`.

```bash
mysql -uzabbix -p<password> zabbix < /usr/share/zabbix/sql-scripts/mysql/option-patches/history_upgrade_prepare.sql
```

* Экспортируйте и импортируйте данные.

Подключитесь через mysqlsh. Если используется подключение через сокет, может потребоваться указать путь.

```bash
sudo mysqlsh -uroot -S /run/mysqld/mysqld.sock --no-password -Dzabbix
```

Переключитесь в режим JavaScript с помощью:

```sqlmysql
\js
```

Затем выполните приведенный ниже код (CSVPATH можно изменить при необходимости):

```javascript
CSVPATH="/var/lib/mysql-files";

util.exportTable("history_old", CSVPATH + "/history.csv", { dialect: "csv" });
util.importTable(CSVPATH + "/history.csv", {"dialect": "csv", "table": "history" });

util.exportTable("history_uint_old", CSVPATH + "/history_uint.csv", { dialect: "csv" });
util.importTable(CSVPATH + "/history_uint.csv", {"dialect": "csv", "table": "history_uint" });

util.exportTable("history_str_old", CSVPATH + "/history_str.csv", { dialect: "csv" });
util.importTable(CSVPATH + "/history_str.csv", {"dialect": "csv", "table": "history_str" });

util.exportTable("history_log_old", CSVPATH + "/history_log.csv", { dialect: "csv" });
util.importTable(CSVPATH + "/history_log.csv", {"dialect": "csv", "table": "history_log" });

util.exportTable("history_text_old", CSVPATH + "/history_text.csv", { dialect: "csv" });
util.importTable(CSVPATH + "/history_text.csv", {"dialect": "csv", "table": "history_text" });
```

Если вы получите сообщение "JavaScript is not supported", значит в вашей установке MySQL Shell отсутствует поддержка JS.
В этом случае установите официальный [пакет MySQL Shell](https://dev.mysql.com/downloads/shell/) от Oracle (или соберите его из исходного кода), чтобы был включен режим JavaScript.

* Следуйте [инструкциям после миграции](#postmigration), чтобы удалить старые таблицы.

[comment]: # ({/ddfd920a-dc89d749})

[comment]: # ({13c44439-b132f69b})
#### MariaDB/MySQL 8.0+ без mysqlsh

Этот способ обновления занимает больше времени и должен использоваться только в том случае, если обновление с помощью *mysqlsh* невозможно.

##### Обновление таблиц

* Войдите в консоль MySQL как root (рекомендуется) или любой пользователь с привилегиями FILE.

* Если выполняется миграция при работающем сервере Zabbix, запустите MySQL с включенной переменной [local_infile](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_local_infile).

* Переименуйте старые таблицы и создайте новые, выполнив `history_upgrade_prepare.sql`:

```bash
mysql -uzabbix -p<password> zabbix < /usr/share/zabbix/sql-scripts/mysql/option-patches/history_upgrade_prepare.sql
```

##### Миграция при остановленном сервере

`max_execution_time` (в MySQL) или `max_statement_time` (в MariaDB) необходимо отключить перед миграцией данных, чтобы избежать тайм-аута во время миграции.

Для MySQL:

```sql
SET @@max_execution_time=0;
```

Для MariaDB:

```sql
SET @@max_statement_time=0;
```

```sql
INSERT IGNORE INTO history SELECT * FROM history_old;
INSERT IGNORE INTO history_uint SELECT * FROM history_uint_old;
INSERT IGNORE INTO history_str SELECT * FROM history_str_old;
INSERT IGNORE INTO history_log SELECT * FROM history_log_old;
INSERT IGNORE INTO history_text SELECT * FROM history_text_old;
```

Следуйте [инструкциям после миграции](#postmigration), чтобы удалить старые таблицы.

##### Миграция при работающем сервере

Проверьте, для каких путей включен импорт/экспорт:

```sql
mysql> SELECT @@secure_file_priv;
+-----------------------+
| @@secure_file_priv    |
+-----------------------+
| /var/lib/mysql-files/ |
+-----------------------+
```

Если значение *secure_file_priv* является путем к каталогу, экспорт/импорт будет выполняться для файлов в этом каталоге.
В этом случае соответствующим образом измените пути к файлам в запросах или установите значение *secure_file_priv* в пустую строку на время обновления.

Если значение *secure_file_priv* пустое, экспорт/импорт можно выполнять из любого расположения.

Если значение *secure_file_priv* равно NULL, задайте путь к каталогу, который содержит экспортированные данные таблиц (в примере выше это '/var/lib/mysql-files/').

Дополнительную информацию см. в [документации MySQL](https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html#sysvar_secure_file_priv) или [документации MariaDB](https://mariadb.com/docs/server/ha-and-performance/optimization-and-tuning/system-variables/server-system-variables#secure_file_priv).

`max_execution_time` (в MySQL) или `max_statement_time` (в MariaDB) необходимо отключить перед экспортом данных, чтобы избежать тайм-аута во время экспорта.

Для MySQL:

```sql
SET @@max_execution_time=0;
```

Для MariaDB:

```sql
SET @@max_statement_time=0;
```

```sql
SELECT * INTO OUTFILE '/var/lib/mysql-files/history.csv' FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n' FROM history_old;
LOAD DATA INFILE '/var/lib/mysql-files/history.csv' IGNORE INTO TABLE history FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n';

SELECT * INTO OUTFILE '/var/lib/mysql-files/history_uint.csv' FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n' FROM history_uint_old;
LOAD DATA INFILE '/var/lib/mysql-files/history_uint.csv' IGNORE INTO TABLE history_uint FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n';

SELECT * INTO OUTFILE '/var/lib/mysql-files/history_str.csv' FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n' FROM history_str_old;
LOAD DATA INFILE '/var/lib/mysql-files/history_str.csv' IGNORE INTO TABLE history_str FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n';

SELECT * INTO OUTFILE '/var/lib/mysql-files/history_log.csv' FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n' FROM history_log_old;
LOAD DATA INFILE '/var/lib/mysql-files/history_log.csv' IGNORE INTO TABLE history_log FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n';

SELECT * INTO OUTFILE '/var/lib/mysql-files/history_text.csv' FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n' FROM history_text_old;
LOAD DATA INFILE '/var/lib/mysql-files/history_text.csv' IGNORE INTO TABLE history_text FIELDS TERMINATED BY ',' ESCAPED BY '"' LINES TERMINATED BY '\n';
```

Следуйте [инструкциям после миграции](#postmigration), чтобы удалить старые таблицы.

[comment]: # ({/13c44439-b132f69b})

[comment]: # ({8e581875-1139207b})
### PostgreSQL

Экспорт и импорт необходимо выполнять в tmux/screen, чтобы сеанс не был разорван.
Для установок с TimescaleDB пропустите этот раздел и перейдите к [PostgreSQL + TimescaleDB](#postgresql-timescaledb).

См. также: [Важные примечания](#important-notes)

#### Обновление таблиц

* Переименуйте таблицы с помощью `history_upgrade_prepare.sql`:

```bash
sudo -u zabbix psql zabbix < /usr/share/zabbix/sql-scripts/postgresql/option-patches/history_upgrade_prepare.sql
```

#### Миграция с остановленным сервером

* Экспортируйте текущую историю, импортируйте ее во временную таблицу, затем вставьте данные в новые таблицы, игнорируя дубликаты:

```sql
INSERT INTO history SELECT * FROM history_old ON CONFLICT (itemid,clock,ns) DO NOTHING;

INSERT INTO history_uint SELECT * FROM history_uint_old ON CONFLICT (itemid,clock,ns) DO NOTHING;

INSERT INTO history_str SELECT * FROM history_str_old ON CONFLICT (itemid,clock,ns) DO NOTHING;

INSERT INTO history_log SELECT * FROM history_log_old ON CONFLICT (itemid,clock,ns) DO NOTHING;

INSERT INTO history_text SELECT * FROM history_text_old ON CONFLICT (itemid,clock,ns) DO NOTHING;
```

См. советы по повышению производительности INSERT: [PostgreSQL: Bulk Loading Huge Amounts of Data](https://www.cybertec-postgresql.com/en/postgresql-bulk-loading-huge-amounts-of-data), [Checkpoint Distance and Amount of WAL](https://www.cybertec-postgresql.com/en/checkpoint-distance-and-amount-of-wal).

* Следуйте [инструкциям после миграции](#postmigration), чтобы удалить старые таблицы.

[comment]: # ({/8e581875-1139207b})

[comment]: # ({e951ec07-7e590ff3})
#### Миграция при работающем сервере

* Экспортируйте текущую историю, импортируйте ее во временную таблицу, затем вставьте данные в новые таблицы, игнорируя дубликаты:

```sql
\copy history_old TO '/tmp/history.csv' DELIMITER ',' CSV
CREATE TEMP TABLE temp_history (
    itemid                   bigint                                    NOT NULL,
    clock                    integer         DEFAULT '0'               NOT NULL,
    value                    DOUBLE PRECISION DEFAULT '0.0000'          NOT NULL,
    ns                       integer         DEFAULT '0'               NOT NULL
);
\copy temp_history FROM '/tmp/history.csv' DELIMITER ',' CSV
INSERT INTO history SELECT * FROM temp_history ON CONFLICT (itemid,clock,ns) DO NOTHING;

\copy history_uint_old TO '/tmp/history_uint.csv' DELIMITER ',' CSV
CREATE TEMP TABLE temp_history_uint (
    itemid                   bigint                                    NOT NULL,
    clock                    integer         DEFAULT '0'               NOT NULL,
    value                    numeric(20)     DEFAULT '0'               NOT NULL,
    ns                       integer         DEFAULT '0'               NOT NULL
);
\copy temp_history_uint FROM '/tmp/history_uint.csv' DELIMITER ',' CSV
INSERT INTO history_uint SELECT * FROM temp_history_uint ON CONFLICT (itemid,clock,ns) DO NOTHING;

\copy history_str_old TO '/tmp/history_str.csv' DELIMITER ',' CSV
CREATE TEMP TABLE temp_history_str (
    itemid                   bigint                                    NOT NULL,
    clock                    integer         DEFAULT '0'               NOT NULL,
    value                    varchar(255)    DEFAULT ''                NOT NULL,
    ns                       integer         DEFAULT '0'               NOT NULL
);
\copy temp_history_str FROM '/tmp/history_str.csv' DELIMITER ',' CSV
INSERT INTO history_str (itemid,clock,value,ns) SELECT * FROM temp_history_str ON CONFLICT (itemid,clock,ns) DO NOTHING;

\copy history_log_old TO '/tmp/history_log.csv' DELIMITER ',' CSV
CREATE TEMP TABLE temp_history_log (
    itemid                   bigint                                    NOT NULL,
    clock                    integer         DEFAULT '0'               NOT NULL,
    timestamp                integer         DEFAULT '0'               NOT NULL,
    source                   varchar(64)     DEFAULT ''                NOT NULL,
    severity                 integer         DEFAULT '0'               NOT NULL,
    value                    text            DEFAULT ''                NOT NULL,
    logeventid               integer         DEFAULT '0'               NOT NULL,
    ns                       integer         DEFAULT '0'               NOT NULL
);
\copy temp_history_log FROM '/tmp/history_log.csv' DELIMITER ',' CSV
INSERT INTO history_log SELECT * FROM temp_history_log ON CONFLICT (itemid,clock,ns) DO NOTHING;

\copy history_text_old TO '/tmp/history_text.csv' DELIMITER ',' CSV
CREATE TEMP TABLE temp_history_text (
    itemid                   bigint                                    NOT NULL,
    clock                    integer         DEFAULT '0'               NOT NULL,
    value                    text            DEFAULT ''                NOT NULL,
    ns                       integer         DEFAULT '0'               NOT NULL
);
\copy temp_history_text FROM '/tmp/history_text.csv' DELIMITER ',' CSV
INSERT INTO history_text SELECT * FROM temp_history_text ON CONFLICT (itemid,clock,ns) DO NOTHING;
```
* Следуйте [инструкциям после миграции](#postmigration), чтобы удалить старые таблицы.

[comment]: # ({/e951ec07-7e590ff3})

[comment]: # ({4e372941-589d2f09})
### PostgreSQL + TimescaleDB

Экспорт и импорт необходимо выполнять в tmux/screen, чтобы сеанс не был разорван.
Во время обновления сервер Zabbix должен быть остановлен.

См. также: [Important notes](#important-notes)

* Переименуйте таблицы с помощью `history_upgrade_prepare.sql`.
  * Если сжатие включено (в стандартной установке), выполните скрипт из `/usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression`:
    ```bash
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression/history_upgrade_prepare.sql | sudo -u zabbix psql zabbix
    ```
  * Если сжатие отключено, выполните скрипт из `/usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression`:
    ```bash
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression/history_upgrade_prepare.sql | sudo -u zabbix psql zabbix
    ```

* Запустите скрипты миграции hypertable TimescaleDB в зависимости от настроек сжатия:
  * Если сжатие включено (в стандартной установке), выполните скрипты из `/usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression`:
    ```bash
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression/history_upgrade.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression/history_upgrade_uint.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression/history_upgrade_log.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression/history_upgrade_str.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression/history_upgrade_text.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/with-compression/trends_upgrade.sql | sudo -u zabbix psql zabbix
    ```
  * Если сжатие отключено, выполните скрипты из `/usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression`:
    ```bash
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression/history_upgrade.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression/history_upgrade_uint.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression/history_upgrade_log.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression/history_upgrade_str.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression/history_upgrade_text.sql | sudo -u zabbix psql zabbix
    cat /usr/share/zabbix/sql-scripts/postgresql/timescaledb/option-patches/without-compression/trends_upgrade.sql | sudo -u zabbix psql zabbix
    ```

См. также: [Tips](https://www.tigerdata.com/blog/13-tips-to-improve-postgresql-insert-performance) для повышения производительности INSERT.

* Следуйте [инструкциям после миграции](#postmigration), чтобы удалить старые таблицы.

[comment]: # ({/4e372941-589d2f09})

[comment]: # ({e73536ae-b041e427})

### Пост-миграция

Для всех баз данных после завершения миграции сделайте следующее:

* Проверьте, что всё работает, как ожидается.

* Удалите старые таблицы:

```sql
DROP TABLE history_old;
DROP TABLE history_uint_old;
DROP TABLE history_str_old;
DROP TABLE history_log_old;
DROP TABLE history_text_old;
```

* Для TimescaleDB также удалите следующую старую таблицу:

```sql
DROP TABLE trends_old;
```

[comment]: # ({/e73536ae-b041e427})

[comment]: # ({16ed11f0-8ee7d9aa})
#### Смотрите также

-   [Подготовка таблицы журнала аудита к партиционированию](/manual/appendix/install/auditlog_primary_keys)

[comment]: # ({/16ed11f0-8ee7d9aa})
