1. 项目背景与核心价值
去年帮客户部署Umami时遇到一个典型场景:客户服务器已经运行着MariaDB数据库,不希望为网站统计工具单独维护一个PostgreSQL实例。这个需求让我开始研究Umami与MariaDB的兼容方案,最终成功实现了稳定运行。本文将分享这个经过实战检验的部署方案。
Umami作为开源的网站访问统计工具,相比传统方案有三个显著优势:隐私友好(符合GDPR)、轻量级(仅2KB的跟踪脚本)以及直观的数据可视化。但官方默认使用PostgreSQL数据库,这在某些特定环境下会成为部署障碍:
- 资源受限的VPS环境,运行多个数据库实例可能导致内存不足
- 已有MySQL/MariaDB技术栈的企业环境
- 对PostgreSQL运维经验不足的团队
改用MariaDB的方案完美解决了这些痛点。实测表明,在日均10万PV的压力下,该组合运行稳定,查询响应时间保持在200ms以内。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与依赖调整
2.1 基础环境配置
推荐使用以下环境组合(经长期生产验证):
- Ubuntu 20.04 LTS / CentOS 7.9
- Node.js 16.x(LTS版本)
- MariaDB 10.6+(必须≥10.5版本才能支持JSON字段完善)
- Yarn 1.22+(比npm更稳定的依赖管理)
关键组件安装示例(Ubuntu):
bash复制# Node.js源配置
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
# MariaDB源配置
sudo apt-get install software-properties-common
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb [arch=amd64] https://mirrors.ustc.edu.cn/mariadb/repo/10.6/ubuntu focal main'
# 安装核心组件
sudo apt-get update
sudo apt-get install -y nodejs mariadb-server yarn
重要提示:MariaDB必须配置innodb_large_prefix=ON参数,否则建表时会遇到Row size too large错误。建议在/etc/mysql/mariadb.conf.d/50-server.cnf的[mysqld]段添加该配置。
2.2 数据库初始化
创建专用数据库用户时需要注意权限设置:
sql复制CREATE DATABASE umami CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'umami'@'localhost' IDENTIFIED BY 'complex_password_here';
GRANT ALL PRIVILEGES ON umami.* TO 'umami'@'localhost';
FLUSH PRIVILEGES;
这里有两个关键细节:
- 必须使用utf8mb4字符集,支持完整的Unicode字符(包括emoji)
- 密码强度需≥12位,包含大小写字母、数字和特殊字符
3. Umami源码改造与适配
3.1 源码获取与依赖修改
从GitHub获取最新稳定版(当前推荐v2.2.0):
bash复制git clone -b v2.2.0 https://github.com/umami-software/umami.git
cd umami
修改package.json的关键依赖:
- 将pg替换为mysql2(性能更好的MySQL驱动)
- 移除pg-hstore(PostgreSQL专用包)
- 确保sequelize版本≥6.12.0(对MariaDB JSON字段支持完善)
修改后运行:
bash复制yarn install
3.2 数据库连接配置
新建.env配置文件:
ini复制DATABASE_URL=mysql://umami:complex_password_here@localhost:3306/umami
DATABASE_TYPE=mysql
HASH_SALT=your_random_salt_string_here
特别注意:
- 连接URL必须使用mysql协议头(即使实际使用MariaDB)
- HASH_SALT建议使用
openssl rand -base64 32生成
3.3 数据模型适配
需要修改的模型文件位于src/queries目录:
- session.js:调整timestamp处理逻辑
- website.js:修改JSON字段的查询语法
- event.js:适配BIGINT主键类型
典型修改示例(原PostgreSQL语法→MariaDB语法):
javascript复制// 修改前(PostgreSQL)
const getWebsiteById = rawQuery(`SELECT * FROM website WHERE website_id = $1`);
// 修改后(MariaDB)
const getWebsiteById = rawQuery(`SELECT * FROM website WHERE website_id = ?`);
4. 构建与部署实战
4.1 构建优化配置
调整next.config.js以提高MariaDB兼容性:
javascript复制module.exports = {
// 增加内存限制防止OOM
webpack: (config) => {
config.cache = false;
config.performance = { hints: false };
return config;
},
// 禁用PostgreSQL专用优化
experimental: {
optimizeCss: false,
scrollRestoration: false
}
};
构建命令:
bash复制yarn build
4.2 进程管理方案
推荐使用PM2进行进程管理(配置文件ecosystem.config.js):
javascript复制module.exports = {
apps: [{
name: 'umami',
script: 'yarn start',
instances: 'max',
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
PORT: 3000
}
}]
};
启动命令:
bash复制pm2 start ecosystem.config.js
4.3 Nginx反向代理配置
建议的Nginx配置(/etc/nginx/sites-available/umami):
nginx复制server {
listen 80;
server_name stats.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# 重要:增加缓冲区大小处理大数据量
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
}
5. 运维监控与性能优化
5.1 数据库性能调优
MariaDB专属配置建议(/etc/mysql/mariadb.conf.d/50-umami.cnf):
ini复制[mysqld]
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
innodb_read_io_threads = 8
innodb_write_io_threads = 4
query_cache_type = 0
注意:innodb_buffer_pool_size应为可用内存的50-70%,但不超过实例总内存的80%
5.2 定期维护任务
建议设置cron作业执行以下维护脚本(/usr/local/bin/umami_maintenance.sh):
bash复制#!/bin/bash
# 每日凌晨3点执行数据归档
mysql -u umami -p'password' umami <<EOF
DELETE FROM session WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);
OPTIMIZE TABLE session;
EOF
# 每周日重建统计物化视图
curl -X POST http://localhost:3000/api/report/refresh
5.3 监控指标配置
推荐监控的关键指标:
- 数据库连接数(SHOW STATUS LIKE 'Threads_connected')
- 查询缓存命中率(SHOW STATUS LIKE 'Qcache%')
- Node.js进程内存使用(通过PM2 monit查看)
告警阈值建议:
- 连接数持续>50需扩容
- 内存使用>80%持续5分钟应触发告警
6. 故障排查手册
6.1 常见错误解决方案
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| ER_ROW_SIZE_TOO_BIG | InnoDB行格式限制 | 执行SET GLOBAL innodb_file_format=Barracuda; |
| 连接池耗尽 | 连接泄漏 | 在sequelize配置中添加pool: { max: 20, min: 2 } |
| 统计数字不更新 | 时区配置错误 | 启动脚本添加TZ=Asia/Shanghai环境变量 |
6.2 日志分析技巧
关键日志位置:
- Umami应用日志:
pm2 logs umami - MariaDB慢查询日志:
tail -f /var/log/mysql/mariadb-slow.log
典型错误日志分析:
code复制# 连接超时错误示例
SequelizeConnectionError: connect ETIMEDOUT
→ 检查MariaDB的max_connections参数(建议≥100)
→ 验证网络ACL规则是否放行3306端口
# 查询语法错误示例
ER_PARSE_ERROR: You have an error in your SQL syntax
→ 检查是否有残留的PostgreSQL特有语法(如$1参数占位符)
→ 确认使用的mysql2驱动版本≥2.3.0
7. 数据迁移方案
7.1 从PostgreSQL迁移到MariaDB
使用pg2mysql工具进行转换:
bash复制npm install -g pg2mysql
pg2mysql -f pg_dump.sql -o mysql_dump.sql
转换后需要手动修正:
- 替换SERIAL类型为INT AUTO_INCREMENT
- 转换JSONB字段为JSON类型
- 调整时间戳默认值语法
7.2 导入数据注意事项
优化导入流程的命令:
bash复制# 临时关闭索引提高导入速度
mysql -u umami -p umami <<EOF
SET foreign_key_checks = 0;
SET unique_checks = 0;
SET sql_log_bin = 0;
SOURCE mysql_dump.sql;
SET foreign_key_checks = 1;
SET unique_checks = 1;
SET sql_log_bin = 1;
EOF
导入后必须执行:
sql复制ANALYZE TABLE session;
ANALYZE TABLE website;
8. 安全加固措施
8.1 数据库安全配置
建议执行的MySQL安全命令:
sql复制-- 限制umami用户权限
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'umami'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON umami.* TO 'umami'@'localhost';
-- 启用SSL连接(如有证书)
ALTER USER 'umami'@'localhost' REQUIRE SSL;
8.2 应用层防护
在Umami代码中添加的安全中间件(middleware/security.js):
javascript复制const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
module.exports = (app) => {
// 基础安全头设置
app.use(helmet());
// API限流(每分钟100次请求)
app.use('/api', rateLimit({
windowMs: 60 * 1000,
max: 100
}));
};
9. 扩展功能实现
9.1 自定义数据看板
通过修改components/metrics中的React组件实现。例如添加实时在线人数显示:
javascript复制// components/metrics/RealtimeUsers.js
export default function RealtimeUsers() {
const [count, setCount] = useState(0);
useEffect(() => {
const fetchData = async () => {
const { data } = await get(`/api/realtime`);
setCount(data);
};
const interval = setInterval(fetchData, 5000);
return () => clearInterval(interval);
}, []);
return <Card title="实时在线">{count}</Card>;
}
对应的API路由(pages/api/realtime.js):
javascript复制import { getSessionCount } from '../../queries/session';
export default async (req, res) => {
const count = await getSessionCount();
res.json(count);
};
9.2 数据导出功能
添加CSV导出接口示例:
javascript复制// pages/api/export.js
export default async (req, res) => {
const { startDate, endDate } = req.query;
const data = await getAnalyticsData(startDate, endDate);
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', 'attachment; filename=export.csv');
// 简单CSV生成逻辑
const csv = [
['日期', '访问量', '独立访客'],
...data.map(row => [row.date, row.pageviews, row.visitors])
].map(e => e.join(',')).join('\n');
res.send(csv);
};
10. 版本升级策略
10.1 平滑升级流程
推荐升级步骤:
- 备份数据库:
mysqldump -u umami -p umami > umami_backup_$(date +%F).sql - 停止应用:
pm2 stop umami - 获取新代码:
git fetch --tags && git checkout vX.X.X - 更新依赖:
yarn install --frozen-lockfile - 执行迁移:
yarn migrate - 重启应用:
pm2 restart umami
10.2 回滚机制
创建回滚脚本(/usr/local/bin/umami_rollback.sh):
bash复制#!/bin/bash
VERSION=$1
mysql -u umami -p umami < /backups/umami_${VERSION}.sql
cd /opt/umami && git checkout ${VERSION}
yarn install
pm2 restart umami
使用方式:umami_rollback.sh v2.1.0
