找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

325

积分

0

好友

45

主题
发表于 昨天 16:05 | 查看: 7| 回复: 0

一、概述

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_methodO_DIRECT绕过OS文件缓存,避免与innodb_buffer_pool双重缓存,减少内存浪费。
  • binlog_formatROW格式记录每一行的变化,保证数据一致性;STATEMENT格式记录SQL语句,binlog更小但可能导致主从数据不一致。
  • sync_binlog1表示每次事务提交同步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存储,遇到以下问题:

  1. 文章字段差异大(有的包含视频,有的包含代码块,有的包含图集),MySQL表有大量NULL字段。
  2. 文章标签、分类等多对多关系需要多表Join,查询性能差。
  3. 评论和点赞数据与文章数据分离,聚合查询复杂。
  4. 文章全文搜索需要额外维护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生成等技术。
  • 学习资源
    • 《高性能MySQL》第4版 - Baron Schwartz等著
    • 《MySQL技术内幕:InnoDB存储引擎》第2版 - 姜承尧著
    • Percona官方博客: https://www.percona.com/blog

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 参考资料

官方文档

技术博客

开源项目

  • Vitess - YouTube开源的MySQL水平扩展方案
  • ProxySQL - 高性能MySQL代理,支持读写分离和查询路由
  • Percona Toolkit - MySQL DBA必备工具集
  • Debezium - 基于CDC的数据变更捕获工具

社区论坛

附录

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自动删除过期数据的机制



上一篇:代码重构实战:5种if/else优化技巧提升可读性
下一篇:VSCode远程开发实战:SSH插件配置连接Ubuntu服务器指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-6 23:53 , Processed in 0.082796 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表