Сохранение изменений в сущностях: правильное ли решение — MySQL?

я хочу сохранить изменения, которые я делаю в своей таблице "сущностей". Это должно быть похоже на журнал. В настоящее время это реализовано с этой таблицей в MySQL:

CREATE TABLE `entitychange` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `entity_id` int(10) unsigned NOT NULL,
  `entitytype` enum('STRING_1','STRING_2','SOMEBOOL','SOMEDOUBLE','SOMETIMESTAMP') NOT NULL DEFAULT 'STRING_1',
  `when` TIMESTAMP NOT NULL,
  `value` TEXT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
  • entity_id = первичный ключ моей таблицы entity.
  • entitytype = поле, которое было изменено в таблице entity. иногда меняется только одно поле, иногда несколько. одно изменение = одна строка.
  • value = строковое представление «нового значения» поля.

Пример при изменении поля entity.somedouble с 3 на 2 я запускаю эти запросы:

UPDATE entity SET somedouble = 2 WHERE entity_id = 123;
INSERT INTO entitychange (entity_id,entitytype,value) VALUES (123,'SOMEDOUBLE',2);

Мне нужно select изменения определенного объекта и типа объекта за последние 15 дней. Например: последние изменения с SOMEDOUBLE для entity_id 123 за последние 15 дней.

Теперь есть две вещи, которые мне не нравятся:

  1. Все данные хранятся как TEXT — хотя большая часть (менее 1%) на самом деле не является текстом, в моем случае большинство значений — DOUBLE. Это большая проблема?
  2. Таблица становится очень, очень медленной при вставке, поскольку в таблице уже 200 миллионов строк. В настоящее время моя нагрузка на сервер составляет до 10-15 из-за этого.

Мой вопрос: Как мне устранить эти два "узких места"? Мне нужно масштабироваться.

Моими подходами будут:

  1. Сохраните его следующим образом: http://sqlfiddle.com/#!2/df9d0 (нажмите на обзор) — сохраните изменения в таблице entitychange, а затем сохранить значение в соответствии с его типом данных в entitychange_[bool|timestamp|double|string]
  2. Используйте разбиение на HASH(entity_id) - я думал о ~ 50 разделах.
  3. Должен ли я использовать другую систему баз данных, может быть, MongoDB?

comment
Правильная нормализация (с использованием типов данных) не вариант?   -  person Strawberry    schedule 11.06.2013
comment
Я бы сказал то же самое, не знаю, почему вы используете enum, вы должны использовать как минимум две таблицы, журнал и другую таблицу для типа объекта. В вашей основной таблице журнала внешний ключ к таблице типов объектов.   -  person xtrm    schedule 11.06.2013
comment
Поскольку у вас есть перечисление с 5 значениями, у вас будет таблица типов сущностей с 32 записями (2 ^ 5 = 32). Каждая запись в таблице entitytype будет иметь свой собственный идентификатор, а в основной таблице журнала — просто foreing_key. Вы сэкономите много места, а создание JOIN будет быстрым. Другой способ, если бы мы правильно использовали нормализацию, заключался бы в том, чтобы установить «ключ вывода объекта» в таблице типов объектов и удалить тип объекта из вашей основной таблицы журнала. В конце я бы посоветовал протестировать на работоспособность.   -  person xtrm    schedule 11.06.2013
comment
Я бы также предложил избавиться от первого столбца id и установить первичный ключ в entity_id без AUTOINCREMENT. Это сэкономит больше места. Кроме того, поскольку это будет первичный ключ, он будет проиндексирован, и ваши поисковые запросы будут выполняться быстрее. Я бы предложил добавить еще один индекс, который соответствует полям, которые вы собираетесь искать. dev.mysql.com/doc/refman/5.6/en/ mysql-indexes.html   -  person xtrm    schedule 11.06.2013
comment
@xtrm: Спасибо за комментарий, но я не могу использовать entity_id в качестве первичного ключа, так как я могу изменить, скажем, SOMEDOUBLE из одного и того же объекта и, таким образом, создать две строки в entitychange с одним и тем же entity_id. Я обновил свой вопрос, поэтому процесс изменения становится более понятным.   -  person Stefan    schedule 11.06.2013
comment
@xtrm: какова схема таблицы для таблицы типов объектов? не могли бы вы сделать sqlfiddle.com для этого?   -  person Stefan    schedule 11.06.2013
comment
@xtrm, тип данных enum на самом деле выполняет нормализацию: он использует 1 или 2 байта для хранения в зависимости от количества значений (65 КБ - макс.).   -  person Stoleg    schedule 15.06.2013
comment
@Stoleg, это не то, что означает нормализация.   -  person Bill Karwin    schedule 16.06.2013
comment
@BillKarwin Я хочу знать, чего мне не хватает. В широком смысле я понимаю нормализацию как извлечение свойств сущностей из таблицы фактов и обращение к этим свойствам, хранящимся отдельно. В идеале у вас должно получиться 2 типа таблиц: таблица данных с ключами к свойствам и таблицы свойств.   -  person Stoleg    schedule 16.06.2013
comment
@Stoleg, то, что вы только что описали, не является тем, что делает enum - enum не создает вторую таблицу, а просто кодирует фиксированный список значений и сохраняет порядковый номер значения в списке. Но это не то, что вы описали normalization. Нормализация — это не обращение к атрибутам с помощью суррогатного ключа. Нормализация не направлена ​​на то, чтобы сделать хранилище более компактным. Нормализация предназначена для предотвращения аномалий данных.   -  person Bill Karwin    schedule 17.06.2013
comment
@BillKarwin Спасибо. Я кое-что прочитал и согласен с тем, что enum не имеет ничего общего с нормализацией.   -  person Stoleg    schedule 17.06.2013
comment
@steve обновил мой пост ниже. Я обязательно попробую MongoDB или Redis для этой цели. Они будут определенно быстрее, чем MySQL, и не будут страдать от безумного количества данных в таблице и необходимости обновлять и оптимизировать индекс таблицы каждый день.   -  person xtrm    schedule 25.06.2013


Ответы (8)


Если бы я столкнулся с упомянутой вами проблемой, я бы разработал таблицу LOG, как показано ниже:

  1. EntityName: (String) Объект, которым манипулируют. (обязательно)
  2. ObjectId: Объект, которым манипулируют, первичный ключ.
  3. FieldName: (строка) имя поля объекта.
  4. OldValue: (строка) старое значение поля сущности.
  5. NewValue: (строка) новое значение поля сущности.
  6. UserCode: Уникальный идентификатор пользователя приложения. (обязательный)
  7. TransactionCode: Любая операция, изменяющая объекты, должна иметь уникальный код транзакции (например, GUID) (обязательно).
    В случае обновления объекта, изменяющего несколько полей, этот столбец будет ключевой точкой для отслеживания всех изменений в обновление (транзакция)
  8. ChangeDate: Дата транзакции. (обязательный)
  9. FieldType: перечисление или текст, показывающий тип поля, например TEXT или Double. (обязательный)

При таком подходе
любой объект (таблица) может быть отслежен
отчеты будут доступны для чтения
будут регистрироваться только изменения.
код транзакции будет ключевой точкой для обнаружения изменений одним действием.

кстати

Store the changes in the entitychange table and then store the value 
according to its datatype in entitychange_[bool|timestamp|double|string]

Не понадобится, в единой таблице у вас будут изменения и типы данных

Use partitioning by HASH(entity_id)

Я предпочитаю разбиение по ChangeDate или создание резервных таблиц для changeDate, которые достаточно устарели для резервного копирования и удаления из основной таблицы LOG.

Should I use another database system, maybe MongoDB?

У любой базы данных есть свои плюсы и минусы, вы можете использовать дизайн на любой СУБД. Полезное сравнение баз данных на основе документов, таких как MongoDB, можно найти здесь

надеюсь быть полезным.

person Community    schedule 15.06.2013
comment
Спасибо за ответ. Зачем нужен FieldType? - person Desprit; 27.08.2018
comment
Это может быть удобно для будущего использования интерпретации. Подумайте, были ли изменения внесены в массив или... . - person Mohsen Heydari; 28.08.2018

Теперь, кажется, я понял, что вам нужно, версионная таблица с историей изменений записей. Это может быть еще один способ добиться того же, и вы можете легко провести несколько быстрых тестов, чтобы увидеть, дает ли это вам лучшую производительность, чем ваше текущее решение. Это то, как Symfony PHP Framework делает это в Doctrine с плагином Versionable.
Имейте в виду, что существует уникальный индекс первичного ключа из двух ключей, версии и fk_entity.
Также взгляните на сохраненные значения. Вы сохраните значение 0 в полях, которые не изменились, и измененное значение в тех, которые изменились.

CREATE TABLE `entity_versionable` (
  `version` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `fk_entity` INT(10) UNSIGNED NOT NULL,
  `str1` VARCHAR(255),
  `str2` VARCHAR(255),
  `bool1` BOOLEAN,
  `double1` DOUBLE,
  `date` TIMESTAMP NOT NULL,
  PRIMARY KEY (`version`,`fk_entity`)
) ENGINE=INNODB DEFAULT CHARSET=latin1;


INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "a1", "0", "0", "0", "2013-06-02 17:13:16");
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "a2", "0", "0", "0", "2013-06-11 17:13:12");
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "0", "b1", "0", "0", "2013-06-11 17:13:21");
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "0", "b2", "0", "0", "2013-06-11 17:13:42");
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "0", "0", "1", "0", "2013-06-16 17:19:31");

/*Another example*/
INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
VALUES ("1", "a1", "b1", "0", "0", CURRENT_TIMESTAMP);


SELECT * FROM `entity_versionable` t WHERE 
(
    (t.`fk_entity`="1") AND 
    (t.`date` >= (CURDATE() - INTERVAL 15 DAY))
);


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

person xtrm    schedule 11.06.2013
comment
Спасибо за ваш ответ, но я не уверен, что сохранение всей строки улучшит производительность. Скажем, в моей производственной среде у меня около 30 столбцов, и в основном изменился только один столбец, на мой взгляд, было бы нехорошо хранить все, включая большие поля TEXT. или я неправильно предполагаю? - person Stefan; 13.06.2013

Здесь есть две основные проблемы:

  1. Как эффективно хранить данные, то есть занимать меньше места и иметь удобный формат

2-3. Управление большой таблицей: архивирование, простота резервного копирования и восстановления

2-3. Оптимизация производительности: более быстрые вставки и выборки

Эффективное хранение данных

  1. value подано. Я бы предложил сделать это VARCHAR (N). Причины:

    • Using N<255 will save 1 byte per row just because of the data type.
    • Использование других типов данных для этого поля: фиксированные типы используют пространство независимо от значения, и обычно это будет 8 байтов на строку (дата-время, длинное целое число, char (8)) и другие переменные типы данных слишком велики для этого поля.
    • Кроме того, тип данных TEXT приводит к снижению производительности: (из руководства по BLOB и текстовым данным типы)

Экземпляры столбцов TEXT в результате запроса, обработанного с использованием временной таблицы, заставляют сервер использовать таблицу на диске, а не в памяти, поскольку механизм хранения MEMORY не поддерживает эти типы данных. Использование диска снижает производительность, поэтому включайте столбцы BLOB или TEXT в результат запроса только в том случае, если они действительно необходимы. Например, избегайте использования SELECT *, который выбирает все столбцы.

Каждое значение BLOB или TEXT внутренне представлено отдельно выделенным объектом. Это отличается от всех других типов данных, для которых память выделяется один раз для каждого столбца при открытии таблицы.

В основном TEXT предназначен для хранения больших строк и фрагментов текста, тогда как VARCHAR() предназначен для относительно коротких строк.

  1. id поле. (обновлено, спасибо @steve) Я согласен, что это поле не несет никакой полезной информации. Используйте 3 столбца для первичного ключа: entity_id и entitype и when . TIMESTAMP гарантирует отсутствие дубликатов. Также одни и те же столбцы будут использоваться для разделения/подразбиения.

Управление таблицами Существует два основных варианта: таблицы MERGE и секционирование. Механизм хранения MERGE основан на My_ISAM, который, насколько я понимаю, постепенно выводится из эксплуатации. Вот некоторые материалы по [движку хранилища MERGE].2< /а>

Основным инструментом является Partitioning, и он обеспечивает два основных преимущества: 1. Переключение разделов (которое часто представляет собой мгновенную операцию с большим блоком данных) и сценарий скользящего окна: вставка новых данных в одну таблицу, а затем мгновенное переключение всех их в архивную таблицу. 2. Хранение данных в отсортированном порядке, что позволяет выполнять обрезку разделов — запрашивать только те разделы, которые содержат необходимые данные. MySQL позволяет подразделять данные для дальнейшей группировки.

Разделение по entity_id имеет смысл. Если вам нужно запрашивать данные в течение длительного периода времени или у вас есть другой шаблон для запросов к вашей таблице, используйте этот столбец для разделения на подразделы. Нет необходимости в подразделении на все столбцы первичного ключа, если только разделы не будут переключаться на этом уровне.

Количество разделов зависит от того, насколько большим должен быть файл базы данных для этого раздела. Количество подразделов зависит от количества ядер, поэтому каждое ядро ​​может выполнять поиск в своем собственном разделе, N-1 подразделов должно быть достаточно, поэтому 1 ядро ​​может выполнять общую работу по координации.

Оптимизация

Вставки:

  • Вставки выполняются быстрее в таблице без индексов, поэтому вставляйте большие объемы данных (выполняйте обновления), а затем создавайте индексы (если возможно).

  • Замените Text на Varchar - это немного нагрузит движок БД

  • Минимальное ведение журнала и блокировка таблиц могут помочь, но не всегда возможно использовать

Выбирает:

  • Text до Varchar определенно должны улучшить ситуацию.

  • Имейте текущую таблицу с последними данными - за последние 15 дней, затем перейдите в архив с помощью переключения разделов. Здесь у вас есть возможность разбить таблицу на разделы, отличные от архивной таблицы (например, сначала по дате, затем entity_id), и изменить способ разделения, переместив небольшие (1 день) данные во временную таблицу и изменив ее разбиение.

Также вы можете рассмотреть разбиение по дате, у вас много запросов по диапазонам дат. Сначала поставьте использование ваших данных и их частей, а затем решите, какая схема лучше всего их поддержит.

А что касается вашего третьего вопроса, я не вижу, как использование MongoDB конкретно выиграет в этой ситуации.

person Community    schedule 14.06.2013
comment
Поле id: использование (entity_id,when) вместо PRIMARY KEY было бы бесполезным, так как сущность может иметь два изменения в этой таблице одновременно (например, каждая строка для SOMEBOOL и SOMEDOUBLE с одной и той же отметкой времени) - Использовать вместо этого (entity_id,when,entitytype)? - person Stefan; 15.06.2013
comment
@steve, если здесь одновременно могут меняться несколько полей - тогда да. Кластеризованный индекс — это сама таблица, поэтому увеличение длины ключа не требует дополнительного места. - person Stoleg; 15.06.2013
comment
@steve Это слишком для этой задачи. Нормализованная структура хороша для управления сущностями, но избыточна для архивирования и замедляет запросы с соединениями. Вам нужно рассмотреть использование данных, а затем применить лучшую модель для их поддержки, а не нормализовать все до степени 3 или 5. Комментарий слишком короткий для подробного анализа. - person Stoleg; 15.06.2013
comment
@steve Например: Varchars всегда использует не менее 1-2 байтов даже для NULL, somedouble всегда использует 8 байтов (НЕ NULL), а somebool 1 байт, enum уже выполняет нормализацию в механизме БД, другие таблицы нужны только для JOIN. Преимущества такой структуры не будут использованы из-за способа использования этой таблицы. - person Stoleg; 15.06.2013

Это называется временной базой данных, и исследователи пытаются найти лучший способ хранения и запроса временные данные за более чем 20 лет.

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

Другой вариант, который иногда называют шестой нормальной формой (хотя существует несколько несвязанных определений 6NF), заключается в хранении дополнительной таблицы для хранения редакций для каждого столбца, который вы хотите отслеживать во времени. Это похоже на решение, предложенное ответом @xtrm, но ему не нужно хранить избыточные копии столбцов, которые не изменились. Но это приводит к взрывному росту количества столов.

Я начал читать об якорном моделировании, которое обещает обрабатывать временные изменения как структуры, так и содержание. Но я еще недостаточно хорошо это понимаю, чтобы объяснять. Я просто дам ссылку на него, и, возможно, это будет иметь смысл для вас.

Вот пара книг, в которых обсуждаются временные базы данных:

person Community    schedule 16.06.2013

Хранить целое число в столбце TEXT нельзя! TEXT — самый дорогой тип.

Я бы дошел до создания одной таблицы журнала для каждого поля, которое вы хотите отслеживать:

CREATE TABLE entitychange_somestring (
    entity_id INT NOT NULL PRIMARY KEY,
    ts TIMESTAMP NOT NULL,
    newvalue VARCHAR(50) NOT NULL, -- same type as entity.somestring
    KEY(entity_id, ts)
) ENGINE=MyISAM;

Разделите их, правда.

Обратите внимание, что я рекомендую использовать движок MyISAM. Вам не нужны транзакции для этой (этих) неограниченных таблиц только для вставки.

person Community    schedule 16.06.2013

Почему операция INSERT выполняется так медленно и что можно сделать, чтобы ускорить ее.

Вот на что я бы посмотрел (и примерно в том порядке, в котором я бы их прорабатывал):

  1. Для создания нового идентификатора AUTO_INCREMENT и вставки его в первичный ключ требуется блокировка (в InnoDB есть специальная блокировка AUTO-INC, которая удерживается до завершения оператора, эффективно действуя как блокировка таблицы в ваш сценарий). Обычно это не проблема, так как это относительно быстрая операция, но, с другой стороны, при значении загрузки (Unix) от 10 до 15 у вас, вероятно, будут процессы, ожидающие освобождения этой блокировки. Судя по предоставленной вами информации, я не вижу смысла в вашем суррогатном ключе «id». Посмотрите, существенно ли повлияет ли удаление этого столбца на производительность. (Кстати, нет правила, согласно которому таблице нужен первичный ключ. Если у вас его нет, это нормально)

  2. InnoDB может быть относительно дорогим для INSERT. Это компромисс, сделанный для обеспечения дополнительных функций, таких как транзакции, и может повлиять или не повлиять на вас. Поскольку все ваши действия атомарны, я не вижу необходимости в транзакциях. Тем не менее, попробуйте MyISAM. Примечание. Обычно MyISAM — плохой выбор для больших таблиц, поскольку он поддерживает только блокировку таблицы, а не блокировку на уровне записи, но поддерживает одновременные вставки, поэтому здесь может быть выбор (особенно если вы отбрасываете первичный ключ, см. выше)

  3. Вы можете поиграть с параметрами механизма хранения базы данных. И InnoDB, и MyISAM имеют параметры, которые вы можете изменить. Некоторые из них влияют на то, как на самом деле хранятся данные TEXT, другие имеют более широкую функцию. Особо следует обратить внимание на innodb_flush_log_at_trx_commit. .

  4. Столбцы TEXT относительно дороги, если (и только если) они имеют значения, отличные от NULL. В настоящее время вы сохраняете все значения в этом столбце TEXT. Стоит попробовать следующее: добавьте в таблицу дополнительные поля value_int и value_double и сохраните эти значения в соответствующем столбце. Да, это приведет к потере дополнительного места, но может быть быстрее, но это будет в значительной степени зависеть от механизма хранения базы данных и его настроек. Обратите внимание, что многое из того, что люди думают о производительности столбца TEXT, не соответствует действительности. (См. мой ответ на связанный вопрос о VARCHAR и TEXT)

  5. Вы предложили распределить информацию по нескольким таблицам. Это хорошая идея, только если ваши таблицы полностью независимы друг от друга. В противном случае вы получите более одной операции INSERT для любого изменения, и вы, скорее всего, сделаете все намного хуже. Хотя нормализация данных обычно хороша (tm), здесь она может снизить производительность.

Что вы можете сделать, чтобы запросы SELECT выполнялись быстрее

  1. Правильные ключи. И нужные ключи. И на всякий случай забыл упомянуть: правильные ключи. Вы не указываете подробно, как выглядят ваши выборки, но я предполагаю, что они похожи на "SELECT * FROM entitychange WHERE entity_id=123 AND ts>...". Одного составного индекса для entity_id и ts должно быть достаточно, чтобы сделать эту операцию быстрой. Поскольку индекс должен обновляться при каждом INSERT, возможно, стоит попробовать производительность как entity_id, ts, так и ts, entity_id: это может иметь значение.

  2. Разделение. Я бы даже не поднимал эту тему, если бы вы не задали свой вопрос. Вы не говорите, почему вы хотите разделить таблицу. С точки зрения производительности это обычно не имеет значения, если у вас есть правильные ключи. Есть некоторые специальные настройки, которые могут повысить производительность, но для этого вам потребуется правильная настройка оборудования. Если вы решите разбить таблицу на разделы, подумайте о том, чтобы сделать это либо по столбцу entity_id, либо по столбцу TIMESTAMP. Используя метку времени, вы можете получить систему архивации, в которой старые данные будут помещены на архивный диск. Однако такая система разделов потребует некоторого обслуживания (добавление разделов с течением времени).

Мне кажется, что вас волнует не столько производительность запросов, сколько скорость необработанной вставки, поэтому я не буду вдаваться в подробности о производительности SELECT. Если это вас интересует, пожалуйста, дайте более подробную информацию.

person Community    schedule 17.06.2013
comment
мне нужно выбрать статистику об определенной комбинации entity_id и entitytype, поэтому, возможно, (entity_id, entitytype, when) будет хорошим первичным ключом. мне также нужно только выбрать записи за последние две недели, так что, может быть, архивирование было бы хорошей идеей, верно? мне нужны все данные, но данные старше двух недель запрашиваются нерегулярно. - person Stefan; 18.06.2013
comment
Ваш составной ключ звучит нормально. Пожалуйста, имейте в виду, что поддержание индекса увеличивает объем работы для вставок. Попробуйте сделать его вторичным (неуникальным) ключом и первичным ключом и посмотрите, окажет ли это значительное влияние. Если вы хотите разделить по when, обратите внимание, что столбец when должен быть частью вашего первичного ключа, желательно первым из его столбцов, (when, entity_id, entitytype) - person Hazzit; 18.06.2013

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

Советы, которые мы видели здесь, действительно хороши, и вы наверняка заметите значительное улучшение скорости при использовании предопределенного типа VARCHAR с размером вместо TEXT. Однако вы можете увеличить скорость, я бы посоветовал не использовать MyISAM из соображений целостности данных, оставайтесь с InnoDB.

ТЕСТИРОВАНИЕ:

1. Настройте таблицу и ВСТАВЬТЕ 200 миллионов данных:

CREATE TABLE `entity_versionable` (
  `version` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `fk_entity` INT(10) UNSIGNED NOT NULL,
  `str1` VARCHAR(255) DEFAULT NULL,
  `str2` VARCHAR(255) DEFAULT NULL,
  `bool1` TINYINT(1) DEFAULT NULL,
  `double1` DOUBLE DEFAULT NULL,
  `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`version`,`fk_entity`)
) ENGINE=INNODB AUTO_INCREMENT=230297534 DEFAULT CHARSET=latin1

Чтобы вставить +200 миллионов строк примерно за 35 минут в таблицу, проверьте мой другой вопрос, где peterm ответил на один из лучших способов заполнения таблицы. Это работает отлично.

Выполните следующий запрос 2 раза, чтобы вставить 200 миллионов строк неслучайных данных (каждый раз меняйте данные, чтобы вставить случайные данные):

INSERT INTO `entity_versionable` (fk_entity, str1, str2, bool1, double1, DATE)
SELECT 1, 'a1', 238, 2, 524627, '2013-06-16 14:42:25'
FROM
(
    SELECT a.N + b.N * 10 + c.N * 100 + d.N * 1000 + e.N * 10000 + f.N * 100000 + g.N * 1000000 + h.N * 10000000 + 1 N FROM 
     (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) c
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) d
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) e
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) f
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) g
    ,(SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) h
) t;


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

<сильный>2. ИЗМЕНИТЕ новую тестовую таблицу для повышения производительности (или используйте мой пример выше на шаге 1, чтобы получить лучшие результаты). Как только мы настроим новую тестовую таблицу и заполним ее случайными данными, мы должны проверить приведенные выше советы и ИЗМЕНИТЬ таблицу, чтобы ускорить ее:

ТАБЛИЦА ОПТИМИЗАЦИИ test.entity_versionable;
ТАБЛИЦА РЕМОНТА test.entity_versionable;
*Создайте скрипт для оптимизации и поддержания вашего индекса в актуальном состоянии, запуская его каждую ночь.


3. Улучшите конфигурацию MySQL и оборудования, внимательно прочитав следующие темы. Их стоит прочитать, и я уверен, что вы получите лучшие результаты.

<сильный>4. Наконец, проверяет ваши ВСТАВКИ и ПОИСКИ в тестовой таблице. Я тестирую +200 миллионов случайных данных с приведенной выше схемой таблицы, он тратит 0,001 секунды на ВСТАВКУ новой строки и около 2 минут на поиск и ВЫБОР 100 миллионов строк. И тем не менее, это только тест и, кажется, хорошие результаты :)


<сильный>5. Моя конфигурация системы:

  • База данных: база данных MySQL 5.6.10 InnoDB (тест).
  • Процессор: ядро ​​AMD Phenom II 1090T X6, 3910 МГц каждое ядро.
  • ОЗУ: 16 ГБ DDR3 1600 МГц CL8.
  • HD: Windows 7 64-бит с пакетом обновления 1 (SP1) на SSD, mySQL установлена ​​на SSD, журналы записываются на механический жесткий диск.
    Вероятно, мы должны добиться лучших результатов с одним из последних Intel i5 или i7 легко разогнать до 4500 МГц+, поскольку MySQL использует только одно ядро ​​для одного SQL . Чем выше частота ядра, тем быстрее оно будет выполняться.

<сильный>6. Подробнее о MySQL:
O 'Reilly High Performance MySQL
MySQL Оптимизация операторов SQL


7. Использование другой базы данных: MongoDB или Redis идеально подходят для этого случая и, вероятно, намного быстрее, чем MySQL. Оба очень просты в освоении, и оба имеют свои преимущества:
- MongoDB: рост файла журнала MongoDB

Редис

Я бы определенно выбрал Redis. Если вы научитесь сохранять журнал в Redis, это будет лучший способ управлять журналом с безумно высокой скоростью: Redis для ведения журнала
Если вы используете Redis, обратите внимание на следующие советы:

Хорошая статья Redis в Блог в Instagram.

person Community    schedule 18.06.2013

На работе у нас есть лог-таблицы почти на каждой таблице из-за условий клиента (финансовый сектор).

Мы сделали это следующим образом: две таблицы («обычная» таблица и таблица журнала), а затем срабатывает при вставке/обновлении/удалении обычной таблицы, в которой хранится ключевое слово (I,U,D) и старая запись (при обновлении , удалить) или новый (при вставке) внутри лог-таблицы

У нас есть обе таблицы в одной и той же схеме базы данных.

person Community    schedule 16.06.2013