MySQL面试题及详细答案 155道(001-020)

发布于:2025-07-29 ⋅ 阅读:(18) ⋅ 点赞:(0)

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

一、本文面试题目录

1. MySQL中的事务是什么?如何使用?

答案
事务是一组原子性的SQL操作,要么全部执行成功,要么全部失败,用于保证数据一致性。MySQL中事务需满足ACID特性(原子性、一致性、隔离性、持久性)。

使用方式
默认情况下,MySQL的InnoDB引擎支持事务,且自动提交(AUTOCOMMIT=1)。可通过以下语句手动控制事务:

-- 关闭自动提交
SET autocommit = 0;

-- 开始事务(可选,关闭自动提交后默认开启)
START TRANSACTION;

-- 执行SQL操作
INSERT INTO users (name) VALUES ('Alice');
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;

-- 提交事务(所有操作生效)
COMMIT;

-- 若出错,回滚事务(所有操作撤销)
ROLLBACK;

原理:InnoDB通过 undo log(回滚日志)实现事务回滚,通过 redo log(重做日志)保证崩溃后的数据恢复,通过锁机制和MVCC(多版本并发控制)实现隔离性。

2. MySQL中如何设置和修改用户密码?

答案
MySQL提供多种方式修改用户密码,需注意不同版本的语法差异(MySQL 5.7及以上推荐ALTER USER)。

示例代码

-- MySQL 5.7及以上推荐方式
ALTER USER 'username'@'host' IDENTIFIED BY 'new_password';

-- 旧版本兼容方式(需有UPDATE权限)
UPDATE mysql.user SET authentication_string = PASSWORD('new_password') 
WHERE User = 'username' AND Host = 'host';
FLUSH PRIVILEGES; -- 刷新权限

-- 登录时修改当前用户密码
SET PASSWORD = 'new_password';

注意PASSWORD()函数在MySQL 8.0中已移除,需直接使用明文密码(内部自动加密)。密码应包含大小写字母、数字和特殊字符,增强安全性。

3. 如何在MySQL中创建新用户并授予权限?

答案
创建用户需指定用户名、允许访问的主机(host),并通过GRANT语句分配权限,权限粒度可细化到库、表甚至列。

示例代码

-- 创建用户(允许从本地访问)
CREATE USER 'new_user'@'localhost' IDENTIFIED BY 'user_password';

-- 创建允许远程访问的用户(指定IP或%表示所有IP)
CREATE USER 'remote_user'@'192.168.1.%' IDENTIFIED BY 'secure_pass';

-- 授予权限:所有库的所有表的全部权限(谨慎使用)
GRANT ALL PRIVILEGES ON *.* TO 'new_user'@'localhost' WITH GRANT OPTION;

-- 授予指定库表的权限(SELECT、INSERT、UPDATE)
GRANT SELECT, INSERT, UPDATE ON mydb.orders TO 'remote_user'@'192.168.1.%';

-- 刷新权限使配置生效
FLUSH PRIVILEGES;

原理:用户信息存储在mysql.user表,权限信息存储在mysql.dbmysql.tables_priv等系统表中,FLUSH PRIVILEGES会重新加载这些表到内存。

4. 什么是视图(View)?如何在MySQL中创建和使用视图?

答案
视图是虚拟表,基于SQL查询结果创建,不存储实际数据,仅保存查询逻辑,用于简化复杂查询、限制数据访问范围。

示例代码

-- 创建视图(查询用户及其订单数量)
CREATE VIEW user_order_stats AS
SELECT u.id, u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name;

-- 使用视图(像表一样查询)
SELECT * FROM user_order_stats WHERE order_count > 5;

-- 修改视图
ALTER VIEW user_order_stats AS
SELECT u.id, u.name, COUNT(o.id) AS order_count, SUM(o.amount) AS total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name;

-- 删除视图
DROP VIEW IF EXISTS user_order_stats;

注意:视图可用于SELECT,但INSERT/UPDATE/DELETE受限于原表结构和视图定义,复杂视图可能不支持写入操作。

5. MySQL中的存储过程是什么?如何创建?

答案
存储过程是预编译的SQL语句集合,可封装复杂逻辑,通过调用名称执行,减少网络传输并提高安全性。

示例代码

-- 创建存储过程(查询指定用户的订单总数)
DELIMITER // -- 修改分隔符,避免与SQL语句中的分号冲突
CREATE PROCEDURE GetUserOrderCount(IN user_id INT, OUT order_count INT)
BEGIN
    SELECT COUNT(*) INTO order_count FROM orders WHERE user_id = user_id;
END //
DELIMITER ; -- 恢复分隔符

-- 调用存储过程
CALL GetUserOrderCount(1, @count);
SELECT @count AS order_count;

优势:代码复用、减少网络交互、权限控制(可授予调用权而不暴露表权限)。

6. 如何在MySQL中调用存储过程或函数?

答案
存储过程通过CALL调用,函数可直接在SQL语句中使用(需有返回值)。

示例代码

-- 调用带参数的存储过程
CALL GetUserOrderCount(1, @count); -- 输入参数1,输出参数@count

-- 创建函数(计算订单总金额)
DELIMITER //
CREATE FUNCTION CalculateOrderTotal(order_id INT) 
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    DECLARE total DECIMAL(10,2);
    SELECT SUM(price * quantity) INTO total FROM order_items WHERE order_id = order_id;
    RETURN total;
END //
DELIMITER ;

-- 调用函数(直接用于SQL语句)
SELECT order_id, CalculateOrderTotal(order_id) AS total FROM orders;

区别:存储过程可无返回值,支持输出参数;函数必须有返回值,可嵌入SQL语句中。

7. 触发器(Trigger)的作用是什么?如何创建触发器?

答案
触发器是与表关联的自动执行的SQL语句,用于在INSERT/UPDATE/DELETE操作前后触发逻辑(如数据校验、日志记录)。

示例代码

-- 创建触发器(订单插入后更新用户订单数)
DELIMITER //
CREATE TRIGGER AfterOrderInsert
AFTER INSERT ON orders
FOR EACH ROW -- 行级触发器,每插入一行执行一次
BEGIN
    UPDATE users SET order_count = order_count + 1 WHERE id = NEW.user_id; -- NEW表示新插入的行
END //
DELIMITER ;

-- 创建更新前的触发器(校验价格不能为负)
DELIMITER //
CREATE TRIGGER BeforeProductUpdate
BEFORE UPDATE ON products
FOR EACH ROW
BEGIN
    IF NEW.price < 0 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '价格不能为负数';
    END IF;
END //
DELIMITER ;

-- 删除触发器
DROP TRIGGER IF EXISTS AfterOrderInsert;

注意:触发器中避免执行耗时操作,可能影响主操作性能;OLD关键字用于UPDATE/DELETE中表示原数据。

8. MySQL中的事件调度器(Event Scheduler)是什么?

答案
事件调度器用于按计划自动执行SQL任务(类似定时任务),可替代外部 cron 作业,适合数据库内部定期操作(如数据清理、统计生成)。

示例代码

-- 开启事件调度器(默认可能关闭)
SET GLOBAL event_scheduler = ON;

-- 创建事件(每天凌晨3点删除30天前的日志)
DELIMITER //
CREATE EVENT PurgeOldLogs
ON SCHEDULE EVERY 1 DAY
STARTS '2023-01-01 03:00:00'
DO
BEGIN
    DELETE FROM logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
END //
DELIMITER ;

-- 查看事件
SHOW EVENTS;

-- 禁用/启用事件
ALTER EVENT PurgeOldLogs DISABLE;
ALTER EVENT PurgeOldLogs ENABLE;

调度类型:支持AT(一次性)、EVERY(周期性),可指定开始和结束时间。

9. 如何查看当前MySQL服务器的状态信息?

答案
通过SHOW命令或系统表查看服务器状态,包括连接数、查询量、缓存使用等关键指标。

示例代码

-- 查看服务器状态变量(如连接数、查询数)
SHOW GLOBAL STATUS; -- 全局状态
SHOW SESSION STATUS; -- 当前会话状态

-- 查看关键指标(如活跃连接数、慢查询数)
SHOW GLOBAL STATUS LIKE 'Threads_connected'; -- 当前连接数
SHOW GLOBAL STATUS LIKE 'Queries'; -- 总查询数
SHOW GLOBAL STATUS LIKE 'Slow_queries'; -- 慢查询数

-- 查看服务器变量配置
SHOW GLOBAL VARIABLES LIKE 'max_connections'; -- 最大连接数
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; -- InnoDB缓冲池大小

-- 查看存储引擎状态
SHOW ENGINE INNODB STATUS; -- InnoDB详细状态(锁、事务等)

应用:通过监控Threads_running(运行中线程数)判断负载,Innodb_buffer_pool_reads(物理读)评估缓存效率。

10. MySQL的复制(Replication)功能是如何工作的?

答案
MySQL复制通过将主库(Master)的binlog(二进制日志)传输到从库(Slave)并执行,实现数据同步,支持读写分离和容灾。

工作流程

  1. 主库:将所有数据修改操作记录到binlog(通过log_bin启用)。
  2. 从库:启动I/O线程连接主库,获取binlog并写入本地relay log(中继日志)。
  3. 从库:SQL线程读取relay log,重放操作以同步数据。

核心组件

  • binlog:主库记录变更的日志(格式可选STATEMENT/ROW/MIXED)。
  • relay log:从库暂存binlog的日志。
  • 复制用户:主库上授权从库连接的用户(需REPLICATION SLAVE权限)。

示例配置(主库)

# my.cnf
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = mydb # 仅记录指定库(可选)

11. 如何配置MySQL主从复制?

答案
配置步骤包括主库准备、从库连接主库并同步初始数据。

示例步骤

  1. 主库配置

    -- 创建复制用户
    CREATE USER 'repl_user'@'slave_ip' IDENTIFIED BY 'repl_pass';
    GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'slave_ip';
    
    -- 锁定主库(避免备份时数据变更)
    FLUSH TABLES WITH READ LOCK;
    
    -- 记录当前binlog位置(文件名和偏移量)
    SHOW MASTER STATUS;
    -- 输出示例:File = mysql-bin.000001, Position = 154
    
  2. 从库配置

    # my.cnf
    server-id = 2
    relay_log = /var/log/mysql/mysql-relay-bin.log
    
  3. 从库连接主库

    -- 配置主库信息
    CHANGE MASTER TO
      MASTER_HOST = 'master_ip',
      MASTER_USER = 'repl_user',
      MASTER_PASSWORD = 'repl_pass',
      MASTER_LOG_FILE = 'mysql-bin.000001', -- 主库SHOW MASTER STATUS的File
      MASTER_LOG_POS = 154; -- 主库SHOW MASTER STATUS的Position
    
    -- 启动复制
    START SLAVE;
    
    -- 检查状态(确保Slave_IO_Running和Slave_SQL_Running均为Yes)
    SHOW SLAVE STATUS\G
    
  4. 解锁主库

    UNLOCK TABLES;
    

12. MySQL中的读写分离是什么意思?如何实现?

答案
读写分离指将读操作分配到从库,写操作保留在主库,以减轻主库压力并提高查询性能(基于主从复制)。

实现方式

  1. 应用层分离:在代码中判断SQL类型,读操作连接从库,写操作连接主库。

    # 伪代码示例
    def execute_sql(sql):
        if sql.strip().upper().startswith(('SELECT', 'SHOW')):
            conn = get_slave_connection()  # 读从库
        else:
            conn = get_master_connection()  # 写主库
        return conn.execute(sql)
    
  2. 中间件分离:使用Proxy(如MySQL Proxy、MyCat、ProxySQL)自动路由,应用无需修改代码。

    • 配置示例(MyCat):通过schema.xml定义主从关系,rule.xml设置路由规则。
  3. 数据库层分离:MySQL 8.0引入的read_only参数,从库设置read_only=1禁止写入(超级用户除外)。

注意:需处理主从延迟问题(如关键读走主库),可通过Seconds_Behind_Master监控延迟。

13. 如何对MySQL数据库进行备份?

答案
MySQL备份分为物理备份(文件级)和逻辑备份(SQL级),各有适用场景。

1. 逻辑备份(mysqldump)

# 备份单个库
mysqldump -u root -p mydb > mydb_backup.sql

# 备份所有库(含系统库)
mysqldump -u root -p --all-databases > all_backup.sql

# 备份表结构(不包含数据)
mysqldump -u root -p --no-data mydb > mydb_schema.sql

# 压缩备份
mysqldump -u root -p mydb | gzip > mydb_backup.sql.gz

恢复

mysql -u root -p mydb < mydb_backup.sql

2. 物理备份(适用于大库)

  • 冷备份:关闭MySQL,复制数据目录(datadir)。
  • 热备份:使用xtrabackup(Percona工具)在不停止服务的情况下备份InnoDB数据。
    xtrabackup --user=root --password=pass --backup --target-dir=/backup/
    

3. 增量备份:基于binlog备份,仅记录上次全量备份后的变更。

# 备份指定时间段的binlog
mysqlbinlog --start-datetime="2023-01-01 00:00:00" --stop-datetime="2023-01-02 00:00:00" /var/log/mysql/mysql-bin.000001 > incremental_backup.sql

14. MySQL中的日志文件有哪些类型?各自的作用是什么?

答案
MySQL日志用于记录运行状态、错误和数据变更,关键日志类型如下:

日志类型 作用 启用方式(my.cnf)
错误日志 记录启动、运行、关闭过程中的错误信息(如崩溃、权限问题) log_error = /var/log/mysql/error.log
binlog(二进制日志) 记录所有数据修改操作(用于复制和时间点恢复) log_bin = /var/log/mysql/binlog
慢查询日志 记录执行时间超过long_query_time的SQL(默认10秒) slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
查询日志 记录所有SQL请求(含读写,性能影响大,一般禁用) general_log = 1
general_log_file = /var/log/mysql/general.log
中继日志 从库复制时暂存主库binlog的日志(仅从库有) relay_log = /var/log/mysql/relay.log
redo log InnoDB用于崩溃恢复的重做日志(确保已提交事务不丢失) 自动启用,innodb_log_file_size配置大小
undo log InnoDB用于事务回滚和MVCC的回滚日志(记录数据修改前的状态) 自动管理,存储在表空间中

15. 如何调整MySQL的配置以提高性能?

答案
性能优化需结合硬件、业务场景调整my.cnf(或my.ini)配置,核心参数如下:

  1. 内存配置

    • innodb_buffer_pool_size:InnoDB缓存表和索引的内存池,建议设为物理内存的50%-70%(专用数据库服务器)。
      innodb_buffer_pool_size = 8G
      
    • key_buffer_size:MyISAM索引缓存,若使用MyISAM表建议设为内存的10%-20%。
  2. 连接配置

    • max_connections:最大并发连接数,需大于业务峰值(默认151)。
      max_connections = 500
      
    • wait_timeout:空闲连接超时时间,避免连接数耗尽(默认8小时)。
      wait_timeout = 300  # 5分钟
      
  3. InnoDB优化

    • innodb_flush_log_at_trx_commit:控制redo log刷新策略,1(默认,最安全,每次提交刷盘)、0(每秒刷盘,性能好但可能丢失1秒数据)。
    • innodb_log_buffer_size:redo log缓冲区,大事务场景调大(默认16M)。
    • innodb_read_io_threads/innodb_write_io_threads:IO线程数,多磁盘可设为8。
  4. 查询优化

    • query_cache_size:查询缓存(MySQL 8.0已移除,低版本慎用,高并发写场景无效)。
    • sort_buffer_size:排序缓冲区,每个连接独占,避免过大导致内存耗尽。
  5. 日志优化

    • 关闭不必要的日志(如general_log),慢查询日志按需开启。

16. MySQL中的查询缓存(Query Cache)是什么?如何启用?

答案
查询缓存是MySQL(5.7及之前版本)用于缓存SELECT语句结果的机制,若相同查询再次执行且数据未变更,直接返回缓存结果,减少CPU和IO消耗。

特点

  • 缓存以SQL语句为键,大小写、空格差异视为不同查询。
  • 表数据发生任何修改(INSERT/UPDATE/DELETE),相关缓存会被清空,因此写密集型场景效率低。

启用与配置(MySQL 5.7):

# my.cnf
query_cache_type = ON  # 启用查询缓存(0=禁用,1=启用,2=仅显式指定SQL_CACHE的查询)
query_cache_size = 64M  # 缓存总大小
query_cache_limit = 2M  # 单个查询结果的最大缓存大小(超过不缓存)

使用示例

-- 显式指定缓存(当query_cache_type=2时生效)
SELECT SQL_CACHE * FROM users WHERE id = 1;

-- 显式禁止缓存
SELECT SQL_NO_CACHE * FROM users WHERE id = 1;

注意:MySQL 8.0已移除查询缓存(因维护成本高且不适用于高并发场景),替代方案是应用层缓存(如Redis)或优化查询本身。

17. 在MySQL中如何使用全文索引(Full-Text Indexing)?

答案
全文索引用于高效搜索文本字段中的关键词(支持自然语言搜索),适用于CHARVARCHARTEXT类型,优于LIKE '%关键词%'(无法使用普通索引)。

使用步骤

  1. 创建全文索引

    -- 新建表时创建
    CREATE TABLE articles (
        id INT PRIMARY KEY AUTO_INCREMENT,
        title VARCHAR(255),
        content TEXT,
        FULLTEXT INDEX ft_idx (title, content)  -- 多列联合全文索引
    );
    
    -- 已有表添加
    ALTER TABLE articles ADD FULLTEXT INDEX ft_idx (title, content);
    
  2. 使用全文搜索

    -- 搜索包含"database"或"mysql"的记录
    SELECT * FROM articles 
    WHERE MATCH(title, content) AGAINST('database mysql' IN BOOLEAN MODE);
    
    -- 搜索必须包含"database"且不包含"oracle"的记录
    SELECT * FROM articles 
    WHERE MATCH(title, content) AGAINST('+database -oracle' IN BOOLEAN MODE);
    
    -- 自然语言搜索(默认,返回相关性排序)
    SELECT *, MATCH(title, content) AGAINST('mysql tutorial') AS relevance 
    FROM articles 
    WHERE MATCH(title, content) AGAINST('mysql tutorial' IN NATURAL LANGUAGE MODE);
    

限制

  • 忽略太短的词(默认4个字符以下,可通过ft_min_word_len调整)。
  • 有_stopword_(停用词,如"the"、“a”)不参与索引。
  • InnoDB和MyISAM均支持,但MyISAM不支持中文分词(需第三方插件如ngram)。

18. MySQL支持哪些不同的字符集和排序规则?

答案
字符集决定存储的字符范围,排序规则决定字符比较方式(如大小写敏感)。

常用字符集

  • utf8:支持基本Unicode字符(3字节,不支持emoji)。
  • utf8mb4:utf8的超集(4字节,支持emoji和所有Unicode字符,推荐)。
  • latin1:单字节字符集,支持西欧语言。
  • gbk:双字节,支持中文字符。

排序规则命名规则

  • 以字符集名开头(如utf8mb4_)。
  • ci:不区分大小写(case insensitive),cs:区分大小写。
  • ai:不区分重音(accent insensitive),as:区分重音。

示例配置

-- 创建库时指定字符集和排序规则
CREATE DATABASE mydb 
CHARACTER SET utf8mb4 
COLLATE utf8mb4_general_ci; -- 不区分大小写

-- 创建表时指定
CREATE TABLE users (
    name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin -- 二进制比较(区分大小写)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 查看支持的字符集和排序规则
SHOW CHARACTER SET;
SHOW COLLATION LIKE 'utf8mb4%';

最佳实践:新系统统一使用utf8mb4utf8mb4_unicode_ci(支持更准确的Unicode排序)。

19. 如何在MySQL中执行批量插入?

答案
批量插入通过单条SQL插入多条记录,减少网络交互和事务开销,提高效率。

示例代码

-- 基本批量插入(多条值用逗号分隔)
INSERT INTO users (name, email)
VALUES 
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Charlie', 'charlie@example.com');

-- 从查询结果批量插入
INSERT INTO user_archives (id, name, archive_time)
SELECT id, name, NOW() FROM users WHERE last_login < '2023-01-01';

-- 批量插入优化(调整参数)
-- 1. 关闭自动提交
SET autocommit = 0;
-- 2. 执行批量插入
INSERT INTO large_table (col1, col2) VALUES (...), (...), ...;
-- 3. 手动提交
COMMIT;

优化建议

  • 单次插入行数控制在1000-5000行(避免SQL过长导致内存问题)。
  • 禁用索引(临时):ALTER TABLE table DISABLE KEYS;,插入后启用:ENABLE KEYS;(仅MyISAM有效,InnoDB可忽略)。
  • 使用LOAD DATA INFILE(比INSERT更快,适合大批量数据):
    LOAD DATA INFILE '/tmp/users.csv'
    INTO TABLE users
    FIELDS TERMINATED BY ',' 
    LINES TERMINATED BY '\n'
    (name, email);
    

20. MySQL中的临时表(Temporary Table)是什么?

答案
临时表是会话级别的表,仅在当前连接可见,连接关闭后自动删除,用于存储中间结果(如复杂查询的临时数据)。

特点

  • 与普通表同名时,临时表优先被访问。
  • 支持索引、触发器,但不支持外键。
  • InnoDB临时表存储在内存或磁盘(超过内存阈值时),MyISAM临时表始终在磁盘。

示例代码

-- 创建临时表
CREATE TEMPORARY TABLE temp_orders (
    order_id INT,
    total_amount DECIMAL(10,2)
);

-- 插入数据
INSERT INTO temp_orders 
SELECT id, SUM(price * quantity) FROM order_items GROUP BY id;

-- 使用临时表
SELECT o.user_id, t.total_amount 
FROM orders o
JOIN temp_orders t ON o.id = t.order_id;

-- 手动删除(可选,连接关闭自动删除)
DROP TEMPORARY TABLE IF EXISTS temp_orders;

适用场景

  • 复杂查询中分步处理数据(如多阶段统计)。
  • 存储会话私有数据(避免多用户冲突)。
  • 替代子查询(提高可读性和性能)。

网站公告

今日签到

点亮在社区的每一天
去签到