一、概述
1.1 背景介绍
在数据库选型上,始终存在着关系型与非关系型的技术路线之争。支持者各有论据:一方强调ACID事务与数据完整性不可或缺;另一方则推崇NoSQL的灵活架构与水平扩展能力。然而,脱离具体的业务场景来讨论技术优劣,往往难以得出有意义的结论。
在实际的生产环境运维中,笔者曾同时管理过上百个MySQL实例与数十个MongoDB集群,目睹过不少因选型失误引发的线上故障。例如,有电商公司为追求NoSQL的灵活性,在订单系统中贸然使用MongoDB,导致数据一致性问题频发;也有内容平台固守MySQL,在面对海量非结构化数据时查询性能急剧下滑,最终不得不经历痛苦的数据库迁移。
本文将从生产环境实战的角度出发,系统性地剖析MySQL与MongoDB各自的技术特性、适用场景、性能表现及运维成本,旨在为架构师和技术负责人提供贴合业务实际的选型决策依据。
1.2 技术特点
MySQL核心特性:
- ACID事务保证:InnoDB存储引擎提供完整的事务支持,通过MVCC机制实现高并发下的数据一致性。
- 成熟的SQL生态:采用标准SQL语法,拥有丰富的查询优化器与完善的索引体系(如B+Tree索引、全文索引、空间索引)。
- 严格的Schema约束:表结构需预定义,字段类型有强校验,并通过外键约束确保数据完整性。
- 主从复制架构:基于Binlog的异步或半同步复制,支持多种高可用方案(如MHA、MGR、Orchestrator)。
- 成熟的运维工具链:拥有完善的备份恢复(Xtrabackup、mysqldump)、监控(PMM、Percona Toolkit)及审计工具。
MongoDB核心特性:
- 灵活的文档模型:无需固定Schema,支持嵌套文档和数组,字段可动态增减。
- 原生分片能力:支持基于Range或Hash的自动数据分片,可水平扩展到数百个节点。
- 丰富的查询语言:提供聚合管道框架以支持复杂数据处理,并具备地理空间查询、文本搜索等能力。
- 副本集架构:基于Raft协议实现自动故障转移,可灵活配置写关注(Write Concern)和读偏好(Read Preference)。
- 高写入性能:WiredTiger存储引擎采用文档级并发控制,其写入性能通常优于传统的行级锁。
1.3 适用场景
MySQL典型场景:
- 金融交易系统:适用于订单、支付、账务等对强一致性有苛刻要求的场景。例如,某证券公司核心交易系统日均处理5000万笔交易,采用MySQL MGR三节点同步复制,实现了RPO=0且RTO小于30秒的高可用目标。
- ERP和CRM系统:适合业务逻辑复杂、多表关联查询频繁的系统,其外键约束和触发器能有效支持复杂的业务规则校验。
- 数据分析和BI:当需要进行复杂的SQL查询、多表Join和聚合统计时,MySQL是可靠的选择。
- 传统Web应用:用户系统、内容管理、论坛博客等表结构相对稳定、查询模式明确的结构化数据场景。
MongoDB典型场景:
- 内容管理系统:适合存储文章、评论、多媒体资源等字段差异大的非结构化数据。例如,某新闻门户存储了1.5亿篇文章,每篇文章的字段组成可能不同,MongoDB的灵活Schema避免了在关系型数据库中产生大量NULL字段的问题。
- 物联网数据采集:适用于传感器上报的时序数据,这类场景写入量巨大,查询多以时间范围和设备ID为主。
- 用户行为日志:点击流、埋点数据等半结构化日志,需要快速写入和灵活查询。
- 实时推荐系统:用户画像、物品特征等高维稀疏数据,需要支持复杂的数组和嵌套查询。
- 游戏后台:玩家数据、背包物品、技能树等结构多变且需要高并发读写的场景。
1.4 环境要求
| MySQL生产环境配置: |
组件 |
版本要求 |
说明 |
| 操作系统 |
CentOS 7+/Ubuntu 20.04+ |
建议使用LTS版本,并进行内核参数调优(如 vm.swappiness=1) |
| MySQL版本 |
5.7.30+/8.0.20+ |
8.0版本性能有显著提升,并支持窗口函数、CTE等新特性 |
| 存储引擎 |
InnoDB |
唯一推荐用于生产环境的事务引擎 |
| 硬件配置 |
16C32G起步 |
核心库建议32C64G,配备NVMe SSD和万兆网卡 |
| 内存配置 |
innodb_buffer_pool_size=物理内存70% |
预留30%内存给操作系统及其他进程 |
| 连接数 |
max_connections=2000 |
根据业务并发调整,建议使用连接池管理 |
| MongoDB生产环境配置: |
组件 |
版本要求 |
说明 |
| 操作系统 |
CentOS 7+/Ubuntu 20.04+ |
需禁用THP,并调整ulimit(如 nofile=64000) |
| MongoDB版本 |
4.4+/5.0+ |
5.0版本对时序集合进行了性能优化 |
| 存储引擎 |
WiredTiger |
默认引擎,提供文档级并发控制 |
| 硬件配置 |
16C32G起步 |
分片集群单节点建议32C128G,配备NVMe SSD |
| 内存配置 |
WiredTiger cache=物理内存50% |
MongoDB会利用文件系统缓存,无需配置过大 |
| 副本集 |
3节点起步 |
生产环境必须部署副本集,建议采用PSA架构 |
二、详细步骤
2.1 准备工作
◆ 2.1.1 系统环境检查
MySQL系统检查:
# 检查系统版本
cat /etc/os-release
# 检查内存和磁盘
free -h
df -h /data/mysql
# 检查内核参数
sysctl -a | grep -E "vm.swappiness|vm.dirty"
# 检查是否安装了numactl
numactl --hardware
# 检查磁盘IO性能
fio --filename=/data/mysql/test --direct=1 --rw=randwrite --bs=16k --size=10G --numjobs=4 --runtime=60 --group_reporting --name=mysql-test
MongoDB系统检查:
# 检查THP状态(必须禁用)
cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defrag
# 检查ulimit设置
ulimit -a
# 检查文件系统(建议XFS)
mount | grep /data/mongodb
# 检查网络延迟(副本集节点间)
ping -c 10 mongo-replica-02
# 检查磁盘随机写IOPS
fio --filename=/data/mongodb/test --direct=1 --rw=randwrite --bs=4k --size=10G --numjobs=8 --runtime=60 --group_reporting --name=mongodb-test
◆ 2.1.2 安装部署
MySQL 8.0安装:
# 添加官方Yum仓库
wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
sudo rpm -ivh mysql80-community-release-el7-3.noarch.rpm
# 安装MySQL Server
sudo yum install -y mysql-community-server
# 启动并获取临时密码
sudo systemctl start mysqld
sudo grep 'temporary password' /var/log/mysqld.log
# 修改root密码并初始化
mysql -uroot -p
ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPassword123!';
FLUSH PRIVILEGES;
MongoDB 5.0副本集部署:
# 添加MongoDB Yum仓库
cat > /etc/yum.repos.d/mongodb-org-5.0.repo <<EOF
[mongodb-org-5.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/7/mongodb-org/5.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-5.0.asc
EOF
# 安装MongoDB
sudo yum install -y mongodb-org
# 创建数据和日志目录
sudo mkdir -p /data/mongodb/{data,logs}
sudo chown -R mongod:mongod /data/mongodb
# 配置文件(三个节点配置相同,只改bind_ip和replSetName)
sudo tee /etc/mongod.conf > /dev/null <<EOF
systemLog:
destination: file
path: /data/mongodb/logs/mongod.log
logAppend: true
storage:
dbPath: /data/mongodb/data
engine: wiredTiger
wiredTiger:
engineConfig:
cacheSizeGB: 32
net:
port: 27017
bindIp: 0.0.0.0
replication:
replSetName: rs0
processManagement:
fork: true
pidFilePath: /var/run/mongodb/mongod.pid
EOF
# 启动MongoDB
sudo systemctl start mongod
sudo systemctl enable mongod
2.2 核心配置
◆ 2.2.1 MySQL性能优化配置
# /etc/my.cnf - MySQL 8.0生产环境配置
[mysqld]
# 基础配置
server-id = 1
port = 3306
datadir = /data/mysql
socket = /tmp/mysql.sock
pid-file = /var/run/mysqld/mysqld.pid
# 字符集配置
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# InnoDB核心配置
innodb_buffer_pool_size = 48G # 物理内存64G的75%
innodb_buffer_pool_instances = 16 # buffer pool实例数,建议每个8G
innodb_log_file_size = 2G # redo log大小,写入密集型可设置4G
innodb_log_buffer_size = 64M # log buffer
innodb_flush_log_at_trx_commit = 1 # 事务日志刷盘策略,1最安全
innodb_flush_method = O_DIRECT # 绕过OS缓存,避免双重缓存
innodb_io_capacity = 4000 # SSD的IOPS值
innodb_io_capacity_max = 8000 # 刷脏页时的最大IOPS
innodb_read_io_threads = 8 # 读IO线程
innodb_write_io_threads = 8 # 写IO线程
# 并发和连接配置
max_connections = 2000 # 最大连接数
max_connect_errors = 1000000 # 避免连接错误导致的阻塞
thread_cache_size = 100 # 线程缓存
table_open_cache = 4096 # 表缓存
table_open_cache_instances = 16 # 表缓存实例数,减少锁竞争
# Binlog配置(主从复制)
log-bin = /data/mysql/binlog/mysql-bin
binlog_format = ROW # 行级别复制,最安全
binlog_row_image = MINIMAL # 只记录修改的列,减少binlog大小
sync_binlog = 1 # 每次事务提交同步binlog,防止数据丢失
expire_logs_days = 7 # binlog保留天数
max_binlog_size = 1G # 单个binlog文件大小
# 慢查询日志
slow_query_log = 1
slow_query_log_file = /data/mysql/logs/slow.log
long_query_time = 1 # 超过1秒记录慢查询
log_queries_not_using_indexes = 1 # 记录未使用索引的查询
# 其他优化
tmp_table_size = 256M # 内存临时表大小
max_heap_table_size = 256M
sort_buffer_size = 4M # 排序缓冲区
join_buffer_size = 4M # join缓冲区
read_rnd_buffer_size = 8M # 随机读缓冲区
参数说明:
innodb_buffer_pool_size:InnoDB最重要的参数,用于缓存数据和索引,建议设置为物理内存的70-80%。该值越大,生产环境中的磁盘IO越少。
innodb_flush_log_at_trx_commit:控制事务日志刷盘策略。1表示每次事务提交都刷盘(最安全,性能最低);2表示每次提交写入OS缓存,每秒刷盘一次(折中);0表示每秒写入并刷盘(性能最高,可能丢失1秒数据)。
innodb_flush_method:O_DIRECT绕过OS文件缓存,避免与innodb_buffer_pool双重缓存,减少内存浪费。
binlog_format:ROW格式记录每一行的变化,保证数据一致性;STATEMENT格式记录SQL语句,binlog更小但可能导致主从数据不一致。
sync_binlog:1表示每次事务提交同步binlog到磁盘,0表示依赖OS刷盘。金融等关键场景必须设置为1。
◆ 2.2.2 MongoDB副本集初始化
在三个节点都启动MongoDB后,在主节点初始化副本集:
// 连接到第一个节点
mongo --host mongo-01:27017
// 初始化副本集配置
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo-01:27017", priority: 2 },
{ _id: 1, host: "mongo-02:27017", priority: 1 },
{ _id: 2, host: "mongo-03:27017", arbiterOnly: true }
]
})
// 查看副本集状态
rs.status()
// 配置写关注级别(默认w:1)
db.adminCommand({
setDefaultRWConcern: 1,
defaultWriteConcern: { w: "majority", wtimeout: 5000 },
defaultReadConcern: { level: "majority" }
})
副本集架构说明:
- PSA架构(Primary-Secondary-Arbiter):一个主节点(Primary),一个从节点(Secondary),一个仲裁节点(Arbiter)。仲裁节点不存储数据,只参与选举,可节省硬件成本。
- priority权重:主节点priority设置为2,从节点为1,确保故障恢复后主节点优先当选。
- 写关注w:majority:写入到大多数节点才返回成功,保证数据不丢失。
w:1只写入主节点就返回,性能更高但可能丢数据。
◆ 2.2.3 MongoDB分片集群部署
大规模场景需要部署分片集群:
# 1. 部署Config Server副本集(3节点)
# config-01/02/03节点配置
sharding:
clusterRole: configsvr
replication:
replSetName: configReplSet
# 启动并初始化Config Server
rs.initiate({
_id: "configReplSet",
configsvr: true,
members: [
{ _id: 0, host: "config-01:27019" },
{ _id: 1, host: "config-02:27019" },
{ _id: 2, host: "config-03:27019" }
]
})
# 2. 部署多个Shard副本集(每个Shard 3节点)
# shard-01/02/03节点配置
sharding:
clusterRole: shardsvr
replication:
replSetName: shard01
# 3. 部署Mongos路由节点(多个,前端LB)
# mongos配置文件
sharding:
configDB: configReplSet/config-01:27019,config-02:27019,config-03:27019
# 启动mongos
mongos --config /etc/mongos.conf
# 4. 添加Shard到集群
mongo --host mongos-01:27017
sh.addShard("shard01/shard01-01:27018,shard01-02:27018,shard01-03:27018")
sh.addShard("shard02/shard02-01:27018,shard02-02:27018,shard02-03:27018")
# 5. 对集合启用分片
sh.enableSharding("mydb")
sh.shardCollection("mydb.users", { "user_id": "hashed" })
2.3 启动和验证
◆ 2.3.1 MySQL主从复制验证
# 主库创建复制用户
mysql -uroot -p
CREATE USER 'repl'@'%' IDENTIFIED BY 'ReplPassword123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
# 查看主库状态
SHOW MASTER STATUS;
# 记录File和Position
# 从库配置复制
CHANGE MASTER TO
MASTER_HOST='192.168.1.10',
MASTER_USER='repl',
MASTER_PASSWORD='ReplPassword123!',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154;
START SLAVE;
SHOW SLAVE STATUS\G
# 检查关键指标
# Slave_IO_Running: Yes
# Slave_SQL_Running: Yes
# Seconds_Behind_Master: 0
验证数据一致性:
# 主库插入测试数据
USE test;
CREATE TABLE repl_test (id INT PRIMARY KEY, data VARCHAR(100));
INSERT INTO repl_test VALUES (1, 'test data');
# 从库查询
USE test;
SELECT * FROM repl_test;
# 应该能查到数据
◆ 2.3.2 MongoDB副本集验证
// 连接到主节点
mongo --host mongo-01:27017
// 检查副本集状态
rs.status()
// 确认有一个PRIMARY和一个SECONDARY
// 测试写入
use test
db.test_collection.insertOne({ test: "data", timestamp: new Date() })
// 连接到从节点验证数据同步
mongo --host mongo-02:27017
rs.slaveOk() // 允许从从节点读取
use test
db.test_collection.find()
// 测试故障切换
// 在主节点执行
db.adminCommand({ shutdown: 1 })
// 等待30秒后检查,从节点应该自动升级为主节点
mongo --host mongo-02:27017
rs.status()
// 应该看到mongo-02变成PRIMARY
三、示例代码和配置
3.1 完整配置示例
◆ 3.1.1 MySQL MGR(Group Replication)配置
MySQL Group Replication提供了更高级的多主复制能力。
# /etc/my.cnf - MGR配置
[mysqld]
# MGR基础配置
plugin_load_add = 'group_replication.so'
transaction_write_set_extraction = XXHASH64
loose-group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
loose-group_replication_start_on_boot = off
loose-group_replication_local_address = "192.168.1.10:33061"
loose-group_replication_group_seeds = "192.168.1.10:33061,192.168.1.11:33061,192.168.1.12:33061"
loose-group_replication_bootstrap_group = off
# MGR单主模式
loose-group_replication_single_primary_mode = ON
loose-group_replication_enforce_update_everywhere_checks = OFF
# Binlog配置(MGR要求)
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_checksum = NONE
log_slave_updates = ON
binlog_format = ROW
master_info_repository = TABLE
relay_log_info_repository = TABLE
# 其他优化参数
innodb_buffer_pool_size = 48G
max_connections = 2000
初始化MGR集群:
-- 第一个节点执行
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
SELECT * FROM performance_schema.replication_group_members;
-- 第二、三个节点执行
START GROUP_REPLICATION;
◆ 3.1.2 MongoDB性能监控脚本
#!/usr/bin/env python3
# mongodb_monitor.py - MongoDB性能监控脚本
from pymongo import MongoClient
import time
import json
def get_mongo_stats(host, port):
"""获取MongoDB性能指标"""
client = MongoClient(host, port)
admin_db = client.admin
# 服务器状态
server_status = admin_db.command('serverStatus')
# 副本集状态
try:
rs_status = admin_db.command('replSetGetStatus')
except:
rs_status = None
stats = {
'timestamp': time.time(),
'connections': {
'current': server_status['connections']['current'],
'available': server_status['connections']['available'],
'utilization': round(server_status['connections']['current'] /
(server_status['connections']['current'] +
server_status['connections']['available']) * 100, 2)
},
'opcounters': server_status['opcounters'],
'memory': {
'resident_mb': server_status['mem']['resident'],
'virtual_mb': server_status['mem']['virtual'],
'mapped_mb': server_status['mem'].get('mapped', 0)
},
'wiredtiger': {
'cache_used_gb': round(server_status['wiredTiger']['cache']
['bytes currently in the cache'] / 1024**3, 2),
'cache_dirty_gb': round(server_status['wiredTiger']['cache']
['tracked dirty bytes in the cache'] / 1024**3, 2),
'cache_read_into': server_status['wiredTiger']['cache']
['pages read into cache'],
'cache_written_from': server_status['wiredTiger']['cache']
['pages written from cache']
},
'network': {
'bytes_in_mb': round(server_status['network']['bytesIn'] / 1024**2, 2),
'bytes_out_mb': round(server_status['network']['bytesOut'] / 1024**2, 2),
'requests': server_status['network']['numRequests']
}
}
# 副本集延迟
if rs_status:
primary_optime = None
secondary_lag = []
for member in rs_status['members']:
if member['state'] == 1: # PRIMARY
primary_optime = member['optimeDate']
elif member['state'] == 2: # SECONDARY
lag = (primary_optime - member['optimeDate']).total_seconds()
secondary_lag.append({
'host': member['name'],
'lag_seconds': lag
})
stats['replication_lag'] = secondary_lag
client.close()
return stats
def get_slow_queries(host, port, dbname):
"""获取慢查询"""
client = MongoClient(host, port)
db = client[dbname]
# 获取当前运行的慢查询(超过1秒)
current_ops = db.current_op({"secs_running": {"$gte": 1}})
slow_queries = []
for op in current_ops.get('inprog', []):
if op.get('op') == 'query':
slow_queries.append({
'opid': op['opid'],
'ns': op['ns'],
'duration_secs': op['secs_running'],
'query': op.get('query', {})
})
client.close()
return slow_queries
if __name__ == '__main__':
mongo_host = 'localhost'
mongo_port = 27017
while True:
stats = get_mongo_stats(mongo_host, mongo_port)
print(json.dumps(stats, indent=2, default=str))
slow_queries = get_slow_queries(mongo_host, mongo_port, 'admin')
if slow_queries:
print("慢查询告警:")
print(json.dumps(slow_queries, indent=2, default=str))
time.sleep(60) # 每分钟采集一次
3.2 实际应用案例
◆ 案例一:电商订单系统数据库选型
场景描述:某中型电商平台,日订单量50万,涉及订单、支付、库存、物流等多个子系统,需要保证强一致性和事务完整性。
技术决策:选择MySQL作为核心交易数据库。
架构设计:
- 主库:一台高配MySQL服务器(32C128G,NVMe SSD RAID10),处理所有写入和实时查询。
- 从库1:同机房从库,承担实时查询和报表查询,采用半同步复制。
- 从库2:异地从库,用于容灾,采用异步复制。
- 中间件:使用ProxySQL实现读写分离和连接池管理。
关键配置:
-- 订单表设计
CREATE TABLE `orders` (
`order_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
`order_no` VARCHAR(32) NOT NULL COMMENT '订单编号',
`total_amount` DECIMAL(10,2) NOT NULL COMMENT '订单金额',
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态:0待支付,1已支付,2已发货,3已完成,4已取消',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`order_id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user_id_create_time` (`user_id`, `create_time`),
KEY `idx_status_create_time` (`status`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';
-- 订单明细表
CREATE TABLE `order_items` (
`item_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`order_id` BIGINT UNSIGNED NOT NULL,
`product_id` BIGINT UNSIGNED NOT NULL,
`product_name` VARCHAR(255) NOT NULL,
`quantity` INT NOT NULL,
`price` DECIMAL(10,2) NOT NULL,
PRIMARY KEY (`item_id`),
KEY `idx_order_id` (`order_id`),
CONSTRAINT `fk_order_items_order_id` FOREIGN KEY (`order_id`)
REFERENCES `orders` (`order_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';
-- 库存表
CREATE TABLE `inventory` (
`product_id` BIGINT UNSIGNED NOT NULL,
`warehouse_id` INT NOT NULL,
`quantity` INT NOT NULL DEFAULT 0,
`version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`product_id`, `warehouse_id`),
KEY `idx_update_time` (`update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存表';
下单事务流程:
import pymysql
from pymysql.cursors import DictCursor
def create_order(user_id, items):
"""创建订单,保证库存扣减和订单创建的原子性"""
conn = pymysql.connect(
host='mysql-master',
port=3306,
user='app_user',
password='password',
db='ecommerce',
charset='utf8mb4',
cursorclass=DictCursor
)
try:
with conn.cursor() as cursor:
# 开启事务
conn.begin()
# 1. 检查并锁定库存(悲观锁)
for item in items:
sql = """
SELECT quantity FROM inventory
WHERE product_id = %s AND warehouse_id = %s
FOR UPDATE
"""
cursor.execute(sql, (item['product_id'], item['warehouse_id']))
result = cursor.fetchone()
if not result or result['quantity'] < item['quantity']:
raise Exception(f"库存不足: product_id={item['product_id']}")
# 2. 扣减库存
for item in items:
sql = """
UPDATE inventory
SET quantity = quantity - %s, version = version + 1
WHERE product_id = %s AND warehouse_id = %s
"""
affected = cursor.execute(sql, (
item['quantity'],
item['product_id'],
item['warehouse_id']
))
if affected != 1:
raise Exception(f"库存扣减失败: product_id={item['product_id']}")
# 3. 创建订单主表
import time
order_no = f"ORD{int(time.time() * 1000)}{user_id}"
total_amount = sum(item['price'] * item['quantity'] for item in items)
sql = """
INSERT INTO orders (user_id, order_no, total_amount, status)
VALUES (%s, %s, %s, 0)
"""
cursor.execute(sql, (user_id, order_no, total_amount))
order_id = cursor.lastrowid
# 4. 创建订单明细
for item in items:
sql = """
INSERT INTO order_items
(order_id, product_id, product_name, quantity, price)
VALUES (%s, %s, %s, %s, %s)
"""
cursor.execute(sql, (
order_id,
item['product_id'],
item['product_name'],
item['quantity'],
item['price']
))
# 提交事务
conn.commit()
return order_id
except Exception as e:
# 回滚事务
conn.rollback()
raise e
finally:
conn.close()
运行结果:
- 订单创建TPS:3000(高峰期)
- 主从延迟:小于100ms(半同步复制)
- 事务成功率:99.99%(0.01%为库存不足等业务异常)
- 3年运行期间,零数据不一致事故
◆ 案例二:内容平台文章系统迁移MongoDB
场景描述:某技术资讯平台,存储1.5亿篇技术文章,早期使用MySQL存储,遇到以下问题:
- 文章字段差异大(有的包含视频,有的包含代码块,有的包含图集),MySQL表有大量NULL字段。
- 文章标签、分类等多对多关系需要多表Join,查询性能差。
- 评论和点赞数据与文章数据分离,聚合查询复杂。
- 文章全文搜索需要额外维护Elasticsearch集群。
迁移方案:将文章系统从MySQL迁移到MongoDB。
MongoDB文档设计:
// 文章文档结构
{
_id: ObjectId("507f1f77bcf86cd799439011"),
title: "深入理解MySQL索引原理与优化实践",
author: {
user_id: 10001,
username: "技术专家",
avatar: "https://cdn.example.com/avatar/10001.jpg"
},
content: "文章正文内容...",
content_type: "markdown", // markdown, html, richtext
// 多媒体资源(灵活字段)
media: {
cover_image: "https://cdn.example.com/cover/123.jpg",
videos: [
{ url: "https://cdn.example.com/video/v1.mp4", duration: 300 }
],
code_blocks: [
{ language: "sql", code: "SELECT * FROM users;" }
]
},
// 标签和分类(数组形式)
tags: ["MySQL", "数据库", "索引优化"],
categories: ["后端开发", "数据库"],
// 统计数据(嵌套文档)
stats: {
views: 15000,
likes: 320,
comments: 56,
favorites: 89
},
// 评论(嵌套数组,前10条热评)
hot_comments: [
{
comment_id: "c001",
user_id: 20001,
username: "读者A",
content: "写得很好,受益匪浅",
likes: 15,
create_time: ISODate("2024-01-15T10:30:00Z")
}
],
// 时间戳
create_time: ISODate("2024-01-10T08:00:00Z"),
update_time: ISODate("2024-01-15T14:20:00Z"),
publish_time: ISODate("2024-01-10T09:00:00Z"),
// 状态
status: 1, // 0草稿,1已发布,2已下架
is_top: false,
is_featured: true
}
索引设计:
// 1. 作者查询索引
db.articles.createIndex({ "author.user_id": 1, "publish_time": -1 })
// 2. 标签查询索引(多值索引)
db.articles.createIndex({ "tags": 1, "publish_time": -1 })
// 3. 分类查询索引
db.articles.createIndex({ "categories": 1, "stats.views": -1 })
// 4. 全文搜索索引
db.articles.createIndex(
{ title: "text", content: "text", tags: "text" },
{
weights: { title: 10, tags: 5, content: 1 },
name: "article_text_search"
}
)
// 5. 状态和发布时间复合索引
db.articles.createIndex({ status: 1, publish_time: -1 })
// 6. 热门文章索引
db.articles.createIndex({ "stats.views": -1, publish_time: -1 })
查询示例:
from pymongo import MongoClient
from bson.objectid import ObjectId
import datetime
client = MongoClient('mongodb://mongo-01:27017,mongo-02:27017,mongo-03:27017/?replicaSet=rs0')
db = client.content_platform
articles = db.articles
# 1. 根据标签查询文章(利用多值索引)
def get_articles_by_tag(tag, page=1, page_size=20):
"""查询指定标签的文章"""
skip = (page - 1) * page_size
cursor = articles.find(
{
"tags": tag,
"status": 1
},
{
"_id": 1,
"title": 1,
"author": 1,
"media.cover_image": 1,
"stats": 1,
"publish_time": 1
}
).sort("publish_time", -1).skip(skip).limit(page_size)
return list(cursor)
# 2. 全文搜索
def search_articles(keyword):
"""全文搜索文章"""
cursor = articles.find(
{
"$text": {"$search": keyword},
"status": 1
},
{
"score": {"$meta": "textScore"},
"title": 1,
"author": 1,
"stats": 1
}
).sort([("score", {"$meta": "textScore"})]).limit(20)
return list(cursor)
# 3. 聚合查询:统计每个作者的文章数和总浏览量
def author_stats():
"""作者统计"""
pipeline = [
{"$match": {"status": 1}},
{"$group": {
"_id": "$author.user_id",
"username": {"$first": "$author.username"},
"article_count": {"$sum": 1},
"total_views": {"$sum": "$stats.views"},
"total_likes": {"$sum": "$stats.likes"}
}},
{"$sort": {"total_views": -1}},
{"$limit": 100}
]
return list(articles.aggregate(pipeline))
# 4. 更新文章浏览量(原子操作)
def increment_views(article_id):
"""增加文章浏览量"""
result = articles.update_one(
{"_id": ObjectId(article_id)},
{
"$inc": {"stats.views": 1},
"$set": {"update_time": datetime.datetime.utcnow()}
}
)
return result.modified_count
# 5. 添加评论(数组操作)
def add_comment(article_id, comment_data):
"""添加评论到文章"""
result = articles.update_one(
{"_id": ObjectId(article_id)},
{
"$push": {
"hot_comments": {
"$each": [comment_data],
"$sort": {"likes": -1},
"$slice": 10 # 只保留点赞数最多的10条评论
}
},
"$inc": {"stats.comments": 1}
}
)
return result.modified_count
迁移效果:
- 查询性能提升:标签查询从300ms降到50ms,全文搜索从500ms降到80ms。
- 存储成本降低:原MySQL占用12TB(包含大量NULL字段和中间表),MongoDB压缩后为8TB。
- 开发效率提升:单文档包含所有相关数据,减少了70%的Join查询代码。
- 运维复杂度降低:省去了独立的Elasticsearch集群,统一使用MongoDB文本索引。
四、最佳实践和注意事项
4.1 最佳实践
◆ 4.1.1 MySQL性能优化
索引优化:
-- 1. 复合索引最左前缀原则
-- 好的索引设计
CREATE INDEX idx_user_status_time ON orders (user_id, status, create_time);
-- 可以加速以下查询:
-- WHERE user_id = ?
-- WHERE user_id = ? AND status = ?
-- WHERE user_id = ? AND status = ? AND create_time > ?
-- 2. 覆盖索引减少回表
CREATE INDEX idx_user_status_amount ON orders (user_id, status, total_amount);
-- 以下查询无需回表
SELECT user_id, status, total_amount FROM orders WHERE user_id=10001 AND status=1;
-- 3. 避免索引失效
-- 错误:对索引字段进行函数操作,导致索引失效
SELECT * FROM orders WHERE DATE(create_time) = '2024-01-15';
-- 正确:改写查询条件
SELECT * FROM orders WHERE create_time >= '2024-01-15 00:00:00' AND create_time < '2024-01-16 00:00:00';
-- 错误:使用OR连接不同字段,可能索引失效
SELECT * FROM orders WHERE user_id = 10001 OR status = 1;
-- 正确:改用UNION
SELECT * FROM orders WHERE user_id = 10001
UNION
SELECT * FROM orders WHERE status = 1;
分区表优化:
-- 按月分区的订单表
CREATE TABLE orders_partitioned (
order_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
order_no VARCHAR(32) NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status TINYINT NOT NULL DEFAULT 0,
create_time DATETIME NOT NULL,
PRIMARY KEY (order_id, create_time),
UNIQUE KEY uk_order_no (order_no),
KEY idx_user_id (user_id),
KEY idx_create_time (create_time)
) ENGINE=InnoDB
PARTITION BY RANGE (TO_DAYS(create_time)) (
PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),
PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),
PARTITION p202403 VALUES LESS THAN (TO_DAYS('2024-04-01')),
PARTITION p202404 VALUES LESS THAN (TO_DAYS('2024-05-01')),
PARTITION p202405 VALUES LESS THAN (TO_DAYS('2024-06-01')),
PARTITION p202406 VALUES LESS THAN (TO_DAYS('2024-07-01')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
-- 查询自动分区裁剪
EXPLAIN PARTITIONS
SELECT * FROM orders_partitioned
WHERE create_time >= '2024-03-01' AND create_time < '2024-04-01';
-- 只扫描p202403分区
-- 自动化分区管理脚本
DELIMITER $$
CREATE PROCEDURE maintain_partitions()
BEGIN
DECLARE next_month DATE;
DECLARE partition_name VARCHAR(20);
SET next_month = DATE_ADD(CURDATE(), INTERVAL 2 MONTH);
SET partition_name = CONCAT('p', DATE_FORMAT(next_month, '%Y%m'));
-- 检查分区是否存在
IF NOT EXISTS (
SELECT 1 FROM information_schema.partitions
WHERE table_name = 'orders_partitioned' AND partition_name = partition_name
) THEN
-- 添加新分区
SET @sql = CONCAT(
'ALTER TABLE orders_partitioned REORGANIZE PARTITION pmax INTO (',
'PARTITION ', partition_name, ' VALUES LESS THAN (TO_DAYS(''', next_month, ''')),',
'PARTITION pmax VALUES LESS THAN MAXVALUE)'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
-- 删除6个月前的分区
SET @old_partition = CONCAT('p', DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL 6 MONTH), '%Y%m'));
IF EXISTS (
SELECT 1 FROM information_schema.partitions
WHERE table_name = 'orders_partitioned' AND partition_name = @old_partition
) THEN
SET @sql = CONCAT('ALTER TABLE orders_partitioned DROP PARTITION ', @old_partition);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END$$
DELIMITER ;
-- 定时执行(通过crontab或Event Scheduler)
CREATE EVENT maintain_partitions_monthly
ON SCHEDULE EVERY 1 MONTH
STARTS '2024-01-01 03:00:00'
DO CALL maintain_partitions();
慢查询优化:
# 分析慢查询日志
mysqldumpslow -s t -t 10 /data/mysql/logs/slow.log
# -s t: 按查询时间排序
# -t 10: 显示前10条
# 使用pt-query-digest分析
pt-query-digest /data/mysql/logs/slow.log > slow_query_report.txt
# 针对慢查询优化
# 1. 使用EXPLAIN分析执行计划
EXPLAIN SELECT o.*, u.username
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id
WHERE o.status = 1 AND o.create_time > '2024-01-01'
ORDER BY o.create_time DESC LIMIT 20;
# 检查关键指标:
# - type: 应该是ref或range,避免ALL(全表扫描)
# - key: 确认使用了正确的索引
# - rows: 扫描行数,越少越好
# - Extra: 避免Using filesort和Using temporary
# 2. 使用SHOW PROFILE分析查询瓶颈
SET profiling = 1;
SELECT ...; -- 执行查询
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;
◆ 4.1.2 MongoDB性能优化
索引策略:
// 1. ESR原则(Equality, Sort, Range)
// 查询条件: 相等条件 -> 排序字段 -> 范围条件
db.articles.find({
status: 1, // Equality
category: "tech" // Equality
}).sort({
publish_time: -1 // Sort
}).limit(20)
// 对应索引设计
db.articles.createIndex({ status: 1, category: 1, publish_time: -1 })
// 2. 复合索引字段顺序优化
// 查询: 高基数字段在前,低基数字段在后
db.users.createIndex({
email: 1, // 高基数(唯一)
status: 1, // 低基数(只有几种状态)
role: 1 // 低基数
})
// 3. 部分索引减少索引大小
db.orders.createIndex(
{ user_id: 1, create_time: -1 },
{
partialFilterExpression: { status: { $in: [0, 1] } },
name: "idx_active_orders"
}
)
// 只对状态为0或1的订单建索引,减少60%索引空间
// 4. TTL索引自动过期数据
db.logs.createIndex(
{ create_time: 1 },
{ expireAfterSeconds: 2592000 } // 30天后自动删除
)
// 5. 检查索引使用情况
db.articles.aggregate([ { $indexStats: {} }])
// 删除未使用的索引
db.articles.dropIndex("unused_index_name")
聚合管道优化:
// 1. 尽早使用$match过滤数据
// 不好的写法:先join再过滤
db.orders.aggregate([
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user"
}
},
{ $match: { status: 1, create_time: { $gte: ISODate("2024-01-01") } } }
])
// 好的写法:先过滤再join
db.orders.aggregate([
{ $match: { status: 1, create_time: { $gte: ISODate("2024-01-01") } } },
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user"
}
}
])
// 2. 使用$project减少文档大小
db.articles.aggregate([
{ $match: { status: 1 } },
{
$project: {
title: 1,
author: 1,
"stats.views": 1, // 只投影需要的嵌套字段
publish_time: 1
}
},
{ $sort: { "stats.views": -1 } },
{ $limit: 100 }
])
// 3. 使用allowDiskUse处理大数据集
db.orders.aggregate(
[
{ $match: { create_time: { $gte: ISODate("2024-01-01") } } },
{ $group: { _id: "$user_id", total: { $sum: "$total_amount" } } },
{ $sort: { total: -1 } },
{ $limit: 1000 }
],
{ allowDiskUse: true } // 允许使用磁盘临时文件
)
// 4. 使用$facet并行执行多个聚合
db.articles.aggregate([
{ $match: { status: 1 } },
{
$facet: {
// 统计总数
total: [
{ $count: "count" }
],
// 分类统计
by_category: [
{ $group: { _id: "$category", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
],
// 热门文章
top_articles: [
{ $sort: { "stats.views": -1 } },
{ $limit: 10 },
{ $project: { title: 1, "stats.views": 1 } }
]
}
}
])
写入性能优化:
// 1. 批量写入使用bulkWrite
const bulk = db.logs.initializeUnorderedBulkOp();
for (let i = 0; i < 10000; i++) {
bulk.insert({
user_id: Math.floor(Math.random() * 100000),
action: "click",
timestamp: new Date()
});
}
bulk.execute();
// 2. 使用insertMany替代多次insertOne
// 不好的写法
for (let doc of documents) {
db.collection.insertOne(doc); // 每次一个网络往返
}
// 好的写法
db.collection.insertMany(documents, { ordered: false });
// ordered: false允许并行插入,即使部分失败也继续
// 3. 写关注级别权衡
// 高一致性场景
db.orders.insertOne(
{ ... },
{ writeConcern: { w: "majority", wtimeout: 5000 } }
)
// 高性能场景(日志、监控数据)
db.logs.insertOne(
{ ... },
{ writeConcern: { w: 1 } } // 只写入主节点就返回
)
// 4. 使用updateMany替代循环update
// 不好的写法
users.forEach(user => {
db.users.updateOne({ _id: user._id }, { $set: { status: 1 } });
});
// 好的写法
db.users.updateMany(
{ _id: { $in: user_ids } },
{ $set: { status: 1 } }
);
◆ 4.1.3 数据库安全加固
MySQL安全配置:
# 1. 删除匿名用户和test数据库
mysql -uroot -p
DELETE FROM mysql.user WHERE User='';
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
FLUSH PRIVILEGES;
# 2. 禁用远程root登录
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
FLUSH PRIVILEGES;
# 3. 创建应用专用用户,遵循最小权限原则
CREATE USER 'app_user'@'192.168.1.%' IDENTIFIED BY 'StrongPassword123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON ecommerce.* TO 'app_user'@'192.168.1.%';
FLUSH PRIVILEGES;
# 4. 启用SSL加密连接
# /etc/my.cnf
[mysqld]
require_secure_transport = ON
ssl-ca = /etc/mysql/ssl/ca.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
# 5. 启用审计日志
INSTALL PLUGIN server_audit SONAME 'server_audit.so';
SET GLOBAL server_audit_logging = ON;
SET GLOBAL server_audit_events = 'CONNECT,QUERY_DDL,QUERY_DML';
SET GLOBAL server_audit_file_path = '/var/log/mysql/audit.log';
MongoDB安全配置:
# /etc/mongod.conf
# 启用认证
security:
authorization: enabled
# 网络加密
net:
ssl:
mode: requireSSL
PEMKeyFile: /etc/mongodb/ssl/mongodb.pem
CAFile: /etc/mongodb/ssl/ca.pem
# 审计日志
auditLog:
destination: file
format: JSON
path: /var/log/mongodb/audit.json
filter: '{ atype: { $in: [ "authenticate", "createUser", "dropDatabase" ] } }'
// 创建管理员用户
use admin
db.createUser({
user: "admin",
pwd: "StrongPassword123!",
roles: [ { role: "root", db: "admin" } ]
})
// 创建应用用户(最小权限)
use myapp
db.createUser({
user: "app_user",
pwd: "AppPassword123!",
roles: [
{ role: "readWrite", db: "myapp" }
]
})
// 启用IP白名单
use admin
db.runCommand({
createRole: "ipRestrictedRole",
privileges: [],
roles: ["readWrite"],
authenticationRestrictions: [ { clientSource: ["192.168.1.0/24"] } ]
})
4.2 注意事项
◆ 4.2.1 MySQL使用注意事项
避免大事务:
大事务会长时间持有锁,导致其他事务等待,甚至引发死锁。
# 错误示例:一次性处理10万条数据
def bad_batch_update():
conn = get_mysql_connection()
cursor = conn.cursor()
conn.begin()
for i in range(100000):
cursor.execute("UPDATE users SET score = score + 1 WHERE user_id = %s", (i,))
conn.commit() # 大事务,可能导致锁等待超时
# 正确示例:分批处理,每批1000条
def good_batch_update():
conn = get_mysql_connection()
cursor = conn.cursor()
batch_size = 1000
for start in range(0, 100000, batch_size):
conn.begin()
for i in range(start, start + batch_size):
cursor.execute("UPDATE users SET score = score + 1 WHERE user_id = %s", (i,))
conn.commit() # 小事务,快速释放锁
time.sleep(0.01) # 稍微休息,避免影响线上服务
避免隐式类型转换:
-- 错误:字符串字段与数字比较,导致索引失效
SELECT * FROM users WHERE phone = 13800138000;
-- phone是VARCHAR类型,MySQL会将所有phone转为数字再比较,全表扫描
-- 正确:使用正确的类型
SELECT * FROM users WHERE phone = '13800138000';
避免SELECT *:
-- 错误:查询所有字段,增加网络传输和内存开销
SELECT * FROM articles WHERE id = 123;
-- 正确:只查询需要的字段
SELECT id, title, author, create_time FROM articles WHERE id = 123;
◆ 4.2.2 MongoDB使用注意事项
避免无界查询:
// 错误:不加limit的查询可能返回百万级文档
db.articles.find({ status: 1 })
// 正确:始终添加limit
db.articles.find({ status: 1 }).limit(1000)
// 分页查询使用skip+limit(小数据量)
db.articles.find({ status: 1 })
.sort({ create_time: -1 })
.skip(page * page_size)
.limit(page_size)
// 大数据量分页使用Range查询
db.articles.find({
status: 1,
create_time: { $lt: last_create_time } // 上一页最后一条的时间
})
.sort({ create_time: -1 })
.limit(page_size)
避免大数组:
MongoDB单文档最大16MB,大数组可能超限。
// 错误:评论直接存在文章文档中,可能无限增长
{
_id: ObjectId("..."),
title: "文章标题",
comments: [ // 可能有数万条评论
{ user: "A", content: "评论1" },
{ user: "B", content: "评论2" },
// ... 数万条
]
}
// 正确:只保留热门评论,完整评论另存
{
_id: ObjectId("..."),
title: "文章标题",
hot_comments: [ // 只保留10条热评
{ user: "A", content: "评论1", likes: 100 }
],
comment_count: 50000
}
// 完整评论存储在另一个集合
db.comments.find({ article_id: ObjectId("...") })
避免不必要的索引:
// 检查索引大小
db.articles.stats().indexSizes
// 索引过多的问题:
// 1. 占用大量内存和磁盘
// 2. 降低写入性能
// 3. 增加维护成本
// 合并相似索引
// 不好:创建多个单字段索引
db.articles.createIndex({ status: 1 })
db.articles.createIndex({ publish_time: -1 })
// 好:创建复合索引覆盖多个查询
db.articles.createIndex({ status: 1, publish_time: -1 })
// 可以加速:
// - { status: 1 }
// - { status: 1, publish_time: -1 }
◆ 4.2.3 常见错误
| 错误现象 |
原因分析 |
解决方案 |
| MySQL主从延迟突增 |
主库大事务或大表DDL操作 |
分批处理,使用pt-online-schema-change做DDL |
| MySQL连接数满 |
应用未使用连接池,连接泄露 |
使用连接池(HikariCP, c3p0),设置超时时间 |
| MongoDB CPU 100% |
缺少索引或索引不合理,大量全表扫描 |
开启profiler分析慢查询,添加合适索引 |
| MongoDB副本集自动切换 |
网络抖动或主节点负载过高心跳超时 |
调整心跳超时参数,升级网络带宽,优化慢查询 |
| MySQL InnoDB锁等待超时 |
大事务或死锁 |
分解大事务,检查死锁日志(SHOW ENGINE INNODB STATUS) |
| MongoDB WriteConflict错误 |
WiredTiger并发写冲突 |
业务层重试,优化文档结构减少冲突 |
五、故障排查和监控
5.1 故障排查
◆ 5.1.1 MySQL故障排查
主从延迟排查:
# 查看从库状态
mysql -e "SHOW SLAVE STATUS\G" | grep -E "Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running"
# 分析binlog事件大小
mysqlbinlog mysql-bin.000123 | grep "end_log_pos" | awk '{print $5-$2}' | sort -n | tail -20
# 查看从库正在执行的SQL
mysql -e "SHOW PROCESSLIST" | grep "system user"
# 使用pt-heartbeat监控延迟
pt-heartbeat --update --database heartbeat --create-table --daemonize
pt-heartbeat --monitor --database heartbeat --master-server-id=1
死锁排查:
-- 查看最近的死锁信息
SHOW ENGINE INNODB STATUS\G
-- 输出示例:
-- LATEST DETECTED DEADLOCK
-- Transaction 1:
-- LOCK WAIT ... waiting for lock on table orders
-- Transaction 2:
-- LOCK WAIT ... waiting for lock on table order_items
-- 避免死锁的方法:
-- 1. 事务访问表的顺序保持一致
-- 2. 尽量使用主键或唯一索引访问数据
-- 3. 减少事务持有锁的时间
-- 4. 使用较低的隔离级别(如RC)
-- 设置死锁超时时间
SET innodb_lock_wait_timeout = 50; -- 默认50秒
慢查询分析:
# 使用pt-query-digest生成慢查询报告
pt-query-digest /var/log/mysql/slow.log \
--limit 20 \
--output slowlog \
--since '2024-01-15 00:00:00' \
--until '2024-01-15 23:59:59' > slow_report.txt
# 实时监控慢查询
pt-query-digest --processlist h=localhost,u=root,p=password \
--print --no-report --run-time 60
# 使用MySQL Enterprise Monitor或Percona Monitoring and Management(PMM)
# 提供可视化的慢查询分析和性能监控
◆ 5.1.2 MongoDB故障排查
慢查询分析:
// 开启profiler(生产环境谨慎使用,有性能开销)
db.setProfilingLevel(1, { slowms: 100 }) // 记录超过100ms的查询
// 查看慢查询
db.system.profile.find({ millis: { $gt: 100 } })
.sort({ ts: -1 })
.limit(10)
.pretty()
// 分析某个慢查询
db.system.profile.find({
ns: "mydb.articles",
millis: { $gt: 1000 }
}).forEach(function(doc) {
print("Query: " + tojson(doc.command));
print("Duration: " + doc.millis + "ms");
print("Execution Stats: " + tojson(doc.execStats));
print("---");
})
// 使用explain分析查询计划
db.articles.find({ tags: "MongoDB" }).explain("executionStats")
// 关键指标:
// - totalKeysExamined: 扫描的索引条目数
// - totalDocsExamined: 扫描的文档数
// - executionTimeMillis: 执行时间
// - stage: 查询阶段(IXSCAN索引扫描,COLLSCAN全表扫描)
副本集故障切换:
# 查看副本集状态
mongo --eval "rs.status()" | grep -E "name|stateStr|health"
# 手动触发切换(维护场景)
mongo --eval "rs.stepDown(60)"
# 主节点降级,60秒内不参与选举
# 查看oplog大小和时间窗口
mongo --eval "
use local;
var first = db.oplog.rs.find().sort({ts:1}).limit(1).next();
var last = db.oplog.rs.find().sort({ts:-1}).limit(1).next();
print('Oplog time window: ' + ((last.ts.t - first.ts.t) / 3600) + ' hours');
print('Oplog size: ' + db.oplog.rs.stats().size / 1024 / 1024 / 1024 + ' GB');
"
# 调整oplog大小(需要重启)
db.adminCommand({ replSetResizeOplog: 1, size: 20480 }) # 20GB
分片集群balancer问题:
// 查看balancer状态
sh.getBalancerState()
sh.isBalancerRunning()
// 查看chunk分布
db.getSiblingDB("config").chunks.aggregate([
{ $group: { _id: "$shard", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
])
// 手动移动chunk(数据倾斜时)
sh.moveChunk("mydb.users", { user_id: 100000 }, "shard02")
// 停止balancer(维护窗口)
sh.stopBalancer()
sh.setBalancerState(false)
◆ 5.1.3 数据恢复
MySQL数据恢复:
# 1. 使用Xtrabackup全量备份
innobackupex --user=root --password=password /backup/
# 2. 增量备份
innobackupex --user=root --password=password --incremental /backup/inc1 \
--incremental-basedir=/backup/2024-01-15_00-00-00
# 3. 恢复全量备份
systemctl stop mysqld
rm -rf /var/lib/mysql/*
innobackupex --apply-log /backup/2024-01-15_00-00-00
innobackupex --copy-back /backup/2024-01-15_00-00-00
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqld
# 4. 基于binlog的时间点恢复
mysqlbinlog --start-datetime="2024-01-15 10:00:00" \
--stop-datetime="2024-01-15 10:30:00" \
mysql-bin.000123 | mysql -uroot -p
# 5. 误删除表的恢复(使用flashback工具)
mysqlbinlog --base64-output=decode-rows -v mysql-bin.000123 > binlog.sql
# 手工反转DELETE为INSERT,UPDATE交换before/after
MongoDB数据恢复:
# 1. 使用mongodump备份
mongodump --host=localhost --port=27017 \
--username=admin --password=password \
--authenticationDatabase=admin \
--out=/backup/mongo-20240115
# 2. 备份单个数据库
mongodump --host=localhost --port=27017 \
--db=myapp --out=/backup/myapp-20240115
# 3. 恢复数据
mongorestore --host=localhost --port=27017 \
--username=admin --password=password \
--authenticationDatabase=admin \
/backup/mongo-20240115
# 4. 恢复单个集合
mongorestore --host=localhost --port=27017 \
--db=myapp --collection=users \
/backup/myapp-20240115/myapp/users.bson
# 5. 时间点恢复(使用oplog)
# 先恢复基础备份
mongorestore /backup/base-backup
# 再重放oplog到指定时间点
mongorestore --oplogReplay \
--oplogLimit "1705305600:1" \
/backup/oplog-backup
# 6. 误删除文档恢复(使用change stream)
# 需要提前开启change stream记录
const changeStream = db.collection.watch();
changeStream.on('change', (change) => {
// 记录所有变更,用于回滚
fs.appendFileSync('changes.log', JSON.stringify(change));
});
5.2 性能监控
◆ 5.2.1 MySQL监控指标
# 1. 核心指标监控脚本
cat > /usr/local/bin/mysql-monitor.sh <<'EOF'
#!/bin/bash
MYSQL_USER="monitor"
MYSQL_PASS="password"
MYSQL_HOST="localhost"
MYSQL_PORT=3306
# 连接数
THREADS_CONNECTED=$(mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASS \
-e "SHOW STATUS LIKE 'Threads_connected';" | awk 'NR==2 {print $2}')
# QPS和TPS
mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASS \
-e "SHOW GLOBAL STATUS LIKE 'Questions';" > /tmp/mysql_status1
sleep 1
mysql -h$MYSQL_HOST -P$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS \
-e "SHOW GLOBAL STATUS LIKE 'Questions';" > /tmp/mysql_status2
QPS=$(awk 'NR==FNR{a=$2;next}{print $2-a}' /tmp/mysql_status1 /tmp/mysql_status2)
# InnoDB缓冲池命中率
BP_HIT_RATE=$(mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASS -e "
SELECT ROUND((1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)) * 100, 2) AS hit_rate
FROM (
SELECT VARIABLE_VALUE AS Innodb_buffer_pool_reads
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads'
) reads,
(
SELECT VARIABLE_VALUE AS Innodb_buffer_pool_read_requests
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'
) requests;" | awk 'NR==2 {print $1}')
# 主从延迟
SLAVE_LAG=$(mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASS \
-e "SHOW SLAVE STATUS\G" | grep "Seconds_Behind_Master" | awk '{print $2}')
echo "$(date '+%Y-%m-%d %H:%M:%S') Threads: $THREADS_CONNECTED, QPS: $QPS, BP_HitRate: $BP_HIT_RATE%, Slave_Lag: ${SLAVE_LAG:-N/A}s"
EOF
chmod +x /usr/local/bin/mysql-monitor.sh
# 2. Prometheus + mysqld_exporter监控
docker run -d \
--name mysqld-exporter \
-p 9104:9104 \
-e DATA_SOURCE_NAME="exporter:password@(mysql-server:3306)/" \
prom/mysqld-exporter
# Prometheus配置
cat >> /etc/prometheus/prometheus.yml <<EOF
- job_name: 'mysql'
static_configs:
- targets: ['localhost:9104']
EOF
| 关键监控指标: |
指标名称 |
正常范围 |
告警阈值 |
说明 |
| Threads_connected |
< max_connections*80% |
> max_connections*90% |
当前连接数 |
| QPS(Questions Per Second) |
依业务而定 |
突增或突降50% |
每秒查询数 |
| InnoDB_buffer_pool_hit_rate |
> 99% |
< 95% |
缓冲池命中率 |
| Innodb_row_lock_waits |
< 10/s |
> 100/s |
行锁等待次数 |
| Seconds_Behind_Master |
< 1s |
> 10s |
主从延迟 |
| Slow_queries |
< 10/min |
> 100/min |
慢查询数量 |
| Aborted_connects |
< 5/min |
> 50/min |
连接失败次数 |
◆ 5.2.2 MongoDB监控指标
// 1. MongoDB自带监控工具mongostat
// 实时监控QPS、连接数、内存使用
mongostat --host mongodb://localhost:27017 -u admin -p password --authenticationDatabase admin
// 输出示例:
// insert query update delete getmore command flushes mapped vsize res ... net_in net_out conn
// 100 200 150 50 10 500 0 8.0G 16.0G 4.0G ... 10m 50m 150
// 2. mongotop监控集合级别的读写时间
mongotop --host mongodb://localhost:27017 -u admin -p password --authenticationDatabase admin 10
// 3. 自定义监控脚本
cat > /usr/local/bin/mongodb-monitor.py <<'EOF'
#!/usr/bin/env python3
from pymongo import MongoClient
import time
client = MongoClient('mongodb://admin:password@localhost:27017/?authSource=admin')
admin_db = client.admin
prev_stats = admin_db.command('serverStatus')
time.sleep(1)
curr_stats = admin_db.command('serverStatus')
# 计算QPS
ops = {}
for op in ['insert', 'query', 'update', 'delete', 'getmore', 'command']:
ops[op] = curr_stats['opcounters'][op] - prev_stats['opcounters'][op]
print(f"Insert: {ops['insert']}/s, Query: {ops['query']}/s, Update: {ops['update']}/s")
# 连接数
print(f"Connections: {curr_stats['connections']['current']} / {curr_stats['connections']['available']}")
# WiredTiger缓存
cache_used = curr_stats['wiredTiger']['cache']['bytes currently in the cache'] / 1024**3
cache_dirty = curr_stats['wiredTiger']['cache']['tracked dirty bytes in the cache'] / 1024**3
print(f"WT Cache: {cache_used:.2f}GB used, {cache_dirty:.2f}GB dirty")
# 副本集延迟
try:
rs_status = admin_db.command('replSetGetStatus')
primary_optime = None
for member in rs_status['members']:
if member['state'] == 1:
primary_optime = member['optimeDate']
break
if primary_optime:
for member in rs_status['members']:
if member['state'] == 2:
lag = (primary_optime - member['optimeDate']).total_seconds()
print(f"Replica lag: {member['name']} = {lag}s")
except:
pass
client.close()
EOF
chmod +x /usr/local/bin/mongodb-monitor.py
# 4. Prometheus + mongodb_exporter
docker run -d \
--name mongodb-exporter \
-p 9216:9216 \
percona/mongodb_exporter:0.40 \
--mongodb.uri=mongodb://exporter:password@mongodb-server:27017
| 关键监控指标: |
指标名称 |
正常范围 |
告警阈值 |
说明 |
| opcounters.insert |
依业务而定 |
突增或突降50% |
每秒插入操作数 |
| opcounters.query |
依业务而定 |
突增或突降50% |
每秒查询操作数 |
| connections.current |
< available*80% |
> available*90% |
当前连接数 |
| mem.resident |
< 物理内存*90% |
> 物理内存*95% |
物理内存使用 |
| wiredTiger.cache.bytes |
< cache_size_gb*90% |
> cache_size_gb*95% |
WT缓存使用 |
| globalLock.currentQueue |
< 10 |
> 100 |
等待全局锁的操作数 |
| repl.lag |
< 1s |
> 10s |
副本集延迟 |
5.3 备份与恢复
◆ 5.3.1 MySQL备份策略
#!/bin/bash
# mysql-backup.sh - MySQL自动化备份脚本
BACKUP_DIR="/data/backup/mysql"
MYSQL_USER="backup"
MYSQL_PASS="password"
MYSQL_HOST="localhost"
MYSQL_PORT=3306
RETENTION_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DIR/$DATE"
mkdir -p $BACKUP_PATH
# 1. 全量备份(Xtrabackup)
innobackupex --user=$MYSQL_USER --password=$MYSQL_PASS \
--host=$MYSQL_HOST --port=$MYSQL_PORT \
--parallel=4 --compress --compress-threads=4 \
$BACKUP_PATH
if [ $? -eq 0 ]; then
echo "$(date) Backup succeeded: $BACKUP_PATH" >> $BACKUP_DIR/backup.log
# 2. 上传到OSS/S3(可选)
aws s3 sync $BACKUP_PATH s3://mysql-backups/$DATE/ --storage-class STANDARD_IA
# 3. 清理过期备份
find $BACKUP_DIR -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;
# 4. 验证备份可用性(定期测试恢复)
if [ $(date +%u) -eq 7 ]; then # 每周日验证
TEST_DIR="/data/backup/test-restore"
rm -rf $TEST_DIR
mkdir -p $TEST_DIR
innobackupex --decompress $BACKUP_PATH
innobackupex --apply-log $BACKUP_PATH
innobackupex --copy-back --datadir=$TEST_DIR $BACKUP_PATH
if [ $? -eq 0 ]; then
echo "$(date) Backup verification succeeded" >> $BACKUP_DIR/backup.log
else
echo "$(date) Backup verification FAILED!" >> $BACKUP_DIR/backup.log
# 发送告警
curl -X POST https://alert.example.com/api/alert \
-d "message=MySQL backup verification failed&level=critical"
fi
fi
else
echo "$(date) Backup FAILED!" >> $BACKUP_DIR/backup.log
# 发送告警
curl -X POST https://alert.example.com/api/alert \
-d "message=MySQL backup failed&level=critical"
fi
# Crontab定时任务(每天凌晨2点执行)
# 0 2 * * * /usr/local/bin/mysql-backup.sh
◆ 5.3.2 MongoDB备份策略
#!/bin/bash
# mongodb-backup.sh - MongoDB自动化备份脚本
BACKUP_DIR="/data/backup/mongodb"
MONGO_HOST="localhost"
MONGO_PORT=27017
MONGO_USER="backup"
MONGO_PASS="password"
RETENTION_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DIR/$DATE"
mkdir -p $BACKUP_PATH
# 1. 全量备份
mongodump --host=$MONGO_HOST --port=$MONGO_PORT \
--username=$MONGO_USER --password=$MONGO_PASS \
--authenticationDatabase=admin \
--gzip --out=$BACKUP_PATH
if [ $? -eq 0 ]; then
echo "$(date) Backup succeeded: $BACKUP_PATH" >> $BACKUP_DIR/backup.log
# 2. 备份oplog(用于时间点恢复)
mongodump --host=$MONGO_HOST --port=$MONGO_PORT \
--username=$MONGO_USER --password=$MONGO_PASS \
--authenticationDatabase=admin \
--db=local --collection=oplog.rs \
--gzip --out=$BACKUP_PATH/oplog
# 3. 压缩备份
cd $BACKUP_DIR && tar -czf $DATE.tar.gz $DATE/
# 4. 上传到OSS/S3
aws s3 cp $DATE.tar.gz s3://mongodb-backups/ --storage-class STANDARD_IA
# 5. 清理本地备份(保留7天)
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
find $BACKUP_DIR -type d -mtime +1 -exec rm -rf {} \;
# 6. 清理S3过期备份(保留30天)
aws s3 ls s3://mongodb-backups/ | while read -r line; do
CREATE_DATE=$(echo $line | awk '{print $1" "$2}')
CREATE_DATE_UNIX=$(date -d "$CREATE_DATE" +%s)
CURRENT_DATE_UNIX=$(date +%s)
AGE_DAYS=$(( ($CURRENT_DATE_UNIX - $CREATE_DATE_UNIX) / 86400 ))
if [ $AGE_DAYS -gt $RETENTION_DAYS ]; then
FILE_NAME=$(echo $line | awk '{print $4}')
aws s3 rm s3://mongodb-backups/$FILE_NAME
echo "$(date) Deleted old backup: $FILE_NAME" >> $BACKUP_DIR/backup.log
fi
done
else
echo "$(date) Backup FAILED!" >> $BACKUP_DIR/backup.log
# 发送告警
curl -X POST https://alert.example.com/api/alert \
-d "message=MongoDB backup failed&level=critical"
fi
# Crontab定时任务(每天凌晨3点执行)
# 0 3 * * * /usr/local/bin/mongodb-backup.sh
六、总结
6.1 技术要点回顾
MySQL核心优势:
- ACID事务保证和成熟的SQL生态,适合金融、电商等对强一致性有苛刻要求的场景。
- 丰富的查询优化器和完善的索引体系,支持复杂的关联查询和聚合分析。
- 主从复制和MGR提供高可用保障,运维工具链完善,社区资源丰富。
- 存储过程、触发器、外键约束等特性适合复杂业务逻辑的封装。
MongoDB核心优势:
- 灵活的文档模型,无需预定义Schema,适合快速迭代和非结构化数据存储。
- 原生分片能力支持水平扩展到数百节点,可处理PB级数据量。
- 丰富的查询语言和聚合框架,支持地理空间查询、文本搜索、时序数据处理。
- WiredTiger存储引擎提供文档级并发控制,写入性能优于传统行级锁。
选型决策原则:
- 数据一致性要求:金融交易、订单系统等强一致性场景选MySQL;内容管理、日志采集等最终一致性场景选MongoDB。
- 查询模式:多表关联、复杂SQL查询选MySQL;嵌套文档、数组查询选MongoDB。
- 扩展性需求:垂直扩展(单机性能)选MySQL;水平扩展(分布式集群)选MongoDB。
- Schema变更频率:稳定的表结构选MySQL;频繁增减字段选MongoDB。
- 团队技能栈:SQL经验丰富的团队选MySQL;NoSQL和分布式经验丰富的团队选MongoDB。
混合架构实践:
在实际生产环境中,许多公司采用MySQL+MongoDB的混合架构,以发挥各自优势。在云栈社区的数据库与中间件板块中,经常有开发者深入探讨两者的选型问题以及混合架构的最佳实践。
- 核心交易数据(订单、支付、账务)存储在MySQL,保证强一致性和事务完整性。
- 用户行为日志、埋点数据、推荐特征存储在MongoDB,利用其灵活Schema和高写入性能。
- 使用Canal或Debezium实时同步MySQL Binlog到MongoDB,构建实时数据分析平台。
- 使用ClickHouse或Elasticsearch作为数据仓库层,从MySQL和MongoDB抽取数据进行OLAP分析。
6.2 进阶学习方向
MySQL深入学习:
- InnoDB存储引擎原理:深入理解B+Tree索引结构、MVCC并发控制、Redo Log和Undo Log机制、Buffer Pool管理、Adaptive Hash Index等核心技术。
- MySQL源码阅读:研究查询优化器、执行器、复制模块的源码实现,理解MySQL内核工作原理。
- 分布式MySQL方案:研究MySQL Cluster、Vitess、TiDB等分布式方案,掌握分库分表、分布式事务、全局ID生成等技术。
- 学习资源:
MongoDB深入学习:
- WiredTiger存储引擎:深入理解文档存储格式、B-Tree索引实现、MVCC机制、Checkpoint和Journal、压缩算法等。
- 分片集群架构:掌握Chunk分裂和迁移机制、Balancer调度算法、Jumbo Chunk处理、Zone Sharding等高级特性。
- 副本集选举机制:研究Raft协议在MongoDB中的实现、心跳检测、选举超时、优先级和标签等配置。
- 时序数据优化:学习Time Series Collection、Bucket Pattern、窗口函数等时序数据最佳实践。
- 学习资源:
数据库性能调优:
- 学习系统性能分析方法论(USE方法、RED方法)。
- 掌握操作系统层面调优(内核参数、文件系统、存储IO)。
- 学习网络调优(TCP参数、带宽、延迟)。
- 实践:搭建测试环境,使用sysbench、YCSB等工具进行压测和性能分析。
6.3 参考资料
官方文档:
技术博客:
开源项目:
社区论坛:
附录
A. 命令速查表
# MySQL常用命令
mysql -uroot -p # 连接MySQL
SHOW DATABASES; # 列出所有数据库
USE dbname; # 切换数据库
SHOW TABLES; # 列出所有表
DESC tablename; # 查看表结构
SHOW CREATE TABLE tablename\G # 查看建表语句
SHOW PROCESSLIST; # 查看当前连接和执行的SQL
SHOW ENGINE INNODB STATUS\G # 查看InnoDB状态
SHOW MASTER STATUS; # 查看主库状态
SHOW SLAVE STATUS\G # 查看从库状态
EXPLAIN SELECT ...; # 分析查询执行计划
SHOW INDEX FROM tablename; # 查看表索引
SHOW VARIABLES LIKE 'innodb%'; # 查看InnoDB参数
SET GLOBAL variable_name = value; # 修改全局变量
# MongoDB常用命令
mongo # 连接MongoDB
show dbs # 列出所有数据库
use dbname # 切换数据库
show collections # 列出所有集合
db.collection.find().pretty() # 查询文档(格式化输出)
db.collection.findOne() # 查询单个文档
db.collection.insertOne({...}) # 插入文档
db.collection.updateOne({filter}, {$set: {...}}) # 更新文档
db.collection.deleteOne({filter}) # 删除文档
db.collection.createIndex({field: 1}) # 创建索引
db.collection.getIndexes() # 查看索引
db.collection.stats() # 查看集合统计信息
db.currentOp() # 查看当前操作
rs.status() # 查看副本集状态
sh.status() # 查看分片集群状态
db.serverStatus() # 查看服务器状态
# 系统监控命令
top -p $(pgrep mysqld) # 监控MySQL进程
iostat -x 1 # 监控磁盘IO
vmstat 1 # 监控系统资源
netstat -antp | grep 3306 # 查看MySQL连接
ss -s # 查看socket统计
B. 配置参数详解
MySQL核心参数:
innodb_buffer_pool_size:InnoDB缓冲池大小,缓存数据和索引,建议设置为物理内存的70-80%。
innodb_log_file_size:Redo日志文件大小,写入密集型场景建议2-4GB。
innodb_flush_log_at_trx_commit:事务日志刷盘策略,1最安全(每次提交刷盘),2折中(每秒刷盘),0性能最高(每秒写入并刷盘)。
innodb_flush_method:IO刷新方法,建议O_DIRECT避免双重缓存。
max_connections:最大连接数,根据业务并发调整,建议配合连接池使用。
binlog_format:Binlog格式,ROW最安全,MIXED折中,STATEMENT最小。
sync_binlog:Binlog同步频率,1最安全(每次提交同步),0依赖OS。
MongoDB核心参数:
storage.wiredTiger.engineConfig.cacheSizeGB:WiredTiger缓存大小,建议物理内存的50%。
replication.oplogSizeMB:Oplog大小,决定副本集时间窗口,建议至少保留24小时的操作记录。
net.maxIncomingConnections:最大连接数,默认65536。
operationProfiling.slowOpThresholdMs:慢查询阈值,默认100ms。
setParameter.cursorTimeoutMillis:游标超时时间,默认10分钟。
sharding.chunkSize:分片Chunk大小,默认64MB,大文档场景可调整为128MB。
C. 术语表
| 术语 |
英文 |
解释 |
| ACID |
Atomicity, Consistency, Isolation, Durability |
事务的四个特性:原子性、一致性、隔离性、持久性 |
| MVCC |
Multi-Version Concurrency Control |
多版本并发控制,通过保存数据的多个版本实现无锁读取 |
| Binlog |
Binary Log |
MySQL的二进制日志,记录所有DDL和DML操作,用于复制和时间点恢复 |
| Oplog |
Operations Log |
MongoDB的操作日志,记录所有写操作,用于副本集数据同步 |
| Sharding |
- |
分片,将数据水平拆分到多个节点,实现分布式存储和计算 |
| Replica Set |
- |
副本集,MongoDB的高可用架构,包含一个主节点和多个从节点 |
| Schema |
- |
模式,数据库中表结构的定义 |
| Index |
- |
索引,用于加速数据查询的数据结构 |
| B+Tree |
- |
B+树,MySQL InnoDB默认的索引数据结构 |
| LSM Tree |
Log-Structured Merge-Tree |
日志结构合并树,适合写多读少的场景 |
| WiredTiger |
- |
MongoDB的默认存储引擎,支持文档级并发控制和压缩 |
| InnoDB |
- |
MySQL的默认存储引擎,支持ACID事务和行级锁 |
| Chunk |
- |
MongoDB分片集群中的数据块,默认64MB |
| Balancer |
- |
MongoDB分片集群的负载均衡器,自动迁移Chunk保持数据均衡 |
| Aggregation |
- |
聚合,对数据进行分组、统计、转换等操作 |
| TTL Index |
Time To Live Index |
生存时间索引,MongoDB自动删除过期数据的机制 |