MySQL中的事务、索引、锁机制

目录

一、MySQL基础架构

 二、MySQL中的索引

1. 什么是索引,索引的作用

2. 索引的类型

1. 主键索引(Primary Key)

2. 二级索引(辅助索引)

3. 索引的底层数据结构

1. B 树& B+树

2. 为什么选择B/B+树,为什么要用 B+树,为什么不用二叉树?

3. 不同引擎对于B+树的实现

4. B+ Tree索引和Hash索引区别?

4. 聚集索引和非聚集索引

1. 介绍聚集索引和非聚集索引

2. 回表查询

3. 索引下推

5. 联合索引和覆盖索引

1. 覆盖索引

2. 联合索引

3. 最左前缀匹配原则

6. 创建索引及索引的优化

7. MySQL 为表字段添加索引

三、MySQL事务

1. 事务的四大特性

2. MySQL如何保证实现四大特性

3. 并发事务带来的问题

4. 事务的隔离级别

1. SQL标准定义了四个隔离级别

2. MySQL 的隔离级别是基于锁实现的吗?

3. 使用间隙锁解决幻读的问题

4. MVCC 的理解

四、MySQL引擎

1. 三大引擎

2. MyISAM与InnoDB的区别?

五、MySQL的锁机制

1. 锁的分类

2. InnoDB 有哪几类行锁?

3. CAS 是什么?

4. 意向锁有什么作用

5. 死锁是什么,怎么解决?

六、数据库相关知识

1. 数据库三大范式

2. SQL中的约束

3. 六种关联查询

4. Delete/Drop/Truncate区别

5. Count(*),Count(1),Count(列名)的区别

6. SQL查询语句的优化

7. SQL的生命周期

8. 数据库的设计原则


一、MySQL基础架构

主要分为三层:

1. 第一层是连接器,负责和客户端建立连接、身份认证和权限相关。(登录 MySQL 的时候)。

2. 第二层负责编译和优化sql,包括分析器(分析sql)、优化器(选择索引)、执行器(执行sql)以及查询缓存(MySQL8.0不再提供对查询缓存的支持)。

3. 第三层是存储引擎,用于存储数据并提供读写接口。支持 InnoDB、MyISAM、Memory 等多种存储引擎。

 二、MySQL中的索引


1. 什么是索引,索引的作用

索引是一种用于快速查询和检索数据的数据结构,帮助mysql提高查询效率的数据结构,而且是排好序的数据结构,存储在磁盘文件里。

索引的作用是在不读取整个表的情况下,使得数据库应用程序可以更快地查找数据,用户无法看到索引,只能被用来加速检索或查询。

优点:

  • 使用索引可以大大加快数据的检索速度(大大减少检索的数据量), 这也是创建索引的最主要的原因。
  • 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。

缺点:

  • 创建索引维护索引需要耗费许多时间。当对表中的数据进行增删改的时候,如果数据有索引,那么索引也需要动态的修改,会降低 SQL 执行效率。
  • 索引需要使用物理文件存储,也会耗费一定空间。

但是,使用索引一定能提高查询性能吗?

大多数情况下,索引查询都是比全表扫描要快的。但是如果数据库的数据量不大,那么使用索引也不一定能够带来很大提升。

索引建立的原则:

  • 在最频繁使用的、用以缩小查询范围的字段上建立索引。
  • 在最频繁使用的、需要排序的字段上建立索引。
  • 对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。

2. 索引的类型


1. 主键索引(Primary Key)

数据表的主键列使用的就是主键索引。

一张数据表有只能有一个主键,并且主键不能为 null,不能重复。(可以理解为一种特殊的唯一索引)

在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在null值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。


2. 二级索引(辅助索引)

二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。

唯一索引,普通索引,前缀索引等索引属于二级索引。

  • 唯一索引(Unique Key) :唯一索引也是一种约束。唯一索引的属性列不能出现重复的数据,但是允许数据为 NULL,一张表允许创建多个唯一索引。
  • 普通索引(Normal Index) :也叫单列索引,给表中的某一个列创建索引,即一个索引只包含单个列;一个表可以有多个单列索引。普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和 NULL。
  • 前缀索引(Prefix) :前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小, 因为只取前几个字符。
  • 全文索引(Full Text) :全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。Mysql5.6 之前只有 MYISAM 引擎支持全文索引,5.6 之后 InnoDB 也支持了全文索引。

3. 索引的底层数据结构


1. B 树& B+树

B 树也称 B-树,全称为 多路平衡查找树 ,B+ 树是 B 树的一种变体。B 树和 B+树中的 B 是 Balanced (平衡)的意思。

目前大部分数据库系统及文件系统都采用 B-Tree 或其变种 B+Tree 作为索引结构。

  • B树是一种平衡多路查找树,满足平衡二叉树的规律,同时可以拥有多个子树,子树的数量取决于关键字的数量,(关键字数量+1),因此从这个特征来看在存储同样数据量,B树会更低一些。

  • B+树是在B树的基础上做了增强。主要有两点:

    • B树的数据存储在每个节点上,而B+树中的数据存储在叶子节点上,并且通过双向链表的方式对叶子节点的数据进行了连接。

    • B树的子树数量等于关键词数量加1,B+树的子树数量等于关键字的数量。


2. 为什么选择B/B+树,为什么要用 B+树,为什么不用二叉树?

B/B+Tree更适合文件系统的索引/更适合硬盘上查询的数据结构。

  • 1.高度低-->io次数少

  • 2.顺序io只需一次扫描数据 > 随机io-->性能高

  • 3.多路子树,数据量大不能一次性全部加载到内存时,会每次加载树的一个节点,由于多路子树的节点数量多于普通树的节点,所以每次加载的数量更多-->速度更快

为什么不是一般二叉树?  

二者存储数据的结构可以看出,二叉树随着数据的增加,树的高度会越来越高,而B+树是越来越胖。

树的高度越来越高,增加了I/O次数,导致查询效率减低。而B+树随着数据量的增加,树的宽度越来越大,这样空间利用率更高,可减少I/O次数,查询效率较快。

我们知道,在内存比在磁盘的数据,查询效率快得多。如果树这种数据结构作为索引,那我们每查找一次数据就需要从磁盘中读取一个节点,也就是我们说的一个磁盘块,但是平衡二叉树可是每个节点只存储一个键值和数据的,如果是B树,可以存储更多的节点数据,每个节点包含多个数据,树的高度也会降低,因此读取磁盘的次数就降下来啦,查询效率就快啦。

为什么不是B树而是B+树呢?

  • B+树的层级更少。相较于B树,B+树每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快。

  • B+树查询效率更高。B+树使用双向链表串连所有叶子节点,而且数据是按照顺序排列的,区间查询效率更高(因为所有数据都在B+树的叶子节点,扫描数据库只需扫一遍叶子结点就行了),但是B树则需要通过中序遍历才能完成查询范围的查找。

  • B+树查询效率更稳定。B+树只有叶子结点存放数据的data值,非叶子节点上只存储key值信息,B+树每次都必须查询到叶子节点才能找到数据,而B树查询的数据可能不在叶子节点,也可能在,这样就会造成查询的效率的不稳定。

  • B+树的磁盘读写代价更小。B+树的内部节点只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度,因此通常B+树矮更胖,查询产生的I/O更少。B树节点中不仅存了数据的key值,还有data值,而每一个页的存储空间是有限的(16KB),如果data数据较大时将会导致一个页能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。


3. 不同引擎对于B+树的实现

B+Tree在两种存储引擎的实现方式是不同的。  

  • MylSAM:B+Tree叶节点的「data域存放的是数据记录的地址」

    在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的key存在,则取出其data域的值,然后以data域的值为地址读取相应的数据记录。这被称为“非聚簇索引”

  • InnoDB:其数据文件「本身就是索引文件」。

    相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,「树的叶节点data域保存了完整的数据记录」。

    这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。


4. B+ Tree索引和Hash索引区别?

  • B+树可以进行范围搜索,hash索引只支持=的操作符,等值查询
  • B+树支持order by进行排序,hash索引没办法利用索引完成排序。
  • B+树支持多列联合索引的最左匹配规则。hash索引不支持
  • 如果有大量重复健值得情况下,hash索引的效率会很低,因为哈希冲突问题。
  • B+树使用like进行模糊查询的时候,like后面%开头可以起到优化作用,Hash索引不行。

Hash索引的缺点

  • 查询性能受hash冲突率影响,性能不稳定

  • 只能通过等值匹配的方式查询,不能范围查询

  • 结构存储上没有顺序,查询时排序无法支持

InnoDB引擎有一个特殊额功能叫做“自适应哈希索引”,当 InnoDB注意到某些索引值被使用得非常频繁时,它会在内存中基于B-Tree索引之上再创建一个哈希索引,这样就让B-Tree索引页具有哈希索引的一些优点,比如快速的哈希查找。


4. 聚集索引和非聚集索引

聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引。

聚集索引是依据主键创建的索引,除了主键以外的其他索引,都是非聚集索引。

不是单独的索引类型,是一种数据存储方式。


1. 介绍聚集索引和非聚集索引

在InnoDB引擎里,一张表的数据对应的物理文件本身是按照B+树来组织的,而聚集索引就是按照每张表的一个主键来构建这样一个B+树,叶子节点里面存储了表里面的每一行数据记录,基于这样一个特征,聚集索引不仅仅是一种索引类型,还是一种数据存储方式

同时意味着每张表里必须有主键,没有主键的话innodb会默认添加一个隐藏列作为主键索引来存储这个表的数据行。一般情况建议使用自增id作为主键,因为id本身具有连续性,对应数据也会按照顺序去存储到磁盘上,写入和检索性能都很高。

innodb里只能存在一个聚集索引,如果有多个,那意味着这个表会有多个副本,这不仅会造成空间浪费,还会导致数据的维护困难。

innodb主键索引存储了一个表的完整数据,主键和行记录放在同一个叶节点,找到了主键也就找到了行记录。所以如果是基于非聚集索引去查找一条数据,最终还是需要访问主键索引来进行检索。

跟MyISAM引擎的非聚集索引不同的是,MyISAM叶节点保存的是地址,而InnoDB是主键,InnoDB非聚集索引的索引文件和数据文件分开存储,索引文件的叶节点只保存主键,在查找时,要先找到叶节点中的主键,再根据主键去主索引文件查找详细行记录


2. 回表查询

上述InnoDB引擎中,非主键索引查找数据时需要先找到主键,再根据主键查找具体行数据,这种现象叫回表查询

如何解决:覆盖索引,即将查询sql中的字段添加到联合索引里面,只要保证查询语句里面的字段都在索引文件中,就无需进行回表查询;让索引范围覆盖住我们select 的范围,就不会发生回表查询。

比方说有个用户表,有id、name、age、addr四个字段,其中id为主键,主键自带主键索引,无需创建

值1:1、小张、18、成都;

值2:2、小黄、20、北京;

这种查询就必须先在索引文件中找到name为小张的索引节点,很明显这个节点里面只有id,因为这张表只有主键索引,再根据id去数据文件查找具体数据。

如果把name、age、addr建立到联合索引,在找到name为小张的索引节点时,发现里面已经有了我们所需要的age、addr,就无需再到数据文件查找;

当然实际开发中,不可能把所有字段建立到联合索引,应根据实际业务场景,把经常需要查询的字段建立到联合索引即可。


3. 索引下推

索引下推是 MySQL 5.6 版本中提供的一项索引优化功能,可以在非聚簇索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表次数。 

不使用索引条件下推优化时的查询过程

获取下一行,首先读取索引信息,然后根据索引将整行数据读取出来。
然后通过where条件判断当前数据是否符合条件,符合返回数据。

使用索引条件下推优化时的查询过程

获取下一行的索引信息。
检查索引中存储的列信息是否符合索引条件,如果符合将整行数据读取出来,如果不符合跳过读取下一行。
用剩余的判断条件,判断此行数据是否符合要求,符合要求返回数据。


5. 联合索引和覆盖索引


1. 覆盖索引

如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道在 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!

覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。


2. 联合索引

使用表中的多个字段创建索引,就是 联合索引,也叫 组合索引 或 复合索引

覆盖索引是通过联合索引来实现的。


3. 最左前缀匹配原则

最左前缀匹配原则指的是,在使用联合索引时,MySQL 会根据联合索引中的字段顺序,从左到右依次到查询条件中去匹配,如果查询条件中存在与联合索引中最左侧字段相匹配的字段,则就会使用该字段过滤一批数据,直至联合索引中全部字段匹配完成,或者在执行过程中遇到范围查询,如 ><between 和 以%开头的like查询 等条件,才会停止匹配。

如有索引 (a,b,c,d),查询条件 a=1 and b=2 and c>3 and d=4,则会在每个节点依次命中a、b、c,无法命中d。(c已经是范围查询了,d肯定是排不了序了)

因此,列的排列顺序决定了可命中索引的列数。


6. 创建索引及索引的优化

1.选择合适的字段创建索引:

  • 不为 NULL 的字段 
  • 被频繁查询的字段 
  • 被作为条件查询的字段 
  • 频繁需要排序的字段
  • 被经常频繁用于连接的字段

2.被频繁更新的字段应该慎重建立索引。

3.尽可能的考虑建立联合索引而不是单列索引。

4.注意避免冗余索引 。

5.考虑在字符串类型的字段上使用前缀索引代替普通索引。


7. MySQL 为表字段添加索引

// 主键索引
ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )

//唯一索引
ALTER TABLE `table_name` ADD UNIQUE ( `column` )

//普通索引
ALTER TABLE `table_name` ADD INDEX index_name ( `column` )

//全文索引
ALTER TABLE `table_name` ADD FULLTEXT ( `column`)

//联合索引
ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )


三、MySQL事务

事务是逻辑上的一组操作,要么都执行,要么都不执行。

数据库事务可以保证多个对数据库的操作(也就是 SQL 语句)构成一个逻辑上的整体。构成这个逻辑上的整体的这些数据库操作遵循:要么全部执行成功,要么全部不执行 。


1. 事务的四大特性

原子性(Atomicity):事务是最小的执行单位,不允许分割,要么全部执行,要么全部不执行。

一致性(Consistency):执行事务前后,数据保持一致,多个事务读取的结果是相同的。比如两个人进行转账,两个的总金额是1000,不过他们进行了多少次转账(事务),两个人的总额1000是不变的,这就是一致性状态。

隔离性(Isolation):并发访问数据时,一个用户事务不会被其他事务所干扰。比如有两个线程A和B对同一数据S进行事务执行,线程A在获取数据S时,线程B的事务要么已经提交结束,要么还未执行。

持久性(Durability):事务完成以后,该事务对数据库所做的更改便持久的保存在数据库之中,并不会被回滚。


2. MySQL如何保证实现四大特性

保证原子性:利用innodb的undo log,undo log名为回滚日志,是实现原子性的关键,当事务执行失败或者调用rollback后,导致事务需要回滚,便可以利用undo log中的日志信息将数据恢复到修改前的样子。

保证一致性:数据库通过原子性、隔离性和持久性来保证一致性,也就是说四大特性中,一致性是目的,原子性、隔离性、持久性都是手段。

保证持久性:利用innodb中的redo log,当对数据做修改的时候,不仅在内存中操作,还在redo log 中记录这次操作,把redo日志从redo log buffer里刷入到磁盘文件里去,当数据库重启后,将redo log 中的内容恢复到数据库中。

保证隔离性:利用锁和MVCC机制。


3. 并发事务带来的问题

脏读:当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

不可重复读:是指在数据库访问中,一个事务内两次读到的数据是不一样的情况。在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。不可重复读的重点是修改。

幻读就是同样的条件, 第1次和第2次读出来的记录数不一样,幻读的重点在于新增或者删除 发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

丢失修改:指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。

不可重复读和幻读有什么区别呢?

  • 不可重复读的重点是内容修改或者记录减少比如多次读取一条记录发现其中某些记录的值被修改;
  • 幻读的重点在于记录新增比如多次执行同一条查询语句时,发现查到的记录增加了。

4. 事务的隔离级别

1. SQL标准定义了四个隔离级别

  • Read uncommitted 读取未提交:事务B可以读取事务A正在修改尚未提交的数据。但是会出现A事务如果回滚,这样前后数据不一致,可能出现脏读。

  • Read committed 读取已提交:A事务只能读取B事务已经提交了的数据。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变,避免了脏读,但是可能会造成不可重复读。

  • Repeatable read 可重复读: 在事务执行期间会锁定该事务以任何方式引用的所有行,因此同一个事务发出多次select,产生的结果是相同的。避免了脏读、不可重复读,但还有可能出现幻读。注:MySQL的默认隔离级别就是Repeatable read。

  • Serializable 可串行化:Serializable是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,读锁和写锁都是排它锁,不仅可以避免脏读、不可重复读,还避免了幻读。

Mysql中使用的InnoDB存储引擎的默认隔离级别是Repeatable read 可重复读,已经可以达到可串行化的级别。

使用的的一种叫MVCC的控制方式解决不可重复读的问题 ,即Mutil-Version Concurrency Control,多版本并发控制,类似于乐观锁的一种实现方式


2. MySQL 的隔离级别是基于锁实现的吗?

MySQL 的隔离级别基于锁和 MVCC 机制共同实现的。

SERIALIZABLE 隔离级别,是通过锁来实现的。除了 SERIALIZABLE 隔离级别,其他的隔离级别都是基于 MVCC 实现。

不过, SERIALIZABLE 之外的其他隔离级别可能也需要用到锁机制,就比如 REPEATABLE-READ 在当前读情况下需要使用加锁读来保证不会出现幻读。


3. 使用间隙锁解决幻读的问题

InnoDB采用Next-key Lock(临键锁)来避免“幻读”问题。

Record Lock:单个行记录的锁,记录锁锁定的是记录行

GAP Lock:间隙锁,锁定记录行之间的间隙范围,但不包含记录本身。

Next-Key Lock:Gap Lock+Record Lock 锁定一个范围并锁定记录本身,也就是前两者的和,锁定的区间是左开右闭的。

这样,当执行 select*from t where d=5 for update 的时候,就不止是给数据库中已有的6个记录加上了行锁,还同时加了7个间隙锁。这样就确保了无法再插入新的记录。

也就是说这时候,在一行行扫描的过程中,不仅将给行加上了行锁,还给行两边的空隙,也加上了间隙锁。


4. MVCC 的理解

MVCC(Multi-Version Concurrency Control)多版本并发控制,是用来在数据库中控制并发的方法,实现对数据库的并发访问用的。其特点就是在同一时间,不同事务可以读取到不同版本的数据,从而去解决脏读和不可重复读的问题。

实现原理:MVCC机制的实现就是通过read-view机制与undo日志,使得不同的事务会根据数据版本链对比规则读取同一条数据在版本链上的不同版本数据。通过保存数据在某个时间点的快照来实现,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。


四、MySQL引擎

1. 三大引擎

MyISAM:不支持事务;表级锁定,并发性能大幅降低;读写互相阻塞。

InnoDB:良好的事务支持(隔离的四个级别);行级锁定;外键约束;支持丢失数据的自动恢复。

Memory:在内存中,默认使用hash索引,等值条件查询速度快,范围查找慢,断电后数据丢失。

MySQL 当前默认的存储引擎是 InnoDB。并且,所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。

MySQL 5.5.5 之前,MyISAM 是 MySQL 的默认存储引擎。5.5.5 版本之后,InnoDB 是 MySQL 的默认存储引擎。

可以通过 select version() 命令查看你的 MySQL 版本。


2. MyISAM与InnoDB的区别?

  • 事务:InnoDB 支持事务;MyISAM 不支持事务

  • 行级锁:InnoDB 支持行级锁;MyISAM 支持表级锁

  • MVCC:InnoDB 支持 MVCC(多版本并发控制);MyISAM 不支持

  • 外键:InnoDB 支持外键,MyISAM 不支持

  • InnoDB 不保存表的总行数,执行 select count(*) from table 时需要全表扫描;MyISAM 用一个变量保存表的总行数,查总行数速度很快;

  • 索引实现:

  • InnoDB 是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。辅助索引需要两次查询,先查询到主键,再通过主键查询到数据。
  • MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针,主键索引和辅助索引是独立的。

总结:

  • InnoDB 存储引擎提供了具有提交、回滚、崩溃恢复能力的事务安全,与 MyISAM 比 InnoDB 写的效率差一些,并且会占用更多的磁盘空间以保留数据和索引。

  • MyISAM 不支持事务、也不支持外键,优势是访问的速度快。对事务的完整性没有要求、以 SELECT 和 INSERT 为主的应用可以使用这个存储引擎。


五、MySQL的锁机制


1. 锁的分类

按照锁的粒度:

  • 行锁:MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对当前操作的行记录进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
  • 表锁:MySQL 中锁定粒度最大的一种锁,是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
  • 页锁:页的粒度上进行锁定,锁定资源和开销介于行锁和表锁之间,并发度一般。

按数据库管理角度:

  • 共享锁:也叫读锁或S锁,锁定的资源可以被其他用户读取,但不能修改。
  • 排它锁:也叫写锁或X锁,锁定的资源只允许进行锁定的事务操作,其他事务无法进行读取和修改。
  • 意向锁:简单说就是给上一级的空间示意里面是否已经上过锁,如需要用到表锁的话,如何判断表中的记录没有行锁呢,使用意向锁可以来快速判断。
  • 当有多个事务对同一数据获得读锁的时候,可能出现死锁的情况。
# 共享锁
SELECT ... LOCK IN SHARE MODE;
# 排他锁
SELECT ... FOR UPDATE;

按程序员的角度:

  • 乐观锁:认为数据的变动不会太频繁,因此允许多个事务同时对数据进行变动。一般采用版本号机制或CAS算法实现。思想是有线程过来,先放过去修改,如果别的线程没修改过,就能修改成功,否则就失败。
  • 悲观锁:只专注于当前事务,当一个事务拥有悲观锁后,任何其他事务都不能对数据进行修改,只能等待锁被释放。
举一个版本号的例子,

  数据表中除了数据还有一个version字段,更新数据时version字段会加一,

  假设线程A在读取数据和version(version = 1)的期间,有另一个线程B也读取了version(version = 1),

  线程A修改数据,更新version(version = 2),提交更新时,在更新version前读取的version(version = 1)和当前数据表中的version(version = 1)相同,则更新成功

  线程B也修改数据,更新version(version = 2)提交更新时,由于读取时version = 1 而当前数据表version = 2 不相等,则更新失败, 

2. InnoDB 有哪几类行锁?

MySQL InnoDB 支持三种行锁定方式:

  • 记录锁(Record Lock) :也被称为记录锁,属于单个行记录上的锁。
  • 间隙锁(Gap Lock) :锁定一个范围,不包括记录本身。
  • 临键锁(Next-key Lock) :Record Lock+Gap Lock,锁定一个范围,包含记录本身。记录锁只能锁住已经存在的记录,为了避免插入新记录,需要依赖间隙锁。

InnoDB 的默认隔离级别 RR(可重读)是可以解决幻读问题发生的,主要有下面两种情况:

  • 快照读(一致性非锁定读) :由 MVCC 机制来保证不出现幻读。就是单纯的 SELECT 语句
  • 当前读 (一致性锁定读): 使用 Next-Key Lock 进行加锁来保证不出现幻读。就是给行记录加 X 锁或 S 锁。

3. CAS 是什么?

CAS(compare and swap) 比较并交换,依赖CPU的CAS指令在更新数据前先判断数据是否被其他线程修改过,如果没有修改则更新,整个操作是原子操作。

CAS指令执行时,比较内存值期望值是否相等,若相等则用目标值替换当前值,(不相等则会循环比较直到相等)整个比较赋值操作是一个原子操作;

过程:

1、取出内存值,比如求a++,首先取a此时的内存值0

2、计算目标值,计算a+1=1,此值是基于a=0得到

3、调用CAS指令

4、取出当前a的内存值,与期望值比较,如果相等,则用目标值替换。

5、如果不一致,则说明其他线程已经更改,转到第一步重新执行。

CAS有三个缺点:

  1,CAS自旋操作:当内存地址V与预期值B不相等时会一直循环比较直到相等,

  2,只能保证一个共享变量的原子操作,

  3,出现ABA问题:如果内存值初次读取的时候为A,在将要赋值的时候再次检查还是A,能说明V没有改变过吗?

  有一种可能时当读取内存值V的时候是A,有一个线程将A改为B,后有改为A,CAS会误认为内存值V没有改变,这称为CAS操作的ABA问题;


4. 意向锁有什么作用

如果需要用到表锁的话,如何判断表中的记录没有行锁呢?一行一行遍历肯定是不行,性能太差。我们需要用到意向锁来快速判断是否可以对某个表使用表锁。

意向锁是表级锁,共有两种:

  • 意向共享锁(Intention Shared Lock,IS 锁):事务有意向对表中的某些加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁。
  • 意向排他锁(Intention Exclusive Lock,IX 锁):事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。

意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。

5. 死锁是什么,怎么解决?

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致的恶性循环现象。

常用解决方法:

  • 如果不同程序并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁的概率。
  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁发生概率。
  • 对于非常容易发生死锁的业务部分,尝试使用升级锁的粒度,通过表级锁来减少死锁发生的概率。

六、数据库相关知识


1. 数据库三大范式

第一范式:每个列都不可以再拆分。

第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。

第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。即不存在传递依赖。


2. SQL中的约束

1、NOT NULL: 约束字段的内容不能为空

2、UNIQUE:约束字段唯一性,一个表可以有多个Unique约束

3、PRIMARY KEY:主键约束,约束字段唯一,一个表只能有一个

4、FOREIGN KEY:外键约束,用于预防破坏表之间连接的动作,也能防止非法数据插入外键

5、CHECK:用于控制字段的值的范围


3. 六种关联查询

A)内连接:join,inner join

B)外连接:left join,left outer join,right join,right outer join,union(重复的记录会合并)

full join (mysql不支持,但是可以用 left join union right join代替)

C)交叉连接:cross join 想得到A,B记录的排列组合,即笛卡儿积

UNION 和 UNION ALL 的区别

Union:对两个结果进行并集操作,不包括重复行,同时会进行默认规则的排序。

Union All:对两个结果集进行并集操作,包括重复行,不进行排序。

Union 的效率高于 Union All。


4. Delete/Drop/Truncate区别

三者都表示删除,但是有一些区别:

Delete:属于DML,可回滚,表结构还在,删除表的全部或部分数据行,速度最慢,需要逐行删除。

Truncate:属于DDL,不可回滚,表结构还在,删除所有数据行,速度快。

Drop:属于DDL,不可回滚,直接删除表,包括所有的数据行、索引、权限。速度最快

DML(Data Manipulation Language)数据操纵语言 适用范围:对数据库中的数据进行一些简单操作,如 insert,delete,update 等。

DDL(Data Definition Language)数据定义语言 适用范围:对数据库中的某些对象(例如,database,table)进行管理,如 Create,Alter 和 Drop。

DQL(Data Query Language)数据查询语言,select关键字和各种简单查询,连接查询。


5. Count(*),Count(1),Count(列名)的区别

count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL

count(1)包括了忽略所有列,用1代表代码行,相当于表中有多少个1,当然也可以设置成2、3.。在统计结果的时候,不会忽略列值为NULL,本质上和count(星)一样。

count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。

执行效率:

  • 列名为主键, count(列名) 会比 count(1)快

  • 列名不为主键, count(1) 会比 count(列名)快

  • 如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*)

  • 如果有主键,则 select count(主键) 的执行效率是最优的

  • 如果表只有一个字段,则 select count(*)最优。


6. SQL查询语句的优化

可以分为两部分,对语句的优化、对索引的优化

对于语句的优化

1、检查是否请求的不需要的数据,select * 的时候会把所有的列都查询出来;

2、语句重构,拆分复杂查询成多个查询,或者改写子查询成为关联表查询

3、优化count(),count (*) 如果只是需要判断结果的数量,可以改成count(1)

4、优化关联查询:确保on/where语句的列上有索引

5、优化limit语句,当limit offset中的offset值很大时,查询性能会直线下降


7. SQL的生命周期

1. 应用服务器与数据库服务器建立连接

2. 数据库进程拿到请求sql

3. 解析并生成执行计划,并执行

4. 读取数据到内存进行逻辑处理

5. 通过步骤一的连接,发送结果到客户端

6. 关闭连接,释放资源


8. 数据库的设计原则

1)一致性原则:对数据来源进行统一、系统的分析与设计,协调好各种数据源,保证数据的一致性和有效性。

2)完整性原则:数据库的完整性是指数据的正确性和相容性。要防止合法用户使用数据库时向数据库加入不合语义的数据。对输入到数据库中的数据要有审核和约束机制。

3)安全性原则:数据库的安全性是指保护数据,防止非法用户使用数据库或合法用户非法使用数据库造成数据泄露、更改或破坏。要有认证和授权机制。

4)可伸缩性与可扩展性原则:数据库结构的设计应充分考虑发展的需要、移植的需要,具有良好的扩展性、伸缩性和适度冗余。

5)规范化:数据库的设计应遵循规范化理论。规范化的数据库设计,可以减少数据库插入、删除、修改等操作时的异常和错误,降低数据冗余度等。

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值