MySql—公司面试题

1、  用过mysql嘛?了解吗?

用过,比较了解。MySQL是一个关系型数据库,它采用表的形式来存储数据。你可以把它理解成是你的Excel表格,既然是表的形式存储数据,就有表结构(行和列)。行代表每一行数据,列代表该行中的每个值。列上的值是有数据类型的,比如:整数、字符串、日期等等。


2、  Mysql事务了解吗,隔离级别呢,每种隔离级别会出现什么问题?

MySQL的四种隔离级别如下:

  • 读未提交(READ UNCOMMITTED)

这就是上面所说的例外情况了,这个隔离级别下,其他事务可以看到本事务没有提交的部分修改.因此会造成脏读的问题(读取到了其他事务未提交的部分,而之后该事务进行了回滚).

这个级别的性能没有足够大的优势,但是又有很多的问题,因此很少使用.

  • 读已提交(READ COMMITTED)

其他事务只能读取到本事务已经提交的部分.这个隔离级别有 不可重复读的问题,在同一个事务内的两次读取,拿到的结果竟然不一样,因为另外一个事务对数据进行了修改.

  • REPEATABLE READ(可重复读)

可重复读隔离级别解决了上面不可重复读的问题(看名字也知道),但是仍然有一个新问题,就是 幻读,当你读取id> 10 的数据行时,对涉及到的所有行加上了读锁,此时例外一个事务新插入了一条id=11的数据,因为是新插入的,所以不会触发上面的锁的排斥,那么进行本事务进行下一次的查询时会发现有一条id=11的数据,而上次的查询操作并没有获取到,再进行插入就会有主键冲突的问题.

  • SERIALIZABLE(可串行化)

这是最高的隔离级别,可以解决上面提到的所有问题,因为他强制将所以的操作串行执行,这会导致并发性能极速下降,因此也不是很常用.


3、  Mysql的锁了解吗(表锁、行锁、间隙锁,读写锁)?

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

间隙锁:

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键
值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,
InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。
【危害】
因为Query执行过程中通过过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个_值并不存在。
间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无
法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害

读写锁:

同一用户并发读取同一条数据,不会出现什么问题,因为读取不会修改数据,但是如果某个用户正在读取某张表,而同一时刻另一用户正在修改这张表的id为1的数据,会产生什么后果?

答案是不确定的,读的用户可能会报错退出,也可能读到不一致的数据。
解决这类经典问题的就是并发控制。在处理并发读写的时候,可以通过实现一个由两种类型的锁组成锁系统来解决问题。这两种锁就是读锁(共享锁)和写锁(排他锁)。

读锁(共享锁)是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。

写锁(排他锁)如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。


4、  Mysql引擎知道吗,innodb和myisam的区别知道吗,说说底层的实现?

 
 

mysq引擎一般是说通过一种存储机制,索引技巧等各种技术,改善我们数据库的功能。

MyISAM和InnoDB的区别

定义

InnoDB:MySQL默认的事务型引擎,也是最重要和使用最广泛的存储引擎。它被设计成为大量的短期事务,短期事务大部分情况下是正常提交的,很少被回滚。InnoDB的性能与自动崩溃恢复的特性,使得它在非事务存储需求中也很流行。除非有非常特别的原因需要使用其他的存储引擎,否则应该优先考虑InnoDB引擎。

MyISAM:在MySQL 5.1 及之前的版本,MyISAM是默认引擎。MyISAM提供的大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM并不支持事务以及行级锁,而且一个毫无疑问的缺陷是崩溃后无法安全恢复。

事务

InnoDB:支持

MyISAM:不支持

InnoDB:支持行锁、表锁。行锁是实现在索引上的,如果没有索引,就没法使用行锁,将退化为表锁。

MyISAM:支持表锁。

主键

InnoDB:必须有,没有指定会默认生成一个隐藏列作为主键

MyISAM:可以没有

索引

InnoDB:聚集索引,使用 B+ 树作为索引结构,数据文件和索引绑在一起,必须要有主键。主键索引一次查询;辅助索引两次查询,先查询主键,再查询数据;

MyISAM:非聚集索引,使用 B+ 树作为索引结构,索引和数据文件是分离的。主键索引和辅助索引是独立的。

外键

InnoDB:支持

MyISAM:不支持

AUTO_INCREMENT

InnoDB:必须包含只有该字段的索引。引擎的自动增长列必须是索引,如果是组合索引也必须是组合索引的第一列。

MyISAM:可以和其他字段一起建立联合索引。引擎的自动增长列必须是索引,如果是组合索引,自动增长可以不是第一列,他可以根据前面几列进行排序后递增。

数据库文件

InnoDB:frm是表定义文件,ibd是数据文件。支持两种存储方式:

  • 共享表空间存储:所有表的数据文件和索引都保存在一个表空间里,一个表空间可以有多个文件,通过 innodb_data_file_path 和 innodb_data_home_dir 参数设置共享表空间的位置和名字,一般共享表空间的名字叫 ibdata1-n
  • 多表空间存储:每个表都有一个表空间文件用于存储每个表的数据和索引,文件名以表名开关,以.ibd为扩展名

MyISAM:frm是表定义文件,myd是数据文件,myi是索引文件。支持三种存储格式:静态表(默认,注意数据末尾不能有空格,会被去掉。)、动态表、压缩表。

表的行数

InnoDB:没有保存。select count(*) from table;会扫描全表。

MyISAM:保存。select count(*) from table;会直接取出该值。

注:但加了 where 条件后,两者处理方式一样,都是扫描全表。

全文索引

InnoDB:5.7及以后版本支持。

MyISAM:支持。

总结

InnoDB

  • 优点:支持事务,支持外键,并发量较大,适合大量 update。
  • 缺点:查询数据相对较快,不适合大量的 select。

MyISAM

  • 优点:查询数据相对较快,适合大量的 select,可以全文索引。
  • 缺点:不支持事务,不支持外键,并发量较小,不适合大量 update。

MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址

InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。

第一个重大区别是InnoDB的数据文件本身就是索引文件。MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。


5、  聚簇索引和非聚簇索引知道么,有什么区别?

首先,先了解下主键索引和普通索引的区别,主键索引索引的是数据,普通索引不直接索引数据,而是索引的主键,主键索引就是聚簇索引,叶子节点存放的是数据

普通索引也叫二级索引就是非聚簇索引,叶子节点存放的是数据的地址,也就是主键.

个人理解是这样的


6、  B+树知道吗?为什么使用B+树?

B树的变种,拥有B树的特点 独有特点: 节点中的关键字与子树数目相同。 关键字对应的子树节点都大于等于该关键字,子树包含该关键字自身。 所有关键字都出现在叶节点之中。 所有叶节点都有指向下一个叶节点的指针。 搜索:只在叶节点搜索。 叶子节点保存关键字和对应的数据,非叶节点只保存关键字和指向叶节点的指针,同等关键字 数量的B树和B+树,B+树更小。 更适合做索引系统,原因: 由于叶节点有指针项链,B+树更适合做范围检索。 由于非叶节点只保存关键字和指向叶节点的指针,B+树可以容纳更多的关键字,树层数变小, 磁盘查询次数更低。 B+树的查询效率比较稳定,查询所有关键字的路径相同。(MySQL索引就提供了B+树的实现 方式) 


7、  分库分表做过吗,如果让你去实现分库分表,你有什么需要考虑或者注意的嘛?

面试官:“有并发的经验没?” 应聘者:“有一点。” 面试官:“那你们为了处理并发,做了哪些优化?” 应聘者:“前后端分离啊,限流啊,分库分表啊。。” 面试官:"谈谈分库分表吧?" ”

1、分库分表的原因

1、随着单库中的数据量越来越大,相应的,查询所需要的时间也越来越多,这个时候,相当于数据的处理遇到了瓶颈2、单库发生意外的时候,需要修复的是所有的数据,而多库中的一个库发生意外的时候,只需要修复一个库(当然,也可以用物理分区的方式处理这种问题)

2、分库分表的常用策略

2.1 垂直切分:

根据业务的不同,将原先拥有很多字段的表拆分为两个或者多个表,这样的代价我个人觉得很大,原来对这应这个表的关系,开始细分,需要一定的重构,而且随着数据量的增多,极有可能还要增加水平切分;

2.2 水平切分:

数据表结构,将数据分散在多个表中;

1.有瑕疵的简单分库分表(按id的大小分库分表)

按照分片键(我们这里就用id表示了)的大小来进行分库分表,如果你的id是自增的,而且能保证在进行分库分表后也是自增的,那么能进行很好的改造,以id大小水平切分,而且极有可能不用迁移数据。

当然,这里只列举了比较小的数据量,实际情况的分库的界限还是要依据具体的情况而定。这样的分库分表,因为新的数据总在一个库里,很可能导致热点过于集中(读写可能集中在一个库中),这是采取这种方式需要考虑的事情。如果无法保证你的id是自增长的,那么你的数据就会凌乱的分散在各个数据库,这样热点确实就分散了,可是每当你增加一个数据库的时候,你就有可能进行大量的数据迁移,应对这种情况,就是尽量减少数据迁移的代价,所以这里运用一致性hash的方式进行分库分表比较好,可以尽可能的减少数据迁移,并且也能让解决热点过于集中的问题。一致性hash的分库策略去百度一下或者谷歌一下应该很容易搜到。这里按id的大小来分库,还可以发散到按照时间来分库,比如说一个月的数据放在一个库,这个使用mycat比较容易实现按时间分库,不过你需要思考的数据的离散性,数据集中于一个两月,而剩下的几个月数据稀疏,这样的又可能需要按照数据的生产规律合并几个月到一个库中,使得数据分布均匀。

2.比较方便的取模分库

一般的取模分库分表是就是将id mod n,然后放入数据库中,这样能够使数据分散,不会有热点的问题,那么,剩下的是,在扩容的时候,是否会有数据迁移的问题,一般的扩容,当然是会有数据迁移的。

取模.PNG

例子中,对3取模,当需要扩容的时候(假设增加两个库),则对5取模,这样的结果必然是要进行数据迁移的,但是可以运用一些方法,让它不进行数据迁移,scale-out扩展方案能够避免在取模扩容的时候进行数据迁移。

(1)第一种扩容的方式:根据表的数据增加库的数量

首先,我们有一个数据库——DB_0,四张表——tb_0,tb_1,tb_2,tb_3那么我们现在数据到数据库是这样的:DB="DB_0"TB=“tb_"+id%4

当数据增加,需要进行扩容的时候,我增加一个数据库DB_1DB="DB_"+((id%4)/2)TB=“tb_"+id%4

当我们的数据继续飙升,这个时候又需要我们增加库,这个时候进行加库操作的时候,就不是增加一个库,而必须是两个,这样才能保证不进行数据迁移。DB="DB_"+id%4TB=“tb_"+id%4

这个时候到了这个方案的加库上限,不能继续加库了,否则就要进行数据迁移,所以这个方案的弊端还是挺大了,这样的方式,也应该会造成单表的数据量挺大的。

(2)第二种扩容的方式:成倍的增加表

首先,我们还是一个数据库——DB_0,两张表——tb_0,tb_1那么我们现在数据到数据库是这样的:DB="DB_0"TB=“tb_"+id%2

假设当我们数据量打到一千万的时候,我们增加一个库,这时需要我们增加两张表tb_0_1,tb_1_1,并且原来的DB_0中库的表tb_1整表迁移到DB_1中,tb_0和tb_0_1放在DB_0中,tb_1和tb_1_1放到DB1中。DB="DB_"+id%2tb:if(id<1千万) { return "tb_" + id % 2 }else if(id>=1千万) { return "tb_"+ id % 2 + "_1" }

数据的增长不可能到此为止,当增加到两千万的时候,我们需要加库,这个时候,按照这种做法,我们需要增加两个库(DB_2,DB_3)和四张表(tb_0_2,tb_1_2,tb_2_2,tb_3_2),将上次新增的表整表分别放进两个新的库中,然后每个库里再生成一张新表。DB:if(id < 1千万) { return "DB_" + id % 2 }else if(1千万 <= id < 2千万) { return "DB_"+ id % 2 +2 }else if(2千万 <= id ) { return "DB_"+ id % 4 }tb:if(id < 1千万) { return "tb_" + id % 2 }else if(1千万 <= id < 2千万) { return "tb_"+ id % 2 +"1" }else if(id >= 2千万) { return "tb"+ id % 4+"_2" }

值得注意的一点,在id超出范围的时候,该给怎么样的提示是值得思考的。


8、  了解死锁嘛?知道他是怎么产生的嘛,如何避免呢?(数据库死锁)

在执行一个事务时可能要获取多个锁,一直持有锁到事务提交,如果A事务需要获取的锁在另 一个事务B中,且B事务也在等待A事务所持有的锁,那么两个事务之间就会发生死锁。但数据库死锁比较少见,数据库会加以干涉死锁问题,牺牲一个事务使得其他事务正常执行。

避免:如果一个线程最多只能获取一个锁,那么就不会发生锁顺序死锁了。如果确实需要获取多个 锁,锁的顺序可以按照某种规约,比如两个资源的id值,程序按规约保证获取锁的顺序一致。 或者可以使用显式的锁Lock,获取锁的时候设置超时时间,超时后可以重新发起,以避免发生死锁。


9、  Sql优化了解过吗?说说你的理解,给你一条sql,你会怎么优化?

1. 开启查询缓存,优化查询

2. explain你的select查询,这可以帮你分析你的查询语句或是表结构的性能瓶颈。 EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的

3. 当只要一行数据时使用limit 1,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据

4. 为搜索字段建索引

5. 使用 ENUM 而不是 VARCHAR。如果你有一个字段,比如“性别”,“国家”,“民族”, “状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是VARCHAR

6. Prepared StatementsPrepared Statements很像存储过程,是一种运行在后台的SQL 语句集合,我们可以从使用 prepared statements 获得很多好处,无论是性能问题还是 安全问题。 Prepared Statements 可以检查一些你绑定好的变量,这样可以保护你的程序不会受到 “SQL注入式”攻击

7. 垂直分表

8. 选择正确的存储引擎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

起个名字是真的南

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值