Estamos evaluando el uso de PostgreSQL para implementar una base de datos multiusuario, Actualmente se están ejecutando algunas pruebas en modelo de una sola base de datos de múltiples esquema (básicamente, todos los inquilinos tienen el mismo conjunto de objetos de bases de datos bajo su propio esquema dentro de la misma base de datos). La aplicación mantendrá un grupo de conexiones que se compartirá entre todos los arrendatarios/esquemas.proceso en segundo plano PostgreSQL problema de uso de memoria alta
p. Ej. Si la base de datos tiene 500 inquilinos/esquemas y cada inquilino tiene 200 tablas/vistas, el número total de tablas/vistas será de 500 * 200 = 100,000.
Dado que todos los inquilinos utilizarán el conjunto de conexiones, eventualmente cada conexión afectará a todas las tablas/vistas.
En nuestras pruebas, cuando la conexión alcanza más vistas, encontramos que el uso de la memoria del proceso de back-end aumenta bastante rápido y la mayoría de ellas son memorias privadas. Esa memoria se mantendrá hasta que se cierre la conexión.
Tenemos un caso de prueba de que un proceso de back-end usa más memoria de 30GB y eventualmente obtiene un error de falta de memoria.
Para ayudar a entender el problema, que escribió el código para crear un caso de prueba simplificados - MTDB_destroy: se utiliza para limpiar los esquemas de inquilinos - MTDB_Initialize: se utiliza para crear un multitenant DB - MTDB_RunTests: caso de prueba simplificado, básicamente, de entre todo el inquilino ve uno por uno.
Las pruebas que he hecho fue en PostgreSQL 9.0.3 en CentOS 5.4
Para asegurarse de que tengo un medio ambiente limpio, me vuelven a crear clúster base de datos y configuraciones dejo mayoritarios como predeterminado, (la única cosa Tengo que cambiar es aumentar "max_locks_per_transaction", ya que MTDB_destroy tiene que caer muchos objetos)
Esto es lo que hago para reproducir el problema:.
- crear una nueva base de datos
- crear las tres funciones utilizando el código adjunto
conectar con el nuevo creado db y ejecutar los scripts de inicialización
- Inicializar
seleccione MTDB_Initialize ('inquilino', 100, 100, true);
- no estoy seguro si el vacío es útil analizar aquí, acabo de funciono
vacío analizar;
- comprobar las tablas/vistas creadas
seleccione esquema_tabla, table_type, count (*) de INFORMATION_SCHEMA.tablas donde table_schema como 'tenant%' group por table_schema, table_type order por table_schema, table_type;
abrir otra conexión a la nueva db creado y ejecutar los scripts de prueba
- obtener identificador de proceso de back-end para la conexión actual
pg_backend_pid SELECT();
- abrir una consola de Linux y ejecutar -p ps y ver VIRT, RES y SHR
- ejecutar pruebas
seleccione MTDB_RunTests ('inquilino', 1);
Observaciones:
cuando se creó primero la conexión para la ejecución de pruebas,
VIRT = 182MB, RES = 6240K, SHR = 4648K
después de ejecutar las pruebas una vez, (tomó 175 segundos)
VIRT = 1661MB RES = 1,5GB SHR = 55MB
re-ejecutar la prueba de nuevo (tomó 167 segundos)
VIRT = 1661MB RES = 1,5 GB SHR = 55MB
re-ejecutar la prueba de nuevo (tomó 165 segundos)
VIRT = 1661MB RES = 1,5 GB SHR = 55MB
a medida que ampliar el número de mesas, los usos de memoria subir en las pruebas también.
¿Alguien puede ayudarme a explicar lo que está sucediendo aquí? ¿Hay alguna manera de controlar el uso de memoria del proceso de back-end de PostgreSQL?
Gracias.
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;
¿Entiendo correctamente que el uso de la memoria solo aumenta mientras mantenga una conexión abierta? ¿No podría simplemente cerrar y volver a abrir la conexión cada 'n' veces que cambie de esquema para evitar esto? –
Sí, si cerramos la conexión, la memoria asignada al proceso vuelve de nuevo a os. Sin embargo, eso tampoco es ideal. La idea del grupo de conexiones es reducir el número de conexiones simultáneas al servidor db y eliminar el costo de conexión/desconexión (que puede ser considerable si tenemos que hacerlo para cada solicitud) – Samuel