最近参加了一场技术面试,涉及了不少 MySQL 相关的问题。面试结束后,我对问题进行了复盘,并整理了答案和思路,希望对大家有所帮助。以下是具体问题及解答:
1. MySQL中有哪些系统表,规定了哪些内容,能否把表格输出出来?
MySQL 中的系统表主要存储在 information_schema
数据库中,用于提供数据库的元数据信息。以下是一些常见的系统表及其作用:
系统表名 | 作用描述 |
---|---|
TABLES |
提供所有表的信息,如表名、引擎、行数等 |
COLUMNS |
提供表中列的信息,如列名、数据类型、是否为空等 |
SCHEMATA |
提供数据库信息,如数据库名、字符集等 |
STATISTICS |
提供索引统计信息,如索引名、列名、基数等 |
PARTITIONS |
提供表分区信息 |
KEY_COLUMN_USAGE |
提供主键和外键约束的列信息 |
TRIGGERS |
提供触发器的信息,如触发器名、触发事件等 |
这些系统表是只读的,主要用于查询数据库结构和状态。例如,可以通过 SELECT * FROM information_schema.TABLES
查看所有表的信息。
2. MySQL记录货币有什么数据类型好?
在 MySQL 中记录货币时,推荐使用 DECIMAL
(或 NUMERIC
)数据类型。原因如下:
- 精度高 :
DECIMAL(M, N)
可以精确指定总位数(M)和小数位数(N),避免浮点数(如FLOAT
或DOUBLE
)的精度丢失问题。 - 适合金融场景 :货币计算需要精确到分(例如 2 个小数位),
DECIMAL(10, 2)
表示总共 10 位,其中 2 位是小数,足以满足大多数需求。
示例:
sql
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
price DECIMAL(10, 2) NOT NULL
);
不推荐:
FLOAT
或DOUBLE
:浮点数可能导致舍入误差。INT
:需要手动换算单位(如存储为分),不够直观。
3. InnoDB 和 MyISAM 的区别?
InnoDB 和 MyISAM 是 MySQL 中两种常见的存储引擎,以下是它们的主要区别:
特性 | InnoDB | MyISAM |
---|---|---|
事务支持 | 支持(ACID 事务) | 不支持 |
外键 | 支持 | 不支持 |
锁机制 | 行级锁 | 表级锁 |
崩溃恢复 | 支持(通过日志恢复) | 不支持 |
全文索引 | 5.6 之后支持 | 原生支持 |
性能 | 写多读少时较优 | 读多写少时较优 |
存储结构 | 聚簇索引(数据和索引一起) | 非聚簇索引(分开存储) |
选择建议:
- 如果需要事务和数据一致性(如金融系统),选择 InnoDB。
- 如果是只读或写少场景(如日志系统),MyISAM 可能更高效。
4. 如何提高 MySQL 的 INSERT 性能?
提升 INSERT
性能可以从以下几个方面入手:
- 批量插入 :使用
INSERT INTO table VALUES (), (), ()
减少单次插入的开销。 - 关闭索引或延迟建索引 :插入数据前禁用索引(
ALTER TABLE table_name DISABLE KEYS
),完成后重建。 - 调整事务:将多次插入放入一个事务中,减少日志同步开销。
- 优化配置 :
- 增大
innodb_buffer_pool_size
,缓存更多数据。 - 调整
innodb_log_file_size
,减少日志切换。
- 增大
- 使用 LOAD DATA INFILE :对于大批量数据导入,效率远超普通
INSERT
。 - 分区表:对大表进行分区,分散写压力。
示例(批量插入):
sql
INSERT INTO users (name, age) VALUES ('Alice', 25), ('Bob', 30), ('Charlie', 35);
5. InnoDB 是行锁,合适会产生行锁,何时是表锁?最初的锁是邻键锁,什么时候会退化为间隙锁和记录锁?
行锁、表锁场景
InnoDB 默认使用行级锁 ,但在以下情况可能升级为表锁:
- 执行
LOCK TABLES
或 DDL 操作(如ALTER TABLE
)。 - 查询没有命中索引,导致全表扫描(表锁或大量行锁)。
- 锁冲突过多,InnoDB 可能主动升级为表锁。
锁类型
InnoDB 的锁包括:
- 记录锁(Record Lock):锁定具体一行记录。
- 间隙锁(Gap Lock):锁定某个范围内的间隙,防止插入。
- 邻键锁(Next-Key Lock):记录锁 + 间隙锁的组合,默认用于范围查询。
锁退化
- 邻键锁退化为间隙锁:当查询范围中没有具体记录命中时,只需防止插入,退化为间隙锁。
- 邻键锁退化为记录锁:当查询条件精确匹配唯一索引(如主键),只需锁住单行,退化为记录锁。
示例:
sql
SELECT * FROM users WHERE id = 10 FOR UPDATE; -- 记录锁(主键精确匹配)
SELECT * FROM users WHERE id > 5 AND id < 10 FOR UPDATE; -- 邻键锁(范围查询)
6. 新创建的 MySQL 数据库要调整哪些参数?
新数据库上线前,建议调整以下参数(根据硬件和业务需求):
innodb_buffer_pool_size
:缓冲池大小,建议设置为物理内存的 60%-80%,用于缓存数据和索引。innodb_log_file_size
:日志文件大小,建议 128M-512M,提升写性能。max_connections
:最大连接数,默认 151,视并发量调整。tmp_table_size
和max_heap_table_size
:临时表和内存表大小,建议 64M 起。innodb_flush_log_at_trx_commit
:- 设为 1(默认):每次事务提交刷盘,安全性高。
- 设为 2:提升性能,但可能丢失 1 秒数据。
character_set_server
和collation_server
:设置字符集(如utf8mb4
)和排序规则。
调整示例(my.cnf):
ini
[mysqld]
innodb_buffer_pool_size = 2G
innodb_log_file_size = 256M
max_connections = 500
7. MySQL 发现 CPU 或者 IO 压力非常大,你会怎么定位问题?
CPU 压力大
- 检查慢查询 :开启
slow_query_log
,分析耗时 SQL(EXPLAIN
查看执行计划)。 - 查看连接数 :
SHOW PROCESSLIST
检查是否有大量活跃线程。 - 分析锁冲突 :
SHOW ENGINE INNODB STATUS
查看死锁或锁等待。 - 系统工具 :用
top
或htop
确认 MySQL 进程 CPU 占用。
IO 压力大
- 检查磁盘 IO :用
iostat
或vmstat
查看磁盘读写情况。 - 分析日志写入 :检查
innodb_flush_log_at_trx_commit
设置,频繁刷盘可能导致 IO 高。 - 优化查询:未命中索引的查询会导致大量物理读。
- 调整缓冲池 :
innodb_buffer_pool_size
过小会导致频繁换页。
定位流程
- 先用
SHOW FULL PROCESSLIST
查看当前执行的 SQL。 - 用
EXPLAIN
优化低效查询,添加索引。 - 检查服务器资源瓶颈,调整参数或升级硬件。