Grafana Sqlite3 Migrations to PostgreSQL

准备环境

  • pgloader
  • PostgreSQL
  • Grafana

初始化数据库

启动 PostgreSQL 和 Grafana

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
version: '3.8'
services:
  postgres:
    image: postgres:15
    network_mode: host
    environment:
      POSTGRES_DB: grafana
      POSTGRES_USER: grafana
      POSTGRES_PASSWORD: grafana
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./postgresql.conf:/etc/postgresql/postgresql.conf
    command: postgres -c config_file=/etc/postgresql/postgresql.conf
  grafana:
    image: grafana/grafana:12.0.2
    container_name: grafana
    network_mode: host
    environment:
      - GF_DATABASE_TYPE=postgres
      - GF_DATABASE_HOST=localhost:5432
      - GF_DATABASE_NAME=grafana
      - GF_DATABASE_USER=grafana
      - GF_DATABASE_PASSWORD=grafana
      - GF_DATABASE_SSL_MODE=disable
    depends_on:
      - postgres
volumes:
  postgres_data:
    driver: local
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

# PostgreSQL 配置文件 - 针对 Grafana 高可用部署优化

# 连接设置
listen_addresses = '*'
port = 5432
max_connections = 200
superuser_reserved_connections = 3

# 内存设置
shared_buffers = 256MB
effective_cache_size = 1GB
maintenance_work_mem = 64MB
work_mem = 4MB

# WAL 设置(用于复制)
wal_level = replica
max_wal_senders = 3
max_replication_slots = 3
wal_keep_size = 1GB
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/archive/%f'

# 检查点设置
checkpoint_completion_target = 0.9
checkpoint_timeout = 15min
max_wal_size = 2GB
min_wal_size = 80MB

# 日志设置
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = 1d
log_rotation_size = 100MB
log_min_duration_statement = 1000
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
log_statement = 'ddl'
log_temp_files = 0

# 性能优化
random_page_cost = 1.1
effective_io_concurrency = 200
default_statistics_target = 100

# 安全设置
ssl = off


# 复制设置
hot_standby = on
hot_standby_feedback = on
max_standby_streaming_delay = 30s
wal_receiver_timeout = 60s

# 自动清理
autovacuum = on
autovacuum_max_workers = 3
autovacuum_naptime = 1min
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50

# 时区设置
timezone = 'UTC'
log_timezone = 'UTC'

# 区域设置
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'
default_text_search_config = 'pg_catalog.english'

导出 grafana 表结构

1
2
docker compose up -d
docker exec -ti postgres pg_dump --schema-only  -U grafana grafana > grafana-schema.sql

清理 postgres 数据库

停止 postgresgrafana,并删除 postgres 数据库

1
2
docker compose down
docker volumes rm postgres-data

导入 grafana 表结构

启动PostgreSQL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
version: '3.8'
services:
  postgres:
    image: postgres:15
    network_mode: host
    environment:
      POSTGRES_DB: grafana
      POSTGRES_USER: grafana
      POSTGRES_PASSWORD: grafana
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./postgresql.conf:/etc/postgresql/postgresql.conf
    command: postgres -c config_file=/etc/postgresql/postgresql.conf -c hba_file=/etc/postgresql/pg_hba.conf
volumes:
  postgres_data:
    driver: local
1
docker exec -i postgres psql -h localhost -p 5432 -U grafana -d grafana < grafana-schema.sql

迁移数据

pgloader 脚本

1
2
3
4
5
6
7
8
9
load database
  from sqlite:///data/services/grafana/grafana.db
  into postgresql://grafana:grafana@127.0.0.1/grafana
  with data only, reset sequences,
       batch rows = 1000,
       batch size = 10MB,
       prefetch rows = 100
  set work_mem to '16MB',
      maintenance_work_mem to '512MB';

运行 pgloader

1
docker run -m 2g --rm -it --network host -v `pwd`:/data/services/grafana  ghcr.io/dimitri/pgloader:latest pgloader /data/services/grafana/main.load

pgloader内存耗尽问题解决指南

在使用pgloader工具将SQLite数据迁移到PostgreSQL时,遇到以下错误:

1
2
3
4
5
fatal error encountered in SBCL pid 1 tid 9:
Heap exhausted, game over.

Welcome to LDB, a low-level debugger for the Lisp runtime environment.
(GC in progress, oldspace=1, newspace=7)

问题分析

根本原因

  1. 内存不足:pgloader基于SBCL(Steel Bank Common Lisp)运行时,默认堆内存限制较小
  2. 数据量过大:SQLite数据库文件可能包含大量数据,超出了默认内存限制
  3. 容器资源限制:Docker容器可能没有分配足够的内存资源

技术背景

  • pgloader使用SBCL作为运行时环境
  • SBCL默认堆大小通常为几百MB
  • 大型数据迁移需要更多内存来处理数据缓冲

解决方案

方案1:增加Docker容器内存限制

1
2
3
4
5
6
# 为容器分配更多内存(例如4GB)
docker run --rm -it --network host \
  --memory=4g \
  -v `pwd`:/data/services/grafana \
  ghcr.io/dimitri/pgloader:latest \
  pgloader /data/services/grafana/main.load

方案2:优化pgloader配置

修改 main.load 文件,添加内存优化参数:

1
2
3
4
5
6
7
8
9
load database
  from sqlite:///data/services/grafana/grafana.db
  into postgresql://grafana:grafana@127.0.0.1/grafana
  with data only, reset sequences,
       batch rows = 1000,
       batch size = 10MB,
       prefetch rows = 100
  set work_mem to '16MB',
      maintenance_work_mem to '512MB';

验证步骤

1. 检查容器资源

1
2
# 查看容器内存使用情况
docker stats --no-stream

2. 验证数据迁移

1
2
3
# 连接PostgreSQL检查数据
psql -h localhost -U grafana -d grafana -c "\dt"
psql -h localhost -U grafana -d grafana -c "SELECT count(*) FROM table_name;"

3. 检查Grafana连接

1
2
3
4
5
# 重启Grafana服务
docker-compose restart grafana

# 检查Grafana日志
docker-compose logs grafana

常见问题

Q1: 为什么pgloader会出现内存耗尽?

A: pgloader基于SBCL运行时,默认堆内存较小。处理大型数据库时需要更多内存来缓冲数据。

Q2: 如何确定需要多少内存?

A: 一般建议分配数据库文件大小的2-3倍内存。例如,100MB的SQLite文件建议分配300MB以上内存。

Q3: 迁移失败后如何恢复?

A: 清空PostgreSQL目标表,检查并修复配置后重新运行迁移。

相关文档

总结

pgloader内存耗尽问题主要由SBCL运行时的默认内存限制引起。通过增加Docker容器内存、优化pgloader配置、或使用分批迁移策略可以有效解决此问题。建议在大型数据迁移前进行充分的资源规划和测试。

0%