php面试题:中高级PHP开发工程师

 

一.MySQL有哪几种数据存储引擎?有什么区别?

    MySQL中通过show ENGINES指令可以看到所有支持的数据库存储引擎。最为常用的就是MyISAM和InnoDB两种。

    MyISAM和InnoDB的区别:

 1.存储文件。MyISAM每个表有两个文件:MYD和MYI。MYD是数据文件,MYI是索引文件。而InnoDB每个表只有一个文件:ibd。

  2.InnoDB支持事务,支持行级锁,支持外键。

  3.InnoDB支持XA事务

  4.InnoDB支持savePoints

二.MySQL的锁有哪些?什么是间隙锁?

从锁的力度来区分

1.行锁:加锁力度小,但是加锁资源开销比较大。InnoDB支持。

           共享锁:读锁,多个事务可以对同一个数据共享同一把锁。持有锁的事务都可以访问数据,但是只能读不能修改。select  xxxx LOCK IN SHARE MODE。

          排他锁:写锁,只有一个事务能够获得排他锁,其他事务都不能获取该行的锁。InnoDB会对update\delete\insert语句自动添加排他锁。SELECT xxx FOR UPDATE。

          自增锁:通常是针对MySQL当中的自增字段,如果有事务回滚这种情况,数据会回滚,但是自增序列不会回滚。

2.表锁:加锁力度大,加锁资源开销比较小。MyISAM和InnoDB都支持。

           表共享读锁

           表排他写锁

           意向锁:是InnoDB自动添加的一种锁,不需要用户干预。

3.全局锁:Flush tables witth read lock 。加锁之后整个数据库实例都处于只读状态,所有的数据变更操作都会被挂起。一般用于全库备份的时候。

常见的所算法:

1.记录锁:是锁住记录的,锁住的是索引记录,而不是我们真正的数据记录:

如果锁的是非主键索引,会在自己的索引上面加锁之后然后再去主键上面加锁锁住。

如果表上没有索引(包括没有主键),则会使用隐藏的主键索引进行加锁。

如果要锁的没有索引,则会进行全表记录加锁。 

2.间隙锁:锁间隙,不锁记录。锁间隙的意思就是锁定某一个范围,间隙锁又叫 gap 锁,其不会阻塞其他的 gap 锁,但是会阻塞插入间隙锁,这也是用来防止幻读的关键。

3.Next-key:这个锁本质是记录锁加上 gap 锁。在 RR 隔离级别下(InnoDB 默认),InnoDB 对于行的扫描锁定都是使用此算法,但是如果查询扫描中有唯一索引会退化成只使用记录锁。

因为唯一索引能确定行数,而其他索引不能确定行数,需要使用间隙锁防止其他事务中再次添加这个索引的数据造成幻读。RR 隔离级别下,InnoDB 使用 Next-Key Lock 算法避免了幻读。

三.MySQL的索引结构是什么样的?聚簇索引和非聚簇索引又是什么?

二叉树-》AVL数-》红黑树-》B-树-》B+树

二叉树:每个节点最多只有两个子节点,左边的子节点都比当前节点小,右边的子节点都比当前节点大。

AVL树:树种任意节点的两个子树的高度差最大为1

红黑树:1.每个节点都是红色或者黑色。2.根节点是黑色。3.每个叶子节点都是黑色的空节点。4.红色节点的父子节点都必须是褐色。5.从任一节点到其每个叶子节点的所有路径都包含相同的黑色节点。

B-树:1.B-树的每个非叶子节点的子节点个数都不会超过D(这个D就是B-树的阶)2.所有的叶子节点都在同一层。3.所有关键字都是按照递增顺序排列。

B+树:1.非叶子节点不存储数据,只进行数据索引。2.所有数据都存储在叶子节点当中。3.每个叶子节点都存有相邻叶子节点的指针。4.叶子节点按照本身关键字从小到大排序。

聚簇索引就是数据和索引是在一起的。

MyISAM使用的是非聚簇索引,树的子节点上的data不是数据本身,而是数据存放的地址。InnoDB采用的是聚簇索引,树的叶子节点上的data就是数据本身。

聚簇索引的数据物理存放顺序和索引顺序是一致的,所以一个表中只能有一个聚簇索引,而非聚簇索引可以有多个。

InnoDB中,如果表定义了主键,那主键就是聚簇索引,如果没有主键,就会找第一个非空的unique列作为聚簇索引。否则,InnoDB会创建 一个隐藏的row-id作为聚簇索引。

四.MySQL的集群是如何搭建的?读写分离是怎么做的?

mysql主从复制搭建原理:

 

MySQL通过将主节点的Binlog同步给从节点完成主从之间的数据同步。

MySQL的主从集群只会将binlog从主节点同步到从节点,而不会反过来同步,由此也就引出了读写分离的问题。 

因为要保证主从之间的数据一致,写数据的操作只能在主节点完成,二读数据的操作,可以在主节点或者从节点上完成。

 五.谈谈如何对MySQL进行分库分表?多大数据量需要进行分库分表?分库分表的方式和分片策略有那些?分库分表后,SQL语句的执行流程是怎样的?

什么是分库分表?就是当表中的数据量过大时,整个查询效率就会降低得非常明显,就是为了提升查询效率,就要将一个表中的数据分散到多个数据库的多个表当中。

分库分表最常见的组件:Mycat、ShardingSphere

数据分片的方法有垂直分片和水平分片,垂直分片就是从业务角度将不同的表拆分的不同的库中,能够解决数据库数据文件过大的问题,但是不能从根本上解决查询问题。水平分片就是从数据角度将一个表中的数据拆分到不同的库或表中,这样可以从根本上解决数据量过大造成的查询效率低的问题。

有非常多的分片策略,比如:取模、按时间、按枚举值。

阿里提供的开发手册当中,建议:一个表的数据量超过500w或者数据文件超过2G,就要考虑分库分表了。

分库分表后的执行流程:

                                    

 一个user表,按照userid进行了分片,然后我需要按照sex字段去查,这要怎么查?强制指定只有一个数据库,要怎么做?查询结果按照userid来排序,要怎么排?

分库分表的问题:跨库查询、跨库排序、分布式事务、公共表、主键重复。

六.什么是倒排索引?有什么好处?

倒排索引:从内容到ID,好处:比较适合做关键字检索,可以控制数据的总量,提高查询效率。

七.聚簇索引和非聚簇索引的区别

都是B+树的数据结构

聚簇索引:将数据存储与索引放到了一块,并且是按照一定的顺序组织的,找到索引也就找到了数据,数据的物理存储与索引结构是一致的,即:只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的。

非聚簇索引:叶子节点不存储数据,存储的是数据行地址,也就是说根据索引查找到数据行的位置再取数据查找数据,这个就有点类似一本书的目录,比如我们要找第三章第一节,那我们先在这个目录里面找,找到对应的页码后再去对应的页码看文章。

 

 InnoDB一定有主键,主键一定是聚簇索引,不手动设置,则会使用unique索引,则会使用数据库内部的一个行的隐藏id来当作主键索引,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引、辅助索引叶子节点存储的不再是行的物理地址,而是主键值。

MyISAM使用的是非聚簇索引,没有聚簇索引,非聚簇索引的两颗B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键索引无需访问主键的索引树。

如果涉及到大数据量的排序、全表扫描、count之类的操作的话,还是MyISAM占优势些,由于索引所占空间小,这些操作是需要在内存中完成的。

八.MySQL索引的数据结构,各自优劣势

索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引、B-树索引、B+树索引等,InnoDB存储引擎的默认索引实现为:B+树索引,对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。

B- 树又叫平衡多路查找树。一棵m阶的B 树 (m叉树)的特性如下:

树中每个结点最多含有m个孩子(m>=2);
除根结点和叶子结点外,其它每个结点至少有[ceil(m / 2)]个孩子(其中ceil(x)是一个取上限的函数);
若根结点不是叶子结点,则至少有2个孩子(特殊情况:没有孩子的根结点,即根结点为叶子结点,整棵树只有一个根节点);
所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息(可以看做是外部接点或查询失败的接点,实际上这些结点不存在,指向这些结点的指针都为null);
每个非终端结点中包含有n个关键字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中:
       a)   Ki (i=1...n)为关键字,且关键字按顺序升序排序K(i-1)< Ki。 
       b)   Pi为指向子树根的接点,且指针P(i-1)指向子树种所有结点的关键字均小于Ki,但都大于K(i-1)。 
       c)   关键字的个数n必须满足: [ceil(m / 2)-1]<= n <= m-1。
 B+树:是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接。在B+树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基本索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高,便于范围查找,因此:B+树索引被广泛应用于数据库、文件系统等场景。

 

 哈希索引:是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。

 

 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;前提是键值都是唯一的,如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据。

如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检查。

哈希索引也没办法利用索引完成排序,以及like 'xxx%'这样的部分模糊查询(这种部分模糊查询,其本质上也是范围查询);

哈希索引也不支持多列联合索引的最左匹配原则;

B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在哈希碰撞问题。

 九.索引设计的原则?

查询更快、占用空间更小

1.适合索引的列是出现在where子句中的列,或者连接子句中的指定的列

2.基数较小的表,索引效果较差,没有必要在此列建立索引

3.使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间,如果搜索词超过索引前缀长度,则使用索引排除不匹配的行,然后检查其余行是否可能匹配。

4.不要过度索引,索引需要额外的磁盘空间,并降低写操作的性能,在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长,所以只保持需要的索引有利于查询即可

5.定义有外键的数据列一定要建立索引

6.更新频繁字段不适合创建索引

7.若是不能有效区分数据的列不适合做索引列如性别,男女未知,最多也就三种,区分度实在太低

8.尽量的扩展索引,不要新建索引,比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可

9.对于那些查询中很少涉及的列,重复值比较多的列不要建立索引

10.对于定义为text、image和bit的数据类型的列不要建立索引。

十.MySQL锁的类型有哪些?

基于所的属性分类:共享锁、排他锁。

基于锁的力度分类:行级锁、表级锁、页级锁、记录锁、间隙锁、临建锁。

基于锁的状态分类:意向共享锁、意向排他锁。

共享锁:

 

排他锁:

 

表锁: 

 

行锁:

 

记录锁:

 

页锁:

 

 间隙锁:

 

临建锁:

 

如果当事务A 加锁成功之后就设置一个状态告诉后面的人,已经有人对表里的行加了一个排他锁了,你们不能对整个表加共享锁或排他锁了,那么后面需要对整个表加锁的人只需要获取这个状态就知道自己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁,而这个状态就是意向锁。

意向共享锁:

 

意向排他锁:

 

 11.MySQL执行计划怎么看?

执行计划就是sql的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数

EXPLAIN SELECT * from A where X=? and Y=?

 

1.id:是一个有顺序的编号,是查询的顺序号,有几个select就显示几行,id的顺序是按select出现的顺序增长的。id列的值越大执行优先级越高越先执行,id列的值相同则从上往下执行,id列的值为NULL最后执行。

2. select_type表示查询中每个select子句的类型。

    SIMPLE:表示查询中每个select子句的类型

    PRIMARY:表示此查询是最外层的查询(包括子查询)

    SUBQUERY:子查询中第一个SELECT

    UNION:表示此查询是UNION的第二或随后的查询

    DEPENDENT UNION:UNION中的第二个或后面的查询语句,取决于后面的查询

    UNION RESULT:UNION的结果

    DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询,即子查询依赖于外层查询的结果

    DERIVED:衍生,表示导出表的SELECT(FROM子句的子查询)

 3.table:表示该语句查询的表

4.type:优化sql的重要字段,也是我们判断sql性能和优化程度重要指标,他的取值类型范围:

     const:通过索引一次命中,匹配一行数据;

     system:表中只有一行数据,相当于系统表;

     eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条数据与之匹配;

     ref:非唯一性索引扫描,返回匹配某个值的所有;

     range:只检查给定范围的行,使用一个索引来选择行,一般用于between、<、>;

     index:只遍历索引树;

     ALL:表示全表扫描,这个类型的查询是性能最差的查询之一,那么基本就是随着表的数量增多,执行效率越慢。

执行效率:ALL<index<range<ref<eq_ref<const<system。最好是避免AALL和index

5.possible_keys:它表示MySQL在执行该sql语句的时候,可能用到的索引信息,仅仅是可能,实际不一定会用到。

6.key:此字段是MySQL在当前查询时所真正使用到的索引,它是possible_keys的子集。

7、key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)

不损失精确性的情况下,长度越短越好 

8、ref:列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

9、rows:估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

10:extra:执行情况的描述和说明

12.关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?

在业务系统中,除了使用主键进行的查询,其他的都会在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们。

慢查询的优化首先要搞明白慢的原因是什么?是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大?

所以优化也是针对这三个方向来的:

       首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。

       分析语句的执行计划,然后获得其实用索引的情况,之后修改语句或者修改索引,使用语句可以尽可能的命中索引。

       如果对语句的优化已经无法进行,可以考虑表中的数量是否太大,如果是的话可以进行横向或者纵向的分表。

13.索引的基本原理?

索引用来快速的寻找那些就有特定值的记录,如果没有索引,一般来说执行查询时遍历整张表。

索引的原理:就是把无序的数据变成有序的查询

       (1)把创建了索引的列的内容进行排序

       (2)对排序结果生成倒排表

       (3)在倒排表内容上拼上数据地址链

       (4)在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据

14.MySQL主从同步原理?

MySQL主从同步的过程:MySQL的主从同步主要有三个线程:master<binlog dump thread>、slave<I/O thread、sql thread>,master一条线程和Slave中的两条线程。

        (1)主节点binlog,主从复制的基础是主库记录数据库的所有变更记录到binlog。binlog是数据库启动的那一刻起,保存所有修改数据库结构或内容的一个文件。

        (2)主节点log dump线程,当binlog有变动时,log_dump线程读取其内容并发送给从节点。

        (3)从节点I/O线程接收binlog内容,并将其写入到relay log文件中。

        (4)从节点的sql线程读取relay log文件内容对数据跟新进行重放,最终保证主从数据库的一致性。

注:主从节点使用binlog文件+position偏移量来定位主从同步的位置,从节点会保存其已接收到的偏移量,如果从节点发生宕机重启,则会自动从position的位置发起同步。

由于MySQL默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了,由此产生两个概念。

全同步复制:主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。

半同步复制:和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成。

15.简述MyISAM和InnoDB的区别?

MyISAM:(1)不支持事务,但每次查询都是原子的;

                 (2)支持表级锁,即每次操作是对整个表加锁;

                 (3)存储表的总行数;

                 (4)一个MyISAM表有三个文件:索引文件、表结构文件、数据文件;

                 (5)采用非聚簇索引,索引文件的数据存储指向数据文件的指针,辅索引与主索引基本一致,但是辅索引不用保证唯一性。

InnoDB:(1)支持ACID事务,支持事务的四种隔离级别;

                (2)支持行级锁及外键约束,因此可以支持写并发;

                (3)不存储总行数;

                (4)一个InnoDB引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为2G),受操作系统文件大小的限制;

                (5)主键索引采用聚簇索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值,因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引,最好使用自增主键,防止插入数据时,为维护B+树结构,文件的大调整。

16.简述MySQL中索引类型及对数据库的性能的影响。

普通索引:允许被索引的数据列包含重复的值。

唯一索引:可以保证数据记录的唯一性。

主键:是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字PRIMARY KEY来创建。

联合索引:索引可以覆盖多个数据列,如像INDEX(columnA,columnB)索引。

全文索引:通过建立倒排索引,可以极大的提升检索效率,解决判断字段是否包含的额问题,是目前搜索引擎使用的一种关键技术,可以通过ALTER TABLE table_name ADD FULLTEXT(column)创建全文索引。

索引可以极大的提高数据的查询速度。

通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件。

索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大,如果非聚簇索引很多,一旦聚簇索引改变,那么所有非聚簇索引都会给跟着变。