Я разрабатываю систему управления складом, написанную на php, laravel framework, mariadb. Чтобы получить всю информацию о каждом продукте, мы используем таблицу «история» продуктов, в которой регистрируются все действия, предпринятые с определенным продуктом. Эта таблица начала очень быстро расширяться, и теперь у нас есть ~ 15 миллионов строк в таблице innoDB, которая начала работать медленно, особенно при запуске функции, которая требует полного анализа того, сколько продуктов было продано, создано, выброшено и т. д., Тогда это занимает все 15 миллионов строк по одному запросу .. Итак, я начал искать способы, как работать с этой большой таблицей, потому что индексация больше не работает. Я начинаю думать о разделении / разбиении этой таблицы по дате, может быть, действие? Так что, может быть, у кого-нибудь есть опыт в этом и он может поделиться со мной советом? большое спасибо за любую помощь!

CREATE TABLE `history` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`barcode` varchar(100) DEFAULT NULL,
`bag` varchar(100) DEFAULT NULL,
`action` int(10) unsigned DEFAULT NULL,
`place` int(10) unsigned DEFAULT NULL,
`price` decimal(10,2) DEFAULT NULL,
`old_price` decimal(10,2) DEFAULT NULL,
`user` int(11) DEFAULT NULL,
`amount` int(10) DEFAULT NULL,
`rotation` int(10) unsigned DEFAULT NULL,
`discount` decimal(10,2) DEFAULT NULL,
`discount_type` tinyint(2) unsigned DEFAULT NULL,
`original` int(10) unsigned DEFAULT NULL,
`was_in_shop` int(10) unsigned DEFAULT NULL,
`cate` int(10) unsigned DEFAULT NULL COMMENT 'grupe',
`sub_cate` int(10) unsigned DEFAULT NULL,
`comment` varchar(255) DEFAULT NULL,
`helper` varchar(255) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `barcode` (`barcode`),
KEY `action` (`action`),
KEY `original` (`original`),
KEY `created_at` (`created_at`),
KEY `bag` (`bag`)
) ENGINE=InnoDB AUTO_INCREMENT=16274267 DEFAULT CHARSET=utf8
например запрос:
select cate,
SUM(amount) AS amount, SUM(IF(discount>0,(price*amount)-discount,
(price*amount))) AS sum, SUM(IF(discount>0,IF(discount_type=1,
(discount*price)/100,discount),0)
) AS discount from history
where (history.action = '4'
and history.created_at >= '2017-11-01 00:00:00'
and history.created_at <= '2017-11-23 23:59:59'
)
and LENGTH(barcode) > 7
and history.deleted_at is null
group by cate
этот запрос используется для получения информации о количестве, сумме, скидке о проданных продуктах (действие 4), в этом примере это информация между 2017-11-01 и 2017-11-23, и EXPLAIN дает мне это:
id - 1
select_type - SIMPLE
table - history
type - ref
possible_keys - action,created_at
key - action
key_len - 5
ref - const
rows - 1444272
Extra - Using where; Using temporary; Using filesort
поэтому требуется 1,5 миллиона строк с таблицей, в которой хранятся данные с 01.01.2017 г. по настоящее время, поэтому через 2 года потребуется 3 миллиона строк и т. д., когда мне нужно взять только информацию о проданных товарах за 2017-11 гг. И у меня есть еще много запросов, похожих на этот.
У меня уже есть 5 индексов (таблица справа на прикрепленном изображении), добавление дополнительных индексов больше не ускоряет работу.
Поддерживает ли EXPLAIN ваши медленные запросы эту гипотезу?
Скриншоты здесь практически бесполезны. Пожалуйста, используйте SHOW CREATE TABLE, чтобы описать вашу ситуацию и показать связанные с этим вопросы.
обновлен основной вопрос с дополнительной информацией






INT занимает 4 байта; доступны другие размеры.PARTITIONing действительно обеспечивает любую производительность нет.history.deleted_at is null - подумайте об удалении строк.INDEX(action, created_at). (Одновременно используется только один индекс.)Большое улучшение происходит за счет создания и поддержки сводных таблиц; см. http://mysql.rjweb.org/doc.php/summarytables. Затем запустите запросы против них. И большинство индексов можно уйти.
Исправьте некоторые из них; тогда я могу вам помочь.
Более
Комментарий спрашивает, как поддерживать идентификатор сводной таблицы двумя разными способами. Любой из них может быть жизнеспособным, в зависимости от большего количества, пока не уточненных деталей:
INSERT INTO Fact и сразу же используйте IODKU для вставки или обновления сводной таблицы.INSERT .. SELECT .., чтобы захватить еще не суммированные строки и поместить подсчеты / промежуточные итоги в сводную таблицу.Последний вариант работает, но есть две вещи, на которые следует обратить внимание:
PRIMARY KEY, такой как дата (день или час) и несколько значений измерения, то у вас проблемы с INSERT. Либо избегайте этого в качестве PK (что приводит к множеству строк, что неплохо), либо используйте IODKU в форме INSERT ... ON DUPLICATE KEY ... SELECT ... GROUP BY ...; и используйте функцию VALUES(xx).Я читал о сводных таблицах. Из этой статьи я понял, что мне нужно ВСТАВИТЬ ... ВЫБРАТЬ из истории в сводную таблицу. Но в моем случае мне нужны отчеты «с точностью до секунды», потому что все статистические отчеты являются «живыми», поэтому рекомендуется тип «по мере необходимости». Итак, я хочу спросить, лучше ли вставлять / обновлять сводную таблицу из таблицы «история» каждый раз, когда я обновляю страницу, а затем выбираю всю информацию из этой новой сводной таблицы, чем просто выбирать эту информацию из таблицы «история» напрямую? или, может быть, есть другой способ сделать это?
@Tomas - Я дам еще несколько советов.
Что вы имеете в виду под «индексацией больше не работает»?