点击上方"数据与人", 右上角选择“设为星标”
分享干货,共同成长!
今天给大家分享MySQL常考的面试题,看看你们能答对多少。
目录大纲:

事务的四大特性?
Atomicity
)、一致性(
Consistency
)、隔离性(
Isolation
)、持久性(
Durability
)。
原子性( atomicity
)
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。一致性( consistency
)
数据库总是从一个一致性的状态转换到另外一个一致性的状态。如:拿转账来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。隔离性( isolation
)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。持久性( durability
)
一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且「不可能有能做到100%的持久性保证的策略」否则还需要备份做什么。
事务隔离级别有哪些?

脏读:在事务A修改数据之后提交数据之前,这时另一个事务B来读取数据,如果不加控制,事务B读取到A修改过数据,之后A又对数据做了修改再提交,则B读到的数据是脏数据,此过程称为脏读Dirty Read
。


幻读:事务A在按查询条件读取某个范围的记录时,事务B又在该范围内插入了新的满足条件的记录,当事务A再次按条件查询记录时,会产生新的满足条件的记录(幻行 Phantom Row
)

不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的「数据不一样」。(因为中间有其他事务提交了修改); 幻读的重点在于新增或者删除:在同一事务中,同样的条件,第一次和第二次读出来的「记录数不一样」。(因为中间有其他事务提交了插入/删除)。
索引
索引的本质?
Index
)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。
索引分类?
primary
的唯一非空索引,不允许有空值。
null
且可以存在多个
null
值。唯一索引的用途:唯一标识数据库表中的每条记录,主要是用来防止数据重复插入。
MyISAM
引擎上才能使用,只能在
CHAR
、
VARCHAR
和
TEXT
类型字段上使用全文索引。
索引的优缺点?
提高数据检索的效率,降低数据库的 IO
成本;通过索引列对数据进行排序,降低数据排序的成本,降低了 CPU
的消耗;
索引提升查询效率的同时也会降低更新的效率,更新表时,MySQL不仅要保存数据,还要保存一下索引文件,每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息; 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。
索引的作用?
索引的使用场景?
主键自动建立唯一索引; 频繁作为查询条件的字段应该创建索引; 查询中与其它表关联的字段,外键关系建立索引; 单键/组合索引的选择问题, 组合索引性价比更高; 查询中排序的字段,如 order by
create_time
,排序字段若通过索引去访问将大大提高排序速度;查询中统计或者分组字段;
索引的失效场景?
以%开头的
LIKE
查询不能够利用B+树索引数据类型中出现隐式转换时不会用到索引
复合索引的情况下,查询条件不满足最左原则不会用到索引
用or分隔的条件,如果or前条件中的列有索引,而后面的列没有索引,那么涉及的索引都不会被用到(前面的索引也不会用到)。
如果Mysql估计使用索引比全表扫描更慢,则不使用索引。
索引的数据结构
InnoDB
引擎默认使用的是 B+ 树来作为索引的数据结构。
key
从左到右递增排列,如果某个指针的左右相邻
key
分别是 keyi 和 keyi+1,则该指针指向节点的所有
key
大于等于 keyi 且小于等于 keyi+1。
key
所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出
key
所对应的数据项。
BTREE
索引,底层基于B+树数据结构来实现。
mysql> show index from blog\G;
*************************** 1. row ***************************
Table: blog
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: blog_id
Collation: A
Cardinality: 4
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
value
值。这样查找一个数据的时间复杂度就是O(1),一般多用于精确查找。
Hash索引和B+树索引的区别?
哈希索引不支持排序,因为哈希表是无序的。 哈希索引不支持范围查找。 哈希索引不支持模糊查询及多列索引的最左前缀匹配。 因为哈希表中会存在哈希冲突,所以哈希索引的性能是不稳定的,而B+树索引的性能是相对稳定的,每次查询都是从根节点到叶子节点。
为什么B+树比B树更适合实现数据库索引?
B+树:有序数组链表+平衡多叉树;
B+ 树查找过程:


什么是最左匹配原则?
>
、
<
、
between
、
like
)就会停止匹配,后面的字段不会用到索引。
(a,b,c)
建立索引,查询条件使用 a/ab/abc 会走索引,使用 bc 不会走索引。如果查询条件为
a = 1 and b > 2 and c = 3
,那么a、b个字两段能用到索引,而c无法使用索引,因为b字段是范围查询,导致后面的字段无法使用索引。

a = 1
时,b值为1,2是有序的状态。当执行
a = 1 and b = 2
时a和b字段能用到索引。而对于查询条件
a < 4 and b = 2
时,a字段能用到索引,b字段则用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b的值不是有序的,因此b字段无法使用索引。
什么是聚集索引?
InnoDB
主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
InnoDB
来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为
NULL
的唯一索引。如果没有主键也没有合适的唯一索引,那么
InnoDB
内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。
什么是覆盖索引?
select
的数据列只用从索引中就能够取得,不需要回表进行二次查询,也就是说查询列要被所使用的索引覆盖。对于
innodb
表的二级索引,如果索引能覆盖到查询的列,那么就可以避免对主键索引的二次查询。
explain
,输出的extra列会显示为
using index
。
什么是前缀索引?
MySQL
在查找时过滤掉更多的数据行。
// email列创建前缀索引
ALTER TABLE table_name ADD KEY(column_name(prefix_length));
索引的设计原则?
索引列的区分度越高,索引的效果越好。比如使用性别这种区分度很低的列作为索引,效果就会很差。 尽量使用短索引,对于较长的字符串进行索引时应该指定一个较短的前缀长度,因为较小的索引涉及到的磁盘 I/O
较少,查询速度更快。索引不是越多越好,每个索引都需要额外的物理空间,维护也需要花费时间。 利用最左前缀原则。
MySQL架构?

sock
通信和大多数基于客户端/服务端工具实现的类似于
tcp/ip
的通信。

API
与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。
show engines
:查看所有的数据库引擎

show variables like '%engine%'
查看默认的数据库引擎



常见的存储引擎有哪些?
MyISAM
、
InnoDB
、
MEMORY
、
ARCHIVE
。MySQL 5.5版本后默认的存储引擎为
InnoDB
。
.MYD
和索引文件
.MYI
。
哈希索引数据不是按照索引值顺序存储,无法用于排序。 不支持部分索引匹配查找,因为哈希索引是使用索引列的全部内容来计算哈希值的。 只支持等值比较,不支持范围查询。 当出现哈希冲突时,存储引擎需要遍历链表中所有的行指针,逐行进行比较,直到找到符合条件的行。
MyISAM和InnoDB的区别?

MVCC 实现原理?
Multiversion concurrency control
) 就是同一份数据保留多版本的一种方式,进而实现并发控制。在查询的时候,通过
read view
和版本链找到对应版本的数据。
DB_TRX_ID
:当前事务id,通过事务id的大小判断事务的时间顺序。DB_ROLL_PRT
:回滚指针,指向当前行记录的上一个版本,通过这个指针将数据的多个版本连接在一起构成undo log
版本链。DB_ROLL_ID
:主键,如果数据表没有主键,InnoDB会自动生成主键。
举例说明:
create table mvcctest( id int primary key auto_increment, name varchar(20));
transaction 1
:
start transaction;insert into mvcctest values(NULL,'mi');insert into mvcctest values(NULL,'kong');commit;

transaction 2
:
start transaction;select * from mvcctest; (1)select * from mvcctest; (2)commit
SELECT:
transaction 3
:
start transaction;insert into mvcctest values(NULL,'qu');commit;

UPDATE:
transaction 4
:
start transaction;update mvcctest set name = 'fan' where id = 2;commit;

DELETE:
transaction 5:
start transaction;delete from mvcctest where id = 2;commit;

快照读和当前读
快照读:读取的是快照版本。普通的 SELECT
就是快照读。通过mvcc来进行并发控制的,不用加锁。当前读:读取的是最新版本。 UPDATE、DELETE、INSERT、SELECT … LOCK IN SHARE MODE、SELECT … FOR UPDATE
是当前读。
mvcc
机制避免了幻读现象。而
mvcc
机制无法避免当前读情况下出现的幻读现象。因为当前读每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。
在快照读情况下,MySQL通过 mvcc
来避免幻读。在当前读情况下,MySQL通过 next-key
来避免幻读(加行锁和间隙锁来实现的)。
Serializable
隔离级别也可以避免幻读,会锁住整张表,并发性极低,一般不会使用。
共享锁和排他锁
select * from table where id<6 lock in share mode;--共享锁
select * from table where id<6 for update;--排他锁
LOCK IN SHARE MODE
多个事务同时更新同一个表单时很容易造成死锁。
commit
语句或
rollback
语句结束为止。
SELECT... FOR UPDATE
使用注意事项:
for update
仅适用于innodb,且必须在事务范围内才能生效。根据主键进行查询,查询条件为 like
或者不等于,主键字段产生表锁。根据非索引字段进行查询,会产生表锁。
bin log/redo log/undo log
bin log
(二进制日志)和
redo log
(重做日志)和
undo log
(回滚日志)。
bin log
是MySQL数据库级别的文件,记录对MySQL数据库执行修改的所有操作,不会记录select和show语句,主要用于恢复数据库和同步数据库。
redo log
属于 MySQL 存储引擎 InnoDB 的事务日志。
Buffer Pool
。这个缓存中包含了磁盘中部分数据页(
page
)的映射,以此来缓解数据库的磁盘压力。
Buffer Pool
中修改完数据后会按照设定的更新策略,定期刷到磁盘中,这个过程称为刷脏页。
Buffer Pool
中修改的数据还没有及时的刷到磁盘中,就会导致数据丢失,无法保证事务的持久性。
redo log
。
redo Log
如其名侧重于重做!它记录的是数据库中每个页的修改,而不是某一行或某几行修改成怎样,可以用来恢复提交后的物理数据页,且只能恢复到最后一次提交的位置。
redo log
用到了 WAL(
Write-Ahead Logging
)技术。这个技术的核心就在于修改记录前,一定要先写日志,并保证日志先落盘,才能算事务提交完成。
redo log
中,在修改
Buffer Pool
中的数据。当提交事务时,调用
fsync
把
redo log
刷入磁盘。至于缓存中更新的数据文件何时刷入磁盘,则由后台线程异步处理。
redo log
的事务状态是
prepare
,还未真正提交成功,要等 bin log 日志写入磁盘完成才会变更为
commit
,事务才算真正提交完成。
undo log
也是属于 MySQL 存储引擎 InnoDB 的事务日志。
undo log
中。
undo log
将数据回滚到事务执行前的状态,保证事务的完整性。
undo log
呢?
redo log
。
undo log
负责完成回滚,redo log 负责完成前滚。
commit
。但该事务内修改的脏页中,可能有一部分脏块已经刷盘。如果此时数据库实例宕机重启,就需要用回滚来将先前那部分已经刷盘的脏块从磁盘上撤销。
commit
,但该事务内修改的脏页中只有一部分数据被刷盘,另外一部分还在
buffer pool
缓存上,如果此时数据库实例宕机重启,就需要用前滚来完成未完全提交的事务。将先前那部分由于宕机在内存上的未来得及刷盘数据,从 redo log 中恢复出来并刷入磁盘。
undo log、redo log、bin log
三种日志都是在刷脏页之前就已经刷到磁盘了,相互协作最大限度保证了用户提交的数据不丢失。
bin log和redo log有什么区别?
bin log会记录所有日志记录,包括 InnoDB、MyISAM
等存储引擎的日志;redo log只记录innoDB自身的事务日志。bin log只在事务提交前写入到磁盘,一个事务只写一次;而在事务进行过程,会有redo log不断写入磁盘。 bin log是逻辑日志,记录的是SQL语句的原始逻辑;redo log是物理日志,记录的是在某个数据页上做了什么修改。
MySQL主从同步

主从复制原理?

master(binlog dump thread)、slave(I/O thread 、SQL thread)
主库写入数据并且生成binlog文件。该过程中MySQL将事务串行的写入二进制日志,依赖binlog dump线程。 在事件写入二进制日志完成后,master通知存储引擎提交事务。 从库服务器上的IO线程连接Master服务器,请求从执行binlog日志文件中的指定位置开始读取binlog至从库。 主库接收到从库的IO线程请求后,其上复制的IO线程会根据Slave的请求信息分批读取binlog文件然后返回给从库的IO线程。 Slave服务器的IO线程获取到Master服务器上IO线程发送的日志内容、日志文件及位置点后,会将binlog日志内容依次写到Slave端自身的Relay Log(即中继日志)文件的最末端,并将新的binlog文件名和位置记录到master-info文件中,以便下一次读取master端新binlog日志时能告诉Master服务器从新binlog日志的指定文件及位置开始读取新的binlog日志内容。 从库服务器的SQL线程会实时监测到本地Relay Log中新增了日志内容,然后把RelayLog中的日志翻译成SQL并且按照顺序执行SQL来更新从库的数据。 从库在relay-log.info中记录当前应用中继日志的文件名和位置点以便下一次数据复制。
主从延迟?
主库 A 执行完成一个事务,写入 binlog,该时刻记为T1. 传给从库B,从库接受完这个binlog的时刻记为T2. 从库B执行完这个事务,该时刻记为T3.
show slave status
,返回结果会显示
seconds_behind_master
,表示当前从库延迟了多少秒。
每一个事务的binlog都有一个时间字段,用于记录主库上写入的时间 从库取出当前正在执行的事务的时间字段,跟当前系统的时间进行相减,得到的就是seconds_behind_master,也就是前面所描述的T3-T1。
relaylog
)的时间段,而造成原因一般是以下几种:
比如将20台主库放在4台机器,把从库放在一台机器。这个时候进行更新操作,由于更新时会触发大量读操作,导致从库机器上的多个从库争夺资源,导致主从延迟。
按照正常的策略,读写分离,主库提供写能力,从库提供读能力。将进行大量查询放在从库上,结果导致从库上耗费了大量的CPU资源,进而影响了同步速度,造成主从延迟。
Hadoop
,让外部系统提供查询能力。
一旦执行大事务,那么主库必须要等到事务完成之后才会写入binlog。 比如主库执行了一条 insert … select
非常大的插入操作,该操作产生了近几百G的binlog文件传输到只读节点,进而导致了只读节点出现应用binlog延迟。
alter、drop、create
)
1、只读节点与主库的DDL同步是串行进行,如果DDL操作在主库执行时间很长,那么从库也会消耗同样的时间,比如在主库对一张500W的表添加一个字段耗费了10分钟,那么从节点上也会耗费10分钟。 2、从节点上有一个执行时间非常长的的查询正在执行,那么这个查询会堵塞来自主库的DDL,表被锁,直到查询结束为止,进而导致了从节点的数据延迟。
锁冲突问题也可能导致从节点的SQL线程执行慢,比如从机上有一些select .... for update的SQL,或者使用了MyISAM引擎等。
一般场景中,因偶然情况导致从库延迟了几分钟,都会在从库恢复之后追上主库。但若是从库执行速度低于主库,且主库持续具有压力,就会导致长时间主从延迟,很有可能就是从库复制能力的问题。
slave_parallel_type
参数设置为
LOGICAL_CLOCK
,这就可以了。
怎么减少主从延迟
降低多线程大事务并发的概率,优化业务逻辑 优化SQL,避免慢SQL,减少批量操作,建议写脚本以 update-sleep
这样的形式完成。提高从库机器的配置,减少主库写binlog和从库读binlog的效率差。 尽量采用短的链路,也就是主库和从库服务器的距离尽量要短,提升端口带宽,减少binlog传输的网络延时。 实时性要求的业务读强制走主库,从库只做灾备,备份。
大表优化?
单表优化?
字段
尽量使用INT而非BIGINT,如果非负则加上 UNSIGNED
(这样数值容量会扩大一倍),当然能使用TINYINT、SMALLINT、MEDIUM_INT
更好。VARCHAR的长度只分配真正需要的空间 使用枚举或整数代替字符串类型 尽量使用 TIMESTAMP
而非DATETIME
,单表不要有太多字段,建议在 20 以内 表字段避免null值出现,null值很难查询优化且占用额外的索引空间,推荐默认数字0代替null。 用整型来存 IP
索引
索引并不是越多越好,要根据查询有针对性的创建,考虑在 WHERE
和ORDER BY
命令涉及的列建立索引,可根据EXPLAIN
来查看是否用了索引还是全表扫描应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描 值分布很稀少的字段不适合建索引,例如 "性别" 这种只有两三个值的字段 字符字段只建前缀索引 字符字段最好不要做主键 不用外键,由程序保证约束 尽量不用 UNIQUE
,由程序保证约束使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引
# 选择合适的数据类型 (1)使用可存下数据的最小的数据类型,整型 < date,time < char,varchar < blob
(2)使用简单的数据类型,整型比字符处理开销更小,因为字符串的比较更复杂。如,int类型存储时间类型,bigint类型转ip函数
(3)使用合理的字段属性长度,固定长度的表会更快。使用enum、char
而不是varchar
(4)尽可能使用not null
定义字段
(5)尽量少用text,非用不可最好分表# 选择合适的索引列
(1)查询频繁的列,在where,group by,order by,on
从句中出现的列
(2)where条件中<,<=,=,>,>=,between,in,以及like 字符串+通配符(%)出现的列
(3)长度小的列,索引字段越小越好,因为数据库的存储单位是页,一页中能存下的数据越多越好
(4)离散度大(不同的值多)的列,放在联合索引前面。查看离散度,通过统计不同的列值来实现,count越大,离散程度越高:
查询 SQL
可通过开启慢查询日志来找出较慢的 SQL 不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移至等号右边 sql 语句尽可能简单:一条 sql 只能在一个 cpu 运算;大语句拆小语句,减少锁时间;一条大 sql 可以堵死整个库 不用SELECT * OR改写成IN:OR的效率是 n 级别,IN的效率是 log(n) 级别,in 的个数建议控制在 200 以内 不用函数和触发器,在应用程序实现 避免%xxx式查询 少用JOIN 使用同类型进行比较,比如用'123'和'123'比,123和123比 尽量避免在WHERE子句中使用!= 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描 对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5 列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大
引擎
MyISAM
不支持行锁,读取时对需要读到的所有表加锁,写入时则对表加排它锁 不支持事务 不支持外键 不支持崩溃后的安全恢复 在表有读取查询的同时,支持往表中插入新纪录 支持BLOB和TEXT的前 500 个字符索引,支持全文索引 支持延迟更新索引,极大提升写入性能 对于不会进行修改的表,支持压缩表,极大减少磁盘空间占用
InnoDB
支持行锁,采用 MVCC 来支持高并发 支持事务 支持外键 支持崩溃后的安全恢复 不支持全文索引
系统调优参数
sysbench
:一个模块化,跨平台以及多线程的性能测试工具iibench-mysql:基于 Java 的 MySQL/Percona/MariaDB 索引进行插入性能测试工具 tpcc-mysql:Percona 开发的 TPC-C 测试工具
back_log:back_log 值指出在 MySQL 暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。也就是说,如果 MySql 的连接数据达到 max_connections 时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即 back_log,如果等待连接的数量超过 back_log,将不被授予连接资源。可以从默认的 50 升至 500 wait_timeout:数据库连接闲置时间,闲置连接会占用内存资源。可以从默认的 8 小时减到半小时 max_user_connection: 最大连接数,默认为 0 无上限,最好设一个合理上限 thread_concurrency:并发线程数,设为 CPU 核数的两倍 skip_name_resolve:禁止对外部连接进行 DNS 解析,消除 DNS 解析时间,但需要所有远程主机用 IP 访问 key_buffer_size:索引块的缓存大小,增加会提升索引处理速度,对 MyISAM 表性能影响最大。对于内存 4G 左右,可设为 256M 或 384M,通过查询show status like 'key_read%',保证key_reads key_read_requests在 0.1% 以下最好 innodb_buffer_pool_size:缓存数据块和索引块,对 InnoDB 表性能影响最大。通过查询show status like 'Innodb_buffer_pool_read%',保证 (Innodb_buffer_pool_read_requests – Innodb_buffer_pool_reads) Innodb_buffer_pool_read_requests越高越好 innodb_additional_mem_pool_size:InnoDB 存储引擎用来存放数据字典信息以及一些内部数据结构的内存空间大小,当数据库对象非常多的时候,适当调整该参数的大小以确保所有数据都能存放在内存中提高访问效率,当过小的时候,MySQL 会记录 Warning 信息到数据库的错误日志中,这时就需要该调整这个参数大小 innodb_log_buffer_size:InnoDB 存储引擎的事务日志所使用的缓冲区,一般来说不建议超过 32MB query_cache_size:缓存 MySQL 中的 ResultSet,也就是一条 SQL 语句执行的结果集,所以仅仅只能针对 select 语句。当某个表的数据有任何任何变化,都会导致所有引用了该表的 select 语句在 Query Cache 中的缓存数据失效。所以,当我们的数据变化非常频繁的情况下,使用 Query Cache 可能会得不偿失。根据命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))进行调整,一般不建议太大,256MB 可能已经差不多了,大型的配置型静态数据可适当调大.
可以通过命令show status like 'Qcache_%'查看目前系统 Query catch 使用大小read_buffer_size:MySql 读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql 会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁,可以通过增加该变量值以及内存缓冲区大小提高其性能 sort_buffer_size:MySql 执行排序使用的缓冲大小。如果想要增加ORDER BY的速度,首先看是否可以让 MySQL 使用索引而不是额外的排序阶段。如果不能,可以尝试增加 sort_buffer_size 变量的大小 read_rnd_buffer_size:MySql 的随机读缓冲区大小。当按任意顺序读取行时 (例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql 会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但 MySql 会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。 record_buffer:每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,可能想要增加该值 thread_cache_size:保存当前没有与连接关联但是准备为后面新的连接服务的线程,可以快速响应连接的线程请求而无需创建新的 table_cache:类似于 thread_cache_size,但用来缓存表文件,对 InnoDB 效果不大,主要用于 MyISAM
升级硬件
读写分离?
缓存?
MySQL 内部:在系统调优参数介绍了相关设置 数据访问层:比如 MyBatis 针对 SQL 语句做缓存,而 Hibernate 可以精确到单个记录,这里缓存的对象主要是持久化对象Persistence Object 应用服务层:这里可以通过编程手段对缓存做到更精准的控制和更多的实现策略,这里缓存的对象是数据传输对象Data Transfer Object Web 层:针对 web 页面做缓存 浏览器客户端:用户端的缓存
直写式(Write Through):在数据写入数据库后,同时更新缓存,维持数据库与缓存的一致性。这也是当前大多数应用缓存框架如 Spring Cache 的工作方式。这种实现非常简单,同步好,但效率一般。 回写式(Write Back):当有数据要写入数据库时,只会更新缓存,然后异步批量的将缓存数据同步到数据库上。这种实现比较复杂,需要较多的应用逻辑,同时可能会产生数据库与缓存的不同步,但效率非常高。
表分区?
EXPLAIN PARTITIONS
来查看某条 SQL 语句会落在那些分区上,从而进行 SQL 优化。
可以让单表存储更多的数据 分区表的数据更容易维护,可以通过清楚整个分区批量删除大量数据,也可以增加新的分区来支持新插入的数据。另外,还可以对一个独立分区进行优化、检查、修复等操作 部分查询能够从查询条件确定只落在少数分区上,速度会很快 分区表的数据还可以分布在不同的物理设备上,从而搞笑利用多个硬件设备 可以使用分区表赖避免某些特殊瓶颈,例如 InnoDB 单个索引的互斥访问、ext3 文件系统的 inode 锁竞争 可以备份和恢复单个分区
一个表最多只能有 1024 个分区 如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来 分区表无法使用外键约束 NULL 值会使分区过滤无效 所有分区必须使用相同的存储引擎
RANGE 分区:基于属于一个给定连续区间的列值,把多行分配给分区 LIST 分区:类似于按 RANGE 分区,区别在于 LIST 分区是基于列值匹配一个离散值集合中的某个值来进行选择 HASH 分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含 MySQL 中有效的、产生非负整数值的任何表达式 KEY 分区:类似于按 HASH 分区,区别在于 KEY 分区只支持计算一列或多列,且 MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值
最适合的场景数据的时间序列性比较强,则可以按时间来分区,如下所示:
CREATE TABLE members (
firstname VARCHAR(25) NOT NULL,
lastname VARCHAR(25) NOT NULL,
username VARCHAR(16) NOT NULL,
email VARCHAR(35),
joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (
PARTITION p0 VALUES LESS THAN (1960),
PARTITION p1 VALUES LESS THAN (1970),
PARTITION p2 VALUES LESS THAN (1980),
PARTITION p3 VALUES LESS THAN (1990),
PARTITION p4 VALUES LESS THAN MAXVALUE
);
查询时加上时间范围条件效率会非常高,同时对于不需要的历史数据能很容的批量删除。
垂直拆分?


可以使得行数据变小,一个数据块 (Block) 就能存放更多的数据,在查询时就会减少 I/O 次数 (每次查询时读取的 Block 就少) 可以达到最大化利用 Cache 的目的,具体在垂直拆分的时候可以将不常变的字段放一起,将经常改变的放一起 数据维护简单
主键出现冗余,需要管理冗余列 会引起表连接 JOIN 操作(增加 CPU 开销)可以通过在业务服务器上进行 join 来减少数据库压力 依然存在单表数据量过大的问题(需要水平拆分) 事务处理复杂
水平拆分?
概述

UserExtras
,这样一共四张表
不存在单库大数据和高并发的性能瓶颈 应用端改造较少 提高了系统的稳定性和负载能力
分片事务一致性难以解决 跨节点 Join 性能差,逻辑复杂 数据多次扩展难度跟维护量极大
分片原则
能不分就不分,参考单表优化 分片数量尽量少,分片尽量均匀分布在多个数据结点上,因为一个查询 SQL 跨分片越多,则总体性能越差,虽然要好于所有数据在一个分片的结果,只在必要的时候进行扩容,增加分片数量 分片规则需要慎重选择做好提前规划,分片规则的选择,需要考虑数据的增长模式,数据的访问模式,分片关联性问题,以及分片扩容问题,最近的分片策略为范围分片,枚举分片,一致性 Hash 分片,这几种分片都有利于扩容 尽量不要在一个事务中的 SQL 跨越多个分片,分布式事务一直是个不好处理的问题 查询条件尽量优化,尽量避免 Select *
的方式,大量数据结果集下,会消耗大量带宽和 CPU 资源,查询尽量避免返回大量结果集,并且尽量为频繁使用的查询语句建立索引。通过数据冗余和表分区赖降低跨库 Join 的可能
查询语句执行流程?
select * from test where id = 1000;
+------+-------+---------+---------------------+
| id | name | Stu_ID | create_time |
+------+-------+---------+---------------------+
|1000 | 刘皇叔 | 2022001 | 2022-08-10 22:29:08 |
+------+-------+---------+---------------------+
来看一下 SQL 语句在 MySQL 的各个模块中的执行过程。

更新语句执行过程?
redo log
(
prepare
状态)、
binlog
、
redo log
(
commit
状态)
update test set i=i+1 where ID=1;

exist和in的区别?
exists
用于对外表记录做筛选。
exists
会遍历外表,将外查询表的每一行,代入内查询进行判断。当
exists
里的条件语句能够返回记录行时,条件就为真,返回外表当前记录。反之如果
exists
里的条件语句不能返回记录行,条件为假,则外表当前记录被丢弃。
select a.* from A awhere exists(select 1 from B b where a.id=b.id)
in
是先把后边的语句查出来放到临时表中,然后遍历临时表,将临时表的每一行,代入外查询去查找。
select * from Awhere id in(select id from B)
exists
可以有效减少总的循环次数来提升速度;当外查询的表比较大的时候,使用
in
可以有效减少对外查询表循环遍历来提升速度。
truncate、delete与drop区别?
truncate
和不带where
子句的delete
、以及drop
都会删除表内的数据。drop
、truncate
都是DDL
语句(数据定义语言),执行后会自动提交。
truncate 和 delete 只删除数据不删除表的结构;drop 语句将删除表的结构被依赖的约束、触发器、索引; 一般来说,执行速度: drop > truncate > delete。
having和where的区别?
二者作用的对象不同, where
子句作用于表和视图,having
作用于组。where
在数据分组前进行过滤,having
在数据分组后进行过滤。
show processlist详解?
show processlist
或
show full processlist
可以查看当前 MySQL 是否有压力,正在运行的
SQL
,有没有慢
SQL
正在执行。返回参数如下:
id:线程ID,可以用 kill id
杀死某个线程db:数据库名称 user:数据库用户 host:数据库实例的IP command:当前执行的命令,比如 Sleep
,Query
,Connect
等time:消耗时间,单位秒 state:执行状态,主要有以下状态: Sleep
,线程正在等待客户端发送新的请求Locked
,线程正在等待锁Sending data
,正在处理SELECT
查询的记录,同时把结果发送给客户端Kill
,正在执行kill
语句,杀死指定线程Connect
,一个从节点连上了主节点Quit
,线程正在退出Sorting for group
,正在为GROUP BY
做排序Sorting for order
,正在为ORDER BY
做排序info:正在执行的 SQL
语句

文章转载自数据与人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。