wiki/HowtoMySQL/ProxySQL.md

26 KiB

categories: databases title: Howto ProxySQL : Outil de proxyfication et de haute disponibilité pour MySQL.

ProxySQL est un proxy haute performance et haute disponibilité pour MySQL et ses dérivées comme Percona Server ou MariaDB

Installation

Plusieurs méthodes d'installation de ProxySQL sont disponible, on va décrire comment l'installer via les dépots officiel pour Debian / Ubuntu

Ajout du dépôt

# apt-get install -y --no-install-recommends lsb-release wget apt-transport-https ca-certificates gnupg
# curl -fsSL https://repo.proxysql.com/ProxySQL/proxysql-2.5.x/repo_pub_key | tee /etc/apt/trusted.gpg.d/ProxySQL-keyring.asc
# echo deb https://repo.proxysql.com/ProxySQL/proxysql-2.5.x/$(lsb_release -sc)/ ./ | tee /etc/apt/sources.list.d/proxysql.list

Voici les différentes release disponible de ProxySQL

Installation de ProxySQL

# apt update
# apt install proxysql

Configurer ProxySQL la première fois

Juste après l'installation de ProxySQL nous devons faire quelques opérations de configuration

Par défaut ProxySQL écoute sur le port 6032, on démarre le service comme ceci :

# systemctl start proxysql.service

Se connecter à l'interface d'admin de ProxySQL :

# mysql -u admin -padmin -h 127.0.0.1 -P6032 --prompt 'ProxySQL Admin> '

Vérifiez que la configuration est bien vierge en vérifiant qu'il n'y a aucune entrées dans les tables mysql_servers, mysql_replication_hostgroups et mysql_query_rules

ProxySQL Admin> SELECT * FROM mysql_servers;
Empty set (0.00 sec)

ProxySQL Admin> SELECT * from mysql_replication_hostgroups;
Empty set (0.00 sec)

ProxySQL Admin> SELECT * from mysql_query_rules;
Empty set (0.00 sec)

Changement du mot de passe compte admin

Le mot de passe par défaut du compte admin est 'admin', on peux changer ça comme ceci dans l'interface d'admin :

ProxySQL Admin> set admin-admin_credentials="admin:admin;admin:IkjeevgujHus"

On sauvegarde la configuration de l'admin en mémoire :

ProxySQL Admin> LOAD ADMIN VARIABLES TO RUNTIME;

Puis rendre persistant les changement de l'admin sur le disque :

ProxySQL Admin> SAVE ADMIN VARIABLES FROM RUNTIME;

Le datadir de ProxySQL est dans /var/lib/proxysql, si pour une raison ou une autre on veux réinialisé toutes la configuration de proxysql :

# service proxysql stop && service proxysql initial

Ceci réinitialise le datadir de ProxySQL baser sur les fichiers de configuration qui sont dans /etc/proxysql/proxysql.cnf

Ajouter des Backend

Dans ProxySQL les backend sont les serveurs sql sur lequels ProxySQL doit se connecter. Pour cet exemple on va ajouter 2 serveurs SQL, on doit les ajouter dans la table mysql_servers

ProxySQL Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (1,'192.168.0.1',3306);
Query OK, 1 row affected (0.01 sec)


ProxySQL Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (1,'192.168.0.2',3306);
Query OK, 1 row affected (0.01 sec)

SELECT * FROM mysql_servers;
+--------------+-----------+------+--------+--------+-------------+-----------------+---------------------+
| hostgroup_id | hostname  | port | status | weight | compression | max_connections | max_replication_lag |
+--------------+-----------+------+--------+--------+-------------+-----------------+---------------------+
| 1            | 192.168.0.1 | 3306 | ONLINE | 1      | 0           | 1000            | 0                   |
| 1            | 192.168.0.2 | 3306 | ONLINE | 1      | 0           | 1000            | 0                   |
+--------------+-----------+------+--------+--------+-------------+-----------------+---------------------+
2 rows in set (0.00 sec)

ProxySQL considère les backend configurer avec read_only = 0 comme des instances d'écriture (WRITER), et les instances avec read_only = 1 comme des répliques. Il faut donc s'assurer ques ces variables soit définie correctement sur toutes les instances, car ProxySQL prendra les décisions de routage en fonction de cette valeur.

Configuration du monitoring

ProxySQL surveille en permanence les backends du serveur MySQL configurés pour identifier l'état de santé. Les informations d'identification pour la surveillance des backends doivent être créées dans MySQL et également configurées dans ProxySQL avec les intervalles de vérification spécifiques à l'environnement.

On créer un utlisateur sur le serveur SQL primaire :

MariaDB> UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username';
Query OK, 1 row affected (0.00 sec)

Et on ajoute l'utilisateur et son mot de passe dans ProxySQL :

ProxySQL Admin> UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username';
Query OK, 1 row affected (0.00 sec)

ProxySQL Admin> UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_password';
Query OK, 1 row affected (0.00 sec)

On configure ensuite les différentes intervales de surveillance, en secondes :

ProxySQL Admin> UPDATE global_variables SET variable_value='2000' WHERE variable_name IN ('mysql-monitor_connect_interval','mysql-monitor_ping_interval','mysql-monitor_read_only_interval');
Query OK, 3 rows affected (0.00 sec)

ProxySQL Admin> SELECT * FROM global_variables WHERE variable_name LIKE 'mysql-monitor_%';
+----------------------------------------+---------------------------------------------------+
| variable_name                          | variable_value                                    |
+----------------------------------------+---------------------------------------------------+
| mysql-monitor_history                  | 600000                                            |
| mysql-monitor_connect_interval         | 2000                                              |
| mysql-monitor_connect_timeout          | 200                                               |
| mysql-monitor_ping_interval            | 2000                                              |
| mysql-monitor_ping_timeout             | 100                                               |
| mysql-monitor_read_only_interval       | 2000                                              |
| mysql-monitor_read_only_timeout        | 100                                               |
| mysql-monitor_replication_lag_interval | 10000                                             |
| mysql-monitor_replication_lag_timeout  | 1000                                              |
| mysql-monitor_username                 | monitor                                           |
| mysql-monitor_password                 | monitor                                           |
| mysql-monitor_query_variables          | SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES |
| mysql-monitor_query_status             | SELECT * FROM INFORMATION_SCHEMA.GLOBAL_STATUS    |
| mysql-monitor_query_interval           | 60000                                             |
| mysql-monitor_query_timeout            | 100                                               |
| mysql-monitor_timer_cached             | true                                              |
| mysql-monitor_writer_is_also_reader    | true                                              |
+----------------------------------------+---------------------------------------------------+
17 rows in set (0.00 sec)

Les modifications apportées au moniteur MySQL dans la table global_variables seront appliquées après l'exécution de l'instruction LOAD MYSQL VARIABLES TO RUNTIME :

ProxySQL Admin> LOAD MYSQL VARIABLES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)

Pour conserver les modifications de configuration lors des redémarrages, SAVE MYSQL VARIABLES TO DISK doit également être exécuté :

ProxySQL Admin> SAVE MYSQL VARIABLES TO DISK;
Query OK, 54 rows affected (0.02 sec)

Groupe d'hôtes de réplication

Les changements de topologie du cluster sont surveillés en fonction des groupes d'hôtes de réplication configuré dans ProxySQL. ProxySQL comprend la topologie de réplication en surveillant la valeur de la variable read_only sur les serveurs configurés dans les groupes d'hôtes dans la table mysql_replication_hostgroups

Cette table est vide par défaut et doit être configuré en spécifiant une paire de groupes d'hôtes READER et WRITER, par exemple :

ProxySQL Admin> SHOW CREATE TABLE mysql_replication_hostgroups\G
*************************** 1. row ***************************
       table: mysql_replication_hostgroups
Create Table: CREATE TABLE mysql_replication_hostgroups (
    writer_hostgroup INT CHECK (writer_hostgroup>=0) NOT NULL PRIMARY KEY,
    reader_hostgroup INT NOT NULL CHECK (reader_hostgroup<>writer_hostgroup AND reader_hostgroup>=0),
    check_type VARCHAR CHECK (LOWER(check_type) IN ('read_only','innodb_read_only','super_read_only','read_only|innodb_read_only','read_only&innodb_read_only')) NOT NULL DEFAULT 'read_only',
    comment VARCHAR NOT NULL DEFAULT '', UNIQUE (reader_hostgroup))


ProxySQL Admin> INSERT INTO mysql_replication_hostgroups (writer_hostgroup,reader_hostgroup,comment) VALUES (1,2,'cluster1');
Query OK, 1 row affected (0.00 sec)

Maintenant, tout les serveurs backend MySQL configurés dans le groupe d'hôtes 1 ou 2 seront placés dans leurs groupe d'hôte respectif en fonction de leur valeur read_only :

  • S'ils ont read_only=0, alors il seront déplacés dans le groupe d'hôtes 1 (writer_hostgroup) qui permet de faire de l'écriture
  • S'ils ont read_only=1, alors il seront déplacés dans le groupe d'hôtes 2 (reader_hostgroup) qui permet de faire de la lecture seule

Pour activé cette configuration et l'activé lors de l'exécution, il faut exécuter la même commande LOAD utilisée pour les serveurs MySQL, car LOAD MYSQL SERVERS TO RUNTIME traite à la fois les tables mysql_servers et mysql_replication_hostgroups

Le résultat du check read_only sont logger dans la table mysql_servers_read_only_log dans la base monitor :

ProxySQL Admin> SELECT * FROM monitor.mysql_server_read_only_log ORDER BY time_start_us DESC LIMIT 3
+---------------+------+------------------+-----------------+-----------+-------+
| hostname      | port | time_start_us    | success_time_us | read_only | error |
+---------------+------+------------------+-----------------+-----------+-------+
| 192.168.4.247 | 3306 | 1680509025040593 | 716             | 0         | NULL  |
| 192.168.4.244 | 3306 | 1680509025018830 | 583             | 1         | NULL  |
| 192.168.4.245 | 3306 | 1680509023039065 | 368             | 1         | NULL  |
+---------------+------+------------------+-----------------+-----------+-------+
3 rows in set (0.000 sec)

ProxySQL surveille la valeur read_only pour les serveurs, et il a également créé le groupe d'hôtes2 (hostgroup_id=2) vers l'endroit où il a déplacé les serveurs avec read_only=1 (readers) du groupe d'hôtes1 (hostgroup_id=1).

ProxySQL Admin> SELECT * FROM mysql_servers;
+--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| hostgroup_id | hostname      | port | gtid_port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
| 1            | 192.168.4.247 | 3306 | 0         | ONLINE | 1      | 0           | 1000            | 0                   | 0       | 0              |         |
| 2            | 192.168.4.224 | 3306 | 0         | ONLINE | 1      | 0           | 1000            | 0                   | 0       | 0              |         |
| 2            | 192.168.4.245 | 3306 | 0         | ONLINE | 1      | 0           | 1000            | 0                   | 0       | 0              |         |
+--------------+---------------+------+-----------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+
5 rows in set (0.000 sec)

Pour sauvegarder, on enregistre la configuration sur le disque :

ProxySQL Admin> SAVE MYSQL SERVERS TO DISK;
Query OK, 0 rows affected (0.01 sec)

ProxySQL Admin> SAVE MYSQL VARIABLES TO DISK;
Query OK, 54 rows affected (0.00 sec)

Utilisateurs MySQL

Après avoir configuré les backend MySQL, dans la table mysql_servers, l'étape suivante est de configuré les utilisateurs MySQL.

Ceci ce fait en ajoutant des entrées à la table mysql_users :

ProxySQL Admin> SHOW CREATE TABLE mysql_users\G
*************************** 1. row ***************************
       table: mysql_users
Create Table: CREATE TABLE mysql_users (
    username VARCHAR NOT NULL,
    password VARCHAR,
    active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1,
    use_ssl INT CHECK (use_ssl IN (0,1)) NOT NULL DEFAULT 0,
    default_hostgroup INT NOT NULL DEFAULT 0,
    default_schema VARCHAR,
    schema_locked INT CHECK (schema_locked IN (0,1)) NOT NULL DEFAULT 0,
    transaction_persistent INT CHECK (transaction_persistent IN (0,1)) NOT NULL DEFAULT 1,
    fast_forward INT CHECK (fast_forward IN (0,1)) NOT NULL DEFAULT 0,
    backend INT CHECK (backend IN (0,1)) NOT NULL DEFAULT 1,
    frontend INT CHECK (frontend IN (0,1)) NOT NULL DEFAULT 1,
    max_connections INT CHECK (max_connections >=0) NOT NULL DEFAULT 10000,
    attributes VARCHAR CHECK (JSON_VALID(attributes) OR attributes = '') NOT NULL DEFAULT '',
    comment VARCHAR NOT NULL DEFAULT '',
    PRIMARY KEY (username, backend),
    UNIQUE (username, frontend))
1 row in set (0.000 sec)

Cette table est vide par défaut, pour ajouter des utilisateurs, spécifiez le nom d'utilisateur, le mot de passe et le groupe d'hôtes par défaut pour la configuration de base :

ProxySQL Admin> INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('foo','bar',1);


ProxySQL Admin> SELECT * FROM mysql_users;
+----------+--------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+------------+---------+
| username | password     | active | use_ssl | default_hostgroup | default_schema | schema_locked | transaction_persistent | fast_forward | backend | frontend | max_connections | attributes | comment |
+----------+--------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+------------+---------+
| root     |              | 1      | 0       | 1                 | NULL           | 0             | 1                      | 0            | 1       | 1        | 10000           |            |         |
| foo      | bar          | 1      | 0       | 1                 | NULL           | 0             | 1                      | 0            | 1       | 1        | 10000           |            |         |
+----------+--------------+--------+---------+-------------------+----------------+---------------+------------------------+--------------+---------+----------+-----------------+------------+---------+
2 rows in set (0.000 sec)

En définissant le groupe d'hôtes par défaut, nous spécifions à quels serveurs principaux un utilisateur doit se connecter PAR DÉFAUT, c'est-à-dire qu'il s'agira de la route par défaut pour le trafic provenant de l'utilisateur spécifique, des règles supplémentaires peuvent être configurées pour rediriger, mais en leur absence, toutes les requêtes iront au groupe d'hôtes spécifique.

ProxySQL Admin> LOAD MYSQL USERS TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)
ProxySQL Admin> SAVE MYSQL USERS TO DISK;
Query OK, 0 rows affected (0.01 sec)

ProxySQL est maintenant prêt à servir le trafic sur le port 6033 (par défaut).

# mysql -u foo -p bar -h 127.0.0.1 -P6033 -e"SELECT @@port"
+--------+
| @@port |
+--------+
|  3306  |
+--------+

Règles de requêtes

Les règles de requetes sont un puissant levier pour contrôler le trafic qui passe a travers ProxySQL, et sont configurés dans la table mysql_query_rules :

ProxySQL Admin>   SHOW CREATE TABLE mysql_query_rules\G
*************************** 1. row ***************************
       table: mysql_query_rules
Create Table: CREATE TABLE mysql_query_rules (
    rule_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 0,
    username VARCHAR,
    schemaname VARCHAR,
    flagIN INT CHECK (flagIN >= 0) NOT NULL DEFAULT 0,
    client_addr VARCHAR,
    proxy_addr VARCHAR,
    proxy_port INT CHECK (proxy_port >= 0 AND proxy_port <= 65535),
    digest VARCHAR,
    match_digest VARCHAR,
    match_pattern VARCHAR,
    negate_match_pattern INT CHECK (negate_match_pattern IN (0,1)) NOT NULL DEFAULT 0,
    re_modifiers VARCHAR DEFAULT 'CASELESS',
    flagOUT INT CHECK (flagOUT >= 0),
    replace_pattern VARCHAR CHECK(CASE WHEN replace_pattern IS NULL THEN 1 WHEN replace_pattern IS NOT NULL AND match_pattern IS NOT NULL THEN 1 ELSE 0 END),
    destination_hostgroup INT DEFAULT NULL,
    cache_ttl INT CHECK(cache_ttl > 0),
    cache_empty_result INT CHECK (cache_empty_result IN (0,1)) DEFAULT NULL,
    cache_timeout INT CHECK(cache_timeout >= 0),
    reconnect INT CHECK (reconnect IN (0,1)) DEFAULT NULL,
    timeout INT UNSIGNED CHECK (timeout >= 0),
    retries INT CHECK (retries>=0 AND retries <=1000),
    delay INT UNSIGNED CHECK (delay >=0),
    next_query_flagIN INT UNSIGNED,
    mirror_flagOUT INT UNSIGNED,
    mirror_hostgroup INT UNSIGNED,
    error_msg VARCHAR,
    OK_msg VARCHAR,
    sticky_conn INT CHECK (sticky_conn IN (0,1)),
    multiplex INT CHECK (multiplex IN (0,1,2)),
    gtid_from_hostgroup INT UNSIGNED,
    log INT CHECK (log IN (0,1)),
    apply INT CHECK(apply IN (0,1)) NOT NULL DEFAULT 0,
    attributes VARCHAR CHECK (JSON_VALID(attributes) OR attributes = '') NOT NULL DEFAULT '',
    comment VARCHAR)
1 row in set (0.000 sec)

Pour configuré ProxySQL pour envoyer les 2 premières requêtes au réplica hostgroup2, et tout le reste au primaire, les règles suivantes seraient requises :

ProxySQL Admin> INSERT INTO mysql_query_rules (rule_id,active,username,match_digest,destination_hostgroup,apply) VALUES (10,1,'foo','^SELECT c FROM test WHERE id=?',2,1);
Query OK, 1 row affected (0.00 sec)

ProxySQL Admin> INSERT INTO mysql_query_rules (rule_id,active,username,match_digest,destination_hostgroup,apply) VALUES (20,1,'foo','DISTINCT c FROM test',2,1);
Query OK, 1 row affected (0.00 sec)

Points clés concernant ces règles de requête (et les règles de requête en général) :

  • Les règles de requête sont traitées selon l'ordre de rule_id
  • Seules les règles qui ont active=1 sont traitées
  • Le premier exemple de règle utilise l'accent circonflexe (^) et le dollar ($) : ce sont des caractères regex spéciaux qui marquent le début et la fin d'un motif. Dans ce cas match_digestormatch_pattern doit correspondre complètement à la requête
  • La deuxième règle de l'exemple n'utilise ni d'accent circonflexe, ni dollar : la correspondance peut se trouver n'importe où dans la requête.
  • Le point d'interrogation est échappé car il a une signification particulière dans regex
  • apply=1 signifie qu'aucune autre règle ne doit être évaluée si la règle actuelle correspondait

La configuration actuelle de la règle peut être vérifiée dans la table mysql_query_rules :

ProxySQL Admin> SELECT match_digest,destination_hostgroup FROM mysql_query_rules WHERE active=1 AND username='test' ORDER BY rule_id;
+-----------------------------------+-----------------------+
| match_digest                      | destination_hostgroup |
+-----------------------------------+-----------------------+
| ^SELECT c FROM sbtest1 WHERE id=? | 2                     |
| DISTINCT c FROM test              | 2                     |
+-----------------------------------+-----------------------+
2 rows in set (0.000 sec)

Pour ces 2 règles spécifiques, des requêtes seront envoyées aux réplica, Si aucune règle ne correspond à la requête, le default_hostgroup configuré pour l'utilisateur s'applique, c'est-à-dire 1 pour l'utilisateur test

La table stats_mysql_query_digest_reset peut être interrogé pour récupérer la charge de travail précédente et effacer le contenu de la table stats_mysql_query_digest, et la TRUNCATE, ceci est recommandé avant d'activer les règles de requête pour revoir facilement les modifications.

On sauvegarde et on active les changements :

ProxySQL Admin> LOAD MYSQL QUERY RULES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)

Une fois le trafic passé par la nouvelle configuration, stats_mysql_query_digest montrera les changements de routage par requête :

ProxySQL Admin> SELECT hostgroup hg, sum_time, count_star, digest_text FROM stats_mysql_query_digest ORDER BY sum_time DESC;
+----+----------+------------+----------------------------------------------------------------------+
| hg | sum_time | count_star | digest_text                                                          |
+----+----------+------------+----------------------------------------------------------------------+
| 2  | 14520738 | 50041      | SELECT c FROM test WHERE id=?                                        |
| 2  | 3203582  | 5001       | SELECT DISTINCT c FROM test WHERE id BETWEEN ? AND ?+? ORDER BY c    |
| 1  | 3142041  | 5001       | COMMIT                                                               |
| 1  | 2270931  | 5001       | SELECT c FROM test WHERE id BETWEEN ? AND ?+? ORDER BY c             |
| 1  | 2021320  | 5003       | SELECT c FROM test WHERE id BETWEEN ? AND ?+?                        |
| 1  | 1768748  | 5001       | UPDATE test SET k=k+? WHERE id=?                                     |
| 1  | 1697175  | 5003       | SELECT SUM(K) FROM test WHERE id BETWEEN ? AND ?+?                   |
| 1  | 1346791  | 5001       | UPDATE test SET c=? WHERE id=?                                       |
| 1  | 1263259  | 5001       | DELETE FROM est WHERE id=?                                           |
| 1  | 1191760  | 5001       | INSERT INTO test (id, k, c, pad) VALUES (?, ?, ?, ?)                 |
| 1  | 875343   | 5005       | BEGIN                                                                |
+----+----------+------------+----------------------------------------------------------------------+
11 rows in set (0.00 sec)

Les 2 premières requêtes identifiées sont envoyées aux réplicas hostgroup2.

Cache de requêtes

Un cas d'utilisation populaire pour ProxySQL consiste à agir comme un cache de requêtes. Par défaut, les requêtes ne sont pas mises en cache, ceci est activé en définissant cache_ttl (en millisecondes) sur une règle définie dans mysql_query_rules

Pour mettre en cache toutes les requêtes envoyées aux réplicas pendant 5 secondes, mettez à jour le cache_ttl sur les règles de requête définies dans l'exemple précédent :

ProxySQL Admin> UPDATE mysql_query_rules set cache_ttl=5000 WHERE active=1 AND destination_hostgroup=2;
Query OK, 2 rows affected (0.000 sec)

ProxySQL Admin> LOAD MYSQL QUERY RULES TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)


ProxySQL Admin> SELECT 1 FROM stats_mysql_query_digest_reset LIMIT 1; -- on remet à zéro le compteur
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

Une fois le trafic passé par la nouvelle configuration, stats_mysql_query_digest affichera les requêtes mises en cache avec une valeur de groupe d'hôtes de "-1" :

ProxySQL Admin> SELECT hostgroup hg, sum_time, count_star, digest_text FROM stats_mysql_query_digest ORDER BY sum_time DESC;
+----+----------+------------+----------------------------------------------------------------------+
| hg | sum_time | count_star | digest_text                                                          |
+----+----------+------------+----------------------------------------------------------------------+
| 1  | 7457441  | 5963       | COMMIT                                                               |
| 1  | 6767681  | 5963       | SELECT c FROM test WHERE id BETWEEN ? AND ?+? ORDER BY c             |
| 2  | 4891464  | 8369       | SELECT c FROM test WHERE id=?                                        |
| 1  | 4573513  | 5963       | UPDATE test SET k=k+? WHERE id=?                                     |
| 1  | 4531319  | 5963       | SELECT c FROM test WHERE id BETWEEN ? AND ?+?                        |
| 1  | 3993283  | 5963       | SELECT SUM(K) FROM test WHERE id BETWEEN ? AND ?+?                   |
| 1  | 3482242  | 5963       | UPDATE test SET c=? WHERE id=?                                       |
| 1  | 3209088  | 5963       | DELETE FROM test WHERE id=?                                          |
| 1  | 2959358  | 5963       | INSERT INTO test (id, k, c, pad) VALUES (?, ?, ?, ?)                 |
| 1  | 2415318  | 5963       | BEGIN                                                                |
| 2  | 2266662  | 1881       | SELECT DISTINCT c FROM test WHERE id BETWEEN ? AND ?+? ORDER BY c    |
| -1 | 0        | 4082       | SELECT DISTINCT c FROM test WHERE id BETWEEN ? AND ?+? ORDER BY c    |
| -1 | 0        | 51261      | SELECT c FROM test WHERE id=?                                        |
+----+----------+------------+----------------------------------------------------------------------+
13 rows in set (0.00 sec)