Проблема, связанная с высоким использованием памяти внутренним процессом в PostgreSQL - PullRequest
6 голосов
/ 08 апреля 2011

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

например. Если база данных имеет 500 арендаторов / схем и каждый арендатор имеет 200 таблиц / представлений, общее количество таблиц / просмотров будет 500 * 200 = 100 000.

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

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

У нас есть тестовый случай, когда один бэкэнд-процесс использует больше памяти объемом 30 ГБ и в итоге выдает ошибку нехватки памяти.

Чтобы помочь понять проблему, я написал код для создания упрощенных тестовых случаев - MTDB_destroy: используется для очистки схем арендатора - MTDB_Initialize: используется для создания многопользовательской БД - MTDB_RunTests: упрощенный контрольный пример, в основном выбираемый из всех представлений арендаторов один за другим.

Тесты, которые я провел, были на PostgreSQL 9.0.3 на CentOS 5.4

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

Вот что я делаю, чтобы воспроизвести проблему:

  1. создать новую базу данных
  2. создать три функции, используя прикрепленный код
  3. подключиться к новой созданной БД и запустить сценарии инициализации

    - Инициализировать

    выберите MTDB_Initialize ('tenant', 100, 100, true);

    - не уверен, что анализ вакуума здесь полезен, я просто запускаю его

    анализ вакуума;

    - проверить созданные таблицы / представления

    выберите table_schema, table_type, count (*) из information_schema.tables, где table_schema похожа на группу 'tenant%' по table_schema, порядок table_type по table_schema, table_type;

  4. открыть другое соединение с новой созданной БД и запустить тестовые сценарии

    - получить идентификатор внутреннего процесса для текущего соединения

    SELECT pg_backend_pid ();

    - открыть консоль linux и запустить ps -p и посмотреть VIRT, RES и SHR

    - запустить тесты

    выбрать MTDB_RunTests ('tenant', 1);

Замечания:

  1. при первом создании соединения для запуска тестов

    VIRT = 182 МБ, RES = 6240 КБ, SHR = 4648 К

  2. после запуска тестов один раз, (заняло 175 секунд)

    VIRT = 1661 МБ RES = 1,5 ГБ SHR = 55 МБ

  3. заново запустить тест (заняло 167 секунд)

    VIRT = 1661 МБ RES = 1,5 ГБ SHR = 55 МБ

  4. заново запустить тест (заняло 165 секунд)

    VIRT = 1661 МБ RES = 1,5 ГБ SHR = 55 МБ

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

Может кто-нибудь помочь объяснить, что здесь происходит? Есть ли способ контролировать использование памяти внутренним процессом PostgreSQL?

Спасибо.

Samuel

-- MTDB_destroy
create or replace function MTDB_destroy (schemaNamePrefix varchar(100))
returns int as $$
declare
   curs1 cursor(prefix varchar) is select schema_name from information_schema.schemata where schema_name like prefix || '%';
   schemaName varchar(100);
   count integer;
begin
   count := 0;
   open curs1(schemaNamePrefix);
   loop
      fetch curs1 into schemaName;
      if not found then exit; end if;           
      count := count + 1;
      execute 'drop schema ' || schemaName || ' cascade;';
   end loop;  
   close curs1;
   return count;
end $$ language plpgsql;

-- MTDB_Initialize
create or replace function MTDB_Initialize (schemaNamePrefix varchar(100), numberOfSchemas integer, numberOfTablesPerSchema integer, createViewForEachTable boolean)
returns integer as $$
declare   
   currentSchemaId integer;
   currentTableId integer;
   currentSchemaName varchar(100);
   currentTableName varchar(100);
   currentViewName varchar(100);
   count integer;
begin
   -- clear
   perform MTDB_Destroy(schemaNamePrefix);

   count := 0;
   currentSchemaId := 1;
   loop
      currentSchemaName := schemaNamePrefix || ltrim(currentSchemaId::varchar(10));
      execute 'create schema ' || currentSchemaName;

      currentTableId := 1;
      loop
         currentTableName := currentSchemaName || '.' || 'table' || ltrim(currentTableId::varchar(10));
         execute 'create table ' || currentTableName || ' (f1 integer, f2 integer, f3 varchar(100), f4 varchar(100), f5 varchar(100), f6 varchar(100), f7 boolean, f8 boolean, f9 integer, f10 integer)';
         if (createViewForEachTable = true) then
            currentViewName := currentSchemaName || '.' || 'view' || ltrim(currentTableId::varchar(10));
            execute 'create view ' || currentViewName || ' as ' ||
                     'select t1.* from ' || currentTableName || ' t1 ' ||
             ' inner join ' || currentTableName || ' t2 on (t1.f1 = t2.f1) ' ||
             ' inner join ' || currentTableName || ' t3 on (t2.f2 = t3.f2) ' ||
             ' inner join ' || currentTableName || ' t4 on (t3.f3 = t4.f3) ' ||
             ' inner join ' || currentTableName || ' t5 on (t4.f4 = t5.f4) ' ||
             ' inner join ' || currentTableName || ' t6 on (t5.f5 = t6.f5) ' ||
             ' inner join ' || currentTableName || ' t7 on (t6.f6 = t7.f6) ' ||
             ' inner join ' || currentTableName || ' t8 on (t7.f7 = t8.f7) ' ||
             ' inner join ' || currentTableName || ' t9 on (t8.f8 = t9.f8) ' ||
             ' inner join ' || currentTableName || ' t10 on (t9.f9 = t10.f9) ';                    
         end if;
         currentTableId := currentTableId + 1;
         count := count + 1;
         if (currentTableId > numberOfTablesPerSchema) then exit; end if;
      end loop;   

      currentSchemaId := currentSchemaId + 1;
      if (currentSchemaId > numberOfSchemas) then exit; end if;     
   end loop;
   return count;
END $$ language plpgsql;

-- MTDB_RunTests
create or replace function MTDB_RunTests(schemaNamePrefix varchar(100), rounds integer)
returns integer as $$
declare
   curs1 cursor(prefix varchar) is select table_schema || '.' || table_name from information_schema.tables where table_schema like prefix || '%' and table_type = 'VIEW';
   currentViewName varchar(100);
   count integer;
begin
   count := 0;
   loop
      rounds := rounds - 1;
      if (rounds < 0) then exit; end if;

      open curs1(schemaNamePrefix);
      loop
         fetch curs1 into currentViewName;
         if not found then exit; end if;
         execute 'select * from ' || currentViewName;
         count := count + 1;
      end loop;
      close curs1;
   end loop;
   return count;  
end $$ language plpgsql;

Ответы [ 2 ]

2 голосов
/ 08 апреля 2011

Эти соединения простаивают в транзакции или просто простаивают?Похоже, незавершенные транзакции удерживают память, или, возможно, у вас утечка памяти или что-то в этом роде.

1 голос
/ 02 мая 2014

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

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

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

1007 * Кен *

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