Как я могу ускорить массовые вставки в MySQL с помощью SQLAlchemy? - PullRequest
1 голос
/ 29 сентября 2019

Я только что увидел, что массовая вставка в базу данных MySQL / MariaDB происходит медленно с SQLAlchemy (даже с session.bulk_save_objects(objects)).Как я могу сделать быстрее?

MVCE

from sqlalchemy import Column, Integer, String, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql import text
import click
import json
import sqlalchemy
import time
import uuid

Base = declarative_base()


class KeyValue(Base):
    __tablename__ = "KeyValue"
    key = Column(String(36), primary_key=True)
    value = Column(Text)

    def __repr__(self):
        return f"KeyValue(key='{self.key}', value='{self.value}')"


def run_benchmark(SQLALCHEMY_DATABASE_URI, n=1000, benchmark_type='orm-bulk'):
    engine = sqlalchemy.create_engine(SQLALCHEMY_DATABASE_URI)
    connection = engine.connect()

    Base.metadata.create_all(engine)

    Session = sessionmaker(bind=engine)
    session = Session()

    keys = [str(uuid.uuid4()) for i in range(n)]
    values = [json.dumps([str(uuid.uuid4()) for _ in range(100)]) for i in range(n)]
    if benchmark_type == 'orm-bulk':
        benchmark_orm_bulk_insert(session, keys, values)
    elif benchmark_type == 'print':
        print_query(keys, values)



def benchmark_orm_bulk_insert(session, keys, values):
    t0 = time.time()
    objects = [
        KeyValue(key=key, value=value)
        for key, value in zip(keys, values)
    ]
    session.bulk_save_objects(objects)
    session.commit()
    t1 = time.time()
    print(f"Inserted {len(keys)} entries in {t1 - t0:0.2f}s with ORM-Bulk "
          f"({len(keys)/(t1 - t0):0.2f} inserts/s).")


def print_query(keys, values):
    print("INSERT INTO KeyValue (`key`, `value`) VALUES")
    for i, (key, value) in enumerate(zip(keys, values)):
        if i == 0:
            print(f"({json.dumps(key)}, {json.dumps(value)})")
        else:
            print(f", ({json.dumps(key)}, {json.dumps(value)})")
    print(";")


@click.command()
@click.option("-n", "n", required=True, type=int)
@click.option(
    "--mode",
    "mode",
    required=True,
    type=click.Choice(["orm-bulk", "print"]),
)
def entry_point(n, mode):
    run_benchmark("mysql+pymysql://root:password@localhost/benchmark", n, mode)


if __name__ == "__main__":
    entry_point()

Это дает:

$ python3 benchmark.py -n 10_000 --mode orm-bulk           
Inserted 10000 entries in 3.28s with ORM-Bulk (3048.23 inserts/s).

# Using extended INSERT statements
$ python3 benchmark.py -n 10_000 --mode print > inserts.txt
$ time mysql benchmark < inserts.txt

real    2,93s
user    0,27s
sys 0,03s

Таким образом, массовая вставка SQLAlchemy получает 3048 вставок / с, тогда как необработанные запросы SQL имеют3412 вставок.

Связанная проблема, но не основной вопрос

Обратите внимание, что оба числа находятся далеко от 313 000 вставок в секунду, упомянутых в Скоростные вставки с MySQL

LOAD DATA LOCAL INFILE 'data.csv' INTO TABLE KeyValue FIELDS TERMINATED BY ',' ENCLOSED BY '"' IGNORE 1 LINES;

я получаю время выполнения 2,22 с (4500 вставок / с), которое все еще намного меньше.Изменяя кавычку с " на ' (значительно уменьшая выход), я получил 1,55 с (6451 вставок / с).

Изменение bulk_insert_buffer_size на256 МБ также не помогли ( howto )

Изменение механизма хранения MySQL с InnoDB на MyISAM изменило скорость до 0,32 сек (31250 вставок / с)!

Попыткаиз других механизмов хранения с 3 запусками каждый:

  • CSV (не позволяет использовать какой-либо ключ!): 0,40 с / 0,53 с / 0,46 с
  • Ария (без первичного ключа): 0,38с / 0,38 с / 0,38 с
  • Ария (с первичным ключом): 0,47 с / 0,49 с / 0,45 с
  • MyISAM (без первичного ключа): 0,26 с / 0,26 с / 0,26 с => 38461 вставок / с
  • MyISAM (с первичным ключом): 0,32 с / 0,30 с / 0,29 с
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...