Mysql知识梳理及常见面试题总结

目录

Mysql架构

事务的执行过程

事务的特点(面试常考)

锁的分类

索引

索引的类型

优化


Mysql架构

Mysql架构主要分为Server层和存储引擎层。

  • Server层:包括连接器、查询缓存、分析器、优化器、执行器等,所有夸存储引擎的功能都在这层实现,比如存储过程,触发器,视图,函数等。
    • 连接器:用于身份认证和权限认证
    • 查询缓存:用户的查询语句会首先访问查询缓存,如果命中数据,则返回给客户端。
    • 分析器:没有命中缓存的话会经过分析器,分析语句的语句功能及语法问题。
    • 优化器:Mysql会按照最优的方案去执行语句
    • 执行器:从存储引擎返回数据
  • 存储引擎层:主要负责数据的存储和读取,采用可替换的插件式架构。支持MyISAM、Memory、InnoDB等多个存储引擎,其中InnoDB在Mysql5.5版本之后成为了默认的存储引擎。

下图为Mysql基本架构图,

图1 Mysql基本架构图

事务的执行过程

首先要明白为什么有事务这个概念,事务是用来干嘛的。

简单来说,事务是逻辑上的一组操作,比如一条sql语句就是一个事务。

事务的特点(面试常考)

  • 原子性(Atomicity):事务是最小的执行单位,事务包含的全部操作要么不完成,要么全部完成
  • 一致性(Consistency):事务只会从一个一致性状态变换为另一个一致状态,而不会处于一个中间态。
  • 隔离性(Isolation):并发访问数据库时,多个事务间不会互相影响
  • 持久性(Durability):事务被提交后,对于数据库数据的改变是持久的

如果多个事务并发执行,会带来什么问题?

  • 脏读:假设有A和B两个事务,A事务访问了数据并对进行了修改,但是这种修改还没有提交到数据库中,此时B事务访问到的数据是没有提交到数据库里的数据,也就是“脏数据”。
  • 丢失修改:假设有A和B两个事务,A事务访问了数据并进行了修改,紧接着B事务访问了数据也对其进行了修改,那么A事务在这个数据上做出的修改就会丢失。
  • 不可重复读:假设有A和B两个事务,A事务先访问数据,随后B事务访问了该数据并进行了修改,此时A事务再访问时发现两次读到的数据不一致。成为不可重复读
  • 幻读:幻读的概念和不可重复读的概念很像,只不过不可重复读强调对数据的修改,而幻读强调数据的新增/删除。

那出现这样的问题怎么去解决呢?

Sql标准定义了四个事务隔离级别:

  • 读未提交READ-UNCOMMITTED):最低的隔离级别,不能解决上面任何问题。
  • 读已提交READ-COMMITTED):只解决了脏读的问题
  • 可重复读REPEATABLE-READ):解决了脏读和不可重复读的问题。
  • 串行化SERIALIZABLE):所有事务排成队挨个儿执行,挤什么挤,没人抢也就不会有问题了。

在Mysql中,默认的隔离级别是可重复读,看似不能解决幻读的问题,其实不然。InnoDB中提供了一个Next-Key Lock算法,可以解决幻读的问题。所以可以最高级别保证事务的隔离性要求。

那怎么才能实现事务的隔离呢?这里就需要用到锁机制了。

锁的分类

  • 表锁:粒度大,性能开销小,不会出现死锁,出现锁冲突的概率比较高
  • 行锁:粒度小,性能开销大,会出现死锁,出现锁冲突的概率比较低
    • Record Lock
    • Gap Lock
    • Next-key Lock
  • 表级锁和行级锁可以进一步划分为共享锁(s)和排他锁(X)。
    • 共享锁(s)

    • 共享锁(Share Locks,简记为S)又被称为读锁,其他用户可以并发读取数据,但任何事务都不能获取数据上的排他锁,直到已释放所有共享锁。

    • 共享锁(S锁)又称为读锁,若事务T对数据对象A加上S锁,则事务T只能读A;其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

    • 排他锁(X):

      排它锁((Exclusive lock,简记为X锁))又称为写锁,若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。在更新操作(INSERT、UPDATE 或 DELETE)过程中始终应用排它锁。

  • 悲观锁和乐观锁

    • 悲观锁:指的是事务在执行前会对数据加锁,直到事务完成再解锁,虽然保证了数据的独占性,但是加锁需要很大的开销。通常使用在写比较多的场景中

    • 乐观锁:指的是事务执行的时候认为只有它自己在操作数据,所以不加锁。在更新的时候会判断下有没有其他事务修改这个数据,采用MVCC和CAS算法实现。适用于读比较多的场景。

索引

为什么会存在索引?

主要原因是因为现在数据量越来愈多,以往的遍历表好费时间太多,得想个聪明的办法儿,所以索引就诞生了。

索引的概念和字典的目录很像,我们在查单词“apple”的时候,会首先去目录中找到a字母对应的页数,之后在找下一个字母对应的页数...直到查到为止。数据库中的索引也是如此,将随机IO变成了顺序IO。

索引为什么会加快查询速度?

首先说一下数据在Mysql中是怎么存的。

在Mysql中,数据都是存放在磁盘中,我们想要查询数据,就会发生磁盘与内存的交互。在每次的交互过程中,磁盘会将一页记录发送给内存,如果是用户想要查询的数据,则呈递给用户,若不是,则用该页的下一页把内存中的当前页置换出来,直到找到数据为止。记录(每行数据)是按照单链表的方式存储在页中的,每个页都会为它里面的记录生成一个页目录。如图2所示:

图2 记录在页中的存储方式

 在查找记录的时候,如果有索引存在,就可以通过页目录使用二分位定位记录的所在页,再遍历该页中的记录就可快速找到指定的记录;如果没有索引存在,那就得一个个去遍历页,再一个个遍历页中的记录,如果表的数据特别大的话,那不知道得遍历到什么时候。

InnoDB中索引的底层数据结构是B+树,那为什么是B+树呢,因为B+树是一个二叉平衡树,查找仅需要O(logN)的时间。

例子:找到id为8的记录简要步骤:

图3 查找id为8的记录

从图中可以发现,通过折半查找法会很快的找到数据。

索引的类型

  • B+树索引
    • 聚集索引:以主键创建的索引,叶子结点存放点的是实际数据
    • 非聚集索引:非主键创建的索引,叶子节点存放的是索引列和主键。
      • 在使用非聚集索引查询数据的时候,如果该数据不是索引列上的字段,则会根据主键再去主键索引上查找,也叫作回表。如果是索引列字段,则直接返回数据,这种特殊的非聚集索引叫做覆盖索引。
      • 例子:现在我创建了索引(username,age),在查询数据的时候:
        select username , age from user where username = 'Java3y' and age = 20。

         此时就会直接返回数据而不需要进行回表操作

  • 哈希索引

    • 采用一定的哈希算法,把字段值换算成新的哈希值,检索的时候只需要一次哈希算法就可定位。

    • 缺点:在有大量键值重复的情况下,效率极低。这里涉及到了哈希碰撞问题。

建立索引应该遵循的原则:

  • 最左前缀匹配原则:(特别重要)Mysql会从左往右一直匹配直到碰到范围查询(>,<,LIKE,IN,BETWEEN)为止。
  • 尽量选择区分度高的字段:区分度越高,扫描的记录也就越少
  • 索引列不参与计算:B+树中存储的都是数据表中的字段值,在查询的时候都需要计算下函数才能比较,那代价太高了。
  • 尽可能扩展索引,不要修改索引

那么索引是不是百利而无一害呢?

也不是,InnoDB中索引采用的是B+树,B+树是一个平衡树,所以在写比较多的场景中,需要不断调整树的结构以保持平衡,这会带来很大的性能开销,所以索引适合在读比较多的场景中出现。

优化

待补充。

参考文献:

1.一份涵盖基本面试问题的总结

2.https://juejin.im/post/5b55b842f265da0f9e589e79#heading-2

3.https://zhuanlan.zhihu.com/p/62018452

4.https://tech.meituan.com/2014/08/20/innodb-lock.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值