MariaDB - INNODB пропускает числовую последовательность при создании добавочных записей - почему? - PullRequest
1 голос
/ 08 марта 2019

Я не знаю, является ли это ожидаемым поведением с INNODB, но я действительно думаю, что это совершенно странно.

Если я использую тот же оператор SQL, используя MYISAM, поведение происходит, как и ожидалось.

MYISAM

CREATE TABLE main_database.numero (
    id INT NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY(id)
) ENGINE = MYISAM DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
INSERT INTO main_database.numero VALUES(NULL); -- First, run once time ...
INSERT INTO main_database.numero SELECT NULL FROM main_database.numero; -- After, more 12 times = 4096 records

Результат (ожидаемое поведение):

enter image description here

Теперь, если я использую точно такое же утверждение, однако, сообщая, что двигатель является INNODB.

INNODB

CREATE TABLE main_database.numero (
    id INT NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY(id)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

INSERT INTO main_database.numero VALUES(NULL); -- First, run once time ...
INSERT INTO main_database.numero SELECT NULL FROM main_database.numero; -- After, more 12 times = 4096 records

Результат (странный результат - Пропуск последовательности чисел):

enter image description here

Фактически оба двигателя создают ожидаемые 4096записей, но меня беспокоит поведение INNO, потому что я переношу свои базы данных с MYISAM на INNODB и не знаю, насколько это может повлиять на мои приложения.

Ответы [ 2 ]

2 голосов
/ 08 марта 2019

Чтобы обобщить причину этого утверждения, это система планирования. что у меня есть, что использует этот оператор для создания таблицы календаря.

Не совсем в рамках вашего вопроса о пропущенных идентификаторах.
Но есть более эффективные способы генерирования чисел и / или календарных таблиц, чем многократное повторение INSERT ... SELECT.
Все подходы могут быть непосредственно объединены с другой таблицей или использованы для заполнения (индексированной) (временной) таблицы

Для генерации номера.

Если ваша версия MariaDB / MySQL поддерживает функции Windows

SET SESSION cte_max_recursion_depth = 5000;

WITH RECURSIVE number_generator(number) AS (
  SELECT 0
  UNION ALL
  SELECT number + 1 FROM number_generator
  WHERE number BETWEEN 0 AND 4096
)
SELECT * FROM number_generator

Для MariaDB / MySQL, который не поддерживает оконные функции.

SELECT 
  number_generator.number
FROM (

SELECT 
 @row := @row + 1 AS number
FROM (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row1
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row2
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row3
CROSS JOIN (
   SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row4
CROSS JOIN (
  SELECT @row := -1 
) init_user_params 
) AS number_generator
WHERE
 number_generator.number BETWEEN 0 AND 4096
ORDER BY 
 number_generator.number ASC

Для создания календаря

Если ваша версия MariaDB / MySQL поддерживает функции Windows

SET SESSION cte_max_recursion_depth = 5000;

WITH RECURSIVE number_generator(number) AS (
  SELECT 0
  UNION ALL
  SELECT number + 1 FROM number_generator
  WHERE number BETWEEN 0 AND 4096
)
SELECT CURRENT_DATE + INTERVAL number_generator.number DAY FROM number_generator

Для MariaDB / MySQL, который не поддерживает оконные функции.

SELECT 
  CURRENT_DATE + INTERVAL number_generator.number DAY
FROM (

SELECT 
 @row := @row + 1 AS number
FROM (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row1
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row2
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row3
CROSS JOIN (
  SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row4
CROSS JOIN (
  SELECT @row := -1 
) init_user_params 
) AS number_generator
WHERE
 number_generator.number BETWEEN 0 AND 4096
ORDER BY 
 number_generator.number ASC

CURRENT_DATE - это просто пример, вы также можете использовать фиксированную дату в прошлом или будущем, например, вы можете использовать '2019-03-01'.

Также + INTERVAL number_generator.number DAY также может использовать минус, чтобы сгенерировать список в прошлое из этой даты и других значений, тогда DAY если вы хотите месяцы, вы можете использовать MONTH, хотите годы, которые вы используете YEAR

2 голосов
/ 08 марта 2019

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

Здесь обсуждается немного: https://bugs.mysql.com/bug.php?id=57643

Нет особой важности для точного создания последовательных значений, поскольку любое значение может быть «потеряно» по другим причинам:

  • Ваш INSERT не работает, например, из-за нарушения ограничения, такого как UNIQUE KEY или FOREIGN KEY.
  • Вы откатываете транзакцию для своей ВСТАВКИ.
  • Вы успешно выполняете коммит, но позже строка удаляется вами или другим сеансом.

Значения авто-включения не возвращаются ни в какую очередь, потому что другие одновременные сеансы могли бы генерировать дополнительные значения идентификаторов за это время. InnoDB не стоит поддерживать пул нераспределенных значений id, потому что этот пул может стать огромным и расточительным.

Кроме того, может быть целесообразно «потерять» значение идентификатора, иначе кто-то подумает, что строка, которую он хотел УДАЛИТЬ, каким-то образом вернулась.

...