1. MySQL 所有使用有哪些注意事项?
可以从三个维度回答这个问题:索引哪些情况会失效,所以不适合哪些场景,索引一些规律特性。
1.1. 所有哪些情况会失效
在 InnoDB 引擎里有两种索引类型,一种是主键索引,一种是普通索引。 InnoDB 使用 B+Tree 的结构来存储索引数据。当使用索引列进行数据查询的时候,最终会到主键索引树中查询对应的数据进行返回。
理论上来说,使用索引列查询,就可以很好的提高查询效率,但是不规范的使用会导致索引失效,从而无法发挥索引本身的价值。导致索引失效的情况有很多:
- 在索引列上做运算
比如使用函数,MySQL 在生成执行计划的时候,根据统计信息判断是否要使用索引。而在索引列上加函数运算,会导致 MySQL 无法识别索引列,所以也就不再走索引了。不过从 MySQL8.0 开始,增加了函数索引可以解决这个问题。
- 组合索引上未按照最左匹配法则
在组合索引中,需要按照最左匹配法则,也就是从索引的最左列开始顺序检索,否则不会走索引。在组合索引中,索引的存储结构是按照索引列的顺序来存储的,因此在 SQL 中也需要按照这个顺序才能逐一匹配。否则 InnoDB 无法识别索引会导致索引失效。
- 索引列存在隐式转换
当索引列存在隐式转换的时候,例如索引列是字符串类型,但是在 SQL 中没有使用引号,那么 MySQL 会自动进行类型转化,从而导致索引失效。
- 索引列中使用 “!=”,“NOT”
在索引列中使用“!=”,“NOT”查询时,由于索引数据的检索效率非常低,因此 MySQL 引擎也不会判断走索引。
- 索引列 “LIKE” 错误匹配
在使用 “LIKE” 通配符匹配后缀 “%xxx” 的使用,由于这种方式不符合索引的最左匹配原则,所以也不会走索引。但是反过来则符合最左匹配,会走索引。
- 使用 “OR” 连接查询
“OR” 语句前后没有同时使用索引,那么索引会失效。只有“OR”左右查询字段都是索引列时,索引才会生效。
除了这些场景外,在多表连接查询场景中,连接顺序也会影响索引的使用。不过最终能否走索引,可以使用 “EXPLAIN” 命令来查看 SQL 的执行计划,然后针对性的进行调优。
1.2. 索引不适合哪些场景
这里简单列举三个场景:
- 数据量少不用添加索引;
- 更新频繁不适合添加索引;
- 区分度低的字段不用添加索引,比如“性别”。
1.3. 索引的一些潜规则
- 覆盖索引
“覆盖索引”是一种优化技术,可以通过避免对实际数据的检索,而是直接使用索引数据提高查询性能。当查询结果集只需要索引列时,“覆盖索引”可以避免“回表”操作,减少 I/O 操作,提高查询性能。“覆盖索引”适用于一些经常查询的列数据,这些列恰好被覆盖的场景。
- 回表
在数据库中进行查询操作时,需要查询的数据并不在索引数据中,需要通过索引数据再次去主表中查询主表数据。这个过程即为“回表”。 “回表”会增加查询时间和性能开销,为了避免“回表”操作,可以使用“覆盖索引”或“联合索引”来查询数据。
- B+Tree数据结构
是索引最常用的一种数据结构,主要特点就是将数据存储在叶子节点中,减少 I/O 消耗,提高查询效率。
- 最左前缀原则
在使用联合索引查询时,MySQL 只能使用索引的最左前缀进行查询,而不能直接使用索引的中间或者尾端部分。例如一个联合索引字段“(A,B, C)”,只有按照(A)或者(A, B)或者(A, B, C)的顺序查询才能使用该索引。
- 索引下推
“索引下推”是一种优化技术。可以在数据查询过程中更早的筛选数据,从而检查查询需要处理的数据量,提高查询性能。利用“覆盖索引”特性,在查询过程中,尽可能多的使用索引中的数据,减少不必要的数据访问。
2. 日常工作中如何优化 SQL
2.1. MySQL 的性能优化可以分为四个部分:
- 硬件和操作系统层面的优化
从硬件层面来说,影响 MySQL 性能的因素包括 CPU,可用内存大小,磁盘读写速度,网络带宽等;从操作系统层面来讲,应用文件句柄数,操作系统网络配置等都会影响到 MySQL 的性能。当然这部分优化通常是 DBA 来完成。
- 架构设计层面的优化
MySQL 是一个磁盘 IO 密集的数据库,在高并发高负载情况下,MySQL 必然承受比较大的压力。优化可以分为以下几部分
1)搭建 MySQL 主从集群,提高可用性,避免单机故障;
2)实现读写分离设计,避免读写冲突产生性能问题;
3)分库分表:分库可以降低单个节点 IO 压力,分表可以降低单表数据量,提高 SQL 查询效率。
4)热点数据引入高效分布式数据库,例如 MongoDB,Redis 等,缓解数据库压力的同时,还可以提高查询效率。
- MySQL 程序配置优化
MySQL是一款被普遍验证的成熟的数据库,通常对 MySQL 本身的优化是通过修改其 my.cnf 文件完成,例如最大连接数,binlog 日志设置,缓存池默认大小等。关于如何配置 MySQL,主要是根据 MySQL 安装的环境,参数作用范围等因素。
- SQL 优化
首先定位慢 SQL,通过慢日志和慢日志分析工具分析具体执行慢的SQL语句列表;然后针对慢 SQL 语句使用 explain 执行计划重点关注 type,key,rows,filterd 等字段,定位 SQL 执行慢的根本原因,针对性的进行优化;其次,使用 show profile 工具用来分析当前会话中消耗资源开销情况(例如 I/O,CPU,内存等。默认关闭状态)。
2.2. 常见的 SQL 优化规则
- SQL 查询要基于索引来进行数据扫描;
- 避免索引列使用函数或者运算,避免索引失效;
- WHERE 子句中的 LIKE 中的 “%” 要放到右边;
- 联合索引查询时从左往右,越多越好;
- SQL 查询排序尽量使用索引排序;
- 查询有效的列信息,避免使用“*”。
3. 说下分库分表的设计
3.1. 分库分表方案
- 水平分库/分表:以字段为维度,按照一定策略(hash,range 等),将一个库/表中的数据拆分到多个库/表中。
- 垂直分库:以表为维度,按照业务归属,将不同的表拆分到不同的库中。
- 垂直分表:以字段为维度,按照字段的活性,将表中的字段拆到不同的表中。
3.2. 常见分库分表中间件
- sharding-jdbc(当当)
- Mycat
- TDDL(淘宝)
- Oceanus(58数据库中间件)
- vitess(Google)
- Atlas(360)
分库分表是一种数据库水平切分的策略,旨在提高数据库的性能、可扩展性和容量。
3.3. 常见分库分表场景
- 大数据量应用
当应用的数据量巨大时,单一数据库可能难以处理如此大的数据量。通过分库分表,可以将数据分散存储在多个数据库中,提高查询和写入的效率。
- 高并发应用
在高并发的应用场景下,单一数据库可能成为性能瓶颈。通过分库分表,可以将负载均衡到多个数据库节点上,减轻数据库服务器的压力,提高并发处理能力。
- 多租户系统
在多租户系统中,不同租户的数据需要隔离存储,以确保数据安全性和隐私。通过分库分表,可以将不同租户的数据存储在不同的数据库或表中,实现数据隔离。
- 垂直切分
当数据表包含大量列,而某些列只在特定的业务场景下被频繁访问时,可以通过垂直切分将这些列分割到单独的表中,减少查询时的数据传输和提高查询效率。
- 水平切分
当数据表的行数非常庞大时,可以通过水平切分将表按照某个规则分割成多个表,每个表存储部分数据。例如,可以按照用户ID范围或日期范围进行水平切分。
- 地域分布式系统
对于地域性的应用,为了降低访问延迟,可以将数据存储在全球各地的数据库节点上,通过分库分表来实现数据的地域分布。
- 提高查询性能
当某个表的查询性能逐渐变慢时,可以考虑对其进行分表,将数据按照一定的规则拆分成多个小表,提高查询速度。
- 按业务拆分
根据业务需求,将不同业务模块的数据分别存储在不同的数据库或表中,以简化业务逻辑,提高系统的可维护性。
3.4. 分库分表可能遇到的问题
1)需要考虑设计分布式事务;
2)跨节点级联 JOIN 查询,通常解决方案是分两次查询实现;
3)跨节点 COUNT,ORDER BY,GROUP BY 等聚合函数使用,需要将各个节点的结果在应用程序段进行合并。
4. InnoDB 和 MyISAM 的区别
MyISAM 和 InnoDB 的区别有四个:
1)数据存储方式不同,MyISAM 索引文件和数据文件分开存储,InnoDB 存储在同一个文件中。
2)对事务支持不同,MyISAM 不支持事务,InnoDB 支持 ACID 特性的事务处理。
3)锁的支持不同,MyISAM 只支持表锁,InnoDB 根据情况支持行锁,表锁,间隙锁,临间锁等。
4)1. MyISAM 不支持外键,InnoDB 支持外键
5. 数据库索引的原理?为什么使用的是 B+Tree,而不是二叉树
索引的原理实际上就是数据表中的某些列的值按照一定的数据结构存储,以提高查询效率。索引通常采用的是 B+Tree,它是一种平衡二叉树,在存储和查询时会更高效。
- B+Tree 结构更扁平,节点可以存储更多的数据。树的高度降低,从而降低了查询的时间复杂度。
- B+Tree 的叶子节点形成一个有序链表,在范围查询和排序上会更高效。
- B+Tree 的非叶子节点不存储数据,只存储索引,会比较节省空间;
- B+Tree 更适合磁盘存储,扁平结构和有序链表会减少磁盘 I/O 次数,提高磁盘访问效率。
6. 聚集索引与非聚集索引
简单理解的话,基于主键创建的索引就是聚集索引,其它字段都是非聚集索引,也叫作二级索引。聚集索引中的数据按照索引的键值进行排序,并且把整张表的数据存储按照索引排序后的数据页中,这样索引的叶子节点就是存储了整张表的数据页。对于一张表只有一个聚集索引。
非聚集索引也叫作辅助索引,指的是索引的键值和数据页的指针绑定在一起。这个指针指向的是包含实际数据的数据页。一个表可以有多个非聚集索引,因此可以以多种方式定义索引键。
聚集索引与非聚集索引的区别是:
- 存储方式不同
聚集索引的叶子节点存储完整的表数据,而非聚集索引存储的是索引的键值和指向实际数据页的指针。
- 查询方式不同
使用聚集索引查询数据时,可以直接查询到表数据,而非聚集索引则需要先查询到实际数据页指针,再通过指针访问实际数据页。
- 更新数据时的影响不同
聚集索引更新数据时需要移动整张表的数据,而非聚集索引只需要更新索引键值和指针即可。
- 唯一性限制不同
聚集索引的键值必须保证唯一,非聚集索引的键值可以重复。
7. LIMIT 1000000 加载很慢,通常如何处理
当使用 limit 查询的结果很大时,必然会导致加载很慢。因为查询的是完整的数据集,而不是前面几条数据。以下是一些可能的解决方案:
- 优化查询条件
尽可能使用索引,WHERE 子句限制返回的数量,避免不必要的连接或子查询。
- 分批查询
可以将较大的结果集拆分为多个小的结果集,例如分页技术,类游标查询等。
- 数据缓存
可以使用缓存将结果集放到内存中,但是要注意缓存更新问题。
- 数据分区
对于非常大的表,可以将表分区为较小的表,每个表有独立的索引和数据文件,也可以提高查询性能,同时降低锁表和同步时的开销。
- 业务优化
对于题中的需求,可以考虑是否必要这么做?真实的需求下并不会查询这么多的数据。
8. 事务的隔离级别有哪些?MySQL 默认的事务隔离级别是什么?
事务的隔离级别指的是多个事务在并发执行的时候,如何控制多个事务之间的影响性,常见的事务隔离级别有四种:
- 读未提交
允许一个事务读取另一个未提交事务的数据。该隔离级别的并发性能最高,但是会导致脏读(Dirty Read),即读取到未提交的数据;
- 读已提交
要求一个事务只能读取到另一个事务已提交的数据。该隔离级别可以避免脏读,但是会导致不可重复读(Non-Repeatable Read),即同一个事务内两次读取的数据行不一致;
- 可重复读
要求一个事务读取的数据是其开启事务以来已经提交的数据,其它事务提交的数据对其不可见。该隔离级别可以避免脏读和不可重复读,但是会导致幻读(Phantom Read),即同一个事务两次查询得到的结果不一致;
- 序列化
要求一个事务完全串行化执行,事务之间互不干扰。该事务隔离级别可以避免脏读、不可重复读和幻读,但是并发性能最差。
MySQL 默认的事务隔离级别是可重复读,可以使用 SET TRANSACTION 来设置事务的隔离级别。例如:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
9. 高并发场景下,如何做到安全的修改同一行数据
高并发场景下,要保证多个线程同时修改同一条数据的安全性,可以使用以下四种方案:
- 乐观锁
在记录上添加一个版本号,当多个线程同时访问时,先读取版本号,如果版本号不一致,则说明有其它线程在修改该记录,需要回滚并重新读取;如果版本号一致,则可以修改该记录并更新版本号。
- 悲观锁
在读取记录时,直接加锁。其它线程在读取记录时只能等待锁释放之后才能访问该记录。悲观锁的缺点是会降低并发性能,因为所有的线程都需要等待锁的释放。
- CAS(Compare And Swap)算法
利用 CPU 提供的 CAS 指令,先读取数据,再去比对该数据期望的值是否一致,如果一致则更新数据,否则重试。CAS 是一种无锁的算法,因此可以提高并发性能。
- 分布式锁
在分布式场景下,可以采用分布式锁来保证数据安全性。常见的分布式锁可以使用 Zookeeper,Redis 等中间件。
10. SQL 优化的一般步骤?如何查看执行计划,如何理解其中各个字段的含义。
SQL 优化的一般步骤是:
- 确认问题,分析业务场景和需求;
- 评估性能瓶颈,查看哪些 SQL 语句耗时比较长;
- 使用工具手机相关性能数据,例如执行计划,慢查询日志等;
- 分析数据,找出瓶颈,确认优化方向;
- 优化 SQL,修改 SQL 语句,建立索引,提高性能;
- 评估优化结果,确认优化是否达到预期目标。
查询 SQL 执行计划,要是用 EXPLAIN 关键字,执行计划通常包括如下几个字段:
- id: 查询的标识符,每个查询都有一个唯一的标识符;
- select_type: 查询的类型,例如 SIMPLE、PRIMARY、DERIVED、SUBQUERY 等;
- table: 表的名称;
- type: 查询的类型,例如 ALL、INDEX、RANGE、REF、CONST 等;
- key: 实际使用的索引;
- key_len: 索引使用的长度;
- ref: 索引的匹配条件;
- rows: 查询影响的行数;
- Extra: 额外的信息,例如是否使用了临时表,排序方式等。
11. SELECT FOR UPDATE 的含义
SELECT FOR UPDATE 用于在数据库中锁定一行或多行数据进行更新,具体来说它会对查询结果集中的每一行加行级锁,以避免其它事务对这些行进行修改和删除。这个语句通常用于并发环境需要对数据进行修改时,以保证操作的原子性和数据一致性。
在使用 SELECT FOR UPDATE 时需要注意以下几点:
- 当执行这个语句时,MySQL 会自动将查询结果中的每一行都添加行级锁,并将锁持有到事务结束或手动释放锁为止。因此在执行该语句时需要确保 SQL 语句执行时间不会过长,并且不会阻塞其它事务的执行。
- 在查询结束之前,其它事务不能对查询结果中的行进行修改删除,否则可能造成死锁。
- 可以与 WHERE 语句一起使用(作用索引,如果没有索引,则MySQL只能锁表),只锁定符合条件的行,而不是整张表。
在使用这个语句时,需要考虑并发和性能问题,避免出现锁的竞争和死锁的情况。同时也可以根据具体的业务场景选择合适的锁机制,如悲观锁,乐观锁等。
12. MySQL 事务的四大特性以及实现原理
MySQL 事务具有四大特性,通常被称为 ACID 特性,即原子性,一致性,隔离性和持久性。
- 原子性
是指事务中要么全部执行成功,要么全部不执行。事务中的任何一个操作失败,都会导致整个事务的回滚,回到事务提交之前的状态。
实现原理:MySQL 使用 undo log(回滚日志)来实现原子性。当事务的执行过程中出现错误或用户选择手动回滚事务时,MySQL 会根据 undo log 记录的历史信息回滚事务,保证事务的原子性。
- 一致性
指事务的执行必须为数据库从一个一致性状态转换为另一个一致性状态。简单来说就是事务开始之前和结束之后应满足业务条件和业务规则。
实现原理:一致性依赖原子性,隔离性和持久性。使用隔离级别来隔离并发事务以及使用 redo log (重做日志)确保数据的持久化。
- 隔离性
是指每个事务都是独立的,不能互相干扰。换句话说,一个事务的执行不能看到另一个事务尚未提交的数据。
实现原理:使用锁,并发控制(MVCC)来实现隔离性。锁可以分为共享锁和排它锁,锁的互斥关系可以防止不同事务对同一数据同时进行修改。MVCC 通过创建数据的不同版本,允许不同事务同时读取数据的不同版本,从而实现隔离性。这里涉及到四个事务的隔离级别:读未提交,读已提交,可重复读,串行化。
- 持久性
是指事务一旦提交,对数据库的更改就是永久性的,即使在事务提交之后出现系统崩溃,更改的数据也不会丢失。
实现原理:使用 redo log(重做日志)来实现持久性。当事务提交时,MySQL 会将事务修改信息写入 redo log,即使系统崩溃,MySQL 也可以通过对 redo log 的重放。
13. 千万条数据的表,CRUD 速度很慢如何优化
- 适当的索引
为经常参与条件查询和排序的列添加索引,可以显著提升查询速度,但是,不要过度的创建索引,会导致插入和修改性能降低。
- 分页查询优化
避免使用 OFFSET 大分页查询,尽量使用 WHERE 结合 LIMIT 进行分页查询数据。
- 慢查询优化
使用慢查询日志找出查询比较慢的 SQL,分析执行计划,优化 SQL 查询。
- SQL 语句优化
SQL 语句编写得当,避免全表扫描,子查询,JOIN 等降低性能的操作。
- 数据库参数调优
根据服务器的硬件资源,适当调整数据库参数,如缓冲区大小,连接数,线程数等。
- 使用缓存
对于热点数据可以使用缓存(Redis)来存储数据,降低对数据库的负担。
- 数据库分区
对于数据分布不均或者有明显分组特征的表,可以考虑分区技术,将数据分布到不同的物理文件中,提高查询效率。
- 数据库分表
根据业务需求,将一个大表拆分成多个小表。分表可以减少单表的数据量,提高查询效率。
- 读写分离
将读请求和写请求分布到不同的数据库服务器上,使得数据库服务器可以专注于读或写请求,从而提高整体的性能。
- 数据库集群和负载均衡
使用数据库集群和负载均衡技术,将请求分发到多个数据库服务器上,提高数据库的并发处理能力。
优化数据库需要针对具体的业务场景和数据特点进行分析,再优化前一定要做好数据的备份操作,避免数据丢失。实际操作中,可能需要结合多种方案,选择合适的方式进行优化。
14. 如何写 SQL 能够有效地使用复合索引
- 索引字段的顺序
复合索引中字段顺序很重要,因为查询中只能使用索引的前缀。因此,应该把最常用的字段放在前面,能够满足查询条件的字段应该尽量靠前。
- 索引字段的数量
复合索引中的字段数量也很重要,因为索引的大小会影响查询的性能。如果索引太大,查询中需要读取更多的数据块,而且更新数据时也需要维护更多的索引。因此,应该避免创建过多的索引,只创建必要的索引。
- 覆盖索引
如果查询只需要访问索引字段,而不需要访问表中的其它字段,那么就可以使用覆盖索引来提高查询性能。覆盖索引可以减少查询时要读取的数据块数量,从而提高查询效率。
- 使用 IN 查询
如果需要查询的字段是一个离散值列表,可以使用 IN 查询来利用复合索引。
- 避免使用函数或表达式
在查询条件中使用函数或表达式可能会导致索引失效,因为索引只能用于直接比较的查询条件。如果需要使用函数或表达式,应该在查询之前计算出来,然后再将结果作为查询条件。
- 避免使用 OR 查询
使用 OR 查询可能会导致索引失效,因为索引只能用于直接比较的查询条件。如果需要使用 OR 查询,应该需要将查询条件拆分成多个子查询,并且使用 UNION ALL 将结果合并起来。
- 避免使用通配符查询
通配符查询可能会导致索引失效(%),因为索引只能用于前缀匹配的查询条件。如果必须使用通配符查询,应该将通配符放在查询条件的末尾。
15. MySQL 中 IN 和 EXISTS 的区别
MySQL 中的 IN 和 EXISTS 都是用来查询符合某个条件数据的。
- IN 语句用于指定一个条件列表,从中选择符合条件的所有数据。
- EXISTS 语句则是用于检查是否有符合条件的数据,如果子查询返回结果不为空,则 EXISTS 的条件为 true。
IN 和 EXISTS 的区别主要在于查询的方式和对查询结果的处理。IN 查询是将指定的条件列表和需要查询的数据表进行匹配,如果匹配成功,则返回所有符合条件的数据。EXISTS 查询是先执行子查询,查看子查询是否有符合条件的数据。
此外,IN 查询通常可以使用复合索引。而 EXISTS 查询则需要在子查询和主查询中都使用索引。
16. 数据库自增主键可能遇到什么问题
- 超出范围
如果使用 int 类型的自增主键,当达到 2^31 - 1 的上限时,再次插入新的记录会出现错误。可以考虑使用 bigint 类型,但是这样就会浪费一定的存储空间。
- 主键冲突
虽然自增主键可以确保每条记录都有唯一的标识符,但是在分布式系统中,多个节点同时插入数据的时候可能会出现主键冲突的情况。可以使用分布式 ID 生成器来避免这个问题。
- 数据库迁移问题
当将一个数据库迁移到另一个数据库时,自增主键可能会遇到问题。如果新数据库中已经存在相同的主键ID值,则插入新纪录时可能会出现冲突。可以使用其它的主键生成方式,例如 UUID,雪花算法来避免这个问题。
17. MySQL 主从延迟是怎么产生的,又如何解决?
MySQL 主从延迟是指在主从复制过程中,从服务器上的数据与主服务器上的数据之间存在一定的延迟。这种延迟可能导致从服务器上的查询结果可能不是最新的。产生主从延迟的原因有:
- 网络延迟
主从服务器之间的网络延迟会影响数据的复制速度。如果网络带宽较低或网络不稳定,可能导致复制延迟。
- 主服务器负载过高
如果主服务器的写操作(CUD)非常频繁,可能导致主服务器的 binglog 生成速度过快,从服务器来不及应用这些更改。
- 从服务器性能不足
从服务器应用主服务器的 binlog 时需要执行相应的 SQL 操作。如果从服务器硬件性能不足,处理能力较低,可能导致主从延迟。
- 复制模式
MySQL 的复制可以是同步或异步。异步模式下,主服务器在写入 binlog 时候不等待从服务器确认就继续执行其他操作,这可能导致主从延迟。
针对上述产生主从延迟原因,可以尝试使用以下方法:
- 优化网络:
提高主从服务器之间的网络质量和带宽,以减少网络延迟。
- 优化主服务器:
减少主服务器上的写操作负载,例如通过分区、分片或其它优化策略。可以考虑将读操作转移到从服务器,以减轻主服务器的负担。
- 优化从服务器:
提高从服务器的硬件性能,例如增加CPU,内存或提高磁盘I/O性能。
- 使用半同步复制:
半同步复制是介于同步复制和异步复制之间的一种模式。在这种模式下,主服务器在写入 binlog 之后会至少等待一个从服务器确认接收到数据,然后继续执行其他操作。这样可以在一定程度上减少主从延迟。
- 并行复制
MySQL5.6及更高的版本支持从服务器并行复制。在这种模式下,从服务器可以并行应用主服务器的 binlog,从而提高复制速度。
- 监控与调优
持续监控主从复制状态,通过观察延迟趋势和性能指标,找到潜在问题并进行针对性优化。
18. 大表查询优化方案
- 为经常用于查询的列创建索引
通过创建索引,可加快查询执行速度。但是需要权衡索引数量,太多也会影响写入性能。
- 优化 SQL 语句
避免在查询过程中使用全表扫描,尽量使用索引。同时尽量减少对大表的 JOIN 操作,避免使用子查询,减少临时表的使用。
- 分区表
通过对大表进行分区可以将数据按照某种业务规则将数据分散到不同的物理分区中。这样,在执行查询时,只需要扫描相关的分区,而不是整张表,从而提高查询性能。
- 分页查询
尽量避免一次性返回大表的全部结果,可以通过 LIMIT 和 OFFSET 关键字开启分页查询,限制返回结果和偏移量。
- 结果缓存
对于相同的查询请求,可以将结果缓存到内存中,相同请求再次查询时,直接返回内存中的数据。适合数据变化不大的场景。
- 读写分离
从数据库服务器负责读,主数据库服务器负责写,减轻主从数据库的负担,提高查询能力。
- 使用数据库中间件
例如 MySQL Proxy 等数据库中间件,对 SQL 进行重写,路由,分片等操作,提高查询性能。
- 使用物化视图
复杂查询可以创建物化视图存储查询结果。查询时可以直接访问物化视图,无需再次执行复杂查询。物化视图需要定期维护保证数据实时性。
- 使用概率表
聚合计算的查询,可以创建概要表存储聚合查询结果。
- 数据库参数调优
根据具体场景,对数据库参数进行调整,如缓冲区大小。
19. 什么是数据库连接池?为什么要使用数据库连接池
数据库连接池(Database Connection Pool)是一种数据库连接管理策略,它允许应用程序在需要时重复使用预先创建的数据库连接,而不是为每个数据库请求创建和销毁连接。连接池会在启动时创建一定数量的数据库连接,并将这些连接保存在内存中。当应用程序需要访问数据库时,它从连接池中获取一个空闲连接,执行数据库操作后,再将连接归还给连接池,以便其它请求使用。
数据库连接池的使用具有以下优点:
- 性能提升:创建和销毁数据库连接是一个昂贵的操作,需要时间和资源。通过重用现有连接,可以显著提升应用程序的性能,特别是在高并发场景下。
- 资源利用优化:数据库连接池可以限制同时打开的数据库连接数量,从而防止系统资源被过度消耗。这有助于避免数据库服务过载,并确保系统在高负载下依然稳定运行。
- 管理简化:数据库连接池提供了集中管理数据库连接的方式,方便配置和调整。例如可以根据实际需求调整连接池大小、超时时间配置等。
- 负载均衡:某些连接池还提供了负载均衡功能,能够在多个数据库服务之间自动分配请求,这有助于提高系统的可扩展性和可用性。
总之,数据库连接池是一种提高应用程序性能、优化资源利用、简化管理和提供负载均衡的有效策略。在许多现代应用程序和框架中,数据库连接池已成为标准做法。
20. 一条 SQL 语句在 MySQL 中是如何执行的
- 客户端发送请求:客户端(MySQL命令行,Java应用程序等)通过网络连接向 MySQL 服务器发送 SQL 语句。
- 连接管理与安全性验证:MySQL 服务器收到客户端的连接请求后,首先对客户端的连接和身份进行验证。如果验证成功,服务器会为客户端分配一个线程来处理该连接。
- 查询缓存:如果启用查询缓存,MySQL 服务器会检查查询缓存,看看之前是否执行过相同的 SQL 语句。如果在缓存中找到查询结果,服务器将直接返回缓存结果,而不再执行后续的解析、优化和执行步骤。
- SQL解析:如果没有命中缓存,MySQL 会对 SQL 语句进行解析。解析器会将 SQL 语句分成各个组成部分,如关键字、表名、列名等,然后构建一棵解析树。
- 优化器:解析器传递给优化器,优化器会对查询进行优化。优化的目标是找到执行查询的最佳方法,如选择合适的索引,确定表的连接顺序等。
- 执行计划生成:优化器生成一个执行计划,描述了如何执行查询。执行计划包括了访问表和索引的顺序、连接类型、排序方法等信息。
- 查询执行:根据执行计划,MySQL 开始执行查询。这个阶段包括访问表和索引、处理 WHERE 语句,连接多个表、排序、分组等操作。执行查询过程中,MySQL 会将数据从磁盘加载到内存,并根据需要进行计算和转换。
- 结果返回:当查询执行完成后,MySQL 会将结果返回给客户端。如果结果集较大,可能会分多个数据包发送。
- 清理与回收:查询结束后,MySQL 会清理相关资源(如临时表、缓存等),并将连接返回到连接池,以供其它客户端使用。
这是 MySQL 中一条 SQL 语句的基本执行过程。实际情况可能因不同的查询类型和配置有所不同。
PS:缓存过期或数据更新确实可能导致查询结果不准确。在数据库中使用缓存时,需要权衡缓存的性能提升与数据一致性之间的关系。
- 缓存过期:当缓存中的数据过期时,数据库会自动清除过期的缓存项。下次查询相同的SQL语句时,数据库将不再使用过期的缓存,而是重新执行查询过程,获取最新的数据。因此,过期的缓存不会导致查询结果不准确。
- 数据更新:当底层数据发生变化(如INSERT、UPDATE、DELETE操作)时,缓存中的数据可能变得过时。这种情况下,缓存中的数据与实际数据不一致,会导致查询结果不准确。为了避免这个问题,数据库系统通常在数据更新时采取一定的策略来保持缓存与数据的一致性。例如,MySQL查询缓存在检测到与缓存相关的表发生变化时,会自动清除与该表相关的缓存项。这样可以确保查询结果的准确性,但可能会降低缓存的命中率。
在实际应用中,根据数据更新频率和查询结果的实时性要求,可以对缓存策略进行调整。如果数据变化不频繁,且对查询结果的实时性要求不高,可以使用较长的缓存过期时间。反之,如果数据经常变动,或对查询结果的实时性要求较高,应该使用较短的缓存过期时间,甚至禁用查询缓存。
21. 当一条 SQL 执行过长的时间,如何优化,从哪些方面入手?
-
优化 SQL 语句
- 确保只查询需要的列,不是使用 SELECT *;
- 尽量避免使用子查询,可以考虑使用 JOIN 代替;
- 减少使用 DISTINCT、GROUP BY、ORDER BY 等操作,除非必要;
- 避免在 WHERE 中使用函数,可能导致索引失效。
-
索引优化
- 为经常用于查询条件的列创建索引;
- 分析执行计划,检查索引是否被正常使用;
- 移除无用或者重复索引,以减少索引维护开销;
- 考虑使用索引覆盖,避免回表操作;
- 如果有多个单列索引,考虑使用复合索引。
-
数据库优化
- 调整数据库配置参数,例如缓冲池大小,日志文件大小等,以提高性能;
- 定期进行数据表分析(ANALYZE TABLE),以更新统计信息,帮助数据库优化器作出更好的决策;
- 考虑分区表,大表拆小表,拆成更易于管理的部分;
- 对于非实时查询,可以考虑数据库副本查询,以减轻主数据库的压力。
-
应用程序优化
- 将多次相同的查询合并为一次批量查询。
- 将重复使用的查询结果缓存起来,以减少对数据库的缓存次数。
- 对于写密集场景,可以考虑使用批量插入或更新,以提高性能。
-
硬件优化
CPU,内存,磁盘,IO密集,SSD,网络等。
在进行优化时,建议先从 SQL 语句和索引优化入手,然后再考虑数据库配置、应用程序和硬件优化。同时,分析执行计划和监控数据库性能指标有助于找到性能瓶颈,从而更有针对性的进行优化。
22. MySQL 数据库性能分析的方法命令有哪些?
- EXPLAIN:用于查看执行计划,可以分析索引是否被正常使用、JOIN 是否有效;
- SHOW PROCESSLIST:显示当前 MySQL 数据库中正在运行的所有线程信息,包括线程 ID、用户、主机、数据库、命令以及执行时间等。使用这个命令可以找到执行时间过长的 SQL 查询。
- SHOW STATUS:显示 MySQL 数据库的各种状态值,包括连接数、吞吐量、缓冲池状态等。
- SHOW VARIABLES:显示 MySQL 服务器的配置参数,通过查看配置参数,可以发现潜在的性能问题,如缓冲池大小不足、日志文件过大等。
- ANALYZE TABLE:分析数据表以更新统计信息。数据库优化器根据统计信息来生成更优的执行计划,定期执行 ANALYZE TABLE,可以提高查询性能。
- 慢日志:通过配置 MySQL 记录执行超时时间,指定阈值的 SQL 查询,然后分析这些查询结果,找到性能瓶颈。
- 各种性能监控工具
23. blob 和 text 有什么区别?
blob 和 text 是 数据库中用于存储大量数据的两种数据类型,它们之间存在一些差异:
- 数据类型:
- blob 用于存储二进制数据,例如图片,视频,音频等。通常存储非文本数据,不进行字符集转换。
- text 用于存储大量文本数据,并根据数据库的字符集进行编码。
- 字符编码:
- blob 不涉及字符编码问题。
- text 涉及字符编码,在存储和检索数据时需要考虑字符集,如果字符集不匹配可能导致数据损坏或乱码。
- 搜索和排序:
- blob 存储二进制数据,对这类数据进行排序和搜索可能具有局限性,可能需要额外的处理。
- text 相比而言可能更适合搜索和排序,因为它们是字符数据。
总之,当需要存储二进制数据(如图片,音频或视频)时,应选择 blob 类型;需要大量文本数据时,建议选择 text 类型。
24. MySQL 中记录货币类型数据使用什么类型比较好?
在 MySQL 中,为了记录货币类型的数据,通常推荐使用 DECIMAL 或 NUMERIC 类型。这两种类型用于存储精确的小数值,特别适合用于财务计算和货币处理,因为它们可以避免浮点数运算中可能出现的精度问题。
DECIMAL 类型的语法如下:
DECIMAL(M, D)
- M:总共的数字位数(包括整数部分和小数部分),最大值为 65。
- D:小数部分的位数,最大值为 30。如果未指定,默认值为 0。
例如,要存储一个最多有 10 位整数和 2 位小数的货币值,可以这样定义列类型:
CREATE TABLE products ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), price DECIMAL(10, 2) );
这将允许存储类似于 1234567890.12 的货币值,保留两位小数。通过使用 DECIMAL 类型,可以确保货币值的精确表示和计算。
25. MySQL 中有哪几种锁,列举一下。
- 全局锁(Global Lock):
全局锁锁定整个数据库,导致其他线程无法访问。这种锁通常用于全局备份或全局读操作。在 MySQL 中,可以使用 FLUSH TABLES WITH READ LOCK (FTWRL) 命令来获取全局锁。
- 表锁(Table Lock):
表锁会锁定整张表,使得其他线程在锁定期间无法对表进行写操作。表锁的粒度比全局表小,但仍然会影响并发性能。在 MyISAM 引擎中,表锁是默认的锁类型。
- 行锁(Row Lock):
行锁是 MySQL 中最细粒度的锁,它只锁定被操作的行。这种锁类型允许多个线程同时操作不同的行,从而提高并发性能。InnoDB 引擎默认支持行锁。
- 间隙锁(Gap Lock):
间隙锁是 InnoDB 引擎特有的类型,它锁定的是索引之间的间隙,而不是具体的行。这种锁主要用于防止幻读问题,可以确保在事务执行过程中,索引范围内不会有新行插入。
- 意向锁(Intention Lock):
InnoDB 引擎中用于支持多粒度锁的一种类型。主要有两种:意向共享锁和意向排他锁。意向锁主要用来在获取更细粒度的锁(如行锁)之前,通知其他事务当前事务的意图。
- 乐观锁(Optimistic Lock):
乐观锁不是由数据库直接实现,而是通过应用程序逻辑来实现。基本思路是:在读取数据时不加锁,而在更新数据时检查是否发生变化。如果数据没有发生变化就执行更新操作;如果数据发生变化,则放弃更新或者重新尝试。
- 悲观锁(Pessimistic Lock):
悲观锁通常由数据库直接实现,它的基本思路是:在读取数据时就加锁,防止其它线程在锁定期间修改数据。悲观锁适用于高并发写入场景,但可能导致性能降低。
26. MySQL 中的内连接、左连接、右连接的区别
在 MySQL 中内连接(INNER JOIN),左连接(LEFT JOIN),右连接(RIGHT JOIN)是三种常见的连接类型,它们用于在多个表之间查询数据。这三种连接的区别在于它们如何处理没有匹配的数据行。
- 内连接:
内连接只返回两个表中匹配的行。如果某一行没有匹配另一个表中的数据,那么这一行将不会出现在查询结果集中。
- 左连接:
左连接返回左表中的所有行,即使右表中没有匹配的数据。在这种情况下,右表中的数据将会被填充为 NULL 值。如果相互匹配,则返回全部的数据。
- 右连接:
同左连接。
- 交叉连接
交叉连接是一种特殊的连接,它返回两个表中所有行的笛卡尔积。在交叉连接中,没有指定匹配条件,结果集中每一行中都是两个表的组合。
- 笛卡尔积
笛卡尔积是两个集合中的元素进行一对一所形成的新组合。在数据库中,如果对两个表进行交叉连接,结果集将包含这两张表所有行的笛卡尔积。需要注意的是,笛卡尔积的结果可能非常大,因此在实际应用中通常会使用连接条件限制返回的大小。
27. MySQL 的基础架构
MySQL 的基础架构可以分为几个主要部分,包括客户端/服务器通信层,SQL层和存储引擎层。这些部分共同实现了MySQL的核心功能。
- 客户端/服务器的通信层:这一层负责与客户端建立连接,管理连接,处理客户端请求和返回请求查询结果。这一层还负责客户端的认证权限,确保用户具有访问特定数据库和表的权限。
- SQL层:负责处理和解析SQL语句,包括查询解析,优化,缓存与执行。这一层还包括对数据库对象的管理,包括表、视图,触发器等。此外,SQL 也处理锁定,事务控制,并发管理等。MySQL 的 SQL 层实现了大部分标准功能,例如 DDL(数据定义语言),DML(数据管理语言)。
- 存储引擎层:负责数据存储、检索和管理。MySQL支持多种存储引擎,每种引擎都有自己的特点。例如 InnoDB 支持事务,行级锁定和外键约束;MyISAM 引擎提供高效的查询能力。在 MySQL 中可以根据具体需求为不同的表选择合适的存储引擎。
28. 数据库的三大范式
数据库的范式是数据库设计中用于组织数据结构的一种方法。范式的目的是去除数据冗余,保证数据完整性和数据一致性。数据库的三大范式包括:
- 第一范式:原子性
第一范式要求表中的每个字段都是不可分的最小数据单位,即具有原子性。这意味着表中的每个字段只包含单一的值。而不是一组值或其它的复杂数据结构。符合第一范式的表可以消除数据冗余,简化数据存储和查询。
- 第二范式:完全功能依赖
第二范式要求满足第一范式的基础上,表中的每个非主键字段都完全依赖于主键。换句话说,非主键字段不应该只依赖于主键的一部分(如果主键是复合主键)。符合第二范式的表设计可以进一步提高数据的完整性,减少数据冗余。
- 第三范式:非传递依赖
第三范式要求满足第二范式的基础上,表中的非主键字段之间不存在传递依赖关系。也就是说,一个非主键字段不应该通过另一个非主键字段间接依赖主键。符合第三范式的数据库表可以保证数据的独立性,避免数据异常和更新异常。
满足这三大范式的数据库表可以提高数据的完整性,一致性和可维护性。然而在实际应用中,为了追求查询性能,可能需要在一定程度上放弃部分范式要求,如冗余数据设计等。数据库设计应根据实际需求进行权衡和取舍。
29. MySQL 中的 binlog 有哪些格式,分别有什么区别?
MySQL 中的 binlog (binary log)用于记录数据库中所有的更改数据操作。binlog 主要用于数据复制和数据恢复。在 MySQL 中 binlog 有三种格式:Statement、Row,Mixed。
- Statement(基于SQL语句)
Statement 格式的 binlog 记录了执行的 SQL 语句。当从服务器需要执行相同的操作时,它会重放记录在 binlog 中的 SQL 语句。这种格式的优点是存储占用相对较小,节省存储空间。然而从服务器需要时间执行 SQL 语句,在遇到类似 now(),random() 函数时,可能导致结果不一致(非确定性结果)。
- Row(基于行)
Row 格式的 binlog 记录了每一行数据的更改。这种格式的 binlog 的优点是从服务器可以及时应用这些更改,不需要重新执行 SQL 语句,从而降低数据不一致的风险。然而 Row 格式的 binlog 通常会比较大,占用存储空间。因为它记录了每一行详细的更改信息。
- Mixed(混合)
Mixed 格式的 binlog 结合了 Statement 和 Row 两种格式的优点。在大多数情况下,MySQL 使用 Statement 格式记录 binlog。在涉及函数等非确定性操作时,则使用 Row 格式记录 binlog。这样可以在保证存储较小的同时降低数据不一致的风险。
选择哪种 binlog 格式取决于需求和场景。Statement 格式适用于存储空间较小和具有确定性的场景。Row 格式适用于对数据一致性要求较高的场景。Mixed 则在这两种格式之间取得一种平衡。在 MySQL 中可以通过 binlog_format 参数来修改 binlog 格式。
30. InnoDB 引擎的四大特性
InnoDB 是 MySQL 数据库中的一种存储引擎。它具有以下四大特性:
- 事务支持:InnoDB 支持事务处理,这意味着可以对 MySQL 进行一系列的操作,要么全部执行成功,要么全部回滚。支持事务有助于确保数据的一致性和完整性。InnoDB 支持 ACID (原子性,一致性,隔离性,持久性)事务模型并支持多种事务隔离级别(读未提交,读已提交,不可重复读,序列化串行)以满足不同场景的需求。
- 行级锁定:InnoDB 支持行级锁定,这意味着在执行数据操作时,只会锁定行,不会锁定整张表。与表级锁相比,行级锁可以大大减小锁竞争,提高并发能力。此外,InnoDB 还支持多版本并发控制(MVCC),进一步降低锁占用的可能性。
- 外键约束:InnoDB 支持外键,有助于确保数据引用的完整性。外键约束可以强制表之间的关系。例如删除父表记录自动更新子表记录。避免出现“游离”的数据行。确保数据的完整性。
- Crash-safe 和数据恢复:InnoDB 引擎具有强大的数据恢复能力。通过 Write-AHead Logging(WAL)技术,InnoDB 将事务日志记录到磁盘中,确保在发生故障时可以恢复数据。在系统崩溃或者其它故障下,InnoDB 可以通过重播事务日志来恢复数据,确保数据的持久性。
这四大特性使得 MySQL 在许多场景下使用 InnoDB 引擎作为主打引擎。
31. 索引有哪些优缺点,有哪些特性?
数据库索引的主要目的是提高查询性能,主要有以下优缺点和特性:
优点:
- 提高查询速度:降低磁盘 I/O 操作次数,提高查询速度;
- 加速排序、分组操作:有序的索引结构可以加快数据库的排序、分组操作;
- 提高数据的完整性:例如使用唯一索引,保证数据表中不会存在重复记录,提高数据的完整性。
缺点:
- 增加磁盘空间的消耗:索引的存储需要额外的磁盘空间存储;
- 降低数据修改性能:新增,修改,删除数据时需要涉及索引的维护,更新索引结构,引入更大的性能开销;
- 索引维护成本:随着数据量的增长和变化,索引可能需要定期的优化和维护。
特性:
- 数据库通常提供多种类型的索引,如 B+Tree,哈希索引,位图索引等,可以适用于不同的业务场景;
- 复合索引:复合索引包含多个列,可以在多个列上同时提高查询性能。在设计复合索引时需要考虑列的查询顺序;
- 聚簇索引和非聚簇索引:聚簇索引将索引和数据行存储在一起,非聚簇索引则将索引和数据分开存储。聚簇索引对查询提升很大,但对插入和更新操作的性能影响比较大。非聚簇索引相比而言查询会慢一些,但是对插入和更新的操作性能影响较小。
- 索引选择性:指的是索引列中不同值的数量和总行数的比例。高选择性的索引通常具有更好的查询性能。
32. 创建索引的原则
创建索引的目的旨在协助优化数据库查询性能,提高查询速度,同时减少维护成本。以下是一些创建索引的时候应该遵循的原则:
- 经常作为查询条件,排序操作,聚合查询的列,优先考虑对这些列添加索引;
- 根据业务需求,选择合适的索引类型,B+Tree,Hash,BitMap;
- 尽量查询包含查询所在列的索引(联合索引),避免额外表的访问。这种操作被称为覆盖索引,可以显著提高查询性能。
- 复合索引包含多列,适用于多列作为查询条件的场景,使用复合索引时需要考虑顺序,将最具筛选可能得列放到最左侧(最左匹配),已获得最佳性能。
- 控制索引的数量,索引数量越多,插入、修改效率越低。合理控制索引数量,只对需要的列添加索引。
- 定期评估维护索引。数据量的增减可能导致索引不适用,所以需要定期评估索引,保证性能。
- 不要对大列创建索引,例如 BLOB,TEXT 等,这会增加磁盘空间占用,以及性能。
33. 百万级别或以上的数据,如何删除?
在处理百万级别或以上的数据时,直接删除可能导致性能问题。尤其是在生产环境中这么做,是一种十分危险的操作。为了避免这种操作,可以采用以下方法:
- 分批删除:
将删除操作分为较小的批次,以减轻数据库的压力。可以通过 LIMIT 子句限制条数。
DELETE FROM my_table WHERE some_condition LIMIT 1000;
可以将这个操作放到循环中,知道满足 some_condition 的数据被全部删除。
- 创建新表并复制需要保留的数据:
如果需要删除的数据占用了大量的空间,那么可以选择先创建一张表将需要保留的数据复制到新表中,然后 DROP 掉旧表,最后重命名新表。
CREATE TABLE your_table_new AS SELECT * FROM your_table WHERE data_to_keep_condition; DROP TABLE your_table; ALTER TABLE your_table_new RENAME TO your_table;
- 利用数据库分区:
在某些数据库管理系统中,可以使用表分区的功能。表分区将一个大表划分为多个较小的物理子表,每个子表独立存储。这样,当需要删除某些数据时,可以直接删除与这些数据有关的数据分区,而无需逐行删除。这种方法可以显著提高删除大量数据的性能。
33. count(1),count(*),count(列名) 的区别?
COUNT() 是 SQL 中的聚合函数,用来统计满足特定条件的行数。使用区别如下:
- COUNT(1)
计算满足条件的所有行数,无论其中的列值是否为 NULL,这里的 1 其实是一个常数,实际上可以用任何为 NULL 常数替代,计算结果都是相同的。
- COUNT(*)
计算满足条件的所有行数,包括列值为 NULL 的行。COUNT() 会计算表中所有的行,因此通常用来统计表中的总行数。与 COUNT(1) 效果相同,但是在某些数据库中,COUNT() 的性能可能更好,因为它可以使用特定的优化策略。
- COUNT(列名)
计算满足条件且指定列不为 NULL 的行数。这里的列表表示具体的列,只有当该列不为 NULL 时才会被统计。
34. 什么是存储过程,有哪些优缺点?
存储过程是一种在数据库中存储并编译的一组SQL语句。它们可以通过调用程序或SQL语句来执行。存储过程类似于编程语言中的函数或方法,可以接收参数,执行逻辑并返回结果。
存储过程的优点:
- 性能优势:存储过程在数据库中进行预编译,因此执行速度较快。另外减少通过网络传输的 SQL 语句数量,可以降低网络负担,提高整体的性能。
- 代码重用:存储过程在多个应用程序或SQL语句中重复使用,减少了代码重复编写的工作量。
- 代码维护:存储过程集中存储在数据库中,易于维护和管理。当需要修改逻辑时,只需要修改存储过程本身,不需要修改调用程序。
- 安全性:存储过程可以限制用户对数据库的访问,提高安全性。例如,通过限制用户仅可以访问特定的存储过程,而不是直接执行SQL语句,可以降低潜在的安全风险。
存储过程的缺点:
- 可移植性:存储过程通常与特定的数据库管理系统(DBMS)相关,因此在不同的 DBMS 之间进行迁移可能会比较困难。
- 调试困难:相较于应用程序代码,存储过程的调试和排错可能会比较困难。
- 版本控制:由于存储过程存储在数据库中,可能版本控制不如应用程序代码容易。
- 学习曲线:存储过程通常由特定的数据库语言编写,意味着开发者需要使用一种特定的数据库语言进行编写。
在实际应用中,需要根据团队、业务、需求来衡量是否使用存储过程。
35. SQL 约束有哪几种?
SQL 约束用于限制在数据库表中插入、更新和删除数据时的规则。这些约束用来确保数据的完整性,一致性和正确性。以下是 SQL 中的常见几种约束:
- 主键约束(PRIMARY KEY):确保表中的每一行具有唯一的标识符。主键列在表中不能为 NULL,且必须唯一。一张表只能有一个主键。
- 外键约束(FOREIGN KEY):用来维护表与表之间的完整性。外键列的值必须与引用表中的主键列值相匹配,或者可以为 NULL。这确保了引用数据的存在,防止出现游离的记录。
- 唯一约束(UNIQUE):确保在指定列中的值是唯一的。与主键约束不同,唯一约束允许为 NULL 值(除非有 NOT NULL 约束)。一张表可以有多个唯一约束。
- 非空约束(NOT NULL):确保指定列的值永远不为 NULL。在插入和更新数据表时,必须为 NOT NULL 约束的列提供非空值。
- CHECK 约束:用于限制列中约束值的范围。CHECK 约束基于指定的条件对列值进行验证,如果不满足条件,则不允许插入和更新记录。
- 默认约束(DEFAULT):为列指定默认值。当插入记录时,如果未提供该列的值,则使用默认值填充。
这些约束有助于确保数据库表中的数据符合预期的规则和结构,从而提高数据质量,避免潜在的错误。
36. MySQL 中 VARCHAR(50) 中 50 的含义
在 MySQL 中,VARCHAR(50) 中的 50 表示可以存储的最大字符数。这意味着 VARCHAR(50) 可以存储长度为 0 到 50 个字符的字符串。这个数字是一个可变的最大长度,实际存储的字符串长度可能小于等于这个最大长度。
与 CHAR 类型相比,VARCHAR 类型更加灵活,因为它可以根据实际字符串长度来分配存储空间。在实际应用中,如果预计字符串长度会有较大的波动,使用 VARCHAR 类型会更加合适,因为它可以有效地节省存储空间。
需要注意的是,VARCHAR 类型字段会额外占用 1 到 2 个字节的存储空间来记录字符串的实际长度。在 MySQL 中如果 VARCHAR 的实际长度小于 255,则使用 1 个字节。如果实际长度大于 255,则使用 2 个字节。所以 VARCHAR(50) 中实际存储空间为字符串长度加 1(用于存储长度信息)。
37. MySQL 中 int(20),char(20) 和 varchar(20) 的区别
在 MySQL 中,int(20),varchar(20),char(20) 是三种不同的数据类型。它们的区别主要是用途和空间占用上不同。
- int(20):整数类型,存储整数值。int 类型占用 4 个字节(32bit),范围是 -2147483648 - 2147483647(有符号整数)或 0 - 4294967295(无符号整数)。括号中的 20 是显示宽度,它只影响显示,不影响存储范围或占用空间。
- varchar(20):可变长字符串类型,存储可变长度的字符串。varchar(20) 可以存储最多 20 个字符的字符串。实际存储空间取决于字符串的实际长度,存储长度额外还需要 1 到 2 个字节。例如一个 10 个字符的字符串将占用 11 或 12 个字节(取决于字符集)。
- char(20):定长字符串类型,存储固定长度的字符串。char(20) 总是存储 20 个长度的字符串,无论字符串的长度是多少。如果字符串长度小于 20 则使用空格填充。char 类型适用于存储固定长度的值,如国家代码,电话号码区号等。
总结一下:
- int(20) 用来存储整数值,占用 4 个字节,括号中的数字只影响显示宽度。
- varchar(20) 用来存储可变长度的字符串,最多 20 个字符,实际占用空间取决于字符串的长度。
- char(20) 用来存储定长的字符串,总是占用 20 个字符存储。
38. DROP,DELETE,TRUNCATE 的区别
DROP,DELETE,TRUNCATE 都是 SQL 语句中用来删除数据的语句。它们的作用和使用场景有所不同。
- DROP:用来删除整个表结构和表中的数据。一旦执行,表将会被永久删除,无法恢复。执行速度很快,因为它不需要逐行删除数据。
- DELETE:用来删除表中的部分或全部数据。可以搭配 WHERE 子句来删除要删除的行。执行速度较慢,因为它是逐行删除数据并将行为记录到事务日志中。这意味着在某些情况下,可以通过事务日志进行回滚来恢复数据。
- TRUNCATE:用来删除表中的所有数据,但是保留表结构。执行速度比 DELETE 快,因为它不是逐行删除数据,而是直接释放表中用于存储数据的空间。TRUNCATE 无法搭配 WHERE 子句使用,因为它总是删除表中的所有数据。TRUNCATE 通常不能回滚,要谨慎使用。
39. UNION 和 UNION ALL 的区别
UNION 和 UNION ALL 都是 SQL 语句中用于合并两个或两个以上 SELECT 查询结果集的操作符。它们的行为和性质有所不同:
- UNION:将多个 SELECT 查询结果集合并为一个结果集。在合并过程中,UNION 会自动去除重复的行,只返回唯一的记录。为了实现去重的功能,UNION 需要对结果集进行排序和比较,这可能会影响查询性能。
- UNION ALL:将多个 SELECT 查询结果集合并为一个结果集。与 UNION 不同的是,UNION ALL 不会去除重复的行,而是直接返回所有的记录。因为 UNION ALL 不需要去重操作,其查询性能通常优于 UNION。
在使用 UNION 和 UNION ALL 时,请确保合并的 SELECT 查询具有相同的列数和兼容的数据类型。
40. SQL 的生命周期
SQL 的生命周期是指从编写 SQL 查询到获取查询结果的整个过程。这个过程通常包含以下几个阶段:
- 编写 SQL 语句:用户根据需求编写 SQL 语句,包括查询,新增,删除,修改数据等操作。
- 提交 SQL 语句:用户将 SQL 查询提交给数据库管理系统(DBMS)。
- 解析 SQL 语句:数据库管理系统对 SQL 语句进行解析,检查语法是否正确,确定查询涉及的表、列,条件等。
- 优化 SQL 语句:数据库管理系统的优化器对 SQL 语句进行优化,找到最佳的查询执行计划。优化器会考虑很多因素,例如索引,表大小,连接方式等,用以降低查询的时间和资源消耗。
- 执行 SQL 查询:根据查询优化器生成的查询计划,数据库管理系统开始执行 SQL 查询。可能涉及到读取或写入数据,对数据进行排序,聚合等操作。
- 返回结果:数据库管理系统将数据返回给用户。
在整个生命周期中,数据库管理系统会处理各种错误和异常,如语法错误、权限问题等。此外,数据库管理系统还会维护各种性能统计信息,以帮助用户了解查询的性能,并在需要时优化查询。
41. 一条SQL的执行顺序
一条 SQL 的执行顺序并不是严格按照书写顺序执行的。执行顺序如下:
- FROM 子句:数据库从指定的表或多表中读取数据;
- ON 子句:如果有多表连接,ON 子句会根据连接条件筛选相关的数据;
- JOIN 操作:数据库执行连接操作,将满足 ON 子句条件的数据进行组合;
- WHERE 子句:根据 WHERE 子句中的条件,进一步筛选数据;
- GROUP BY 子句:对筛选后的数据按照指定的列进行分组;
- HAVING 子句:对分组后的数据进行条件筛选;
- SELECT 子句:选择需要的列,执行聚合函数(如 COUNT,SUM,AVG 等);
- DISTINCT 子句:去除重复的数据;
- ORDER BY 子句:对数据按照指定的列进行排序;
- LIMIT 子句:根据指定的偏移量和行数,返回最终的结果。
42. SQL 慢查询如何优化
SQL 慢查询优化是一个复杂的过程,需要对数据库原理,SQL 语言,数据结构和索引等知识有深入的了解。常见的 SQL 慢查询方法有:
- 添加索引:WHERE 子句或者 JOIN 字段添加索引。
- EXPLAIN 查询计划分析:例如 WHERE 子句中使用函数计算,导致全表扫描;避免使用 SELECT *;避免在关联查询中使用子查询。
- 使用分区表:表很大的情况下,推荐使用。
- 遵循数据库设计范式:尽量遵循三范式,避免数据冗余和异常。也可以根据具体场景使用反范式设计,比如物化视图。
- 优化数据库参数:连接数,内存大小等。
- 使用缓存:提高读多写少的场景下的性能。
- 硬件升级
43. 主键使用自增 ID 还是 UUID
选择主键的类型,是自增ID还是UUID,取决于具体的需求和情况。下面是一些考虑因素:
自增ID的优点:
- 简单性:自增ID由数据库直接管理,开发人员无需自己生成,使用方便;
- 效率:自增ID在数据库索引和查询中效率较高,可以更快的执行插入和排序操作;
- 可读性:自增ID通常是连续的整数,可以直观的了解数据的插入顺序。
UUID 的优点:
- 唯一性:UUID 是全局唯一的标识符,不同的计算机和数据库生成的 ID 几乎不会重复;
- 分散性:UUID 的生成不依赖数据库,可以在不同的系统和数据库中独立生成。支持分布式系统和多个数据库之间的数据合并。
- 隐私性:UUID 不透露数据的实际顺序,数量和生成时间的信息,有助于保护隐私。
如果应用程序不特别关心数据的可读性和顺序,而更关注全局唯一性和分布式支持,那么 UUID 可能更适合。如果应用需要简单的整数主键,顺序插入和高效的索引查询,那么自增 ID 可能更适合,特别是单实例数据库情况下,自增 ID 是一种常见且经过广泛测试的主键选择。
44. MySQL 数据库 CPU 飙升的话,该如何处理?
MySQL 的 CPU 使用率飙升可能由多种原因引起的,包括查询效率低下、索引不合理、硬件资源不足、配置不合理等。以下是几种可能的解决办法:
- 优化 SQL 查询:对于查询非常复杂或者处理大量数据的查询,可能会导致 CPU 使用率飙升。可以使用 EXPLAIN 命令来查看查询的执行计划,找出可能的性能瓶颈。
- 创建和优化索引:如果表没有被合理的索引,那么查询就可能会导致全表扫描,这会消耗大量的 CPU 资源。可以考虑添加或者优化索引,以提高查询效率。
- 调整 MySQL 的配置:某些 MySQL 的配置项可能会影响到 CPU 的使用率。例如,可以增加 innodb_buffer_pool_size 的值,使得更多的数据可以被缓存在内存中,从而减少 CPU 的使用。不过需要注意,调整配置需要小心,避免引起其它问题。
- 升级硬件:如果服务器硬件资源不足,可能需要考虑升级硬件,增加 CPU 的核心数,或者升级更快的 CPU。
- 分析服务器负载:如果服务器的负载比较高,可能会导致 MySQL CPU 使用率飙升。可以通过操作系统工具 top 或 htop 来查看系统负载情况。
在调试时,结合上面的建议,从多角度分析和解决问题。具体情况具体分析。
45. 常见的读写分离方案
读写分离是一种常见的数据库架构策略,主要目的是为了提高数据库系统的性能和可用性。它将数据库的读操作和写操作分开,通常是将写操作发送到主数据库,而读操作则发送到一个或多个从数据库。
以下是一些常见的读写分离方案:
- MySQL 自带的主从复制:MySQL 自带的主从复制功能可以使用读写分离。主数据库负责写,从数据库负责读。主数据库将更改记录到二进制日志 binlog,然后从数据库复制并应用这些更改。
- MySQL Proxy:MySQL Proxy 是 MySQL 提供的一个轻量级代理服务。它可以在应用程序和 MySQL 服务器之间创建一个中间层,根据预设的规则将查询请求分发到不同的数据库。
- Middleware:在应用程序和数据库之间使用中间件进行读写分离。例如 ProxySQL,MyCAT 等都可以使用这个功能。
- 数据库集群:一些数据库集群技术如 MySQL Cluster 也提供了读写分离的功能。它们在后台处理数据复制和故障转移,使得读写分离更加透明。
- 应用程序层:在应用程序层实现读写分离,应用程序需要知道哪些操作是读或写操作,并将它们发送到适当的数据库。这可能需要修改代码,但提供了最大的灵活性。
注意,读写分离虽然可以提高性能,但也有一些缺点和挑战,比如数据一致性问题(由于主从复制延迟,从库数据可能会落后于主库数据),以及复制过程中的故障处理等问题。
46. MySQL 的复制流程
MySQL 的复制是一个基于日志的复制机制,主要包括主库和从库两部分。复制过程大致分为以下部分:
- 在主库上进行操作
主库上的所有改变操作(如 INSERT,UPDATE,DELETE 等)都会被写入到 binlog(二进制日志) 中。这些记录被称为 binlog 事件。
- 从库连接到主库
从库将启动 I/O 线程和 SQL 线程。I/O 线程负责连接到主库,并请求主库从指定的位置开始发送日志的内容。
- 主库发送 binlog 事件
主库接收到从库的请求后,会启动一个 binlog 转储线程,该线程会从指定的位置开始,将 binlog 事件发送给从库的 I/O 线程。
- 从库接收并写入中继日志
从库的 I/O 线程接收到主库发送的 binlog 事件后,会将这些事件写入到自己的中继日志(Relay Log)。
- 从库执行 Relay Log 中的事件
从库的 SQL 线程会读取 Relay Log 中的事件,并将这些事件转换为数据更改操作,然后在从库上执行这些操作,从而实现数据的复制。
以上就是 MySQL 数据的复制原理和流程。需要注意的是,由于复制过程存在一定的延迟,所以从库的数据可能会稍微落后于主库。如果需要实时、一致的读操作,可能需要考虑其他的解决方案,如多主复制或 Galera Cluster 等。
47. 谈谈 MySQL 的 EXPLAIN
MySQL 的 EXPLAIN 是一个非常有用的工具,可以帮助理解 MySQL 是如何执行一个 SQL 查询的。通过 EXPLAIN 可以看到 MySQL 执行查询的详细步骤,包括使用了哪些索引,扫描了多少行,排序和连接等方式等。这些信息可以帮助查找查询时的性能瓶颈,从而优化查询。
以下是 EXPLAIN 结果中重要的列:
- select_type:这个字段表示查询的类型,比如 SIMPLE(简单查询,不包含子查询或者 UNION),PRIMARY(查询中若包含任何复杂的子部分,最外层查询则会被标记为 PRIMARY),SUBQUERY(子查询中的第一个 SELECT)等等。
- table:这个字段表示当前操作的表。
- type:这个字段是非常重要的字段,表示 MySQL 如何定位要获取的行。可能的值包括:const,eq_ref,ref,range,index 和 ALL 等。其中 ALL(全表扫描)通常是最差的情况。
- possible_keys:这个字段表示 MySQL 可能使用的索引。
- key:这个字段表示 MySQL 实际选择的索引。
- key_len:这个字段表示 MySQL 在索引中使用的字节数。
- ref:这个字段表示哪些列或者常数被用来查找索引列。
- rows:这个字段表示 MySQL 认为需要检查的行数。
- Extra:这个字段包括 MySQL 解决查询的详细信息。例如 MySQL 可能使用了文件排序或者索引等,或者 MySQL 可能查找更多的行等。
在优化查询的过程中,通常会关注 type,key,rows 和 Extra 这几个字段。特别是当 type 字段为 ALL,或者Extra 字段包含 Useing Filesort 或 Using temporary 时,可能就需要进行优化。