{"id":20094626,"url":"https://github.com/alansastre/jakartaee-performance","last_synced_at":"2025-03-02T16:18:19.670Z","repository":{"id":152300356,"uuid":"582619716","full_name":"alansastre/jakartaee-performance","owner":"alansastre","description":"Mejoras y optimizaciones de rendimiento en aplicaciones de Jakarta EE. Formerly Java EE.","archived":false,"fork":false,"pushed_at":"2022-12-27T11:38:52.000Z","size":5,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-13T03:32:28.758Z","etag":null,"topics":["application-performance","database-perfomance","jakartaee","jakartaee10","jakartaee9","java","java11","java17","java8","monitoring","performance","performance-monitoring","performance-optimization","performance-testing"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alansastre.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-27T11:38:47.000Z","updated_at":"2024-09-26T08:56:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"d13ab25a-6a44-4a44-9946-1339c0bbc040","html_url":"https://github.com/alansastre/jakartaee-performance","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alansastre%2Fjakartaee-performance","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alansastre%2Fjakartaee-performance/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alansastre%2Fjakartaee-performance/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alansastre%2Fjakartaee-performance/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alansastre","download_url":"https://codeload.github.com/alansastre/jakartaee-performance/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241533639,"owners_count":19977826,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application-performance","database-perfomance","jakartaee","jakartaee10","jakartaee9","java","java11","java17","java8","monitoring","performance","performance-monitoring","performance-optimization","performance-testing"],"created_at":"2024-11-13T16:51:29.029Z","updated_at":"2025-03-02T16:18:19.650Z","avatar_url":"https://github.com/alansastre.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jakarta EE: mejoras de rendimiento\n\n## Servidor de aplicaciones\n\nEjemplos para: Apache Tomcat / Apache TomEE.\n\nPosibles optimizaciones en el archivo ``server.xml``:\n\n```xml \n\n \u003cConnector port=\"8088\" protocol=\"HTTP/1.1\"\n\t   connectionTimeout=\"20000\"\n\t   redirectPort=\"8443\"\n\t   enableLookups=\"false\"\n\t   compression=\"off\"\n\t   acceptCount=\"2048\"\n\t   maxConnections=\"1024\"\n\t   maxThreads=\"1000\"\n\t   tcpNoDelay=\"true\"/\u003e\n\n```\n\nCrear archivo ``setenv.sh`` dentro del directorio donde está ``catalina.sh``: \n\n```bash\nCATALINA_OPTS=\" -Xms1024m -Xmx5012m -XX:PermSize=2048m -XX:MaxPermSize=2048m -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n\"\n```\n\n## Servidor web\n\nTanto **Nginx** como **Apache web server** se pueden optimizar instalando el módulo [PageSpeed](https://www.modpagespeed.com/doc/).\n\n```\nModPagespeedModifyCachingHeaders off\nModPagespeedXHeaderValue \"*\"\nModPagespeedEnableFilters lazyload_images\nModPagespeedEnableFilters collapse_whitespace\nModPagespeedEnableFilters dedup_inlined_images\nModPagespeedEnableFilters defer_javascript\nModPagespeedEnableFilters elide_attributes\nModPagespeedEnableFilters convert_gif_to_png\nModPagespeedEnableFilters inline_images\nModPagespeedEnableFilters hint_preload_subresources\nModPagespeedEnableFilters remove_comments\nModPagespeedEnableFilters insert_dns_prefetch\nModPagespeedEnableFilters remove_quotes\nModPagespeedEnableFilters rewrite_css\n\nModPagespeedEnableFilters inline_css\nModPagespeedEnableFilters prioritize_critical_css\nModPagespeedEnableFilters trim_urls\nModPagespeedEnableFilters inline_javascript\nModPagespeedRewriteLevel OptimizeForBandwidth\nModPagespeedEnableFilters combine_css\nModPagespeedEnableFilters combine_javascript\nModPagespeedEnableFilters combine_heads\nModPagespeedEnableFilters rewrite_javascript\n\n```\n\n## Base de datos\n\nEjemplos sobre PostgreSQL.\n\n### Configuración\n\nMejorar el rendimiento de base de datos PosgtreSQL modificando el archivo de configuración (C:/Program Files/PostgreSQL/13/data/postgresql.conf):\n\n```conf\nmax_connections = 20\nshared_buffers = 8GB\neffective_cache_size = 24GB\nmaintenance_work_mem = 2GB\ncheckpoint_completion_target = 0.9\nwal_buffers = 16MB\ndefault_statistics_target = 100\nrandom_page_cost = 1.1\neffective_io_concurrency = 200\nwork_mem = 104857kB\nmin_wal_size = 1GB\nmax_wal_size = 4GB\nmax_worker_processes = 8\nmax_parallel_workers_per_gather = 4\nmax_parallel_workers = 8\nmax_parallel_maintenance_workers = 4\n```\n\nSe utiliza el programa [pgtune](https://pgtune.leopard.in.ua/#/), ejemplo de uso: \n\n```bash\npgtune -i C:/Program Files/PostgreSQL/13/data/postgresql.conf/postgresql.conf -o C:/Program Files/PostgreSQL/13/data/postgresql.conf.pgtune\n```\n\n\n### Índices\n\nUn índice es una estructura de datos separada que permite optimizar la recuperación de datos\n\n```sql\n\n-- Crear índices:\n\nCREATE INDEX index_name ON table_name(col1, col2);\n\nCREATE INDEX access_log_client_ip_ix ON access_log (client_ip)\nWHERE NOT (client_ip \u003e inet '192.168.100.0' AND\nclient_ip \u003c inet '192.168.100.255');\n\n\n-- ver índices de una tabla :\n\nSELECT\n    tablename,\n    indexname,\n    indexdef\nFROM\n    pg_indexes\nWHERE\n    tablename = 'my_table';\n\n\n-- Ver índices en desuso: \n\nSELECT\n  relname                                               AS TableName,\n  to_char(seq_scan, '999,999,999,999')                  AS TotalSeqScan,\n  to_char(idx_scan, '999,999,999,999')                  AS TotalIndexScan,\n  to_char(n_live_tup, '999,999,999,999')                AS TableRows,\n  pg_size_pretty(pg_relation_size(relname :: regclass)) AS TableSize\nFROM pg_stat_all_tables\nWHERE schemaname = 'public'\n      AND 50 * seq_scan \u003e idx_scan -- more than 2%\n      AND n_live_tup \u003e 10000\n      AND pg_relation_size(relname :: regclass) \u003e 5000000\nORDER BY relname ASC;\n\n```\n\nTipos de índices:\n* Single column\n* Multi column\n* Unique\n* Partial (Cuando se aplican predicados, WHERE)\n* Implicit (el que se crea por defecto sobre la pk)\n\n### Query Plan\n\nUso de EXPLAIN ANALYZE para detectar índices mal usados y tiempos de ejecución lentos:\n\n```sql\n\n-- Ejemplo:\nEXPLAIN ANALYZE SELECT * FROM credit_cards;\n\n-- Formatos de salida:\nexplain (format json) select * from measurement;\nexplain (format yaml) select * from measurement;\nexplain (format xml) select * from measurement;\nexplain (format text) select * from measurement;\n\n```\n\n### VACUUM\n\n```sql\n\nVACUUM table_name;\nVACUUM ANALYZE table_name;\nVACUUM FULL table_name;\nVACUUM VERBOSE table_name;\n\n```\n\n### Particionamiento\n\n```sql\n-- 1. Rango\n\ndrop table if exists employees;\n\nCREATE TABLE employees(\n    id BIGSERIAL,\n    birth_date DATE NOT NULL,\n\tfirst_name varchar(20) NOT null,\n    primary KEY(id, birth_date)\n) PARTITION BY RANGE (birth_date);\n\n\nCREATE TABLE employees_2019 PARTITION OF employees\nFOR VALUES FROM ('2019-01-01') TO ('2020-01-01');\n-- from inclusive\n-- to exclusive\n\nCREATE TABLE employees_2020 PARTITION OF employees\nFOR VALUES FROM ('2020-01-01') TO ('2021-01-01');\n\nCREATE TABLE employees_2021 PARTITION OF employees\nFOR VALUES FROM ('2021-01-01') TO ('2022-01-01');\n\nINSERT INTO employees(birth_date, first_name) values\n('2019-02-14', 'Employee 1'),\n('2020-06-14', 'Employee 2'),\n('2021-07-14', 'Employee 3');\n\nSELECT * from employees;\nSELECT * from ONLY employees;\nSELECT * from employees_2019;\nSELECT * from employees_2020;\nSELECT * from employees_2021;\n\nINSERT INTO employees(birth_date, first_name) values\n('2022-01-14', 'Employee 4');\n\nINSERT INTO employees(birth_date, first_name) values\n('2020-06-17', 'Employee 5');\n\nEXPLAIN ANALYZE select * from employees;\n\nEXPLAIN ANALYZE select * from employees where first_name = 'Employee 3';\n\nEXPLAIN ANALYZE select * from employees where birth_date = '2020-06-14';\n\nEXPLAIN ANALYZE select * from employees where EXTRACT(month FROM birth_date) = 6 and EXTRACT(year FROM birth_date) = 2020;\n\nEXPLAIN ANALYZE select * from employees where birth_date \u003e '2020-06-01';\n\n-- 2. Lista\n\ndrop table if exists employees;\nCREATE TABLE employees(\n    id BIGSERIAL,\n    birth_date DATE NOT NULL,\n    first_name varchar(20) NOT null,\n    country_code varchar(2) NOT NULL,\n    primary KEY(id, country_code)\n) PARTITION BY LIST (country_code);\n\nCREATE TABLE employees_co_1 PARTITION OF employees FOR VALUES IN ('ES', 'FR', 'DE');\n\nCREATE TABLE employees_co_2 PARTITION OF employees FOR VALUES IN ('AT', 'NZ', 'CA');\n\nINSERT INTO employees(birth_date, first_name, country_code) values\n('2019-02-14', 'Employee 1', 'ES'),\n('2020-06-14', 'Employee 2', 'FR' ),\n('2021-07-14', 'Employee 3', 'NZ');\n\nEXPLAIN ANALYZE select * from employees ;\nEXPLAIN ANALYZE select * from employees where country_code = 'ES';\n\nINSERT INTO employees(birth_date, first_name, country_code) values\n('2019-02-14', 'Employee 1', 'ZW');\n\n-- 3. Hash\n\ndrop table if exists dept;\n\nCREATE TABLE dept (\n    id int primary key\n) PARTITION BY HASH(id);\n\nCREATE TABLE dept_hash1 PARTITION OF dept FOR VALUES WITH (MODULUS 3, REMAINDER 0);\n\nCREATE TABLE dept_hash2 PARTITION OF dept FOR VALUES WITH (MODULUS 3, REMAINDER 1);\n\nCREATE TABLE dept_hash3 PARTITION OF dept FOR VALUES WITH (MODULUS 3, REMAINDER 2);\n\n\nINSERT INTO dept (\n    SELECT generate_series(0, 200000)\n);\n\nEXPLAIN ANALYZE select * from dept;\n\nselect count(*) from dept_hash1;\nselect count(*) from dept_hash2;\nselect count(*) from dept_hash3;\nselect * from dept_hash2;\nselect * from dept_hash3;\nEXPLAIN ANALYZE select * from dept where id = 3;\nEXPLAIN ANALYZE select * from dept where id = 1258;\n```\n\n\n### reindexdb\n\nReconstruir los índices ayuda a optimizarlos:\n\n```bash\nreindexdb -U postgres --verbose postgres\n\nreindexdb -U postgres --table=foo --index=bar postgres\n\n```\n\n\n## Persistencia con JPA\n\nEl uso de la API Jakarta Persistence (JPA) también se puede optimizar para mejorar la interacción con la base de datos:\n\n1. Buscar consultas lentas: \n\n```xml\n\u003cproperty name=\"hibernate.session.events.log.LOG_QUERIES_SLOWER_THAN_MS\" value=\"1\"/\u003e\n```\n\n2. Deshabilitar show_sql true en producción y debuggers.\n3. Utilizar FetchType.LAZY en las asociaciones para evitar cargar datos innecesarios\n\t1. Por defecto todas las asociaciones pueden ser lazy y para recuperar dichos datos se pueden crear consultas separadas para usarlas solo cuando sea necesario\n\t2. Uso de join fetch\n\t3. Uso de EntityGraph\n4. Evitar asociaciones ManyToMany, en caso de necesitarlas entonces realizarlas con Set en vez de con List.\n5. Uso de caché\n\t1. Hibernate caché de 1 nivel\n\t2. Hibernate caché de 2 nivel\n\t3. EhCache\n\t4. Caffeine\n\t5. HazelCast\n\t6. Infinispan\n\t7. Memcached\n\t8. Redis\n6. Guardar múltiples entidades a la vez en lugar de por separado en las operaciones de escritura a base de datos\n7. Utilizar proyecciones y DTOs en las consultas en lugar de entidades puede optimizarlas bastante\n\n## Tests de rendimiento\n\nMedición del rendimiento con **Apache JMeter**, crear un test y ejecutarlo a través de consola:\n\n```\njmeter -n -t example1.jmx -l example1-results.csv\n```\n\nOtras herramientas: \n* [Gatling](https://gatling.io/)\n* [Netdata](https://github.com/netdata/netdata)\n* [NewRelic](https://newrelic.com/)\n* [Datadog](https://www.datadoghq.com/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falansastre%2Fjakartaee-performance","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falansastre%2Fjakartaee-performance","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falansastre%2Fjakartaee-performance/lists"}