前言:大部分业务研发同窗在工做的90%的场景下会和各类数据打交道,在此过程当中迈不过去的一个坎就是MySQL了,这篇文章从研发同窗的视角来说述在MySQL使用中须要了解的各类关键技术;
下面我会分别从MySQL的体系架构、事务、锁、索引、性能、部署架构几个方面来介绍; 非特殊注明的状况下专指innodb引擎,但愿各位在看完以后可以有所收获!html
想要了解一个技术体系很是建议的一种方式是先看系统总体的架构设计,这样可以快速的预览整个框架,下降陌生感;而后经过分析一个典型的执行过程加强对过程的把控;mysql
在MySQL的设计中总体能够分为三层:文件系统、存储(索引)、服务层;咱们所直接面向主要是服务层相关的模块如:链接池、SQL、优化器、缓存等,能够经过下图对MySQL的组成有个大概的了解 程序员
再看一下一个SQL的执行过程,SQL Cache->解析器->优化器->执行引擎->存储引擎;是否是相似于咱们设计的高性能服务端架构经过缓存->业务逻辑->业务数据;只是这个业务逻辑是在数据库领域的而已;面试
数据库事务应该是大部分程序员在面试时的必经题目了,先一句话描述一下我所理解的事务:
在并发场景保障一次会话对数据操做符合预期(原子性、一致性、隔离性、持久性)的方式;
下面会分为名词解释和隔离级别两趴来帮助咱们理解事务;
sql
想象一下咱们有多个客户端(A/B/N)在并行的操做一张表T1中的数据,那么就可能会存在以下几种状况:
数据库
一、脏读:A可能读取到B会话中未提交事务修改的数据(不可预期的数据结果)
二、不可重复读:一个事物内屡次查询同一数据行,结果是不一致
三、幻读:范围检索时两次检索会看到不一样的数据行(范围检索不只限于大于小于,部分的in场景或者全表扫描同样会被覆盖)
segmentfault
在以上场景下,MySQL定义了以下四种隔离级别:
PS:默认隔离级别是Repeatable_Read缓存
敲重点:session
面试必考题:
一、MySQL事务的的隔离级别及其特性
二、解释一下来脏读、不可重复读、幻读的含义及其出现的场景 可以很好的回答以上问题,在MySQL事务单项上就及格了;
进阶:
一、数据库默认的隔离级别是什么?大家线上数据库用的是哪一个级别?为何选择这个级别?
二、MySQL RR 级别是如何解决不可重复读?
三、MySQL RR 级别是否解决了幻读?
数据结构
想回答好进阶问题,就要对MySQL的锁 & MVCC有必定的了解了
锁是并发场景下解决共享资源操做冲突问题的常见手段,同理MySQL也采用了该种策略,下面会分别介绍一下MySQL的锁
在正式介绍以前补充一点背景信息,对于MySQL而言,读分为快照读和当前读,
快照读:select * from t1 where ;
当前读:select xxx lock in share mode;select xxx for
update;insert/update/delete xxx;
表锁
表锁字义是对标进行加锁,特色是开销小,加锁快,并发度低; MYISAM 引擎仅支持表锁
在innodb引擎写操做中两种意向锁(意向共享锁和意向排他锁)也是一种表锁;
页锁
BDB引擎支持的锁类型,引擎和相关资料都比较少,锁粒度介于行和表之间
行锁
一、行锁和字面意思有一点区别,主要是区分于Oracle数据库的行锁是针对物理数据块进行加锁,而MySQL是针对索引进行加锁的,也就意味着只有走索引才能加行锁不然在须要加锁的场景下就是表锁
二、行锁开销大、加锁慢、并发度高(冲突几率低)
三、行锁一样分为共享锁和排他锁
间隙锁
间隙锁是在RR级别下生效,在当前读场景下会锁定索引间的间隙,保证索引间的记录不变;以防止插入或更新间隙之间的记录;
而RR级别下的间隙锁也就部分解决了幻读的问题
nextKey lock
等于 行锁+间隙锁
锁冲突表
. | IX | IS | X | S |
---|---|---|---|---|
IX | 兼容 | 兼容 | 冲突 | 冲突 |
IS | 兼容 | 兼容 | 冲突 | 兼容 |
X | 冲突 | 冲突 | 冲突 | 冲突 |
S | 冲突 | 兼容 | 冲突 | 兼容 |
MVCC翻译过来是多版本并发控制;在RC和RR级别下生效,就是解决上面所提到不一样隔离级别下的不可重复读和幻读的问题;
咱们假设本身来来实现这个效果,通常的思路是否是在一个事物开启的时候
一、经过记录当前记录的快照,在事物结束以前一直读该快照来保障单次事物内结果是可预期的;
二、再加上锁来阻止其余并发更新
思路是正确的可是MySQL对快照的处理上不像咱们想象的同样真的插入一条记录而是经过undo_log+readview的机制来实现的
数据结构
innodb引擎在数据中增长了
事务号DB_TRX_ID(建立、更新、删除)
回滚段指针DB_ROLL_PTR(用于追溯历史版本数据)
DB_ROW_ID(随着新行插入而单调递增的行ID)
undo_log
insert/update/delete时会产生undo log
undo log中包含重建该行记录被更新以前内容
readview
RC:事务中每条select语句都会建立一个快照(read view);
RR:事务在begin/start transaction以后的第一条select读操做后, 会建立一个快照(read view), 将当前系统中活跃的其余事务记录记录起来 而这个区分也就决定了RR和RC两个级别下可见性的区别;
补充说明一下,有一种说法是幻读在当前读场景下是解决了; 可是在快照读的场景下仍是仍是存在对方已经提交可是在当前session读不出来该数据,因此是部分解决了(对细节感兴趣的话能够开两个session 自行验证一下);
根据最上面的MySQL的体系架构图能够得知数据最终是存储在硬盘文件系统中的,计算机体系中硬盘数据是访问最慢的,可是受限于内存数据的空间,在这种约束下经过针对具体业务场景,对高频数据访问场景经过增长索引的方式提升数据访问效率
聚簇索引
以主键建立的索引
一、汇集索引在叶子节点存储的是表中的数据
非聚簇索引(二级索引)
非主键建立的索引
一、索引叶子节点存储的是主键和索引列
二、在检索列中包含非索引列时,须要用叶子节点的主键,再去表中检索其余列(回表)
数据结构应该都不陌生,而索引就是经过不一样数据结构的特性:查询复杂度、空间、查询和新增性能等来作出的选择;
在一个海量数据的场景下咱们想要保障查询的效率很直接的一种方式就是根据构建hash表;
另一种相对可控的就是树形结构能够经过控制树的深度来保证查询的效率;
B+ tree
它是一个矮胖子;左右两个子树的高度差的绝对值不超过1,而且左右两个子树都是一棵平衡二叉树;
这样的结构保障了查询效率的相对高效和稳定;
可是咱们看一下在更新和新增场景下因为要维持以上特性,须要进行分裂或合并,这样对插入性能来讲是有必定损失的;
Hash表
hash表的好处是查询复杂度理论值是o(1),,可是带来的问题以下
一、空间的消耗
二、不支持范围和区间查询
三、大数据量场景下的hash冲突,可能会致使退化成链表
在目前的MySQL版本中hash索引是MySQL自适应的没法干预
基于以上索引的设计能够看出,能够分析出在索引使用的过程当中有以下特色
一个表可能建立多个索引
一次只能走一个索引
最左匹配
索引选择上根据 where 条件中的列字段,从左至右的匹配,直到遇到区间或范围查询列;
注意此处和SQL中where 后 in、=条件的列字段顺序无关;
查询列不参与计算
若是对列字段添加了计算函数,则没法走该字段的索引
where 和 order by limit 场景下的索引
在存在where 和 order by的场景下,本觉得会按照where条件走索引,可是在实际状况下可能会发现最终索引是order by 字段,优先选择order by字段索引;
缘由是:limit存在时,查询的顺序就有可能发生变化,查询过程不是先经过where过滤再排序再limit而是根据order by索引反向取进而而后匹配where 条件看看是否知足,有点相似于全表扫描了;
即便是知道了以上各类原则,但在实际过程当中仍是可能遇到不符合预期的状况,这时就须要MySQL提供的debug利器执行计划了
type index: 扫描所有索引树
range: 扫描部分索引,索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行,常见于between、<、>等的查询
ref: 非惟一性索引扫描,返回匹配某个单独值的全部行。常见于使用非惟一索引即惟一索引的非惟一前缀进行的查找
eq_ref:惟一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配。常见于主键或惟一索引扫描
const, system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。system是const类型的特例,当查询的表只有一行的状况下, 使用system。
NULL: MySQL在优化过程当中分解语句,执行时甚至不用访问表或索引。
possible_keys:可选的索引列
key:当前选择的索引列
ref:链接匹配条件,若是走主键索引的话,该值为: const, 全表扫描的话,为null值
rows:扫描行数
extra: using index表示在相应的select中使用了覆盖索引。 usingwhere表示存储引擎搜到记录后进行了后过滤(POST-FILTER),若是查询未能使用索引,usingwhere的做用只是提醒咱们mysql要用where条件过滤z结果集。 using temporay表示用临时表来存储结果集,常见于排序和分组查询。 usingfilesort,mysql中没法用索引完成的排序成为文件排序。
索引区分度
选择高区分度列
覆盖索引:查询列尽可能包含在索引中,不然须要回表
SQL优化
大表翻页:查询列尽可能是索引列,不然建议用join或子查询的方式经过主键完成
乐观锁
select for update 和基于version的乐观锁,在极限冲突场景下到底哪一种性能更高待验证
数据量控制
分库分表 冷热数据分离
读写分离:备库能够考虑承担一部分读流量
物理或者逻辑架构的设计对于面向客户提供的可靠性相当重要,做为数据的核心数据的不可丢失以及提供高性能的解决方案,从架构层面有一些设计方式;
可靠性的一个重要保障方式就是作副本,一种经典的MySQL部署方式master-slave模式,部署两个节点:master和slave;
master节点提供读写服务,经过解析binlog把数据变动复制到另一台slave节点;存在ms级的延时,在某种程度上保障了数据的物理隔离,下降单点故障带来的损失;
分库实际上是一种逻辑上的架构,不过通常来讲比较建议不一样的逻辑库能够散列分布,来提升吞吐性能,在各个逻辑库上又能够作主从架构,每个节点作一个备份。
无论是分库仍是分表都要注意维度,按照业务场景来评估,尽可能保证检索场景覆盖了拆分维度。
传统上而言主备都是在同地域甚至是同一个机房来下降同步延时,可是在一些极限场景下对数据的可用性以及吞吐量要求更高,这个时候跨地域的逻辑库以及多副本的一致性架构就开始出现了,阿里巴巴电商交易在15年开始规划跨地域数据解决方案,其中主要用到的策略
一、按router路由,保障同一用户落在同一地域
二、跨地域的数据同步
三、同一sequence生产方式(集中式、分布式)
当前基于最新分布式一致协议(Paxos)构建的 多副本强一致方案也有落地
了解一些其余类型的数据库,在作技术选型和决策的时候可能会有所帮助,目前主流的大概是这些
MySQL/Oracle/PG/SQLServer 等
Hbase/MongoDB/Cassandra/LevelDB/Redis等
Neo4j/GraphDB等
欢迎交流关于数据库的各类有意思的场景和问题