https://www.jianshu.com/p/c189439fb32emysql
1、MySQL架构ios
和其它数据库相比,MySQL有点不同凡响,它的架构能够在多种不一样场景中应用并发挥良好做用。主要体如今存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构能够根据业务的需求和实际须要选择合适的存储引擎。程序员
❝面试
画出 MySQL 架构图,这种变态问题都能问的出来算法
MySQL 的查询流程具体是?or 一条SQL语句在MySQL中如何执行的?sql
客户端请求 ---> 链接器(验证用户身份,给予权限) ---> 查询缓存(存在缓存则直接返回,不存在则执行后续操做) ---> 分析器(对SQL进行词法分析和语法分析操做) ---> 优化器(主要对执行的sql优化选择最优的执行方案方法) ---> 执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口) ---> 去引擎层获取数据返回(若是开启查询缓存则会缓存查询结果)图:极客时间数据库
❝缓存
说说MySQL有哪些存储引擎?都有哪些区别?安全
存储引擎是MySQL的组件,用于处理不一样表类型的SQL操做。不一样的存储引擎提供不一样的存储机制、索引技巧、锁定水平等功能,使用不一样的存储引擎,还能够得到特定的功能。性能优化
使用哪种引擎能够灵活选择,一个数据库中多个表能够使用不一样引擎以知足各类性能和实际需求,使用合适的存储引擎,将会提升整个数据库的性能 。
MySQL服务器使用可插拔的存储引擎体系结构,能够从运行中的 MySQL 服务器加载或卸载存储引擎 。
-- 查看支持的存储引擎 SHOW ENGINES -- 查看默认存储引擎 SHOW VARIABLES LIKE 'storage_engine' --查看具体某一个表所使用的存储引擎,这个默认存储引擎被修改了! show create table tablename --准确查看某个数据库中的某一表所使用的存储引擎 show table status like 'tablename' show table status from database where name="tablename"
-- 建表时指定存储引擎。默认的就是INNODB,不须要设置 CREATE TABLE t1 (i INT) ENGINE = INNODB; CREATE TABLE t2 (i INT) ENGINE = CSV; CREATE TABLE t3 (i INT) ENGINE = MEMORY; -- 修改存储引擎 ALTER TABLE t ENGINE = InnoDB; -- 修改默认存储引擎,也能够在配置文件my.cnf中修改默认引擎 SET default_storage_engine=NDBCLUSTER;
默认状况下,每当 CREATE TABLE 或 ALTER TABLE 不能使用默认存储引擎时,都会生成一个警告。为了防止在所需的引擎不可用时出现使人困惑的意外行为,能够启用 NO_ENGINE_SUBSTITUTION SQL 模式。若是所需的引擎不可用,则此设置将产生错误而不是警告,而且不会建立或更改表
常见的存储引擎就 InnoDB、MyISAM、Memory、NDB。
InnoDB 如今是 MySQL 默认的存储引擎,支持事务、行级锁定和外键
在 MySQL中创建任何一张数据表,在其数据目录对应的数据库目录下都有对应表的 .frm 文件,.frm 文件是用来保存每一个数据表的元数据(meta)信息,包括表结构的定义等,与数据库存储引擎无关,也就是任何存储引擎的数据表都必须有.frm文件,命名方式为 数据表名.frm,如user.frm。
查看MySQL 数据保存在哪里:show variables like 'data%'
MyISAM 物理文件结构为:
InnoDB 物理文件结构为:
❝
ps:正经公司,这些都有专业运维去作,数据备份、恢复啥的,让我一个 Javaer 搞这的话,加钱不?
对比项MyISAMInnoDB主外键不支持支持事务不支持支持行表锁表锁,即便操做一条记录也会锁住整个表,不适合高并发的操做行锁,操做时只锁某一行,不对其它行有影响,适合高并发的操做缓存只缓存索引,不缓存真实数据不只缓存索引还要缓存真实数据,对内存要求较高,并且内存大小对性能有决定性的影响表空间小大关注点性能事务默认安装是是
❝
一张表,里面有ID自增主键,当insert了17条记录以后,删除了第15,16,17条记录,再把Mysql重启,再insert一条记录,这条记录的ID是18仍是15 ?
若是表的类型是MyISAM,那么是18。由于MyISAM表会把自增主键的最大ID 记录到数据文件中,重启MySQL自增主键的最大ID也不会丢失;
若是表的类型是InnoDB,那么是15。由于InnoDB 表只是把自增主键的最大ID记录到内存中,因此重启数据库或对表进行OPTION操做,都会致使最大ID丢失。
❝
哪一个存储引擎执行 select count(*) 更快,为何?
MyISAM更快,由于MyISAM内部维护了一个计数器,能够直接调取。
InnoDB 中 count(*) 语句是在执行的时候,全表扫描统计总数量,因此当数据愈来愈大时,语句就愈来愈耗时了,为何 InnoDB 引擎不像 MyISAM 引擎同样,将总行数存储到磁盘上?这跟 InnoDB 的事务特性有关,因为多版本并发控制(MVCC)的缘由,InnoDB 表“应该返回多少行”也是不肯定的。
主要包括如下五大类:
❝
CHAR 和 VARCHAR 的区别?
char是固定长度,varchar长度可变:
char(n) 和 varchar(n) 中括号中 n 表明字符的个数,并不表明字节个数,好比 CHAR(30) 就能够存储 30 个字符。
存储时,前者无论实际存储数据的长度,直接按 char 规定的长度分配存储空间;然后者会根据实际存储的数据分配最终的存储空间
相同点:
不一样点:
char是适合存储很短的、通常固定长度的字符串。例如,char很是适合存储密码的MD5值,由于这是一个定长的值。对于很是短的列,char比varchar在存储空间上也更有效率。
❝
列的字符串类型能够是什么?
字符串类型是:SET、BLOB、ENUM、CHAR、CHAR、TEXT、VARCHAR
❝
BLOB和TEXT有什么区别?
BLOB是一个二进制对象,能够容纳可变数量的数据。有四种类型的BLOB:TINYBLOB、BLOB、MEDIUMBLO和 LONGBLOB
TEXT是一个不区分大小写的BLOB。四种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。
BLOB 保存二进制数据,TEXT 保存字符数据。
❝
说说你对 MySQL 索引的理解?
数据库索引的原理,为何要用 B+树,为何不用二叉树?
汇集索引与非汇集索引的区别?
InnoDB引擎中的索引策略,了解过吗?
建立索引的方式有哪些?
聚簇索引/非聚簇索引,mysql索引底层实现,为何不用B-tree,为何不用hash,叶子结点存放的是数据仍是指向数据的内存地址,使用索引须要注意的几个地方?
建立:
建立索引:CREATE [UNIQUE] INDEX indexName ON mytable(username(length));若是是CHAR,VARCHAR类型,length能够小于字段实际长度;若是是BLOB和TEXT类型,必须指定 length。
修改表结构(添加索引):ALTER table tableName ADD [UNIQUE] INDEX indexName(columnName)
删除:DROP INDEX [indexName] ON mytable;
查看:SHOW INDEX FROM table_name\G --能够经过添加 \G 来格式化输出信息。
使用ALERT命令
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 该语句添加一个主键,这意味着索引值必须是惟一的,且不能为NULL。
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list 这条语句建立索引的值必须是惟一的(除了NULL外,NULL可能会出现屡次)。
ALTER TABLE tbl_name ADD INDEX index_name (column_list) 添加普通索引,索引值可出现屡次。
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list)该语句指定了索引为 FULLTEXT ,用于全文索引。
❝
为何MySQL 索引中用B+tree,不用B-tree 或者其余树,为何不用 Hash 索引
聚簇索引/非聚簇索引,MySQL 索引底层实现,叶子结点存放的是数据仍是指向数据的内存地址,使用索引须要注意的几个地方?
使用索引查询必定能提升查询的性能吗?为何?
首先要明白索引(index)是在存储引擎(storage engine)层面实现的,而不是server层面。不是全部的存储引擎都支持全部的索引类型。即便多个存储引擎支持某一索引类型,它们的实现和行为也可能有所差异。
MyISAM 和 InnoDB 存储引擎,都使用 B+Tree的数据结构,它相对与 B-Tree结构,全部的数据都存放在叶子节点上,且把叶子节点经过指针链接到一块儿,造成了一条数据链表,以加快相邻数据的检索效率。
先了解下 B-Tree 和 B+Tree 的区别
B-Tree是为磁盘等外存储设备设计的一种平衡查找树。
系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是须要什么取什么。
InnoDB 存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB 存储引擎中默认每一个页的大小为16KB,可经过参数 innodb_page_size 将页的大小设置为 4K、8K、16K,在 MySQL 中可经过以下命令查看页的大小:show variables like 'innodb_page_size';
而系统一个磁盘块的存储空间每每没有这么大,所以 InnoDB 每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小 16KB。InnoDB 在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时若是一个页中的每条数据都能有助于定位数据记录的位置,这将会减小磁盘I/O次数,提升查询效率。
B-Tree 结构的数据可让系统高效的找到数据所在的磁盘块。为了描述 B-Tree,首先定义一条记录为一个二元组[key, data] ,key为记录的键值,对应表中的主键值,data 为一行记录中除主键外的数据。对于不一样的记录,key值互不相同。
一棵m阶的B-Tree有以下特性:
B-Tree 中的每一个节点根据实际状况能够包含大量的关键字信息和分支,以下图所示为一个 3 阶的 B-Tree:
图片:DobbinSoong
每一个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分红的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。
模拟查找关键字29个过程:
分析上面过程,发现须要3次磁盘I/O操做,和3次内存查找操做。因为内存中的关键字是一个有序表结构,能够利用二分法查找提升效率。而3次磁盘I/O操做是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了做用,从而提升了查询效率。
B+Tree 是在 B-Tree 基础上的一种优化,使其更适合实现外存储索引结构,InnoDB 存储引擎就是用 B+Tree 实现其索引结构。
从上一节中的B-Tree结构图中能够看到每一个节点中不只包含数据的key值,还有data值。而每个页的存储空间是有限的,若是data数据较大时将会致使每一个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时一样会致使B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,全部数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样能够大大加大每一个节点存储的key值数量,下降B+Tree的高度。
B+Tree相对于B-Tree有几点不一样:
将上一节中的B-Tree优化,因为B+Tree的非叶子节点只存储键值信息,假设每一个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构以下图所示:
一般在B+Tree上有两个头指针,一个指向根节点,另外一个指向关键字最小的叶子节点,并且全部叶子节点(即数据节点)之间是一种链式环结构。所以能够对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另外一种是从根节点开始,进行随机查找。
可能上面例子中只有22条数据记录,看不出B+Tree的优势,下面作一个推算:
InnoDB存储引擎中页的大小为16KB,通常表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也通常为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(由于是估值,为方便计算,这里的K取值为103)。也就是说一个深度为3的B+Tree索引能够维护103 * 10^3 * 10^3 = 10亿 条记录。
实际状况中每一个节点可能不能填充满,所以在数据库中,B+Tree的高度通常都在2-4层。MySQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只须要1~3次磁盘I/O操做。
B+Tree性质
MyISAM引擎的索引文件和数据文件是分离的。MyISAM引擎索引结构的叶子节点的数据域,存放的并非实际的数据记录,而是数据记录的地址。索引文件与数据文件分离,这样的索引称为"非聚簇索引"。MyISAM的主索引与辅助索引区别并不大,只是主键索引不能有重复的关键字。
在MyISAM中,索引(含叶子节点)存放在单独的.myi文件中,叶子节点存放的是数据的物理地址偏移量(经过偏移量访问就是随机访问,速度很快)。
主索引是指主键索引,键值不可能重复;辅助索引则是普通索引,键值可能重复。
经过索引查找数据的流程:先从索引文件中查找到索引节点,从中拿到数据的文件指针,再到数据文件中经过文件指针定位了具体的数据。辅助索引相似。
InnoDB引擎索引结构的叶子节点的数据域,存放的就是实际的数据记录(对于主索引,此处会存放表中全部的数据记录;对于辅助索引此处会引用主键,检索的时候经过主键到主键索引中找到对应数据行),或者说,InnoDB的数据文件自己就是主键索引文件,这样的索引被称为“聚簇索引”,一个表只能有一个聚簇索引。
咱们知道InnoDB索引是汇集索引,它的索引和数据是存入同一个.idb文件中的,所以它的索引结构是在同一个树节点中同时存放索引和数据,以下图中最底层的叶子节点有三行数据,对应于数据表中的id、stu_id、name数据项。
在Innodb中,索引分叶子节点和非叶子节点,非叶子节点就像新华字典的目录,单独存放在索引段中,叶子节点则是顺序排列的,在数据段中。Innodb的数据文件能够按照表来切分(只须要开启innodb_file_per_table),切分后存放在xxx.ibd中,默认不切分,存放在xxx.ibdata中。
此次咱们以示例中学生表中的name列创建辅助索引,它的索引结构跟主键索引的结构有很大差异,在最底层的叶子结点有两行数据,第一行的字符串是辅助索引,按照ASCII码进行排序,第二行的整数是主键的值。
这就意味着,对name列进行条件搜索,须要两个步骤:
① 在辅助索引上检索name,到达其叶子节点获取对应的主键;
② 使用主键在主索引上再进行对应的检索操做
这也就是所谓的“回表查询”
InnoDB 索引结构须要注意的点
正如咱们上面介绍 InnoDB 存储结构,索引与数据是共同存储的,无论是主键索引仍是辅助索引,在查找时都是经过先查找到索引节点才能拿到相对应的数据,若是咱们在设计表结构时没有显式指定索引列的话,MySQL 会从表中选择数据不重复的列创建索引,若是没有符合的列,则 MySQL 自动为 InnoDB 表生成一个隐含字段做为主键,而且这个字段长度为6个字节,类型为整型。
❝
那为何推荐使用整型自增主键而不是选择UUID?
❝
为何非主键索引结构叶子节点存储的是主键值?
保证数据一致性和节省存储空间,能够这么理解:商城系统订单表会存储一个用户ID做为关联外键,而不推荐存储完整的用户信息,由于当咱们用户表中的信息(真实名称、手机号、收货地址···)修改后,不须要再次维护订单表的用户数据,同时也节省了存储空间。
空间索引是MyISAM的一种特殊索引类型,主要用于地理空间数据类型
❝
为何Mysql索引要用B+树不是B树?
用B+树不用B树考虑的是IO对性能的影响,B树的每一个节点都存储数据,而B+树只有叶子节点才存储数据,因此查找相同数据量的状况下,B树的高度更高,IO更频繁。数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引所有加载到内存了,只能逐一加载每个磁盘页(对应索引树的节点)。其中在MySQL底层对B+树进行进一步优化:在叶子节点中是双向链表,且在链表的头结点和尾节点也是循环指向的。
❝
面试官:为什么不采用Hash方式?
由于Hash索引底层是哈希表,哈希表是一种以key-value存储数据的结构,因此多个数据在存储关系上是彻底没有任何顺序关系的,因此,对于区间查询是没法直接经过索引查询的,就须要全表扫描。因此,哈希索引只适用于等值查询的场景。而B+ Tree是一种多路平衡查询树,因此他的节点是自然有序的(左子节点小于父节点、父节点小于右子节点),因此对于范围查询的时候不须要作全表扫描。
哈希索引不支持多列联合索引的最左匹配规则,若是有大量重复键值得状况下,哈希索引的效率会很低,由于存在哈希碰撞问题。
覆盖索引(Covering Index),或者叫索引覆盖, 也就是平时所说的不须要回表操做
❝
count(*) 和 count(1)和count(列名)区别 ps:这道题说法有点多
执行效果上:
执行效率上:
❝
MySQL中 in和 exists 的区别?
SELECT * FROM A WHERE A.id IN (SELECT id FROM B); SELECT * FROM A WHERE EXISTS (SELECT * from B WHERE B.id = A.id);
若是查询的两个表大小至关,那么用in和exists差异不大。
若是两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in:
❝
UNION和UNION ALL的区别?
UNION和UNION ALL都是将两个结果集合并为一个,两个要联合的SQL语句 字段个数必须同样,并且字段类型要“相容”(一致);
❝
mysql 的内链接、左链接、右链接有什么区别?
什么是内链接、外链接、交叉链接、笛卡尔积呢?
❝
事务的隔离级别有哪些?MySQL的默认隔离级别是什么?
什么是幻读,脏读,不可重复读呢?
MySQL事务的四大特性以及实现原理
MVCC熟悉吗,它的底层原理?
MySQL 事务主要用于处理操做量大,复杂度高的数据。好比说,在人员管理系统中,你删除一我的员,你即须要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操做语句就构成一个事务!
事务是由一组SQL语句组成的逻辑处理单元,具备4个属性,一般简称为事务的ACID属性。
并发事务处理带来的问题
幻读和不可重复读的区别:
并发事务处理带来的问题的解决办法:
“更新丢失”一般是应该彻底避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,须要应用程序对要更新的数据加必要的锁来解决,所以,防止更新丢失应该是应用的责任。
“脏读” 、 “不可重复读”和“幻读” ,其实都是数据库读一致性问题,必须由数据库提供必定的事务隔离机制来解决:
一种是加锁:在读取数据前,对其加锁,阻止其余事务对数据进行修改。
另外一种是数据多版本并发控制(MultiVersion Concurrency Control,简称 MVCC 或 MCC),也称为多版本数据库:不用加任何锁, 经过必定机制生成一个数据请求时间点的一致性数据快照 (Snapshot), 并用这个快照来提供必定级别 (语句级或事务级) 的一致性读取。从用户的角度来看,好象是数据库能够提供同一数据的多个版本。
数据库事务的隔离级别有4种,由低到高分别为
查看当前数据库的事务隔离级别:
show variables like 'tx_isolation'
下面经过事例一一阐述在事务的并发操做中可能会出现脏读,不可重复读,幻读和事务隔离级别的联系。
数据库的事务隔离越严格,并发反作用越小,但付出的代价就越大,由于事务隔离实质上就是使事务在必定程度上“串行化”进行,这显然与“并发”是矛盾的。同时,不一样的应用对读一致性和事务隔离程度的要求也是不一样的,好比许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。
读未提交,就是一个事务能够读取另外一个未提交事务的数据。
事例:老板要给程序员发工资,程序员的工资是3.6万/月。可是发工资时老板不当心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,可是事务尚未提交,就在这时,程序员去查看本身这个月的工资,发现比往常多了3千元,觉得涨工资了很是高兴。可是老板及时发现了不对,立刻回滚差点就提交了的事务,将数字改为3.6万再提交。
分析:实际程序员这个月的工资仍是3.6万,可是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。
读提交,顾名思义,就是一个事务要等另外一个事务提交后才能读取数据。
事例:程序员拿着信用卡去享受生活(卡里固然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱所有转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额固然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,如有事务对数据进行更新(UPDATE)操做时,读操做事务要等待这个更新操做事务提交后才能读取数据,能够解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不一样数据,这就是不可重复读。
那怎么解决可能的不可重复读问题?Repeatable read !
重复读,就是在开始读取数据(事务开启)时,再也不容许修改操做。MySQL的默认事务隔离级别
事例:程序员拿着信用卡去享受生活(卡里固然是只有3.6万),当他埋单时(事务开启,不容许其余事务的UPDATE修改操做),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就能够扣款了。
分析:重复读能够解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操做。可是可能还会有幻读问题。由于幻读问题对应的是插入INSERT操做,而不是UPDATE操做。
何时会出现幻读?
事例:程序员某一天去消费,花了2千元,而后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,彷佛出现了幻觉,这就是幻读。
那怎么解决幻读问题?Serializable!
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,能够避免脏读、不可重复读与幻读。简单来讲,Serializable会在读取的每一行数据上都加锁,因此可能致使大量的超时和锁争用问题。这种事务隔离级别效率低下,比较耗数据库性能,通常不使用。
事务隔离级别读数据一致性脏读不可重复读幻读读未提交(read-uncommitted)最低级被,只能保证不读取物理上损坏的数据是是是读已提交(read-committed)语句级否是是可重复读(repeatable-read)事务级否否是串行化(serializable)最高级别,事务级否否否
须要说明的是,事务隔离级别和数据访问的并发性是对立的,事务隔离级别越高并发性就越差。因此要根据具体的应用来肯定合适的事务隔离级别,这个地方没有万能的原则。
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。咱们能够经过SELECT @@tx_isolation;命令来查看,MySQL 8.0 该命令改成SELECT @@transaction_isolation;
这里须要注意的是:与 SQL 标准不一样的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 算法,所以能够避免幻读的产生,这与其余数据库系统(如 SQL Server)是不一样的。因此说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)已经能够彻底保证事务的隔离性要求,即达到了 SQL标准的 SERIALIZABLE(可串行化)隔离级别,并且保留了比较好的并发性能。
由于隔离级别越低,事务请求的锁越少,因此大部分数据库系统的隔离级别都是READ-COMMITTED(读已提交):,可是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有任何性能损失。
MySQL的大多数事务型存储引擎实现都不是简单的行级锁。基于提高并发性考虑,通常都同时实现了多版本并发控制(MVCC),包括Oracle、PostgreSQL。只是实现机制各不相同。
能够认为 MVCC 是行级锁的一个变种,但它在不少状况下避免了加锁操做,所以开销更低。虽然实现机制有所不一样,但大都实现了非阻塞的读操做,写操做也只是锁定必要的行。
MVCC 的实现是经过保存数据在某个时间点的快照来实现的。也就是说无论须要执行多长时间,每一个事物看到的数据都是一致的。
典型的MVCC实现方式,分为乐观(optimistic)并发控制和悲观(pressimistic)并发控制。下边经过 InnoDB的简化版行为来讲明 MVCC 是如何工做的。
InnoDB 的 MVCC,是经过在每行记录后面保存两个隐藏的列来实现。这两个列,一个保存了行的建立时间,一个保存行的过时时间(删除时间)。固然存储的并非真实的时间,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会做为事务的版本号,用来和查询到的每行记录的版本号进行比较。
REPEATABLE READ(可重读)隔离级别下MVCC如何工做:
SELECTInnoDB会根据如下两个条件检查每行记录:只有符合上述两个条件的才会被查询出来
InnoDB只查找版本早于当前事务版本的数据行,这样能够确保事务读取的行,要么是在开始事务以前已经存在要么是事务自身插入或者修改过的
行的删除版本号要么未定义,要么大于当前事务版本号,这样能够确保事务读取到的行在事务开始以前未被删除
INSERT:InnoDB为新插入的每一行保存当前系统版本号做为行版本号
DELETE:InnoDB为删除的每一行保存当前系统版本号做为行删除标识
UPDATE:InnoDB为插入的一行新纪录保存当前系统版本号做为行版本号,同时保存当前系统版本号到原来的行做为删除标识
保存这两个额外系统版本号,使大多数操做都不用加锁。使数据操做简单,性能很好,而且也能保证只会读取到符合要求的行。不足之处是每行记录都须要额外的存储空间,须要作更多的行检查工做和一些额外的维护工做。
MVCC 只在 COMMITTED READ(读提交)和REPEATABLE READ(可重复读)两种隔离级别下工做。
InnoDB 使用日志来减小提交事务时的开销。由于日志中已经记录了事务,就无须在每一个事务提交时把缓冲池的脏块刷新(flush)到磁盘中。
事务修改的数据和索引一般会映射到表空间的随机位置,因此刷新这些变动到磁盘须要不少随机 IO。
InnoDB 假设使用常规磁盘,随机IO比顺序IO昂贵得多,由于一个IO请求须要时间把磁头移到正确的位置,而后等待磁盘上读出须要的部分,再转到开始位置。
InnoDB 用日志把随机IO变成顺序IO。一旦日志安全写到磁盘,事务就持久化了,即便断电了,InnoDB能够重放日志而且恢复已经提交的事务。
InnoDB 使用一个后台线程智能地刷新这些变动到数据文件。这个线程能够批量组合写入,使得数据写入更顺序,以提升效率。
事务日志能够帮助提升事务效率:
目前来讲,大多数存储引擎都是这样实现的,咱们一般称之为预写式日志(Write-Ahead Logging),修改数据须要写两次磁盘。
事务的实现是基于数据库的存储引擎。不一样的存储引擎对事务的支持程度不同。MySQL 中支持事务的存储引擎有 InnoDB 和 NDB。
事务的实现就是如何实现ACID特性。
事务的隔离性是经过锁实现,而事务的原子性、一致性和持久性则是经过事务日志实现 。
❝
事务是如何经过日志来实现的,说得越深刻越好。
事务日志包括:重作日志redo和回滚日志undo
二种日志都可以视为一种恢复操做,redo_log是恢复提交事务修改的页操做,而undo_log是回滚行记录到特定版本。两者记录的内容也不一样,redo_log是物理日志,记录页的物理修改操做,而undo_log是逻辑日志,根据每行记录进行记录。
❝
又引出个问题:你知道MySQL 有多少种日志吗?
❝
分布式事务相关问题,可能还会问到 2PC、3PC,,,
分布式事务的实现方式有不少,既能够采用 InnoDB 提供的原生的事务支持,也能够采用消息队列来实现分布式事务的最终一致性。这里咱们主要聊一下 InnoDB 对分布式事务的支持。
MySQL 从 5.0.3 InnoDB 存储引擎开始支持XA协议的分布式事务。一个分布式事务会涉及多个行动,这些行动自己是事务性的。全部行动都必须一块儿成功完成,或者一块儿被回滚。
在MySQL中,使用分布式事务涉及一个或多个资源管理器和一个事务管理器。
如图,MySQL 的分布式事务模型。模型中分三块:应用程序(AP)、资源管理器(RM)、事务管理器(TM):
分布式事务采用两段式提交(two-phase commit)的方式:
❝
数据库的乐观锁和悲观锁?
MySQL 中有哪几种锁,列举一下?
MySQL中InnoDB引擎的行锁是怎么实现的?
MySQL 间隙锁有没有了解,死锁有没有了解,写一段会形成死锁的 sql 语句,死锁发生了如何解决,MySQL 有没有提供什么机制去解决死锁
锁是计算机协调多个进程或线程并发访问某一资源的机制。
在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用之外,数据也是一种供许多用户共享的资源。数据库锁定机制简单来讲,就是数据库为了保证数据的一致性,而使各类共享资源在被并发访问变得有序所设计的一种规则。
打个比方,咱们到淘宝上买一件商品,商品只有一件库存,这个时候若是还有另外一我的买,那么如何解决是你买到仍是另外一我的买到的问题?这里确定要用到事物,咱们先从库存表中取出物品数量,而后插入订单,付款后插入付款表信息,而后更新商品数量。在这个过程当中,使用锁能够对有限的资源进行保护,解决隔离和并发的矛盾。
从对数据操做的类型分类:
从对数据操做的粒度分类:
为了尽量提升数据库的并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操做的数据的方案会获得最大的并发度,可是管理锁是很耗资源的事情(涉及获取,检查,释放锁等动做),所以数据库系统须要在高并发响应和系统性能两方面进行平衡,这样就产生了“锁粒度(Lock granularity)”的概念。
适用:从锁的角度来讲,表级锁更适合于以查询为主,只有少许按索引条件更新数据的应用,如Web应用;而行级锁则更适合于有大量按索引条件并发更新少许不一样数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。
行锁表锁页锁MyISAM
√
BDB
√√InnoDB√√
Memory
√
MyISAM 的表锁有两种模式:
MyISAM 表的读操做与写操做之间,以及写操做之间是串行的。当一个线程得到对一个表的写锁后, 只有持有锁的线程能够对表进行更新操做。其余线程的读、 写操做都会等待,直到锁被释放为止。
默认状况下,写锁比读锁具备更高的优先级:当一个锁释放时,这个锁会优先给写锁队列中等候的获取锁请求,而后再给读锁队列中等候的获取锁请求。
InnoDB 实现了如下两种类型的行锁:
为了容许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁:
索引失效会致使行锁变表锁。好比 vchar 查询不写单引号的状况。
乐观锁与悲观锁是两种并发控制的思想,可用于解决丢失更新问题
乐观锁会“乐观地”假定大几率不会发生并发更新冲突,访问、处理数据过程当中不加锁,只在更新数据时再根据版本号或时间戳判断是否有冲突,有则处理,无则提交事务。用数据版本(Version)记录机制实现,这是乐观锁最经常使用的一种实现方式
悲观锁会“悲观地”假定大几率会发生并发更新冲突,访问、处理数据前就加排他锁,在整个数据处理过程当中锁定数据,事务提交或回滚后才释放锁。另外与乐观锁相对应的,悲观锁是由数据库本身实现了的,要用的时候,咱们直接调用数据库的相关语句就能够了。
❝
select for update有什么含义,会锁表仍是锁行仍是其余
for update 仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操做时,经过“for update”语句,MySQL会对查询结果集中每行数据都添加排他锁,其余线程对该记录的更新与删除操做都会阻塞。排他锁包含行锁、表锁。
InnoDB这种行锁实现特色意味着:只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁!假设有个表单 products ,里面有id跟name二个栏位,id是主键。
SELECT * FROM products WHERE id='3' FOR UPDATE; SELECT * FROM products WHERE id='3' and type=1 FOR UPDATE;
SELECT * FROM products WHERE id='-1' FOR UPDATE;
SELECT * FROM products WHERE name='Mouse' FOR UPDATE;
SELECT * FROM products WHERE id<>'3' FOR UPDATE;
SELECT * FROM products WHERE id LIKE '3' FOR UPDATE;
注1: FOR UPDATE仅适用于InnoDB,且必须在交易区块(BEGIN/COMMIT)中才能生效。注2: 要测试锁定的情况,能够利用MySQL的Command Mode ,开二个视窗来作测试。
❝
MySQL 遇到过死锁问题吗,你是如何解决的?
死锁产生:
检测死锁:数据库系统实现了各类死锁检测和死锁超时的机制。InnoDB存储引擎能检测到死锁的循环依赖并当即返回一个错误。
死锁恢复:死锁发生之后,只有部分或彻底回滚其中一个事务,才能打破死锁,InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。因此事务型应用程序在设计时必须考虑如何处理死锁,多数状况下只须要从新执行因死锁回滚的事务便可。
外部锁的死锁检测:发生死锁后,InnoDB 通常都能自动检测到,并使一个事务释放锁并回退,另外一个事务得到锁,继续完成事务。但在涉及外部锁,或涉及表锁的状况下,InnoDB 并不能彻底自动检测到死锁, 这须要经过设置锁等待超时参数 innodb_lock_wait_timeout 来解决
死锁影响性能:死锁会影响性能而不是会产生严重错误,由于InnoDB会自动检测死锁情况并回滚其中一个受影响的事务。在高并发系统上,当许多线程等待同一个锁时,死锁检测可能致使速度变慢。有时当发生死锁时,禁用死锁检测(使用innodb_deadlock_detect配置选项)可能会更有效,这时能够依赖innodb_lock_wait_timeout设置进行事务回滚。
MyISAM避免死锁:
InnoDB避免死锁:
若是出现死锁,能够用 show engine innodb status;命令来肯定最后一个死锁产生的缘由。返回结果中包括死锁相关事务的详细信息,如引起死锁的 SQL 语句,事务已经得到的锁,正在等待什么锁,以及被回滚的事务等。据此能够分析死锁产生的缘由和改进措施。
❝
平常工做中你是怎么优化SQL的?
SQL优化的通常步骤是什么,怎么看执行计划(explain),如何理解其中各个字段的含义?
如何写sql可以有效的使用到复合索引?
一条sql执行过长的时间,你如何优化,从哪些方面入手?
什么是最左前缀原则?什么是最左匹配原则?
业务需求对MySQL的影响(合适合度)
存储定位对MySQL的影响
系统各类配置及规则数据
活跃用户的基本信息数据
活跃用户的个性化定制信息数据
准实时的统计信息数据
其余一些访问频繁但变动较少的数据
二进制多媒体数据
流水队列数据
超大文本数据
不适合放进MySQL的数据
须要放进缓存的数据
Schema设计对系统的性能影响
尽可能减小对数据库访问的请求
尽可能减小无用数据的查询请求
硬件环境对系统性能的影响
在优化MySQL时,一般须要对数据库进行分析,常见的分析手段有慢查询日志,EXPLAIN 分析查询,profiling分析以及show命令查询系统状态及系统变量,经过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。
咱们能够经过 show 命令查看 MySQL 状态及变量,找到系统的瓶颈:
Mysql> show status ——显示状态信息(扩展show status like ‘XXX’) Mysql> show variables ——显示系统变量(扩展show variables like ‘XXX’) Mysql> show innodb status ——显示InnoDB存储引擎的状态 Mysql> show processlist ——查看当前SQL执行,包括执行状态、是否锁表等 Shell> mysqladmin variables -u username -p password——显示系统变量 Shell> mysqladmin extended-status -u username -p password——显示状态信息
是什么:使用 Explain 关键字能够模拟优化器执行SQL查询语句,从而知道 MySQL 是如何处理你的 SQL 语句的。分析你的查询语句或是表结构的性能瓶颈
能干嘛:
怎么玩:
expalin
各字段解释
id(select 查询的序列号,包含一组数字,表示查询中执行select子句或操做表的顺序)
id相同,执行顺序从上往下
id全不一样,若是是子查询,id的序号会递增,id值越大优先级越高,越先被执行
id部分相同,执行顺序是先按照数字大的先执行,而后数字相同的按照从上往下的顺序执行
select_type(查询类型,用于区别普通查询、联合查询、子查询等复杂查询)
SIMPLE :简单的select查询,查询中不包含子查询或UNION
PRIMARY:查询中若包含任何复杂的子部分,最外层查询被标记为PRIMARY
SUBQUERY:在select或where列表中包含了子查询
DERIVED:在from列表中包含的子查询被标记为DERIVED,MySQL会递归执行这些子查询,把结果放在临时表里
UNION:若第二个select出如今UNION以后,则被标记为UNION,若UNION包含在from子句的子查询中,外层select将被标记为DERIVED
UNION RESULT:从UNION表获取结果的select
table(显示这一行的数据是关于哪张表的)
type(显示查询使用了那种类型,从最好到最差依次排列 system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL )tip: 通常来讲,得保证查询至少达到range级别,最好到达ref
system:表只有一行记录(等于系统表),是 const 类型的特例,平时不会出现
const:表示经过索引一次就找到了,const 用于比较 primary key 或 unique 索引,由于只要匹配一行数据,因此很快,如将主键置于 where 列表中,mysql 就能将该查询转换为一个常量
eq_ref:惟一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配,常见于主键或惟一索引扫描
ref:非惟一性索引扫描,范围匹配某个单独值得全部行。本质上也是一种索引访问,他返回全部匹配某个单独值的行,然而,它可能也会找到多个符合条件的行,多以他应该属于查找和扫描的混合体
range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪一个索引,通常就是在你的where语句中出现了between、<、>、in等的查询,这种范围扫描索引比全表扫描要好,由于它只需开始于索引的某一点,而结束于另外一点,不用扫描所有索引
index:Full Index Scan,index于ALL区别为index类型只遍历索引树。一般比ALL快,由于索引文件一般比数据文件小。(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
ALL:Full Table Scan,将遍历全表找到匹配的行
possible_keys(显示可能应用在这张表中的索引,一个或多个,查询涉及到的字段若存在索引,则该索引将被列出,但不必定被查询实际使用)
key
实际使用的索引,若是为NULL,则没有使用索引
查询中若使用了覆盖索引,则该索引和查询的 select 字段重叠,仅出如今key列表中
explain-key
key_len
表示索引中使用的字节数,可经过该列计算查询中使用的索引的长度。在不损失精确性的状况下,长度越短越好
key_len显示的值为索引字段的最大可能长度,并不是实际使用长度,即key_len是根据表定义计算而得,不是经过表内检索出的
ref(显示索引的哪一列被使用了,若是可能的话,是一个常数。哪些列或常量被用于查找索引列上的值)
rows(根据表统计信息及索引选用状况,大体估算找到所需的记录所须要读取的行数)
Extra(包含不适合在其余列中显示但十分重要的额外信息)
using filesort: 说明mysql会对数据使用一个外部的索引排序,不是按照表内的索引顺序进行读取。mysql中没法利用索引完成的排序操做称为“文件排序”。常见于order by和group by语句中
Using temporary:使用了临时表保存中间结果,mysql在对查询结果排序时使用临时表。常见于排序order by和分组查询group by。
using index:表示相应的select操做中使用了覆盖索引,避免访问了表的数据行,效率不错,若是同时出现using where,代表索引被用来执行索引键值的查找;不然索引被用来读取数据而非执行查找操做
using where:使用了where过滤
using join buffer:使用了链接缓存
impossible where:where子句的值老是false,不能用来获取任何元祖
select tables optimized away:在没有group by子句的状况下,基于索引优化操做或对于MyISAM存储引擎优化COUNT(*)操做,没必要等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化
distinct:优化distinct操做,在找到第一匹配的元祖后即中止找一样值的动做
case:
explain-demo
MySQL 的慢查询日志是 MySQL 提供的一种日志记录,它用来记录在 MySQL 中响应时间超过阈值的语句,具体指运行时间超过 long_query_time 值的 SQL,则会被记录到慢查询日志中。
查看开启状态
SHOW VARIABLES LIKE '%slow_query_log%'
开启慢查询日志
mysql> set global slow_query_log='ON'; mysql> set global slow_query_log_file='/var/lib/mysql/hostname-slow.log'; mysql> set global long_query_time=2;
也可set文件位置,系统会默认给一个缺省文件host_name-slow.log
使用set操做开启慢查询日志只对当前数据库生效,若是MySQL重启则会失效。
[mysqld] slow_query_log = ON slow_query_log_file = /var/lib/mysql/hostname-slow.log long_query_time = 3
注:log-slow-queries 参数为慢查询日志存放的位置,通常这个目录要有 MySQL 的运行账号的可写权限,通常都将这个目录设置为 MySQL 的数据存放目录;long_query_time=2 中的 2 表示查询超过两秒才记录;在my.cnf或者 my.ini 中添加 log-queries-not-using-indexes 参数,表示记录下没有使用索引的查询。
能够用 select sleep(4) 验证是否成功开启。
在生产环境中,若是手工分析日志,查找、分析SQL,仍是比较费劲的,因此MySQL提供了日志分析工具mysqldumpslow。
经过 mysqldumpslow --help 查看操做帮助信息
也可以使用 pt-query-digest 分析 RDS MySQL 慢查询日志
经过慢日志查询能够知道哪些 SQL 语句执行效率低下,经过 explain 咱们能够得知 SQL 语句的具体执行状况,索引使用等,还能够结合Show Profile命令查看执行状态。
Show Profile 是 MySQL 提供能够用来分析当前会话中语句执行的资源消耗状况。能够用于SQL的调优的测量
默认状况下,参数处于关闭状态,并保存最近15次的运行结果
分析步骤mysql> show profiles; +----------+------------+---------------------------------+ | Query_ID | Duration | Query | +----------+------------+---------------------------------+ | 1 | 0.00385450 | show variables like "profiling" | | 2 | 0.00170050 | show variables like "profiling" | | 3 | 0.00038025 | select * from t_base_user | +----------+------------+---------------------------------+
converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了。
create tmp table 建立临时表,这个要注意
Copying to tmp table on disk 把内存临时表复制到磁盘
locked
诊断SQL,show profile cpu,block io for query id(上一步前面的问题SQL数字号码)
平常开发须要注意的结论
是否支持,看看当前的mysql版本是否支持mysql>Show variables like 'profiling'; --默认是关闭,使用前须要开启
开启功能,默认是关闭,使用前须要开启mysql>set profiling=1;
运行SQL
查看结果
❝
查询中哪些状况不会使用索引?
通常性建议
永远小标驱动大表(小的数据集驱动大的数据集)
slect * from A where id in (select id from B)`等价于 #等价于 select id from B select * from A where A.id=B.id
当 B 表的数据集必须小于 A 表的数据集时,用 in 优于 exists
select * from A where exists (select 1 from B where B.id=A.id) #等价于 select * from A select * from B where B.id = A.id`
当 A 表的数据集小于B表的数据集时,用 exists优于用 in
注意:A表与B表的ID字段应创建索引。
order by关键字优化
order by子句,尽可能使用 Index 方式排序,避免使用 FileSort 方式排序
MySQL 支持两种方式的排序,FileSort 和 Index,Index效率高,它指 MySQL 扫描索引自己完成排序,FileSort 效率较低;
ORDER BY 知足两种状况,会使用Index方式排序;①ORDER BY语句使用索引最左前列 ②使用where子句与ORDER BY子句条件列组合知足索引最左前列
尽量在索引列上完成排序操做,遵守索引建的最佳最前缀
若是不在索引列上,filesort 有两种算法,mysql就要启动双路排序和单路排序
双路排序:MySQL 4.1以前是使用双路排序,字面意思就是两次扫描磁盘,最终获得数据
单路排序:从磁盘读取查询须要的全部列,按照order by 列在 buffer对它们进行排序,而后扫描排序后的列表进行输出,效率高于双路排序
优化策略
增大sort_buffer_size参数的设置
增大max_lencth_for_sort_data参数的设置
GROUP BY关键字优化
MySQL 支持的数据类型很是多,选择正确的数据类型对于获取高性能相当重要。无论存储哪一种类型的数据,下面几个简单的原则都有助于作出更好的选择。
通常状况下咱们建立的表对应一组存储文件,使用MyISAM存储引擎时是一个.MYI和.MYD文件,使用Innodb存储引擎时是一个.ibd和.frm(表结构)文件。
当数据量较大时(通常千万条记录级别以上),MySQL的性能就会开始降低,这时咱们就须要将数据分散到多组存储文件,保证其单个文件的执行效率
能干吗
怎么玩
首先查看当前数据库是否支持分区
分区类型及操做
看上去分区表很帅气,为何大部分互联网仍是更多的选择本身分库分表来水平扩展咧?
❝
随着业务的发展,业务愈来愈复杂,应用的模块愈来愈多,总的数据量很大,高并发读写操做均超过单个数据库服务器的处理能力怎么办?
这个时候就出现了数据分片,数据分片指按照某个维度将存放在单一数据库中的数据分散地存放至多个数据库或表中。数据分片的有效手段就是对关系型数据库进行分库和分表。
区别于分区的是,分区通常都是放在单机里的,用的比较多的是时间范围分区,方便归档。只不过度库分表须要代码实现,分区则是mysql内部实现。分库分表和分区并不冲突,能够结合使用。