Подтвердить что ты не робот

Почему MySQL InnoDB вставляется так медленно?

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

Я проиллюстрировал очень простой тест, чтобы проиллюстрировать. В тестовой таблице я попытался сделать ее максимально простой; у моего реального кода нет такой простой компоновки и есть отношения и дополнительные индексы и т.д. Однако более простая настройка показывает эквивалентную производительность.

Вот результаты:

creating the MyISAM table took 0.000 seconds
creating 1024000 rows of test data took 1.243 seconds
inserting the test data took 6.335 seconds
selecting 1023742 rows of test data took 1.435 seconds
fetching 1023742 batches of test data took 0.037 seconds
dropping the table took 0.089 seconds
creating the InnoDB table took 0.276 seconds
creating 1024000 rows of test data took 1.165 seconds
inserting the test data took 3433.268 seconds
selecting 1023748 rows of test data took 4.220 seconds
fetching 1023748 batches of test data took 0.037 seconds
dropping the table took 0.288 seconds

Вставка 1M строк в MyISAM занимает 6 секунд; в InnoDB занимает 3433 секунды!

Что я делаю неправильно? Что неправильно настроено? (MySQL - обычная установка Ubuntu с настройками по умолчанию)

Здесь тестовый код:

import sys, time, random
import MySQLdb as db

# usage: python script db_username db_password database_name

db = db.connect(host="127.0.0.1",port=3306,user=sys.argv[1],passwd=sys.argv[2],db=sys.argv[3]).cursor()

def test(engine):

    start = time.time() # fine for this purpose
    db.execute("""
CREATE TEMPORARY TABLE Testing123 (
k INTEGER PRIMARY KEY NOT NULL,
v VARCHAR(255) NOT NULL
) ENGINE=%s;"""%engine)
    duration = time.time()-start
    print "creating the %s table took %0.3f seconds"%(engine,duration)

    start = time.time()
    # 1 million rows in 100 chunks of 10K
    data = [[(str(random.getrandbits(48)) if a&1 else int(random.getrandbits(31))) for a in xrange(10*1024*2)] for b in xrange(100)]
    duration = time.time()-start
    print "creating %d rows of test data took %0.3f seconds"%(sum(len(rows)/2 for rows in data),duration)

    sql = "REPLACE INTO Testing123 (k,v) VALUES %s;"%("(%s,%s),"*(10*1024))[:-1]
    start = time.time()
    for rows in data:
        db.execute(sql,rows)
    duration = time.time()-start
    print "inserting the test data took %0.3f seconds"%duration

    # execute the query
    start = time.time()
    query = db.execute("SELECT k,v FROM Testing123;")
    duration = time.time()-start
    print "selecting %d rows of test data took %0.3f seconds"%(query,duration)

    # get the rows in chunks of 10K
    rows = 0
    start = time.time()
    while query:
        batch = min(query,10*1024)
        query -= batch
        rows += len(db.fetchmany(batch))
    duration = time.time()-start
    print "fetching %d batches of test data took %0.3f seconds"%(rows,duration)

    # drop the table
    start = time.time()
    db.execute("DROP TABLE Testing123;")
    duration = time.time()-start
    print "dropping the table took %0.3f seconds"%duration


test("MyISAM")
test("InnoDB")
4b9b3361

Ответ 1

InnoDB не справляется с "случайными" первичными ключами. Попробуйте последовательный ключ или автоинкремент, и я считаю, что вы увидите лучшую производительность. Ваше "реальное" ключевое поле все еще может быть проиндексировано, но для массовой вставки вам может быть лучше сбросить и воссоздать этот индекс за один удар после завершения вставки. Было бы интересно увидеть ваши тесты для этого!

Некоторые связанные вопросы

Ответ 2

У InnoDB есть поддержка транзакций, вы не используете явные транзакции, поэтому innoDB должен делать фиксацию после каждого утверждения ( "выполняет флеш-журнал на диске для каждой вставки" ).

Выполните эту команду перед циклом:

START TRANSACTION

и это после цикла

COMMIT

Ответ 3

Мне нужно было одновременно тестировать вставное приложение как в MyISAM, так и в InnoDB. Был единственный параметр, который разрешил проблемы с производительностью, которые у меня были. Попробуйте установить следующее:

innodb_flush_log_at_trx_commit = 2

Удостоверьтесь, что вы понимаете риски, читая о настройке здесь.

Также см. https://dba.stackexchange.com/questions/12611/is-it-safe-to-use-innodb-flush-log-at-trx-commit-2/12612 и https://dba.stackexchange.com/a/29974/9405

Ответ 4

Я получаю очень разные результаты в своей системе, но это не использует значения по умолчанию. Вероятнее всего, вы ограничены размером файла innodb-log-file, по умолчанию это 5M. В innodb-log-file-size = 100M я получаю такие результаты (все числа в секундах):

                             MyISAM     InnoDB
create table                  0.001      0.276
create 1024000 rows           2.441      2.228
insert test data             13.717     21.577
select 1023751 rows           2.958      2.394
fetch 1023751 batches         0.043      0.038
drop table                    0.132      0.305

Увеличение innodb-log-file-size ускорит это на несколько секунд. Снижение гарантий долговечности путем установки innodb-flush-log-at-trx-commit=2 или 0 также улучшит номера вставки.

Ответ 5

Значение по умолчанию для InnoDB на самом деле довольно плохое. InnoDB очень зависим от ОЗУ, вы можете найти лучший результат, если вы настроите настройки. Вот руководство, в котором я использовал базовая оптимизация InnoDB

Ответ 6

Какой размер вашего пула буферов innodb? Убедитесь, что вы установили его на 75% от вашей ОЗУ. Обычно вставки лучше, когда в основном ключевом порядке для InnoDB. Но с большим размером пула вы должны видеть хорошие скорости.

Ответ 7

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

innodb-flush-log-at-trx-commit=0
sync_binlog=0

затем снова включите (если необходимо) после завершения обновления.

innodb-flush-log-at-trx-commit=1
sync_binlog=1

для полного соответствия ACID.

Существует огромная разница в производительности записи и обновления, когда оба они отключены и включены. По моему опыту, другие вещи, рассмотренные выше, имеют определенную разницу, но только маргинальные.

Еще одна вещь, которая влияет на update/insert, - это полнотекстовый индекс. В одном случае таблица с двумя текстовыми полями, имеющими полный текстовый индекс, вставляя 2 мили в строки, занимала 6 часов, а то же самое занимает всего 10 минут после удаления полного текстового индекса. Больше индексов, больше времени. Таким образом, индексы поиска, отличные от уникального и первичного ключа, могут быть удалены до массивных вставок/обновлений.

Ответ 8

вещи, которые ускоряют вставки:

  • Я удалил все ключи из таблицы перед большой вставкой в ​​пустую таблицу
  • то обнаружил, что у меня была проблема, что индекс не помещался в память.
  • также обнаружил, что у меня был sync_binlog = 0 (должен быть 1), даже если binlog не используется.
  • также найдено, что я не установил innodb_buffer_pool_instances

Ответ 9

mysql 5.7: Я сделал тест прямо сейчас, и я не заметил заметной разницы между случайным первичным ключом и автоматически увеличившимся.