Увеличить скорость записи PostgreSQL за счет вероятной потери данных? - PullRequest
30 голосов
/ 27 февраля 2011

Мне нравится, что PostgreSQL устойчив к сбоям, так как я не хочу тратить время на исправление базы данных .Однако я уверен, что должны быть некоторые вещи, которые я могу отключить / изменить, чтобы вставки / обновления работали быстрее, даже если я потеряю пару записей до отключения / сбоя питания.Меня не беспокоит пара записей - только база данных в целом.

Я пытаюсь оптимизировать PostgreSQL для большого количества записей.В настоящее время вставка 1 миллиона строк занимает 22 минуты, что кажется немного медленным.

Как я могу ускорить запись в PostgreSQL?

Некоторые из опций, которые я рассмотрел (например, full_page_writes), также, похоже, рискуют повредить данные, которые нене то, что я хочу.Я не возражаю против потерянных данных - я просто не хочу порчи.

Обновление 1

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

CREATE TABLE "user"
(
  id serial NOT NULL,
  username character varying(40),
  email character varying(70),
  website character varying(100),
  created integer,
  CONSTRAINT user_pkey PRIMARY KEY (id)
)
WITH ( OIDS=FALSE );
CREATE INDEX id ON "user" USING btree (id);

У меня есть около 10 сценариев, каждый из которых выдает 100 000 запросов одновременно, используя подготовленные операторы.Это для имитации реальной нагрузки, которую мое приложение будет отдавать базе данных.В моем приложении каждая страница имеет 1+ вставок.

Обновление 2

Я уже использую асинхронные коммиты, потому что у меня

synchronous_commit = off

в основном файле конфигурации.

Ответы [ 8 ]

57 голосов
/ 28 февраля 2011

1M записей, вставленных за 22 минуты, составляют 758 записей в секунду.Каждый INSERT здесь представляет собой отдельную фиксацию на диске, в которую в конечном итоге добавляются как журнал записи с опережением, так и компоненты базы данных.Обычно я ожидаю, что даже хорошее оборудование с кэшем с батарейным питанием и всем, что вам повезет, достигнет 3000 коммитов / секунду.Таким образом, вы на самом деле не так уж плохо, если это обычное оборудование без такого ускорения записи.Нормальный предел здесь находится в диапазоне от 500 до 1000 коммитов / секунду в ситуации, в которой вы находитесь, без специальной настройки для этой ситуации.

Что касается того, как это будет выглядеть, если вы не можете сделатькоммиты включают в себя больше записей, ваши варианты ускорения включают:

  • Отключить синхронный_коммит (уже сделано)

  • Увеличьте wal_writer_delay.Когда синхронный_коммит выключен, буферы базы данных фиксируются для записи каждые 200 мс.Вместо этого вы можете сделать это за несколько секунд, если захотите, настроив это вверх, это просто увеличит размер потери данных после сбоя.

  • Увеличьте wal_buffers до 16 МБ, просто чтобыЭта целая операция более эффективна.

  • Увеличьте контрольные_сегменты, чтобы сократить частоту записи обычных данных на диск.Вы, вероятно, хотите по крайней мере 64 здесь.Недостатками являются более высокое использование дискового пространства и более длительное время восстановления после сбоя.

  • Увеличение shared_buffers.Значение по умолчанию здесь крошечное, обычно 32 МБ.Вы должны увеличить объем разделяемой памяти UNIX, выделяемой системой.После этого полезные значения обычно составляют> 1/4 от общего объема ОЗУ, до 8 ГБ.Скорость усиления здесь падает выше 256 МБ, хотя увеличение по умолчанию может оказаться очень полезным.

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

4 голосов
/ 27 февраля 2011

22 минуты для миллиона строк не кажется медленным , особенно если у вас много индексов.

Как вы делаете вставки?Я предполагаю, что вы используете пакетные вставки, а не одну строку на транзакцию.

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

Пожалуйста, опубликуйте код, который вы используете для загрузки записей 1M, и люди посоветуют.

Пожалуйста, напишите:

  • Оператор CREATE TABLE для таблицы, которую вы загружаете в
  • Код, который вы используете для загрузки в
  • небольшой пример данных (если возможно)

РЕДАКТИРОВАТЬ: Кажется, OP не заинтересован в массовых вставках, но выполняет тест производительности для многих однорядных вставок.Я предполагаю, что каждая вставка находится в своей собственной транзакции.

  • Рассмотрите возможность пакетирования вставок на стороне клиента для каждого узла, записи их во временный файл (надеюсь, надежно / надежно) и наличиедемон или некоторый периодический процесс, который асинхронно выполняет пакетную вставку выдающихся записей в партиях разумного размера.
  • Этот механизм пакетирования для каждого устройства действительно дает наилучшую производительность, по моему опыту, в данных аудита, таких как данные -складские приложения, где данные не нужно помещать в базу данных , сейчас .Это также обеспечивает устойчивость приложения к недоступности базы данных.
  • Конечно, обычно у вас будет несколько оконечных устройств, создающих записи аудита (например, телефонные коммутаторы, почтовые реле, серверы веб-приложений), у каждого должно быть своесобственный экземпляр этого механизма, который полностью независим.
  • Это действительно «умная» оптимизация, которая вносит большую сложность в дизайн приложения и имеет много мест, где могут возникать ошибки.Не применяйте его, если вы не действительно уверены, вам это нужно.
3 голосов
/ 12 сентября 2013

Я думаю, что проблему нельзя решить, имея дело только с сервером.

Я обнаружил, что PostgreSQL может фиксировать 3000+ строк в секунду, и сервер, и клиент не были заняты, но время прошло,В отличие от SQL Server может достигать 5000+ строк в секунду, а Oracle даже быстрее, он может достигать 12000+ в секунду, около 20 полей в строке.

Полагаю, проблема в обратном пути: отправьте строку на сервер и получите ответ от сервера.И SQL Server, и Oracle поддерживают пакетные операции: отправьте более одной строки в вызове функции и дождитесь ответа.

Много лет назад я работал с Oracle: пытаясь улучшить производительность записи с помощью OCI, я читал документыи обнаружил, что слишком много поездок туда и обратно снизит производительность.Наконец, я решил эту проблему с помощью пакетных операций: отправьте 128 или более строк на сервер в пакетном режиме и дождитесь ответа.Это достигло 12000+ строк в секунду.Если вы не используете пакеты и отправляете все строки по отдельности (включая ожидание), оно достигает только около 2000 строк в секунду.

3 голосов
/ 27 февраля 2011

Ну, ты не даёшь нам многого, чтобы продолжать. Но, похоже, вы ищете асинхронные коммиты .

Не пропустите обновление оборудования - более быстрое оборудование обычно означает более быструю базу данных.

2 голосов
/ 17 ноября 2016

1M фиксация за 22 минуты кажется разумной, даже с synchronous_commit = off, но если вы можете избежать необходимости фиксировать каждую вставку, вы можете получить намного быстрее, чем это.Я только что попытался вставить 1M (идентичных) строк в вашу таблицу примеров от 10 одновременно работающих авторов, используя команду bulk-insert COPY:

$ head -n3 users.txt | cat -A # the rest of the file is just this another 99997 times
Random J. User^Irjuser@email.com^Ihttp://example.org^I100$
Random J. User^Irjuser@email.com^Ihttp://example.org^I100$
Random J. User^Irjuser@email.com^Ihttp://example.org^I100$
$ wc -l users.txt
100000 users.txt
$ time (seq 10 | xargs --max-procs=10 -n 1 bash -c "cat users.txt | psql insertspeed -c 'COPY \"user\" (username, email, website, created) FROM STDIN WITH (FORMAT text);'")

real    0m10.589s
user    0m0.281s
sys     0m0.285s
$ psql insertspeed -Antc 'SELECT count(*) FROM "user"'
1000000

Очевидно, что там всего 10 коммитов, что не совсем то, чтоВы ищете, но, надеюсь, это даст вам некоторое представление о скорости, которая может быть возможна при объединении ваших вкладышей.Это на виртуальной машине VirtualBox, работающей под управлением Linux, на довольно стандартном хост-компьютере Windows, так что это не совсем возможное высокопроизводительное аппаратное обеспечение.

Чтобы дать немного меньших игрушек, у нас есть сервис, работающий в производстве, который имеетодин поток, который передает данные в Postgres с помощью команды COPY, аналогичной приведенной выше.Он завершает пакет и фиксируется после определенного числа строк или, если транзакция достигает определенного возраста (в зависимости от того, что наступит раньше).Он может выдерживать 11 000 вставок в секунду с максимальной задержкой ~ 300 мс, выполняя ~ 4 коммита в секунду.Если бы мы увеличили максимально допустимый возраст транзакций, мы получили бы больше коммитов в секунду, что уменьшило бы задержку, а также пропускную способность.Опять же, это не очень впечатляющее аппаратное обеспечение.

Исходя из этого опыта, я настоятельно рекомендую использовать COPY вместо INSERT и пытаться максимально уменьшить количество коммитов.при достижении цели по задержке.

2 голосов
/ 27 февраля 2011

Вы также должны увеличить checkpoint_segments (например, до 32 или даже выше) и, скорее всего, также wal_buffers

Редактировать: если это массовая загрузка, вы должны использовать COPY для вставки строк.Это намного быстрее, чем обычные INSERT.

Если вам нужно использовать INSERT, вы рассматривали возможность использования пакетной (для JDBC) или многострочной вставки?

1 голос
/ 28 февраля 2011

Хорошо, вы могли бы ускорить процесс создания индекса, который вы создаете вручную - ограничение primary key уже автоматически создает уникальный индекс для этого столбца, как вы можете видеть ниже (я тестирую на 8.3) :

postgres=> CREATE TABLE "user"
postgres-> (
postgres(>   id serial NOT NULL,
postgres(>   username character varying(40),
postgres(>   email character varying(70),
postgres(>   website character varying(100),
postgres(>   created integer,
postgres(>   CONSTRAINT user_pkey PRIMARY KEY (id)
postgres(> )
postgres-> WITH ( OIDS=FALSE );
NOTICE:  CREATE TABLE will create implicit sequence "user_id_seq" for serial column "user.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "user_pkey" for table "user"
CREATE TABLE
postgres=> CREATE INDEX id ON "user" USING btree (id);
CREATE INDEX
postgres=> \d user
                                  Table "stack.user"
  Column  |          Type          |                     Modifiers
----------+------------------------+---------------------------------------------------
 id       | integer                | not null default nextval('user_id_seq'::regclass)
 username | character varying(40)  |
 email    | character varying(70)  |
 website  | character varying(100) |
 created  | integer                |
Indexes:
    "user_pkey" PRIMARY KEY, btree (id)
    "id" btree (id)

Также рассмотрите возможность изменения wal_sync_method на параметр, который использует O_DIRECT - , это не значение по умолчанию в Linux

0 голосов
/ 30 ноября 2018

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...