当前位置:Gxlcms > 数据库问题 > [Java复习] 面试突击 - MySQL

[Java复习] 面试突击 - MySQL

时间:2021-07-01 10:21:17 帮助过:9人阅读

 

MySQL的MyISAM和InnoDB存储引擎的区别是啥?

myisam,不支持事务,不支持外键约束,索引文件和数据文件分开,这样在内存里可以缓存更多的索引,对查询的性能会更好,适用于那种少量的插入,大量查询的场景。

innodb,主要特点就是支持事务,走聚簇索引,强制要求有主键,支持外键约束,高并发、大数据量、高可用等相关成熟的数据库架构,分库分表、读写分离、主备切换,全部都可以基于innodb存储引擎。

 

聊聊MySQL的索引实现原理?各种索引你们平时都怎么用的?

  • MySQL索引的原理和数据结构能介绍一下吗?
  • b+树和b-树有什么区别?
  • Myisam和Innodb存储引擎的索引实现?
  • MySQL聚簇索引和非聚簇索引的区别是什么?
  • 他们分别是如何存储的?
  • 使用MySQL索引都有哪些原则?
  • MySQL复合索引如何使用?

先说什么索引?

索引就是用一个数据结构组织某一个列数据,然后根据这一列数据查询时,不用全表扫描,根据特定数据结构找到那一列的值,然后找到对应行的物理地址。

先说b-树,画图如下:

技术图片

 

 

 

查找时,从根节点开始二分查找。

b+树是b-树的变形,不一样的地方:

技术图片

 

 

 

B-和B+树区别:

1. B+树只有叶子节点存储数据,其他中间节点只有索引没有数据,同样的大小的磁盘页可以容纳更多的节点元素。相同数量下,B+树更“矮胖”,查询IO次数更少

2. B+树查询必须查找叶子节点,B+树查询更稳定(并不慢)。B-树查询性能不稳定,最好只查根节点,最坏查到叶子节点。

3. B+树范围查询更简便。B+树中序遍历要简单得多

 

Myisam和Innodb存储引擎的索引实现?

myisam的索引中,每个叶子节点的data存放的是数据行的物理地址,每行对应一个物理地址。数据文件单独存放一个文件。最大特点是数据文件和索引文件是分开的。

innodb的数据文件本身就是索引文件,必须有主键,会根据主键建立一个默认索引,叫聚簇索引

innodb下,对某个非主键字段创建索引,那么最后那个叶子节点的值就是主键的值,再用主键的值到聚簇索引里查找到数据,叫回表

innodb,建议统一用auto_increment自增值作为主键,可以保持聚簇索引直接加记录,不用修改原来b+树的结构,浪费时间。

 

索引常见使用规则?

比如product表,创建索引:

create index (shop_id, product_id, gmt_create)

  1. 全列匹配

      where条件正好用到这3个字段,就用到联合索引

  2. 最左前缀匹配

      SQL里,正好用到联合索引最左边的一个或几个列表。

  3. 最左前缀匹配,但中间某个值没有匹配

      如果sql用了联合索引第一列和第三列,会按照第一列在索引里找,找完后对结果集根据第三列做全表扫描,不会走第三列的索引了。

  4. 前缀匹配

      如果不是等值,或比如=, >, <=等操作,而是Like操作。则只有Like ‘xx%’ (%在最后)这种才能用上索引。

  5. 范围匹配

      只有符号最左前缀的列的范围才用到索引,范围之后的列用不到索引。

      比如: select * from product where shop_id >= 1 and product_id = 1;

      这里就只用到shop_id索引。

  6. 包含函数

     对某个列用了函数,则该列不走索引。

 

索引缺点以及使用注意?

    常见缺点:增加磁盘消耗,占用磁盘文件,同时高并发频繁插入和修改索引,会导致性能损耗。

    使用注意:1. 尽量创建少的索引,一个表,两三个。

                       2. 一个字段的值几乎都不太一样的,比如id,这样用索引效果最好。

小结:

    互联网系统中,一般尽量降低SQL复杂度,用简单的主键索引(聚簇索引)+少数联合索引,

    可以覆盖一个表的所有SQL查询,更复杂的业务逻辑,应该放在Java代码里实现。

    SQL越简单,后面迁移,分库分表,读写分离的成本更低,减少对SQL的改造。

    MySQL最好用在在线及时存储,不要用于计算(Join, 子查询,函数等等)。高并发场景下,计算放在Java内存里

 

说说事务的几个特性是啥?有哪几种隔离级别?

特性:ACid

原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

隔离级别:

  1. 读未提交

       事务A把id=1, name=张三修改为李四,但是还未提交,这时事务B已经看到id=1的name为李四了。

  2. 读已提交(不可重复读)

      事务A把id=1, name=张三修改为李四,但是还未提交。事务B第一次读,读到是张三,看不见李四。

      紧接着事务A提交了事务,事务A在第二次读的时候,读到的是修改后的李四。

      这个也叫不可重复读,就是所谓一个事务内对一个数据的两次读,可能读到不一样的值。

  3. 可重复读

      无论事务A什么时候把张三修改为李四,事务B不管什么时刻读到值,都是事务B刚开启时读到的值。事务开启后对一行读取的值都是一样的。

      幻读(不是隔离级别):事务A第一次查询所有数据,就一行id=1。后面事务B插入一行数据id=2,事务A查询时发现2条数据,感觉发生幻觉。

                                              针对数据件数发生变化。

   4. 串行化

      事务A运行期间,事务B不允许运行。事务A提交完事务后,事务B才开始运行。串行化是为了解决幻读。

 

MySQL是怎么实现可重复读的?

    MySQL是通过MVCC机制来实现的,就是多版本并发控制(Multi-Version Concurrency Control)。

    Innodb存储引擎会在每行数据最后增加两个隐藏列

    一个保证行的创建时间,一个保存行的删除时间,但这里存放的不是时间,是事务id,事务id是mysql自己维护的自增,全局唯一。

举例1:
  id name   创建事务id   删除事务id
  1 张三     101      空
   

    事务id=101的事务查询id=1的这一行,一定能找到创建事务id<=当前事务id的一行。
    当事务id=102把id=1的一行删除了,这时id=1的行的删除事务id设置为102.

id name   创建事务id   删除事务id
1  张三     101      102


    事务id=101的事务,再次查询id=1的行,仍然能查到。因为创建事务id<=当前事务id,且当前事务id<删掉事务id。

举例2:(同一行被修改)
id   name   创建事务id   删除事务id
2  李四     201      空
2  王五     202      空

      事务201创建了id=2的数据,事务202修改了id=2的数据,把name修改为王五,这时候事务201仍然可以查询到创建事务id<=当前事务id的数据,

      查不到比自己创建事务id大的记录。


MySQL就是通过MVCC实现可重复读,事务可以读取该事务启动时创建事务id的记录,读不到后面事务的版本。

 

说说MySQL数据库锁的实现原理吗?如果死锁了咋办?

数据库锁有哪些类型?锁如何实现的?MySQL行级锁有哪两种?一定会锁指定行吗?为什么?

悲观锁和乐观锁是什么?使用场景是什么?

MySQL的死锁原理以及如何定位和解决?

 

1. MySQL锁

锁类型:表锁行锁页锁

  myisam一般加表锁,查询时,默认加共享锁,也就是表读锁,别人可以查,但不能写;

  myisam写的时候,加表独占锁,也就是表写锁,别人不能读也不能写。现在用的很少。

 

  innodb的行锁分共享锁(S)和排他锁(X)。

  共享锁,多个事务可以加共享锁读同一行数据,但别的事务不能写这行数据;

  排他锁,一个事务可以写这行数据,别的事务只能读不能写。

  innodb表锁,分成意向共享锁,就是加共享行锁的时候,必须先加这个共享表锁;

   还有一个意向排他锁,给某行加排他锁的时候,必须先给表加排他锁。这个表锁,是innodb引擎自动加的。

   insert、update、delete时,innodb会自动为这一行加行级的排他锁。

   select时,innodb啥锁都不加,默认实现可重复读,MVCC机制,所有多个事务随便读一个数据,不会有冲突,大家读的是自己的快照,不涉及锁。

  手动加共享锁

select * from table where id=1 lock in share mode; //id=1这行加了共享锁,其他事务不能修改

  手动加排他锁(悲观锁)

select * from table where id=1 for update; //id=1这行加了排他锁,表示该事务准备修改,其他事务被卡住等待。慎用!线上一般不用,容易出问题。

  乐观锁:

select id, name, version from table where id = 1;

update table set name=’new name’, version = version + 1 where id = 1 and version = 1;

  每次修改比较这条数据和之前查出的数据版本号是否一致,一致就修改并且版本号+1,否则就不更新。

  乐观锁可以提高并发访问的效率,但是如果出现了冲突只能向上抛出,然后重来一遍;悲观锁可以避免冲突的发生,但是会降低效率。 

 高并发场景用乐观锁!

  死锁:

    事务A,B对自己的资源持有锁的同时,又要去请求对方持有的锁,结果谁也拿不到锁,导致死锁。

    死锁排查,查看死锁日志,找到对应的sql,找到代码,具体判断为什么死锁。

 

MySQL的SQL调优一般都有哪些手段?你们一般怎么做?

  1. SQL慢,一般就是没有用索引。看执行计划,有没有走索引。

explain select * from table

table|type|possible_keys|key|key_len|ref|rows|extra

table: 哪个表

type: 类型  all:全部扫描  const:读常量,最多一条记录匹配  eq_ref:走主键,一般最多一条记录  index:扫描全部索引  range: 扫描部分索引  possible_keys: 显示可能使用的索引

key: 实际使用的索引

key_len: 使用索引的长度

ref: 联合索引哪一列被用到

rows: 一共扫描和返回了多少行

extra: using filesort:需要额外进行排序, using temporary: mysql构建了临时表,比如排序的时候 using where:就是对索引扫描出来的数据再次根据where来过滤出结果

 

参考资料:

互联网Java工程师面试突击(第三季)-- 中华石杉

[Java复习] 面试突击 - MySQL

标签:默认   卡住   举例   设置   索引   特点   额外   行数据   次数   

人气教程排行