PostgreSQL 与 MySQL 的快速启动与连接
前言
在本地开发和测试时,经常需要一个临时的数据库环境。使用 Docker 可以在几秒钟内启动一个干净的数据库实例,用完即删,不会污染本地环境。
这篇文章将详细介绍如何使用 Docker 启动 PostgreSQL 和 MySQL,包括各种配置选项、数据持久化、连接方式等。
PostgreSQL
快速启动
最简单的方式,一行命令启动:
docker run -d \
--name postgres-test \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
postgres:15-alpine
参数说明:
| 参数 | 说明 |
|---|---|
-d |
后台运行 |
--name postgres-test |
容器名称 |
-e POSTGRES_PASSWORD=postgres |
设置密码(必填) |
-p 5432:5432 |
端口映射 |
postgres:15-alpine |
使用 Alpine 版本(更小) |
完整配置启动
docker run -d \
--name postgres-dev \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=mypassword \
-e POSTGRES_DB=mydb \
-e PGDATA=/var/lib/postgresql/data/pgdata \
-e TZ=Asia/Shanghai \
-p 5432:5432 \
-v postgres_data:/var/lib/postgresql/data \
--restart unless-stopped \
postgres:15-alpine
环境变量说明
| 环境变量 | 说明 | 默认值 |
|---|---|---|
POSTGRES_USER |
超级用户名 | postgres |
POSTGRES_PASSWORD |
超级用户密码 | 必填 |
POSTGRES_DB |
默认创建的数据库 | 与用户名相同 |
PGDATA |
数据目录 | /var/lib/postgresql/data |
POSTGRES_INITDB_ARGS |
initdb 额外参数 | - |
POSTGRES_HOST_AUTH_METHOD |
认证方式 | scram-sha-256 |
连接数据库
方式一:使用 psql 命令行
# 进入容器内使用 psql
docker exec -it postgres-dev psql -U myuser -d mydb
# 在容器内执行 SQL
docker exec -it postgres-dev psql -U myuser -d mydb -c "SELECT version();"
方式二:从宿主机连接
首先安装 psql 客户端:
# macOS
brew install libpq
brew link --force libpq
# Ubuntu
sudo apt-get install postgresql-client
# CentOS
sudo yum install postgresql
然后连接:
psql -h localhost -p 5432 -U myuser -d mydb
# 输入密码:mypassword
方式三:使用连接字符串
# 标准格式
psql "postgresql://myuser:mypassword@localhost:5432/mydb"
# 带 SSL(本地测试通常不需要)
psql "postgresql://myuser:mypassword@localhost:5432/mydb?sslmode=disable"
方式四:代码连接示例
Go:
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
func main() {
// 连接字符串
connStr := "host=localhost port=5432 user=myuser password=mypassword dbname=mydb sslmode=disable"
// 或使用 URL 格式
// connStr := "postgresql://myuser:mypassword@localhost:5432/mydb?sslmode=disable"
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 测试连接
err = db.Ping()
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to PostgreSQL!")
// 查询版本
var version string
err = db.QueryRow("SELECT version()").Scan(&version)
if err != nil {
log.Fatal(err)
}
fmt.Println("Version:", version)
}
Python:
import psycopg2
# 连接参数
conn = psycopg2.connect(
host="localhost",
port=5432,
database="mydb",
user="myuser",
password="mypassword"
)
# 或使用连接字符串
# conn = psycopg2.connect("postgresql://myuser:mypassword@localhost:5432/mydb")
cursor = conn.cursor()
cursor.execute("SELECT version()")
print(cursor.fetchone())
cursor.close()
conn.close()
Node.js:
const { Pool } = require('pg');
const pool = new Pool({
host: 'localhost',
port: 5432,
database: 'mydb',
user: 'myuser',
password: 'mypassword',
});
pool.query('SELECT version()', (err, res) => {
console.log(res.rows[0]);
pool.end();
});
初始化脚本
可以在启动时自动执行 SQL 脚本:
# 创建初始化脚本目录
mkdir -p ./init-scripts
# 创建初始化 SQL
cat > ./init-scripts/01-init.sql << 'EOF'
-- 创建额外的数据库
CREATE DATABASE testdb;
-- 创建用户
CREATE USER testuser WITH PASSWORD 'testpass';
-- 授权
GRANT ALL PRIVILEGES ON DATABASE testdb TO testuser;
-- 连接到 testdb 创建表
\c testdb
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users (name, email) VALUES
('Alice', '[email protected]'),
('Bob', '[email protected]');
EOF
# 启动并挂载初始化脚本
docker run -d \
--name postgres-init \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
-v $(pwd)/init-scripts:/docker-entrypoint-initdb.d \
postgres:15-alpine
💡
/docker-entrypoint-initdb.d目录下的.sql和.sh文件会按字母顺序自动执行。
数据持久化
使用命名数据卷(推荐)
# 创建数据卷
docker volume create pg_data
# 启动并使用数据卷
docker run -d \
--name postgres-persist \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
-v pg_data:/var/lib/postgresql/data \
postgres:15-alpine
使用本地目录
# 创建本地目录
mkdir -p ./pgdata
# 启动(注意权限问题)
docker run -d \
--name postgres-local \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
-v $(pwd)/pgdata:/var/lib/postgresql/data \
postgres:15-alpine
备份与恢复
# 备份整个数据库
docker exec postgres-dev pg_dump -U myuser mydb > backup.sql
# 备份为压缩格式
docker exec postgres-dev pg_dump -U myuser -Fc mydb > backup.dump
# 恢复
docker exec -i postgres-dev psql -U myuser mydb < backup.sql
# 恢复压缩格式
docker exec -i postgres-dev pg_restore -U myuser -d mydb < backup.dump
查看日志
# 查看实时日志
docker logs -f postgres-dev
# 查看最近 100 行
docker logs --tail 100 postgres-dev
MySQL
快速启动
docker run -d \
--name mysql-test \
-e MYSQL_ROOT_PASSWORD=root123 \
-p 3306:3306 \
mysql:8
完整配置启动
docker run -d \
--name mysql-dev \
-e MYSQL_ROOT_PASSWORD=rootpassword \
-e MYSQL_DATABASE=mydb \
-e MYSQL_USER=myuser \
-e MYSQL_PASSWORD=mypassword \
-e TZ=Asia/Shanghai \
-p 3306:3306 \
-v mysql_data:/var/lib/mysql \
-v $(pwd)/mysql-conf:/etc/mysql/conf.d \
--restart unless-stopped \
mysql:8
环境变量说明
| 环境变量 | 说明 | 默认值 |
|---|---|---|
MYSQL_ROOT_PASSWORD |
root 密码 | 必填 |
MYSQL_DATABASE |
默认创建的数据库 | - |
MYSQL_USER |
创建的普通用户 | - |
MYSQL_PASSWORD |
普通用户密码 | - |
MYSQL_ALLOW_EMPTY_PASSWORD |
允许空密码 | no |
MYSQL_RANDOM_ROOT_PASSWORD |
随机 root 密码 | no |
自定义配置
创建配置文件:
mkdir -p ./mysql-conf
cat > ./mysql-conf/my.cnf << 'EOF'
[mysqld]
# 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
# 时区
default-time-zone='+08:00'
# 性能配置
max_connections=200
innodb_buffer_pool_size=256M
# 日志
slow_query_log=1
slow_query_log_file=/var/lib/mysql/slow.log
long_query_time=2
# 允许大数据包
max_allowed_packet=64M
[client]
default-character-set=utf8mb4
EOF
连接数据库
方式一:使用 mysql 命令行
# 进入容器内使用 mysql
docker exec -it mysql-dev mysql -u myuser -pmypassword mydb
# 使用 root 用户
docker exec -it mysql-dev mysql -u root -prootpassword
# 执行单条 SQL
docker exec -it mysql-dev mysql -u myuser -pmypassword -e "SELECT version();"
方式二:从宿主机连接
安装 MySQL 客户端:
# macOS
brew install mysql-client
echo 'export PATH="/opt/homebrew/opt/mysql-client/bin:$PATH"' >> ~/.zshrc
# Ubuntu
sudo apt-get install mysql-client
# CentOS
sudo yum install mysql
连接:
mysql -h 127.0.0.1 -P 3306 -u myuser -pmypassword mydb
# 或者交互式输入密码
mysql -h 127.0.0.1 -P 3306 -u myuser -p mydb
方式三:代码连接示例
Go:
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// DSN 格式: user:password@tcp(host:port)/dbname?params
dsn := "myuser:mypassword@tcp(localhost:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 配置连接池
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(time.Hour)
// 测试连接
err = db.Ping()
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MySQL!")
// 查询版本
var version string
err = db.QueryRow("SELECT version()").Scan(&version)
if err != nil {
log.Fatal(err)
}
fmt.Println("Version:", version)
}
Python:
import pymysql
# 连接参数
conn = pymysql.connect(
host='localhost',
port=3306,
user='myuser',
password='mypassword',
database='mydb',
charset='utf8mb4'
)
cursor = conn.cursor()
cursor.execute("SELECT version()")
print(cursor.fetchone())
cursor.close()
conn.close()
Node.js:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: 'localhost',
port: 3306,
user: 'myuser',
password: 'mypassword',
database: 'mydb'
});
connection.query('SELECT version() as version', (err, results) => {
console.log(results[0].version);
connection.end();
});
初始化脚本
mkdir -p ./init-scripts
cat > ./init-scripts/01-init.sql << 'EOF'
-- 创建数据库
CREATE DATABASE IF NOT EXISTS testdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建用户并授权
CREATE USER IF NOT EXISTS 'testuser'@'%' IDENTIFIED BY 'testpass';
GRANT ALL PRIVILEGES ON testdb.* TO 'testuser'@'%';
FLUSH PRIVILEGES;
-- 使用数据库
USE testdb;
-- 创建表
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 插入数据
INSERT INTO users (name, email) VALUES
('Alice', '[email protected]'),
('Bob', '[email protected]');
EOF
# 启动
docker run -d \
--name mysql-init \
-e MYSQL_ROOT_PASSWORD=root123 \
-p 3306:3306 \
-v $(pwd)/init-scripts:/docker-entrypoint-initdb.d \
mysql:8
备份与恢复
# 备份单个数据库
docker exec mysql-dev mysqldump -u root -prootpassword mydb > backup.sql
# 备份所有数据库
docker exec mysql-dev mysqldump -u root -prootpassword --all-databases > all_backup.sql
# 恢复
docker exec -i mysql-dev mysql -u root -prootpassword mydb < backup.sql
MySQL 8.0 认证问题
如果遇到 Authentication plugin 'caching_sha2_password' cannot be loaded 错误:
# 方式一:修改用户认证方式
docker exec -it mysql-dev mysql -u root -p
ALTER USER 'myuser'@'%' IDENTIFIED WITH mysql_native_password BY 'mypassword';
FLUSH PRIVILEGES;
# 方式二:启动时使用旧认证
docker run -d \
--name mysql-legacy \
-e MYSQL_ROOT_PASSWORD=root123 \
-p 3306:3306 \
mysql:8 \
--default-authentication-plugin=mysql_native_password
docker-compose 多数据库环境
创建一个包含 PostgreSQL 和 MySQL 的开发环境:
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: dev-postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: devdb
TZ: Asia/Shanghai
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init/postgres:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
mysql:
image: mysql:8
container_name: dev-mysql
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: devdb
MYSQL_USER: devuser
MYSQL_PASSWORD: devpass
TZ: Asia/Shanghai
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./init/mysql:/docker-entrypoint-initdb.d
- ./mysql-conf:/etc/mysql/conf.d
command: --default-authentication-plugin=mysql_native_password
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# 可选:数据库管理界面
adminer:
image: adminer
container_name: dev-adminer
ports:
- "8080:8080"
depends_on:
- postgres
- mysql
volumes:
postgres_data:
mysql_data:
使用:
# 启动所有服务
docker-compose up -d
# 查看状态
docker-compose ps
# 查看日志
docker-compose logs -f postgres
docker-compose logs -f mysql
# 停止
docker-compose down
# 停止并删除数据
docker-compose down -v
访问 Adminer(数据库管理界面):http://localhost:8080
常用场景
场景一:单元测试数据库
每次测试前启动干净的数据库,测试后删除:
#!/bin/bash
# test-db.sh
# 启动测试数据库
docker run -d --rm \
--name test-postgres \
-e POSTGRES_PASSWORD=test \
-e POSTGRES_DB=testdb \
-p 15432:5432 \
postgres:15-alpine
# 等待数据库就绪
echo "Waiting for database..."
sleep 3
until docker exec test-postgres pg_isready -U postgres; do
sleep 1
done
# 运行测试
echo "Running tests..."
DATABASE_URL="postgresql://postgres:test@localhost:15432/testdb?sslmode=disable" go test ./...
# 停止数据库(--rm 会自动删除)
docker stop test-postgres
场景二:模拟生产数据
从生产环境导出数据,在本地测试:
# 1. 从生产导出(在生产服务器执行)
pg_dump -h prod-db -U app_user -Fc app_db > prod_backup.dump
# 2. 在本地启动数据库
docker run -d \
--name local-prod-replica \
-e POSTGRES_PASSWORD=local123 \
-p 5432:5432 \
postgres:15-alpine
# 3. 恢复数据
docker cp prod_backup.dump local-prod-replica:/tmp/
docker exec local-prod-replica pg_restore -U postgres -d postgres -C /tmp/prod_backup.dump
场景三:多版本测试
同时运行不同版本的数据库:
# PostgreSQL 13
docker run -d --name pg13 -e POSTGRES_PASSWORD=test -p 5413:5432 postgres:13-alpine
# PostgreSQL 14
docker run -d --name pg14 -e POSTGRES_PASSWORD=test -p 5414:5432 postgres:14-alpine
# PostgreSQL 15
docker run -d --name pg15 -e POSTGRES_PASSWORD=test -p 5415:5432 postgres:15-alpine
# MySQL 5.7
docker run -d --name mysql57 -e MYSQL_ROOT_PASSWORD=test -p 3357:3306 mysql:5.7
# MySQL 8.0
docker run -d --name mysql80 -e MYSQL_ROOT_PASSWORD=test -p 3380:3306 mysql:8
连接字符串速查
PostgreSQL
# 标准格式
postgresql://user:password@host:port/database?sslmode=disable
# 示例
postgresql://postgres:postgres@localhost:5432/mydb?sslmode=disable
# Go (lib/pq)
host=localhost port=5432 user=postgres password=postgres dbname=mydb sslmode=disable
# Go (pgx)
postgres://postgres:postgres@localhost:5432/mydb?sslmode=disable
MySQL
# 标准格式
user:password@tcp(host:port)/database?charset=utf8mb4&parseTime=True
# 示例
myuser:mypassword@tcp(localhost:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local
# JDBC 格式
jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai
常见问题
Q1: 容器启动后立即退出
原因:可能是配置错误或端口冲突。
排查:
# 查看日志
docker logs postgres-test
# 检查端口占用
lsof -i :5432
Q2: 权限不足无法写入数据
原因:绑定挂载的目录权限问题。
解决:
# 使用命名数据卷代替绑定挂载
docker volume create my_data
docker run -v my_data:/var/lib/postgresql/data ...
# 或修改目录权限
sudo chown -R 999:999 ./pgdata # PostgreSQL
sudo chown -R 999:999 ./mysql_data # MySQL
Q3: 中文乱码
PostgreSQL:
docker run -e POSTGRES_INITDB_ARGS="--encoding=UTF8" ...
MySQL:
docker run ... mysql:8 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
Q4: 时区不对
# PostgreSQL
docker exec -it postgres-dev psql -U postgres -c "SET timezone = 'Asia/Shanghai'; SHOW timezone;"
# MySQL
docker exec -it mysql-dev mysql -u root -p -e "SET GLOBAL time_zone = '+08:00'; SELECT NOW();"
总结
| 数据库 | 镜像 | 默认端口 | 数据目录 |
|---|---|---|---|
| PostgreSQL | postgres:15-alpine |
5432 | /var/lib/postgresql/data |
| MySQL | mysql:8 |
3306 | /var/lib/mysql |
使用 Docker 启动测试数据库的核心命令:
# PostgreSQL
docker run -d --name pg -e POSTGRES_PASSWORD=pass -p 5432:5432 postgres:15-alpine
# MySQL
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 mysql:8
参考资料
版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:《 Docker 系列——测试数据库部署 》
本文链接:http://localhost:3015/ai/Docker%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%E5%BA%93%E9%83%A8%E7%BD%B2.html
本文最后一次更新为 天前,文章中的某些内容可能已过时!
目录
- 前言
- PostgreSQL
- 快速启动
- 完整配置启动
- 环境变量说明
- 连接数据库
- 方式一:使用 psql 命令行
- 方式二:从宿主机连接
- 方式三:使用连接字符串
- 方式四:代码连接示例
- 初始化脚本
- 数据持久化
- 使用命名数据卷(推荐)
- 使用本地目录
- 备份与恢复
- 查看日志
- MySQL
- 快速启动
- 完整配置启动
- 环境变量说明
- 自定义配置
- 连接数据库
- 方式一:使用 mysql 命令行
- 方式二:从宿主机连接
- 方式三:代码连接示例
- 初始化脚本
- 备份与恢复
- MySQL 8.0 认证问题
- docker-compose 多数据库环境
- 常用场景
- 场景一:单元测试数据库
- 场景二:模拟生产数据
- 场景三:多版本测试
- 连接字符串速查
- PostgreSQL
- MySQL
- 常见问题
- Q1: 容器启动后立即退出
- Q2: 权限不足无法写入数据
- Q3: 中文乱码
- Q4: 时区不对
- 总结
- 参考资料