Чому InnoDB не зберігає кількість рядків?


19

Всім відомо, що в таблицях, які використовують InnoDB в якості двигуна, такі запити SELECT COUNT(*) FROM mytableдуже неточні і дуже повільні, особливо коли таблиця збільшується і постійно виконуються вставки / видалення рядків під час виконання цього запиту.

Як я зрозумів, InnoDB не зберігає кількість рядків у внутрішній змінній, що є причиною цієї проблеми.

Моє запитання: Чому це так? Чи було б так важко зберігати таку інформацію? Це важлива інформація, яку слід знати в багатьох ситуаціях. Єдина складність, яку я бачу, чи був би реалізований такий внутрішній підрахунок, - це коли задіяні транзакції: якщо транзакція не відключена, чи підраховуєте ви вставлені ними рядки чи ні?

PS: Я не фахівець з БД, я просто той, хто має MySQL як просте хобі. Тож якщо я просто запитав щось дурне, не будьте надмірно критичні: D.


6
Повільно, так. Неточний, ні. Це повільно, оскільки дає точний результат. Коли у вас є таблиця рядків 200М і, можливо, багато інших транзакцій, які вставляють / видаляють в ту саму таблицю, можливо, багато рядків в секунду, ще одне питання: "чи потрібна точна кількість?"
ypercubeᵀᴹ

@ypercube Я знаю, що я кілька разів бачив у phpmyadmin деякі значення кількості рядків, які були дуже вимкненими. Плюс, там є коментар, який говорить про щось на кшталт "може бути неточним".
Раду Мурзеа

1
@RaduMurzea phpMyAdmin використовує альтернативний метод обчислення кількості таблиць для таблиць InnoDB з причин швидкості, про яку ви знаєте. Тут грає неточність, яку ви згадали. Фактичні SELECT COUNT(*) FROM ...запити точні. Якщо ви віддаєте перевагу, phpMyAdmin може бути налаштований так, щоб завжди використовувати точні підрахунки рядків за рахунок швидкості. Більш детальна інформація: stackoverflow.com/questions/11926259 / ...
DOOManiac

Відповіді:


9

Я згоден з @RemusRusanu (+1 за його відповідь)

SELECT COUNT(*) FROM mydb.mytableв InnoDB поводиться так, як повинен рухатися оператор зберігання даних. Порівняйте його з MyISAM.

MyISAM

Якщо mydb.mytableце таблиця MyISAM, запуск SELECT COUNT(*) FROM mydb.mytable;подібно до запуску SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';. Це запускає швидкий пошук кількості рядків у заголовку таблиці MyISAM.

InnoDB

Якщо mydb.mytableце таблиця InnoDB, ви отримуєте те, що відбувається. У вас є MVCC, керуючи наступним:

  • ib_logfile0 / ib_logfile1 (Повторити журнали)
  • ібдата1
    • Скасувати журнали
    • Відкат
    • Зміни словника даних
  • Управління буферним басейном
  • Ізоляція транзакцій (4 типи)
    • Повторне читання
    • Читати прихильно
    • Читати не надходить
    • Серіалізація

Прохання InnoDB про кількість таблиць вимагає навігації по цих зловісних речах. Насправді, ніколи насправді не відомо, чи SELECT COUNT(*) from mydb.mytableзараховується лише повторне читання чи включає читання, які були здійснені, та ті, що не були передані.

Ви можете спробувати трохи стабілізувати речі, включивши innodb_stats_on_metadata .

Відповідно до документації MySQL про innodb_stats_on_meta_data

Коли ця змінна ввімкнена (що є типовим, як і до створення змінної), InnoDB оновлює статистику під час операторів метаданих, таких як SHOW TABLE STATUS або SHOW INDEX, або під час доступу до таблиць INFORMATION_SCHEMA TABLES або STATISTICS. (Ці оновлення схожі на те, що відбувається з ANALYZE TABLE.) Якщо вимкнено, InnoDB не оновлює статистику під час цих операцій. Вимкнення цієї змінної може підвищити швидкість доступу для схем, які мають велику кількість таблиць або індексів. Це також може підвищити стабільність планів виконання запитів, що включають таблиці InnoDB.

Якщо вимкнути його, можливо, це не може призвести до більш стабільного підрахунку щодо створення планів EXPLAIN. Це може вплинути на продуктивність SELECT COUNT(*) from mydb.mytableабо в хороший спосіб, в поганий спосіб, або зовсім не буде. Спробуйте і побачите !!!


16

Для стартера немає такої речі, як "поточний підрахунок" для зберігання в змінній. Цей запит SELECT COUNT(*) FROM ...залежить від поточного рівня ізоляції та всіх одночасних транзакцій, що очікують на розгляд. Залежно від рівня ізоляції, запит може бачити чи не бачити рядки, вставлені чи видалені в результаті очікування невпорядкованих транзакцій. Єдиний спосіб відповісти - підрахувати рядки, видимі для поточної транзакції.

Зауважте, що я навіть не торкнувся ще більш тернистого предмета одночасних транзакцій, які починаються чи закінчуються під час підрахунку. Не кажучи вже про відкати ...


1
Гаразд, тому це залежить від рівня ізоляції, що має сенс. Але це все-таки можна реалізувати.
Раду Мурзеа

@SoboLAN Існує маса причин, чому цього не може бути і не може бути, більшість з яких перераховано вище. Чи реалізуєте ви це, підтримуючи список підрахунків за таблицю за кожну операцію (що б SCN Oracle не був у MySQL)? Управління такими підрахунками було б величезним накладним витратом - подумайте про базу даних зі 100 або 1000 одночасними сеансами, кожен з яких робить велику кількість ВСТАВКИ / УДАЛЕННЯ в одній таблиці. Неможливо підтримувати.
Philᵀᴹ

Реалізувати це досить складно. Подумайте лише, що підрахунок повинен зберігатись у БД, це означає десь у метаданих, і цей підрахунок повинен підтримуватися кожною транзакцією, яка вставляє або видаляє рядок. Як би ви заблокували ці метадані? А як би ви попрацювали з відкатами? Це далеко не банально. І результат був би корисний для дуже вузького підмножини запитів.
Рем Русану

3
@JackDouglas Цікаво. З того, що я бачив у минулому, COUNT(*)запити рідко потрібні в реальності і, як правило, є результатом досвіду розробника (порахуйте рядки, перш ніж ми їх виберемо!) Або поганого дизайну додатків.
Філ

1
@SoboLAN - ні, не буде. Наявність сервісу, який оновлює якусь таблицю статистики за попередньо визначеними інтервалами часу, набагато краще. Уявіть, що у вас є велика база даних та кілька адміністраторів, які запитують більшість таблиць SELECT COUNT(*), додайте до неї неоптимізовану WHEREтаблицю, і у вас буде декілька користувачів, які підносять db на коліна за кілька сумнівно корисних статистичних лічильників.
NB

0

Хоча теоретично було б можливо зберегти точний підрахунок кількості рядків для даної таблиці за допомогою InnoDB, це буде коштувати багато блокування, що негативно вплине на продуктивність. Це також відрізнятиметься від рівня ізоляції.

MyISAM вже робить блокування рівня таблиці, тому додаткових витрат там немає.

Мені рідко потрібна кількість рядків для таблиці, хоча я зовсім небагато використовую COUNT (*). У мене, як правило, додано пункт WHERE. Використовуючи ефективний індекс для невеликого набору результатів, я вважаю, що вони досить швидкі.

Я не погоджуюся з тим, що підрахунки є неточними. Підрахунки являють собою огляд даних, і я завжди вважав їх точними.

Коротше кажучи, MySQL залишає за вами вам реалізувати це для InnoDB. Ви можете зберігати кількість та збільшення / зменшення її після кожного запиту. Хоча, простіше рішення - це, мабуть, перехід на MyISAM.


2
Це НЕ дозволяє вести точний підрахунок рядків в транзакционной системі. Тому що існує стільки різних (і правильних) рахунків рядків, скільки активних транзакцій.
a_horse_with_no_name

5
Я дав -1 тут для "Хоча, простіше рішення - це, мабуть, перехід на MyISAM." Я ніколи не рекомендував би перейти на MyISAM просто для підрахунку рядків.
Дерек Дауні

@a_horse_with_no_name, тож ви погоджуєтесь, що для кожної транзакції буде "правильна" кількість рядків. Мені здається можливим.
Маркус Адамс

1
@DTest, я ніколи не казав "просто для підрахунку рядків".
Маркус Адамс

@a_horse_with_no_name, це не здається правильним. Звичайно, ми підраховуємо лише кількість рядків, коли транзакції здійснюються правильно?
Пейс’єр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.