KeepAlived 搭建 MySQL 双主模式高可用集群(详细安装配置教程)

一、核心架构与工作原理

1. 整体架构

采用两台服务器(双节点) 部署,每台节点同时安装MySQLKeepAlived,实现MySQL 双主(互为主从)+ KeepAlived 虚拟 IP(VIP)漂移的高可用架构:

  • 双节点 IP 示例:Node1(192.168.1.10)、Node2(192.168.1.11)
  • 虚拟 VIP:192.168.1.100(应用端仅需连接此 VIP,无需感知后端真实节点)
  • 核心特性:两台 MySQL 互为主从(均可读写),KeepAlived 监控节点状态,故障时自动将 VIP 漂移到健康节点,实现秒级故障切换,应用无感知。

2. 关键原理

(1)MySQL 双主模式

两台 MySQL 互相作为对方的主库(Master)从库(Slave) ,开启二进制日志和中继日志,实现双向数据同步;通过错开自增 ID 规则,避免双写时的主键冲突,保证数据一致性。

(2)KeepAlived 高可用

基于VRRP(虚拟路由冗余协议) 实现,两台节点分为MASTER(主节点,高优先级)和BACKUP(备节点,低优先级):

  • 正常情况下,MASTER 节点持有 VIP,所有应用请求通过 VIP 转发到该节点的 MySQL;
  • KeepAlived 默认仅监控自身进程,需通过自定义检测脚本监控 MySQL 状态(进程 / 连通性);
  • 当 MASTER 节点 MySQL 宕机 / 服务器故障时,KeepAlived 检测到异常,会释放 VIP,BACKUP 节点检测到主节点失效后,抢占 VIP 并提供服务;
  • 故障恢复后,可配置「抢占 / 非抢占」模式,决定 VIP 是否飘回原主节点。

3. 核心依赖与环境说明

  • 操作系统:CentOS 7/8(RHEL 系列通用,Ubuntu 需微调命令)
  • MySQL 版本:5.7(稳定版,8.0 配置仅需微调同步账号权限,教程以 5.7 为例)
  • KeepAlived 版本:系统 YUM 源自带版本(无需手动编译,简单稳定)
  • 前置要求:两台节点网络互通、关闭防火墙 / SELinux(学习环境,生产需配置规则)、主机名解析正常。

二、环境准备(两台节点均需执行)

1. 基础环境配置

复制代码
# 1. 关闭防火墙(生产环境需开放3306/VRRP协议端口)
systemctl stop firewalld && systemctl disable firewalld

# 2. 关闭SELinux
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

# 3. 配置主机名解析(避免同步/VRRP协议解析问题)
cat >> /etc/hosts << EOF
192.168.1.10  mysql-node1
192.168.1.11  mysql-node2
EOF

# 4. 安装基础依赖
yum install -y wget net-tools vim lrzsz

2. 时间同步(保证双主同步 binlog 时间一致)

复制代码
# 安装chrony时间同步服务
yum install -y chrony
systemctl start chrony && systemctl enable chrony
# 验证时间同步
timedatectl status

三、安装配置 MySQL 双主模式(核心步骤)

步骤 1:安装 MySQL 5.7(两台节点均执行)

采用 MySQL 官方 YUM 源安装,步骤统一:

复制代码
# 1. 下载MySQL 5.7 YUM源
wget -O /etc/yum.repos.d/mysql57-community.repo https://repo.mysql.com/yum/mysql-5.7-community/el7/x86_64/mysql57-community.repo

# 2. 安装MySQL服务
yum install -y mysql-community-server --nogpgcheck

# 3. 启动MySQL并设置开机自启
systemctl start mysqld && systemctl enable mysqld

# 4. 查看初始密码(MySQL5.7默认随机生成)
grep 'temporary password' /var/log/mysqld.log
# 输出示例:A temporary password is generated for root@localhost: xxxxxxxx

步骤 2:初始化 MySQL(两台节点均执行)

复制代码
# 1. 登录MySQL(输入上述初始密码)
mysql -uroot -p

# 2. 修改root密码(需符合复杂度:数字+字母+特殊符号,学习环境可降低复杂度)
ALTER USER 'root'@'localhost' IDENTIFIED BY 'MySQL@123456';
# 刷新权限
FLUSH PRIVILEGES;

# 3. 降低密码复杂度(学习环境可选,生产环境不建议)
set global validate_password_policy=0;
set global validate_password_length=6;
FLUSH PRIVILEGES;

# 4. 远程登录授权(应用/另一节点需远程连接)
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'MySQL@123456';
FLUSH PRIVILEGES;

# 5. 退出MySQL
exit;

步骤 3:配置 MySQL 双主核心参数(两台节点分别配置)

修改 MySQL 主配置文件/etc/my.cnf核心参数不可重复 / 缺失 ,双节点配置差异仅server-id和自增 ID 规则。

(1)Node1(192.168.1.10)配置
复制代码
# 备份原配置文件
cp /etc/my.cnf /etc/my.cnf.bak
# 覆盖写入新配置
cat > /etc/my.cnf << EOF
[mysqld]
# 基础配置
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
# 双主核心配置
server-id=1  # 唯一标识,不可重复(Node2设为2)
port=3306
bind-address=0.0.0.0  # 允许所有IP访问
# 开启二进制日志(主库必须开启,用于同步)
log-bin=mysql-bin
binlog_format=ROW  # 行级复制,同步更精准
# 开启中继日志(从库必须开启,双主互为主从,故均需开启)
relay-log=mysql-relay-bin
# 从库更新写入自身binlog(双主核心,否则另一台主库无法同步此节点数据)
log-slave-updates=1
# 关闭只读(双主均可写,从库需开启read-only=1,此处互为主从故关闭)
read-only=0
# 跳过主从同步错误(学习环境可选,生产需定位错误)
slave_skip_errors=1062
# 自增ID规则(避免双写主键冲突):起始值1,步长2
auto_increment_offset=1
auto_increment_increment=2
# 指定同步的数据库(*代表所有,学习环境可选)
# replicate-do-db=test_db
EOF

# 重启MySQL使配置生效
systemctl restart mysqld
(2)Node2(192.168.1.11)配置

仅需修改server-idauto_increment_offset,其余与 Node1 一致:

复制代码
cp /etc/my.cnf /etc/my.cnf.bak
cat > /etc/my.cnf << EOF
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
# 核心差异:server-id=2
server-id=2
port=3306
bind-address=0.0.0.0
log-bin=mysql-bin
binlog_format=ROW
relay-log=mysql-relay-bin
log-slave-updates=1
read-only=0
slave_skip_errors=1062
# 核心差异:自增起始值2,步长2
auto_increment_offset=2
auto_increment_increment=2
# replicate-do-db=test_db
EOF

# 重启MySQL
systemctl restart mysqld

步骤 4:创建 MySQL 同步账号(两台节点均执行)

双主互相同步,需为对方创建专用同步账号(授予复制权限),避免使用 root:

复制代码
# 登录MySQL
mysql -uroot -pMySQL@123456

# 创建同步账号(repl为账号,Repl@123456为密码,%允许所有IP访问)
CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl@123456';
# 授予复制权限(主从同步核心权限)
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;

# 退出MySQL
exit;

步骤 5:配置双主互为主从(关键步骤,需精准执行)

核心:Node1 作为从库 指向 Node2 的主库,Node2 作为从库指向 Node1 的主库,需先获取对方主库的 binlog 文件和位置。

(1)获取 Node1 主库状态(在 Node1 执行)
复制代码
mysql -uroot -pMySQL@123456 -e "SHOW MASTER STATUS;"

输出示例 (记录FilePosition,后续 Node2 配置用):

File Position Binlog_Do_DB Binlog_Ignore_DB
mysql-bin.000001 154

注意:执行后不要在 Node1 执行任何写操作,否则 Position 会变化,导致同步失败。

(2)Node2 配置为 Node1 的从库(在 Node2 执行)

<Node1_IP><binlog_File><binlog_Position>替换为实际值:

复制代码
mysql -uroot -pMySQL@123456 << EOF
# 停止从库服务(若已配置)
STOP SLAVE;
# 重置从库状态(清空原有同步信息)
RESET SLAVE ALL;
# 配置主库信息(指向Node1)
CHANGE MASTER TO
MASTER_HOST='192.168.1.10',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@123456',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001',  # 对应Node1的Show Master Status的File
MASTER_LOG_POS=154,  # 对应Node1的Show Master Status的Position
MASTER_CONNECT_RETRY=30;  # 连接失败重试间隔(秒)
# 启动从库服务
START SLAVE;
# 查看从库状态
SHOW SLAVE STATUS\G
EOF
(3)获取 Node2 主库状态(在 Node2 执行)
复制代码
mysql -uroot -pMySQL@123456 -e "SHOW MASTER STATUS;"

记录FilePosition,后续 Node1 配置用。

(4)Node1 配置为 Node2 的从库(在 Node1 执行)

替换为 Node2 的实际 binlog 信息:

复制代码
mysql -uroot -pMySQL@123456 << EOF
STOP SLAVE;
RESET SLAVE ALL;
CHANGE MASTER TO
MASTER_HOST='192.168.1.11',
MASTER_USER='repl',
MASTER_PASSWORD='Repl@123456',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001',  # 对应Node2的File
MASTER_LOG_POS=154,  # 对应Node2的Position
MASTER_CONNECT_RETRY=30;
START SLAVE;
SHOW SLAVE STATUS\G
EOF

步骤 6:验证 MySQL 双主同步(两台节点均需验证)

执行SHOW SLAVE STATUS\G后,两个核心字段必须为 Yes,否则同步失败:

复制代码
# 登录MySQL执行
mysql -uroot -pMySQL@123456 -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO_Running|Slave_SQL_Running"

正确输出

复制代码
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

若为 No,查看Last_Error字段定位错误(常见原因:server-id 重复、binlog 位置错误、同步账号密码错误、防火墙未关)。

步骤 7:验证双向数据同步(可选,确认双主有效性)

复制代码
# 1. 在Node1创建数据库,Node2查看是否同步
# Node1执行
mysql -uroot -pMySQL@123456 -e "CREATE DATABASE test_ha;"
# Node2执行
mysql -uroot -pMySQL@123456 -e "SHOW DATABASES;"  # 应能看到test_ha

# 2. 在Node2创建表,Node1查看是否同步
# Node2执行
mysql -uroot -pMySQL@123456 -e "CREATE TABLE test_ha.t1(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20));"
# Node1执行
mysql -uroot -pMySQL@123456 -e "DESC test_ha.t1;"  # 应能看到表结构

# 3. 双节点插入数据,验证自增ID无冲突
# Node1插入(id应为1、3、5...)
mysql -uroot -pMySQL@123456 -e "INSERT INTO test_ha.t1(name) VALUES('node1');"
# Node2插入(id应为2、4、6...)
mysql -uroot -pMySQL@123456 -e "INSERT INTO test_ha.t1(name) VALUES('node2');"
# 两台节点均执行查询,应能看到所有数据
mysql -uroot -pMySQL@123456 -e "SELECT * FROM test_ha.t1;"

四、安装配置 KeepAlived(实现 VIP 漂移与 MySQL 监控)

核心说明

  1. 两台节点均需安装 KeepAlived,配置分为「主节点(MASTER)」和「备节点(BACKUP)」,仅优先级 / 状态不同;
  2. KeepAlived默认不监控 MySQL ,需编写自定义检测脚本,当 MySQL 宕机时,自动停止当前节点的 KeepAlived,释放 VIP;
  3. 脚本核心逻辑:通过mysqladmin ping检测 MySQL 连通性,失败则执行systemctl stop keepalived

步骤 1:安装 KeepAlived(两台节点均执行)

直接通过 YUM 源安装,简单稳定:

复制代码
yum install -y keepalived
# 验证安装
keepalived -v

步骤 2:编写 MySQL 检测脚本(两台节点均执行)

创建脚本目录,编写检测脚本/etc/keepalived/check_mysql.sh必须赋予执行权限

复制代码
# 1. 创建脚本目录
mkdir -p /etc/keepalived/scripts
# 2. 编写检测脚本
cat > /etc/keepalived/check_mysql.sh << EOF
#!/bin/bash
# 定义MySQL连接参数
MYSQL_USER="root"
MYSQL_PWD="MySQL@123456"
MYSQL_HOST="127.0.0.1"
# 检测MySQL连通性(mysqladmin ping是最可靠的方式)
mysqladmin -u\${MYSQL_USER} -p\${MYSQL_PWD} -h\${MYSQL_HOST} ping &>/dev/null
if [ \$? -ne 0 ]; then
    # MySQL宕机,停止当前节点KeepAlived,释放VIP
    systemctl stop keepalived
    echo "MySQL is down, stop keepalived at \$(date)" >> /var/log/keepalived_check_mysql.log
fi
EOF

# 3. 赋予执行权限(关键,否则KeepAlived无法执行脚本)
chmod +x /etc/keepalived/check_mysql.sh
# 4. 创建日志文件
touch /var/log/keepalived_check_mysql.log

步骤 3:配置 KeepAlived 主配置文件(分主 / 备节点)

KeepAlived 主配置文件为/etc/keepalived/keepalived.conf主节点(Node1)备节点(Node2) 配置仅 3 处差异,其余完全一致。

(1)主节点(Node1:192.168.1.10)配置
复制代码
# 备份原配置
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
# 覆盖写入新配置
cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
   router_id MYSQL_HA_10  # 唯一标识,建议用节点IP后两位,与备节点区分
   script_user root       # 执行检测脚本的用户
   enable_script_security # 开启脚本安全验证
}

# 定义检测脚本(调用上述MySQL检测脚本)
vrrp_script check_mysql {
    script "/etc/keepalived/check_mysql.sh"  # 脚本路径
    interval 2                               # 检测间隔(秒)
    weight -20                               # 检测失败,节点优先级降低20
    fall 3                                   # 连续3次检测失败,判定为故障
    rise 2                                   # 连续2次检测成功,判定为恢复
}

# VRRP实例配置(核心,定义VIP和主备规则)
vrrp_instance VI_1 {
    state MASTER  # 主节点设为MASTER,备节点设为BACKUP
    interface eth0  # 网卡名称(需根据自身服务器修改,用ip addr查看)
    virtual_router_id 51  # VRRP组ID(1-255,双节点必须一致)
    priority 100  # 优先级(主节点高于备节点,备节点设为90)
    advert_int 1  # 心跳发送间隔(秒),双节点必须一致
    nopreempt     # 非抢占模式(可选,主节点恢复后不主动抢回VIP,避免抖动)
    authentication {  # 认证(双节点必须一致,防止非法节点加入)
        auth_type PASS
        auth_pass 1111  # 认证密码(任意4-8位)
    }
    # 调用检测脚本
    track_script {
        check_mysql
    }
    # 配置虚拟VIP(双节点必须一致,可配置多个,用空格分隔)
    virtual_ipaddress {
        192.168.1.100/24 dev eth0  # VIP/子网掩码 绑定网卡
    }
}
EOF
(2)备节点(Node2:192.168.1.11)配置

仅修改3 处差异 ,其余与主节点一致,已标注# 差异点

复制代码
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.bak
cat > /etc/keepalived/keepalived.conf << EOF
! Configuration File for keepalived
global_defs {
   router_id MYSQL_HA_11  # 差异点1:唯一标识,与主节点区分
   script_user root
   enable_script_security
}

vrrp_script check_mysql {
    script "/etc/keepalived/check_mysql.sh"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP  # 差异点2:备节点设为BACKUP
    interface eth0
    virtual_router_id 51
    priority 90  # 差异点3:优先级低于主节点(90<100)
    advert_int 1
    nopreempt
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    track_script {
        check_mysql
    }
    virtual_ipaddress {
        192.168.1.100/24 dev eth0
    }
}
EOF

网卡名称确认命令:ip addr(常见为 eth0、ens33、ens192 等)。

步骤 4:启动 KeepAlived 并设置开机自启(两台节点均执行)

复制代码
# 1. 启动KeepAlived
systemctl start keepalived
# 2. 设置开机自启
systemctl enable keepalived
# 3. 查看状态(确认running)
systemctl status keepalived
# 4. 查看VIP是否在主节点(Node1应能看到VIP,Node2无)
ip addr

主节点 Node1 正确输出(包含 VIP 192.168.1.100):

复制代码
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.10/24 brd 192.168.1.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet 192.168.1.100/24 scope global secondary eth0  # VIP标识
       valid_lft forever preferred_lft forever

五、高可用集群测试(核心验证,必做)

测试目标:验证MySQL 宕机服务器宕机两种场景下,VIP 能否自动漂移,应用能否通过 VIP 正常访问,数据是否一致。

测试工具:可在第三方测试机 / 本地执行ping 192.168.1.100(查看 VIP 连通性)、mysql -h192.168.1.100 -uroot -pMySQL@123456(查看 MySQL 访问性)。

测试 1:主节点(Node1)MySQL 宕机,VIP 漂移到备节点(Node2)

步骤 1:主节点停止 MySQL
复制代码
# Node1执行
systemctl stop mysqld
# 查看KeepAlived状态(检测脚本会自动停止KeepAlived)
systemctl status keepalived
步骤 2:验证 VIP 漂移
复制代码
# 1. Node1查看IP(VIP已消失)
ip addr
# 2. Node2查看IP(VIP已漂移过来)
ip addr
# 3. 第三方测试机ping VIP(连通性正常)
ping 192.168.1.100
# 4. 第三方测试机通过VIP访问MySQL(正常连接,数据与原主节点一致)
mysql -h192.168.1.100 -uroot -pMySQL@123456 -e "SELECT * FROM test_ha.t1;"
步骤 3:恢复主节点 MySQL,验证 VIP 状态
复制代码
# Node1启动MySQL和KeepAlived
systemctl start mysqld && systemctl start keepalived
# 查看VIP(因配置了nopreempt,VIP仍在Node2,避免抖动;若需抢占,删除nopreempt参数即可)
ip addr

测试 2:主节点(Node1)服务器宕机,VIP 漂移到备节点(Node2)

步骤 1:关闭主节点服务器(或断开网络)

直接在 Node1 执行init 0关闭服务器,或在虚拟机中关闭节点。

步骤 2:验证 VIP 漂移与服务可用性
复制代码
# 1. Node2查看IP(VIP已漂移)
ip addr
# 2. 第三方测试机通过VIP访问MySQL(正常连接,可读写)
mysql -h192.168.1.100 -uroot -pMySQL@123456 -e "INSERT INTO test_ha.t1(name) VALUES('vip_test');"
步骤 3:恢复主节点服务器,验证数据同步

启动 Node1 服务器,等待 MySQL 和 KeepAlived 自动启动(已设置开机自启):

复制代码
# Node1执行,查看数据是否同步(备节点写入的数据已同步)
mysql -uroot -pMySQL@123456 -e "SELECT * FROM test_ha.t1;"
# 查看VIP(仍在Node2,非抢占模式)
ip addr

测试 3:备节点故障,主节点正常提供服务(反向验证)

停止 Node2 的 MySQL/KeepAlived,或关闭 Node2 服务器,验证 Node1 的 VIP 始终存在,应用通过 VIP 访问正常,无任何影响。

六、常见问题与避坑指南(重点)

  1. 主从同步 Slave_IO_Running=No

    • 原因:server-id 重复、防火墙未关、同步账号密码错误、binlog 位置错误;
    • 解决:检查server-id唯一、开放 3306 端口、重新确认同步账号密码、重新获取主库 binlog 位置并配置。
  2. VIP 无法漂移

    • 原因:KeepAlived 检测脚本无执行权限、VRRP 组 ID / 认证密码不一致、网卡名称配置错误、防火墙屏蔽 VRRP 协议;
    • 解决:给脚本加chmod +x、统一双节点virtual_router_id/auth_pass、确认网卡名称、关闭防火墙(生产开放 VRRP 协议:iptables -A INPUT -p vrrp -j ACCEPT)。
  3. 双写 MySQL 出现主键冲突

    • 原因:未配置auto_increment_offsetauto_increment_increment,或配置错误;
    • 解决:双节点分别配置offset=1/step=2offset=2/step=2,重启 MySQL。
  4. 检测脚本不执行

    • 原因:未开启enable_script_security、脚本用户非 root、脚本路径错误;
    • 解决:配置script_user rootenable_script_security、确认脚本路径正确。
  5. 主节点恢复后 VIP 飘回,导致应用抖动

    • 解决:在主备节点均配置nopreempt(非抢占模式),主节点恢复后不主动抢回 VIP,由人工决定是否切回。

七、生产环境优化建议(拓展)

  1. 防火墙 / SELinux:开启防火墙,仅开放 3306(MySQL)、VRRP 协议端口,SELinux 配置白名单;
  2. 密码管理:使用加密方式存储 MySQL/KeepAlived 密码,避免明文写在配置 / 脚本中;
  3. 监控告警:结合 Zabbix/Prometheus 监控 MySQL 主从状态、KeepAlived 状态、VIP 漂移情况,异常及时告警;
  4. 数据备份:双主同步不替代备份,定期执行全量 + 增量备份,防止数据丢失;
  5. 抢占模式:生产建议使用非抢占模式(nopreempt),避免主节点恢复后频繁漂移导致应用抖动;
  6. 多 VIP 配置:若需多实例,可配置多个 VRRP 实例,每个实例对应一个 VIP;
  7. 脚本增强:检测脚本中增加 MySQL 端口检测、磁盘空间检测,避免单一检测维度误判。

八、整体流程总结

  1. 环境准备:关闭防火墙 / SELinux、配置主机名解析、时间同步(两台节点均执行);
  2. MySQL 安装:双节点通过 YUM 安装 MySQL 5.7,初始化密码并授权远程访问;
  3. 双主配置:修改 my.cnf 核心参数(server-id、自增 ID、日志),创建同步账号,互为主从并验证同步状态;
  4. KeepAlived 安装:双节点安装 KeepAlived,编写 MySQL 检测脚本并赋予执行权限;
  5. KeepAlived 配置:分主 / 备节点修改配置文件(仅 3 处差异),启动并验证 VIP 在主节点;
  6. 高可用测试:验证 MySQL 宕机、服务器宕机两种场景下的 VIP 漂移和服务可用性,确认数据同步;
  7. 故障排查:根据常见问题处理同步 / 漂移故障,生产环境做针对性优化。
相关推荐
生成滞涨网络~2 小时前
MySQL 索引优化实战指南:从原理到实践
数据库·mysql
凯子坚持 c2 小时前
Qt常用控件指南(5)
开发语言·数据库·qt
無森~2 小时前
HBase概述、架构
数据库·架构·hbase
qq_312920112 小时前
MySQL数据库备份恢复策略:全量、增量与binlog应用
数据库·mysql
L1624762 小时前
基于 Xenon 实现 MySQL 高可用集群(完整配置教程,含监控告警 + 定时备份)
android·mysql·adb
补三补四2 小时前
Django与模板
数据库·python·django·sqlite
what丶k2 小时前
SQL三大核心查询语法(WHERE/ORDER BY/GROUP BY)综合运用指南
大数据·数据库·sql·mysql·面试
程序辅导开发2 小时前
django体育用品数据分析系统 毕业设计---附源码28946
数据库·vue.js·python·mysql·django·sqlite
工业互联网专业2 小时前
基于Django的智能水果销售系统设计
数据库·vue.js·django·毕业设计·源码·课程设计