Her er en liten ytelsesobservasjon rundt nikita. Har en Nikita-instans som kjører på en VM koblet mot PostgreSQL, og med nginx som reversmellomtjener foran nikita for https. Importerte UiOs leganto-datasett som består av:
Imported entries and their count arkiv 1 arkivdel 1 arkivskaper 1 mappe 16813 registrering 282881
Hele prosessen tok like under 10 timer, totalt ca. 8 registrering per sekund.
[Petter Reinholdtsen]
Hele prosessen tok like under 10 timer, totalt ca. 8 registrering per sekund.
Etter å ha økt minnetilfanget for Nikita med JAVA_OPTS=-Xms512m -Xmx2g i nikita.service, så tok det fortsatt like under 10 timer med totalt ca. 8 registrering per sekund. Hadde håpet det skulle ha større effekt. Nå skal jeg gjøre et nytt forsøk med noen nye SQL-indekser.
Jeg opprettet følgende med psql, og det ser ut til å ha redusert andelen sekvensielle søk i tabellene, om ikke annet. Målinger fra tidligere kjøring viste at spesielt bsm_base.fk_bsm_record_id fikk mange sekvensielle søk under leganto-importen.
create index bsm_base_fk_bsm_record_id_index on bsm_base USING btree(fk_bsm_record_id); create index bsm_base_fk_bsm_part_id_index on bsm_base USING btree(fk_bsm_part_id); create index bsm_base_fk_bsm_file_id_index on bsm_base USING btree(fk_bsm_file_id);
Vi får se hvor mye dette reduserer totalkjøretiden, kanskje om en ti timers tid.
Hvordan kan Nikita-koden endres slik at slike indekser opprettes av Spring?
Et annet forslag til endring som jeg ikke har kommet til å teste ennå, men som vi håper også skal gi ytelsesforbedring, er byttet til SQL-typen UUID fra varchar(36). Jeg møtte problemer med å bygge fra master, så må løse det før jeg får testet kodeendringer.
Indekser opprettes i @Entity klassene. Man kan se et eksempel på det i DocumentObject.java:
@Table(name = TABLE_DOCUMENT_OBJECT, indexes = @Index(name = "index_filname", columnList = "original_filename"))
Disse må hardkodes. Grunnen det ikke har vært fokus på det er fordi når vi brukte spring-data-jpa med repositories så ble de opprettet automatisk dersom det var et søk på et attributt. Senere tok vi i bruk og forkastet elasticsearch så behovet for indekser var fraværende da ES skulle ta seg av slike søk.
Støtter endring av systemID fra varchar, men det er flere klasser som må sjekkes.
Thomas
________________________________ Fra: Petter Reinholdtsen pere@hungry.com Sendt: tirsdag 11. mars 2025 10:15 Til: nikita-noark@nuug.no nikita-noark@nuug.no Emne: Re: Ytelsesobservasjoner
Jeg opprettet følgende med psql, og det ser ut til å ha redusert andelen sekvensielle søk i tabellene, om ikke annet. Målinger fra tidligere kjøring viste at spesielt bsm_base.fk_bsm_record_id fikk mange sekvensielle søk under leganto-importen.
create index bsm_base_fk_bsm_record_id_index on bsm_base USING btree(fk_bsm_record_id); create index bsm_base_fk_bsm_part_id_index on bsm_base USING btree(fk_bsm_part_id); create index bsm_base_fk_bsm_file_id_index on bsm_base USING btree(fk_bsm_file_id);
Vi får se hvor mye dette reduserer totalkjøretiden, kanskje om en ti timers tid.
Hvordan kan Nikita-koden endres slik at slike indekser opprettes av Spring?
Et annet forslag til endring som jeg ikke har kommet til å teste ennå, men som vi håper også skal gi ytelsesforbedring, er byttet til SQL-typen UUID fra varchar(36). Jeg møtte problemer med å bygge fra master, så må løse det før jeg får testet kodeendringer.
-- Vennlig hilsen Petter Reinholdtsen _______________________________________________ nikita-noark mailing list -- nikita-noark@nuug.no To unsubscribe send an email to nikita-noark-leave@nuug.no
[Thomas John Sødring]
Indekser opprettes i @Entity klassene. Man kan se et eksempel på det i DocumentObject.java:
@Table(name = TABLE_DOCUMENT_OBJECT, indexes = @Index(name = "index_filname", columnList = "original_filename"))
Disse må hardkodes. Grunnen det ikke har vært fokus på det er fordi når vi brukte spring-data-jpa med repositories så ble de opprettet automatisk dersom det var et søk på et attributt. Senere tok vi i bruk og forkastet elasticsearch så behovet for indekser var fraværende da ES skulle ta seg av slike søk.
Takk. Jeg skal teste følgende endring i dag, og håper det gir de samme indeksene som jeg manuelt lagde via psql for testing i går:
diff --git a/src/main/java/app/domain/noark5/bsm/BSMBase.java b/src/main/java/app/domain/noark5/bsm/BSMBase.java index 013400564..a8d995ee9 100644 --- a/src/main/java/app/domain/noark5/bsm/BSMBase.java +++ b/src/main/java/app/domain/noark5/bsm/BSMBase.java @@ -23,7 +23,21 @@ import static app.utils.constants.Constants.*; import static jakarta.persistence.FetchType.LAZY;
@Entity -@Table(name = TABLE_BSM_BASE) +@Table(name = TABLE_BSM_BASE, + indexes = { + @Index(name = TABLE_BSM_BASE + "_" + BSM_FILE_ID + "_index", + columnList = BSM_FILE_ID), + @Index(name = TABLE_BSM_BASE + "_" + BSM_RECORD_ID + "_index", + columnList = BSM_RECORD_ID), + @Index(name = TABLE_BSM_BASE + "_" + BSM_PART_ID + "_index", + columnList = BSM_PART_ID), + @Index(name = TABLE_BSM_BASE + "_" + BSM_CORRESPONDENCE_PART_ID + "_index", + columnList = BSM_CORRESPONDENCE_PART_ID), + @Index(name = TABLE_BSM_BASE + "_" + BSM_DOCUMENT_DESCRIPTION_ID + "_index", + columnList = BSM_DOCUMENT_DESCRIPTION_ID), + @Index(name = TABLE_BSM_BASE + "_" + BSM_ADMINISTRATIVE_UNIT_ID + "_index", + columnList = BSM_ADMINISTRATIVE_UNIT_ID) + }) @JsonDeserialize(using = BSMDeserializer.class) @LinksPacker(using = BSMBaseLinksBuilder.class) @LinksObject(using = BSMBaseLinks.class)
Med slike indekser på plass tok importen av leganto-data rundt ni og en halv time, så det virker som de hadde positiv effekt. Det kom inn rundt 8.3 registreringer per sekund denne gangen, litt høyere enn uten indekser.
ER blitt tipset om følgende triks for å hente ut trege PostgreSQL-forespørsler:
SELECT calls, mean_exec_time, query FROM pg_stat_statements WHERE userid = 'pgsql_user'::regrole AND mean_exec_time > 200 ORDER BY calls DESC;
Det gir tips om hvilke indekser som kan hjelpe. Regner med at eksporten også vil trenge noen indekser for å få opp farten.
[Petter Reinholdtsen]
Med slike indekser på plass tok importen av leganto-data rundt ni og en halv time, så det virker som de hadde positiv effekt. Det kom inn rundt 8.3 registreringer per sekund denne gangen, litt høyere enn uten indekser.
Med SQL-type UUID og automatisk opprettede indekser på bsm_base, så tok neste kjøring litt under ni og en halv time, og det kom inn rundt 8,4 registreringer per sekund.
Jeg ser fra grafene at det er noen sekvensielle søk igjen. Vil forsøke å spore opp hvilke indekser som trengs her og se om vi kan bli kvitt dem.
Jeg har en export-all --xml gående, og hentet ut informasjon om hvilke SQL-spørringer som har brukt mest tid så langt ved hjelp av "select calls,mean_exec_time,query from pg_stat_statements where userid = 'nikita_noark5_test_user'::regrole and mean_exec_time*calls > 100 order by calls desc;".
De som er kallt flest ganger er følgende munnfuller:
* select re1_0.system_id, re1_1.dtype, re1_1.created_by, re1_1.created_date, re1_1.last_modified_by, re1_1.last_modified_date, re1_1.organisation, re1_1.version, re1_0.archived_by, re1_0.archived_date, re1_0.description, re1_0.document_medium_code, re1_0.document_medium_code_name, re1_0.public_title, re1_0.record_id, re1_0.record_class_id, re1_0.record_classified_id, re1_0.record_disposal_id, re1_0.record_file_id, re1_0.record_screening_id, re1_0.record_series_id, re1_0.title, re1_2.document_date, re1_2.due_date, re1_2.freedom_assessment_date, re1_2.loaned_to, re1_2.loaned_date, re1_2.number_of_attachments, re1_2.received_date, re1_2.sent_date, re1_3.document_date, re1_3.due_date, re1_3.freedom_assessment_date, re1_3.loaned_to, re1_3.loaned_date, re1_3.number_of_attachments, re1_3.received_date, re1_3.record_date, re1_3.record_sequence_number, re1_3.record_year, re1_3.records_management_unit, re1_3.system_id, re1_3.registry_entry_number, re1_3.registry_entry_status_code, re1_3.registry_entry_status_code_name, re1_3.registry_entry_type_code, re1_3.registry_entry_type_code_name, re1_3.sent_date, re1_4.administrative_unit, re1_4.case_handler, re1_4.meeting_case_type, re1_4.meeting_record_status, re1_4.meeting_record_type, re1_4.reference_from_meeting_registration_system_id, re1_4.reference_to_meeting_registration_system_id from as_record_entity re1_0 join system_id_entity re1_1 on re1_0.system_id=re1_1.system_id left join sa_record_note re1_2 on re1_0.system_id=re1_2.system_id left join sa_registry_entry re1_3 on re1_0.system_id=re1_3.system_id left join mu_meeting_record re1_4 on re1_0.system_id=re1_4.system_id where re1_1.system_id=$1
* select el1_0.system_id, el1_1.dtype, el1_1.created_by, el1_1.created_date, el1_1.last_modified_by, el1_1.last_modified_date, el1_1.organisation, el1_1.version, el1_0.event_initiator_system_id, el1_0.href_reference_archive_unit, el1_0.reference_archive_unit, el1_0.reference_event_initiator_system_id, el1_0.system_id_entity_id, el1_0.rel_reference_archive_unit, el1_2.changed_by_system_id, el1_2.new_value, el1_2.old_value, el1_2.reference_changed_by_system_id, el1_2.reference_metadata from log_event_log el1_0 join system_id_entity el1_1 on el1_0.system_id=el1_1.system_id left join log_change_log el1_2 on el1_0.system_id=el1_2.system_id left join log_create_log el1_3 on el1_0.system_id=el1_3.system_id left join log_delete_log el1_4 on el1_0.system_id=el1_4.system_id left join log_read_log el1_5 on el1_0.system_id=el1_5.system_id where el1_1.organisation=$1 and el1_0.reference_archive_unit=$2 offset $3 rows fetch first $4 rows only
* select p1_0.system_id, p1_1.dtype, p1_1.created_by, p1_1.created_date, p1_1.last_modified_by, p1_1.last_modified_date, p1_1.organisation, p1_1.version, p1_0.name, p1_0.part_role_code, p1_0.part_role_code_name, p1_2.system_id, p1_2.d_number, p1_2.social_security_number, p1_3.system_id, p1_3.contact_person, p1_3.organisation_number from as_part p1_0 join system_id_entity p1_1 on p1_0.system_id=p1_1.system_id left join as_part_person p1_2 on p1_0.system_id=p1_2.system_id left join as_part_unit p1_3 on p1_0.system_id=p1_3.system_id join as_record_part rre1_0 on p1_1.system_id=rre1_0.f_pk_part_id where p1_1.organisation=$1 and rre1_0.f_pk_record_id=$2 offset $3 rows fetch first $4 rows only
* select dd1_0.system_id, dd1_1.created_by, dd1_1.created_date, dd1_1.last_modified_by, dd1_1.last_modified_date, dd1_1.organisation, dd1_1.version, dd1_0.associated_by, dd1_0.associated_with_record_as_code, dd1_0.associated_with_record_as_code_name, dd1_0.association_date, dd1_0.description, dd1_0.document_medium_code, dd1_0.document_medium_code_name, dd1_0.document_number, dd1_0.document_status_code, dd1_0.document_status_code_name, dd1_0.document_type_code, dd1_0.document_type_code_name, dd1_0.external_reference, dd1_0.document_description_classified_id, dd1_0.document_description_deletion_id, dd1_0.document_description_disposal_id, dd1_0.document_description_disposal_undertaken_id, dd1_0.document_description_screening_id, dd1_0.storage_location, dd1_0.title from as_document_description dd1_0 join system_id_entity dd1_1 on dd1_0.system_id=dd1_1.system_id join as_record_document_description rre1_0 on dd1_0.system_id=rre1_0.f_pk_document_description_id where dd1_1.organisation=$1 and rre1_0.f_pk_record_id=$2 offset $3 rows fetch first $4 rows only
* select cp1_0.system_id, cp1_1.dtype, cp1_1.created_by, cp1_1.created_date, cp1_1.last_modified_by, cp1_1.last_modified_date, cp1_1.organisation, cp1_1.version, cp1_0.correspondence_part_type_code, cp1_0.correspondence_part_type_code_name, cp1_0.f_pk_record_id, cp1_2.administrative_unit, cp1_2.case_handler, cp1_2.reference_administrative_unit_system_id, cp1_2.user_system_id, cp1_3.system_id, cp1_3.d_number, cp1_3.name, cp1_3.social_security_number, cp1_4.system_id, cp1_4.contact_person, cp1_4.name, cp1_4.unit_identifier from as_correspondence_part cp1_0 join system_id_entity cp1_1 on cp1_0.system_id=cp1_1.system_id left join as_correspondence_part_internal cp1_2 on cp1_0.system_id=cp1_2.system_id left join as_correspondence_part_person cp1_3 on cp1_0.system_id=cp1_3.system_id left join as_correspondence_part_unit cp1_4 on cp1_0.system_id=cp1_4.system_id join system_id_entity rre1_1 on rre1_1.system_id=cp1_0.f_pk_record_id join as_record_entity rre1_0 on rre1_0.system_id=rre1_1.system_id where cp1_1.organisation=$1 and rre1_1.system_id=$2 offset $3 rows fetch first $4 rows only
Jeg har ikke studert dem i detalj, men lurer jo på om det er alternative måter å hente ut informasjonen som ikke innebærer like mange tabellsammeslåinger. Nevner det her som et innspill til morgendagens diskusjon om Hibernate. Merk, ingen av disse spørringene går spesielt trege i snitt, de påviker kjøretiden mest på grunn av at de kalles så ofte.