76 lines
2.7 KiB
Markdown
76 lines
2.7 KiB
Markdown
# Benchmark go-migrate — 2,000,000 filas
|
|
|
|
**Tabla**: `Cartografia.MANZANA`
|
|
**Fecha**: 2026-05-29
|
|
**Entorno**: Docker local (MSSQL 2022 Developer / PostgreSQL 16 + PostGIS)
|
|
|
|
---
|
|
|
|
## Resultado final — 5 pasadas cada dirección
|
|
|
|
| Métrica | MSSQL → PostgreSQL | PostgreSQL → MSSQL |
|
|
|---|---|---|
|
|
| **Promedio** | **8.37s** | **16.77s** |
|
|
| **Mediana** | 8.16s | 16.33s |
|
|
| **Mínimo** | 7.75s | 16.03s |
|
|
| **Máximo** | 9.17s | 18.46s |
|
|
| **Desv. estándar** | 0.56s | 1.01s |
|
|
| **Throughput promedio** | **~238,892 filas/seg** | **~119,261 filas/seg** |
|
|
| **Factor** | 1x | **~2x más lento** |
|
|
|
|
---
|
|
|
|
## Evolución del tuning PG → MSSQL
|
|
|
|
| Etapa | Config | Tiempo | Throughput | Δ |
|
|
|---|---|---|---|---|
|
|
| Corrida 1 — original | conservadora | 236.8s | ~8,446 /seg | baseline |
|
|
| Corrida 2 — igualada | mismos parámetros | 21.94s | ~91,148 /seg | +10.8x |
|
|
| Tuning A | 4ext/8load 50k | 17.37s | ~115,200 /seg | +1.27x |
|
|
| Tuning C | 16 loaders | 17.26s | ~115,900 /seg | +1.28x |
|
|
| **Tuning D — óptimo** | **8ext/8load 50k** | **~16.77s** | **~119,261 /seg** | **+1.37x** |
|
|
| Tablock + 8 loaders | lock exclusivo serial | ~44s | ~45,000 /seg | ❌ regresión |
|
|
| Tablock + 1 loader | minimal logging | ~47s | ~42,000 /seg | ❌ regresión |
|
|
|
|
---
|
|
|
|
## Configuración óptima — `config-reverse.yaml`
|
|
|
|
```yaml
|
|
max_parallel_workers: 4
|
|
defaults:
|
|
batches_per_partition: 4
|
|
max_extractors: 8 # ← mayor lever de mejora
|
|
extractor_batch_size: 25000
|
|
extractor_queue_size: 32
|
|
max_transformers: 8
|
|
transformer_batch_size: 50000
|
|
transformer_queue_size: 32
|
|
max_loaders: 8
|
|
loader_batch_size: 50000 # sweet spot — 75k y 100k peores
|
|
```
|
|
|
|
---
|
|
|
|
## Análisis de la brecha final (~2x)
|
|
|
|
La diferencia residual entre ambas direcciones es estructural y está en el protocolo de escritura:
|
|
|
|
| Protocolo | Mecanismo | Overhead |
|
|
|---|---|---|
|
|
| `pgx.CopyFrom` (→ PG) | PostgreSQL COPY protocol — streaming binario sin SQL | mínimo |
|
|
| `mssql.CopyIn` (→ MSSQL) | BCP protocol — row-by-row dentro de un bulk statement | mayor por fila |
|
|
|
|
`mssql.CopyIn` itera fila a fila via `stmt.ExecContext(row...)` antes del flush final, lo que introduce overhead por fila independientemente del batch size. `pgx.CopyFrom` hace streaming puro.
|
|
|
|
---
|
|
|
|
## Hallazgos sobre Tablock
|
|
|
|
`Tablock: true` en `mssql.BulkOptions` resultó contraproducente en ambos escenarios:
|
|
|
|
- **Con 8 loaders paralelos**: cada loader compite por un lock exclusivo de tabla → serialización completa (~44s)
|
|
- **Con 1 loader + batch enorme**: sin contención de locks, pero overhead de log + gestión de la lock exclusiva superó el beneficio de minimal logging (~47s)
|
|
|
|
**Conclusión**: para este patrón de carga (múltiples loaders concurrentes), `Tablock: false` (default) es siempre mejor.
|