#postgresql #spring-boot #hibernate #database-partitioning
Вопрос:
Наша система основана на SAAS, мы используем идентификатор клиента в качестве маскировки для извлечения данных. Нагрузка на БД зависит от размера Компании. Поэтому мы разделили базу данных на основе идентификатора клиента
Пример: Перед Разделением
клиентская база
клиентид | имя клиента | адрес клиента |
---|---|---|
1 | азбука | … |
2 | ЭМН | … |
3 | XYZ | … |
штатный сотрудник
клиентид | сотрудник | имя сотрудника |
---|---|---|
1 | 123 | ААА |
1 | 124 | ВВВ |
2 | 125 | ССС |
2 | 126 | DDD |
3 | 127 | EEEE |
рабочий стол
клиентид | джобид | название работы |
---|---|---|
1 | 234 | YTR |
1 | 235 | DER |
2 | 236 | СВЕ |
3 | 237 | VFT |
3 | 238 | GHJ |
Пример: После Раздела
клиентская база
клиентид | имя клиента | адрес клиента |
---|---|---|
1 | азбука | … |
2 | ЭМН | … |
3 | XYZ | … |
штатный сотрудник
employeetable_1
клиентид | сотрудник | имя сотрудника |
---|---|---|
1 | 123 | AAA |
1 | 124 | BBB |
employeetable_2
clientid | employeeid | employeename |
---|---|---|
2 | 125 | CCC |
2 | 126 | DDD |
employeetable_3
clientid | employeeid | employeename |
---|---|---|
3 | 127 | EEE |
jobtable
jobtable_1
clientid | jobid | jobname |
---|---|---|
1 | 234 | YTR |
1 | 235 | DER |
jobtable_2
clientid | jobid | jobname |
---|---|---|
2 | 236 | СВЕ |
вакансия_3
клиентид | джобид | название работы |
---|---|---|
3 | 237 | VFT |
3 | 238 | GHJ |
Когда мы пишем запросы select:
Select employeeid,employeename from employeetable where clientid=2;
Этот запрос выполняется быстрее после раздела. Проблема, с которой мы сталкиваемся, заключается в том, что у нас есть определенная пользователем функция для управления некоторыми данными.
CREATE OR REPLACE FUNCTION GET_JOB_COUNT(NUMERIC, NUMERIC) RETURNS NUMERIC AS $BODY$ DECLARE p_client_id ALIAS FOR $1; p_employee_id ALIAS FOR $2; v_is_count NUMERIC := 0; BEGIN SELECT COUNT(JOB_ID) INTO v_is_count FROM JOBTABLE where CLIENTID=p_client_id AND CREATEDBY=p_employee_id; RETURN v_is_count; END; $BODY$ LANGUAGE plpgsql;
Select employeeid,employeename,GET_JOB_COUNT(2,employeeid) from employeetable where clientid=2;
Этот запрос выполняется медленно после раздела. Означает ли это, что функция GET_JOB_COUNT выполняется через раздел?
В этом проблема, значит, мы не можем использовать подобные функции в запросе выбора после раздела?
Комментарии:
1. Функция будет вызываться один раз для каждой строки (для всех строк, выбранных с помощью
clientid = 2
2. Как именно вы разделили стол? И какую версию Postgres вы используете?
3. мы используем PostgreSQL 13.4 на x86_64-pc-linux-gnu
4. Мы разделили разделы на основе «Идентификатора клиента», см. Раздел «После раздела» в вопросе
5. Каковы типы столбцов? Если вы сравниваете значения int с числовыми, это может помешать обрезке разделов.
Ответ №1:
Функция будет вызываться один раз для каждой строки таблицы employeetable (которая выбирается с помощью предложения WHERE). Я сомневаюсь, что вы сможете значительно повысить производительность, используя такой подход.
Лучше выполнить агрегацию (=подсчет) для всех строк сразу, а не для каждой строки отдельно:
select e.employeeid, employeename, t.cnt from employeetable e left join ( select clientid, createdby, count(job_id) as cnt from jobtable group by client_id, created_by ) j on j.clientid = e.clientid and j.createdby = e.employeeid where e.clientid = 2;
Другой вариант, который можно попробовать, — это использовать боковое соединение для раннего удаления строк из таблицы заданий-я не уверен, что оптимизатор достаточно умен для этого в приведенном выше запросе. Так что вы можете попробовать это в качестве альтернативы:
select e.employeeid, employeename, j.cnt from employeetable e left join lateral ( select count(jt.job_id) as cnt from jobtable jt where jt.clientid = e.clientid and jtcreatedby = e.employeeid ) j on true where e.clientid = 2;
Если вы действительно хотите придерживаться этой функции, возможно, оптимизатору поможет сделать ее функцией SQL. Это, по крайней мере, устраняет накладные расходы на вызов кода PL/pgSQL:
CREATE OR REPLACE FUNCTION get_job_count(p_client_id numeric, p_employee_id numeric) returns bigint as $body$ SELECT COUNT(JOB_ID) FROM JOBTABLE where CLIENTID = p_client_id AND CREATEDBY = p_employee_id; $BODY$ LANGUAGE sql stable parallel safe;
Но я сомневаюсь, что вы увидите существенное улучшение от этого.
В качестве побочного нет: использование numeric
для столбца «ИДЕНТИФИКАТОР» кажется довольно странным выбором. Почему вы не используете int
или bigint
для этого?
Комментарии:
1. Функция подсчета PLSQL, приведенная здесь, является лишь примером, у нас есть сложные манипуляции, выполняемые внутри функции, которые мы не можем решить как функцию внешнего соединения/ подзапроса/ SQL. В любом случае из вашего ответа мы делаем вывод, что вызов функции select запрос будет выполняться для каждой строки для выбранных строк, а не для каждого раздела, спасибо.
2. @RSK: если вы хотите запустить функцию для каждого раздела, вам нужно сделать ее функцией возврата набора, передающей ей ключ раздела, затем вы можете присоединиться к выходу функции