Как повысить производительность INSERT (число строк в секунду) до CockroachDB (примерно в 20 раз медленнее по сравнению с PostgreSQL) - PullRequest
0 голосов
/ 23 июня 2018

Скрипт Python3, прикрепленный к концу этого поста, создает простую таблицу с 5 INT столбцами, 3 из них с индексом.

Затем он использует многострочные вставки заполнить таблицу.

В начале ему удается вставить около 10000 строк в секунду.

Took   0.983 s to INSERT 10000 rows, i.e. performance =  10171 rows per second.
Took   0.879 s to INSERT 10000 rows, i.e. performance =  11376 rows per second.
Took   0.911 s to INSERT 10000 rows, i.e. performance =  10982 rows per second.
Took   1.180 s to INSERT 10000 rows, i.e. performance =   8477 rows per second.
Took   1.030 s to INSERT 10000 rows, i.e. performance =   9708 rows per second.
Took   1.114 s to INSERT 10000 rows, i.e. performance =   8975 rows per second.

Но когда таблица уже содержит около 1000000 строк, производительность снижается примерно до 2000строк в секунду.

Took   3.648 s to INSERT 10000 rows, i.e. performance =   2741 rows per second.
Took   3.026 s to INSERT 10000 rows, i.e. performance =   3305 rows per second.
Took   5.495 s to INSERT 10000 rows, i.e. performance =   1820 rows per second.
Took   6.212 s to INSERT 10000 rows, i.e. performance =   1610 rows per second.
Took   5.952 s to INSERT 10000 rows, i.e. performance =   1680 rows per second.
Took   4.872 s to INSERT 10000 rows, i.e. performance =   2053 rows per second.

Для сравнения: при использовании PostgreSQL вместо CockroachDB производительность составляет около 40000 строк в секунду.

Took   0.212 s to INSERT 10000 rows, i.e. performance =  47198 rows per second.
Took   0.268 s to INSERT 10000 rows, i.e. performance =  37335 rows per second.
Took   0.224 s to INSERT 10000 rows, i.e. performance =  44548 rows per second.
Took   0.307 s to INSERT 10000 rows, i.e. performance =  32620 rows per second.
Took   0.234 s to INSERT 10000 rows, i.e. performance =  42645 rows per second.
Took   0.262 s to INSERT 10000 rows, i.e. performance =  38124 rows per second.

Took   0.301 s to INSERT 10000 rows, i.e. performance =  33254 rows per second.
Took   0.220 s to INSERT 10000 rows, i.e. performance =  45547 rows per second.
Took   0.260 s to INSERT 10000 rows, i.e. performance =  38399 rows per second.
Took   0.222 s to INSERT 10000 rows, i.e. performance =  45136 rows per second.
Took   0.213 s to INSERT 10000 rows, i.e. performance =  46950 rows per second.
Took   0.211 s to INSERT 10000 rows, i.e. performance =  47436 rows per second.

Есть ли способ улучшитьпроизводительность при использовании CockroachDB?

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


db_insert_performance_test.py:

import random
from timeit import default_timer as timer
import psycopg2


def init_table(cur):
    """Create table and DB indexes"""
    cur.execute("""
        CREATE TABLE entities (a INT NOT NULL, b INT NOT NULL,
                               c INT NOT NULL, d INT NOT NULL,
                               e INT NOT NULL);""")
    cur.execute('CREATE INDEX a_idx ON entities (a);')
    cur.execute('CREATE INDEX b_idx ON entities (b);')
    cur.execute('CREATE INDEX c_idx ON entities (c);')
    # d and e does not need an index.


def create_random_event_value():
    """Returns a SQL-compatible string containing a value tuple"""
    def randval():
        return random.randint(0, 100000000)
    return f"({randval()}, {randval()}, {randval()}, {randval()}, {randval()})"


def generate_statement(statement_template, rows_per_statement):
        """Multi-row insert statement for 200 random entities like this:
        INSERT INTO entities (a, b, ...) VALUES (1, 2, ...), (6, 7, ...), ...
        """
        return statement_template.format(', '.join(
                create_random_event_value()
                for i in range(rows_per_statement)))


def main():
    """Write dummy entities into db and output performance."""

    # Config
    database = 'db'
    user = 'me'
    password = 'pwd'
    host, port = 'cockroach-db', 26257
    #host, port = 'postgres-db', 5432

    rows_per_statement = 200
    statements_per_round = 50
    rounds = 100
    statement_template = 'INSERT INTO entities (a, b, c, d, e) VALUES {}'

    # Connect to DB
    conn = psycopg2.connect(database=database, user=user, password=password,
                            host=host, port=port)
    conn.set_session(autocommit=True)
    cur = conn.cursor()

    init_table(cur)

    for _ in range(rounds):
        # statements_per_round multi-row INSERTs
        # with rows_per_statement rows each
        batch_statements = [generate_statement(statement_template,
                                               rows_per_statement)
                            for _ in range(statements_per_round)]

        # Measure insert duration
        start = timer()
        for batch_statement in batch_statements:
            cur.execute(batch_statement)
        duration = timer() - start

        # Calculate performance
        row_count = rows_per_statement * statements_per_round
        rows_per_second = int(round(row_count / duration))
        print('Took {:7.3f} s to INSERT {} rows, '
            'i.e. performance = {:>6} rows per second.'
            ''.format(duration, row_count, rows_per_second), flush=True)

    # Close the database connection.
    cur.close()
    conn.close()


if __name__ == '__main__':
    main()

Чтобы быстро воспроизвести мои результаты, введите docker-compose.yml:

version: '2.4'

services:

  cockroach-db:
    image: cockroachdb/cockroach:v2.0.3
    command: start --insecure --host cockroach-db --vmodule=executor=2
    healthcheck:
      test: nc -z cockroach-db 26258

  cockroach-db-init:
    image: cockroachdb/cockroach:v2.0.3
    depends_on:
     - cockroach-db
    entrypoint: /cockroach/cockroach sql --host=cockroach-db --insecure -e "CREATE DATABASE db; CREATE USER me; GRANT ALL ON DATABASE db TO me;"

  postgres-db:
    image: postgres:10.4
    environment:
      POSTGRES_USER: me
      POSTGRES_PASSWORD: pwd
      POSTGRES_DB: db
    healthcheck:
      test: nc -z postgres-db 5432

  db-insert-performance-test:
    image: python:3.6
    depends_on:
     - cockroach-db-init
     - postgres-db
    volumes:
     - .:/code
    working_dir: /
    entrypoint: bash -c "pip3 install psycopg2 && python3 code/db_insert_performance_test.py"

Чтобы начать тестирование, просто запустите docker-compose up db-insert-performance-test.

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

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

0 голосов
/ 28 июня 2018

Как уже говорилось в Livius, удаление индексов - это метод, используемый многими хранилищами данных, поскольку он помогает значительно ускорить массовые вставки.Я знаю, вы говорите, что строки «заполняются непрерывно», но значит ли это от других связей?Если нет, вы можете заблокировать таблицу, удалить индексы, вставить строки, а затем повторно добавить индексы.Проблема с сохранением текущих индексов заключается в том, что индекс B-Tree должен обрабатываться для каждой вставленной строки и периодически перебалансироваться.

Кроме того, вам действительно нужны три отдельных индекса, каждый из которых содержит только один столбец?Можно ли создать меньше индексов, добавив столбцы к одному из других индексов?Другими словами, вы обычно запрашиваете эту таблицу, используя только столбец a в предложении WHERE для этой таблицы?Обычно используется в сочетании с одним из других столбцов?Возможно, вы склонны делать запросы к этой таблице, JOIN редактируемой другой таблицей, используя b и a в предложении WHERE.Если это так, почему бы не объединить индекс в idx_ab (a, b)?

Просто некоторые мысли.Честно говоря, я не знаю CockroachDB, но традиционные реляционные базы данных, как правило, работают аналогично.

0 голосов
/ 25 июня 2018

CockroachDB хранит данные внутри «диапазонов», а диапазоны разделяются, когда они достигают 64 МБ.Первоначально таблица помещается в один диапазон, поэтому каждая вставка является операцией с одним диапазоном.После разделения диапазонов каждая вставка должна включать несколько диапазонов для обновления таблицы и индексов;так что там ожидаемое снижение производительности.

...