2019-03-08面试记录

作者: 雨夜的博客 | 来源:发表于2019-03-10 12:24 被阅读0次

    面试

    PHP7.0和PHP5.6.8有什么区别

    • 标量类型声明:函数/方法中的参数,是可以加一个类型声明的,这个是php5.0就开始支持的。
    function testClass(类名 $c)
    
    {
    
    }
    
    function testArray(array $a)
    
    {
    
    }
    
    • 返回值类型声明:PHP7增加了对返回类型声明的支持。类似于参数类型声明,返回类型声明指明了函数返回值得类型。可用的类型与参数声明中可用的类型相同。
    image

    返回值的类型,跟参数的类型一致。

    【注意】在默认情况下,函数返回值如果和定义的不一致,会进行强制转换,在严格模式下,则会报错TypeError。


    image
    • null合并运算符
      image
    • 太空船操作符(暂时不清楚这个操作符有什么实际用处,后续了解清楚再补充上)
      image
    • define允许定义常量数组
      image
      我们来看下define的声明,参考:http://php.net/manual/zh/function.define.php
      image
      这个新特性大概是为了丰富define的功能,const已经实现了这个功能了的。
    • 匿名类
      image
    • use分组命名空间
      image
      这里可以用一行代码引入同一个命名空间下的多个类,而不是之前那样需要每个类一行代码,算是一种优化。
      image
    • 新增整数整除的函数inidiv()
      image
      返回值为整型。
    • 新增的随机函数
      image

    如何查看负载均衡

    如何查看服务器当前的并发访问量

    服务器访问人数可以通过命令: netstat -pnt | grep :80 列出,比如此时请求服务器上一个页面,通过该命令可以查看结果,后面的ESTABLISHED表示服务器正在被访问,可以通过添加 wc -l 统计数量,最终命令是: netstat -pnt | grep :80 | wc -l 此命令可以统计服务器所有url被请求的数量

    image
    734555-20160722175754888-374791670 (1).png

    因为服务器本身占用一个连接,所以此时并发数是7个用户访问,当关闭网页后输入命令会发现还是8个用户,实际上查看详细信息都是从ESTABLISHED变成了FIN_WAIT2超时状态,因为http有一个保持连接的时间,过一会再查看用户数就为1了,此时说明所有连接都彻底断开了,访问一个页面后再访问另一个页面,之前的http超时时间将加快,所以当连续访问网站时,连接总体上还是保持稳定的。

    事物的隔离层有几种

    第1级别:Read Uncommitted(读取未提交内容)
    (1)所有事务都可以看到其他未提交事务的执行结果
    (2)本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
    (3)该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据

    
    #首先,修改隔离级别
    set tx_isolation='READ-UNCOMMITTED';
    select @@tx_isolation;
    +------------------+
    | @@tx_isolation   |
    +------------------+
    | READ-UNCOMMITTED |
    +------------------+
    
    #事务A:启动一个事务
    start transaction;
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |    1 |
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    
    #事务B:也启动一个事务(那么两个事务交叉了)
           在事务B中执行更新语句,且不提交
    start transaction;
    update tx set num=10 where id=1;
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |   10 |
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    
    #事务A:那么这时候事务A能看到这个更新了的数据吗?
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |   10 |   --->可以看到!说明我们读到了事务B还没有提交的数据
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    
    #事务B:事务B回滚,仍然未提交
    rollback;
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |    1 |
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    
    #事务A:在事务A里面看到的也是B没有提交的数据
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |    1 |      --->脏读意味着我在这个事务中(A中),事务B虽然没有提交,但它任何一条数据变化,我都可以看到!
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    

    第2级别:Read Committed(读取提交内容)
    (1)这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)
    (2)它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
    (3)这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
    |——>导致这种情况的原因可能有:(1)有一个交叉的事务有新的commit,导致了数据的改变;(2)一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit

    #首先修改隔离级别
    set tx_isolation='read-committed';
    select @@tx_isolation;
    +----------------+
    | @@tx_isolation |
    +----------------+
    | READ-COMMITTED |
    +----------------+
    
    #事务A:启动一个事务
    start transaction;
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |    1 |
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    
    #事务B:也启动一个事务(那么两个事务交叉了)
           在这事务中更新数据,且未提交
    start transaction;
    update tx set num=10 where id=1;
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |   10 |
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    
    #事务A:这个时候我们在事务A中能看到数据的变化吗?
    select * from tx; --------------->
    +------+------+                |
    | id   | num  |                |
    +------+------+                |
    |    1 |    1 |--->并不能看到!  |
    |    2 |    2 |                |
    |    3 |    3 |                |
    +------+------+                |——>相同的select语句,结果却不一样
                                   |
    #事务B:如果提交了事务B呢?         |
    commit;                        |
                                   |
    #事务A:                         |
    select * from tx; --------------->
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |   10 |--->因为事务B已经提交了,所以在A中我们看到了数据变化
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    

    第3级别:Repeatable Read(可重读)
    (1)这是MySQL的默认事务隔离级别
    (2)它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
    (3)此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
    (4)InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题

    #首先,更改隔离级别
    set tx_isolation='repeatable-read';
    select @@tx_isolation;
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+
    
    #事务A:启动一个事务
    start transaction;
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |    1 |
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    
    #事务B:开启一个新事务(那么这两个事务交叉了)
           在事务B中更新数据,并提交
    start transaction;
    update tx set num=10 where id=1;
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |   10 |
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    commit;
    
    #事务A:这时候即使事务B已经提交了,但A能不能看到数据变化?
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |    1 | --->还是看不到的!(这个级别2不一样,也说明级别3解决了不可重复读问题)
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    
    #事务A:只有当事务A也提交了,它才能够看到数据变化
    commit;
    select * from tx;
    +------+------+
    | id   | num  |
    +------+------+
    |    1 |   10 |
    |    2 |    2 |
    |    3 |    3 |
    +------+------+
    

    第4级别:Serializable(可串行化)
    (1)这是最高的隔离级别
    (2)它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。
    (3)在这个级别,可能导致大量的超时现象和锁竞争

    #首先修改隔离界别
    set tx_isolation='serializable';
    select @@tx_isolation;
    +----------------+
    | @@tx_isolation |
    +----------------+
    | SERIALIZABLE   |
    +----------------+
    
    #事务A:开启一个新事务
    start transaction;
    
    #事务B:在A没有commit之前,这个交叉事务是不能更改数据的
    start transaction;
    insert tx values('4','4');
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    update tx set num=10 where id=1;
    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    
    image

    MySQL的优化有几种

    • 1、选取最适用的字段属性
    MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。
    
    例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。
    
    另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOTNULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
    对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
    
    • 2、使用连接(JOIN)来代替子查询(Sub-Queries)
    MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,如下所示:
    
    DELETE FROM customerinfo
    
    WHERE CustomerID NOT IN (SELECT CustomerID FROM salesinfo)
    
    使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN)..替代。例如,假设我们要将所有没有订单记录的用户取出来,可以用下面这个查询完成:
    
    SELECT * FROM customerinfo
    
    WHERE CustomerID NOT IN (SELECTC ustomerID FROM salesinfo)
    
    如果使用连接(JOIN)..来完成这个查询工作,速度将会快很多。尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好,查询如下:
    
    SELECT * FROM customerinfo
    
    LEFT JOIN salesinfo ON customerinfo.CustomerID=salesinfo.CustomerID
    
    WHERE salesinfo.CustomerID ISNULL
    
    连接(JOIN)..之所以更有效率一些,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
    
    • 3、使用联合(UNION)来代替手动创建的临时表
    MySQL从4.0的版本开始支持union查询,它可以把需要使用临时表的两条或更多的select查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要想同。下面的例子就演示了一个使用UNION的查询。
    
    SELECT Name,Phone FROM client UNION
    
    SELECT Name,BirthDate FROM author UNION
    
    SELECT Name,Supplier FROM product
    
    • 4、事务
    尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。
    
    BEGIN; INSERT INTO salesinfo SET CustomerID=14; UPDATE inventory SET Quantity=11 WHERE item='book'; COMMIT;
    
    事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。
    
    • 5、锁定表
    尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。
    
    其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。下面的例子就用锁定表的方法来完成前面一个例子中事务的功能。
    
    LOCK TABLE inventory WRITE SELECT Quantity FROM inventory WHERE Item='book';
    
    ...
    
    UPDATE inventory SET Quantity=11 WHERE Item='book'; UNLOCKTABLES
    
    这里,我们用一个select语句取出初始数据,通过一些计算,用update语句将新值更新到表中。包含有WRITE关键字的LOCKTABLE语句可以保证在UNLOCKTABLES命令被执行之前,不会有其它的访问来对inventory进行插入、更新或者删除的操作。
    
    • 6、使用外键
    锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。
    
    例如,外键可以保证每一条销售记录都指向某一个存在的客户。在这里,外键可以把customerinfo表中的CustomerID映射到salesinfo表中CustomerID,任何一条没有合法CustomerID的记录都不会被更新或插入到salesinfo中。
    
    CREATE    TABLE    customerinfo( CustomerIDINT    NOT    NULL,PRIMARYKEY(CustomerID))TYPE=INNODB;
    
    CREATE    TABLE    salesinfo( SalesIDNT    NOT    NULL,CustomerIDINT    NOT    NULL,
    
    PRIMARYKEY(CustomerID,SalesID),
    
    FOREIGNKEY(CustomerID)    REFERENCES    customerinfo(CustomerID)    ON    DELETE    CASCADE)TYPE=INNODB;
    注意例子中的参数“ON DELETE CASCADE”。该参数保证当customerinfo表中的一条客户记录被删除的时候,salesinfo表中所有与该客户相关的记录也会被自动删除。如果要在MySQL中使用外键,一定要记住在创建表的时候将表的类型定义为事务安全表InnoDB类型。该类型不是MySQL表的默认类型。定义的方法是在CREATETABLE语句中加上TYPE=INNODB。如例中所示。
    
    • 7、使用索引
    索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。
    
    那该对哪些字段建立索引呢?
    
    一般说来,索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况
    
    例如customerinfo中的“province”..字段,在这样的字段上建立索引将不会有什么帮助;相反,还有可能降低数据库的性能。我们在创建表的时候可以同时创建合适的索引,也可以使用ALTERTABLE或CREATEINDEX在以后创建索引。此外,MySQL从版本3.23.23开始支持全文索引和搜索。全文索引在MySQL中是一个FULLTEXT类型索引,但仅能用于MyISAM类型的表。对于一个大的数据库,将数据装载到一个没有FULLTEXT索引的表中,然后再使用ALTERTABLE或CREATEINDEX创建索引,将是非常快的。但如果将数据装载到一个已经有FULLTEXT索引的表中,执行过程将会非常慢。
    
    • 8、优化的查询语句
    如果SQL语句使用不恰当的话,索引将无法发挥它应有的作用。
    
    下面是应该注意的几个方面。
    
    首先,最好是在相同类型的字段间进行比较的操作。
    
    在MySQL3.23版之前,这甚至是一个必须的条件。例如不能将一个建有索引的INT字段和BIGINT字段进行比较;但是作为特殊的情况,在CHAR类型的字段和VARCHAR类型字段的字段大小相同的时候,可以将它们进行比较。
    
    其次,在建有索引的字段上尽量不要使用函数进行操作。
    
    例如,在一个DATE类型的字段上使用YEAE()函数时,将会使索引不能发挥应有的作用。所以,下面的两个查询虽然返回的结果一样,但后者要比前者快得多。
    
    第三,在搜索字符型字段时,我们有时会使用LIKE关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。
    例如下面的查询将会比较表中的每一条记录。
    
    
    SELECT    *    FROM    books
    
    WHERE    name    like"MySQL%"
    但是如果换用下面的查询,返回的结果一样,但速度就要快上很多:
    
    
    SELECT    *    FROM    books
    
    WHERE    name>="MySQL"    andname    <"MySQM"
    最后,应该注意避免在查询中让MySQL进行自动类型转换,因为转换过程也会使索引变得不起作用。
    

    mysql的索引有几种?

    • FULLTEXT
    即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE INDEX创建FULLTEXT索引,要比先为一张表建立FULLTEXT然后再将数据写入的速度快很多。
    
    全文索引并不是和MyISAM一起诞生的,它的出现是为了解决WHERE name LIKE “%word%"这类针对文本的模糊查询效率较低的问题。在没有全文索引之前,这样一个查询语句是要进行遍历数据表操作的,可见,在数据量较大时是极其的耗时的,如果没有异步IO处理,进程将被挟持,很浪费时间,当然这里不对异步IO作进一步讲解,想了解的童鞋,自行谷哥。
    
    全文索引的使用方法并不复杂:
    
    创建ALTER TABLE table ADD INDEX `FULLINDEX` USING FULLTEXT(`cname1`[,cname2…]);
    
    使用SELECT * FROM table WHERE MATCH(cname1[,cname2…]) AGAINST ('word' MODE );
    
    其中, MODE为搜寻方式(IN BOOLEAN MODE ,IN NATURAL LANGUAGE MODE ,IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION / WITH QUERY EXPANSION)。
    
    关于这三种搜寻方式,愚安在这里也不多做交代,简单地说,就是,布尔模式,允许word里含一些特殊字符用于标记一些具体的要求,如+表示一定要有,-表示一定没有,*表示通用匹配符,是不是想起了正则,类似吧;自然语言模式,就是简单的单词匹配;含表达式的自然语言模式,就是先用自然语言模式处理,对返回的结果,再进行表达式匹配。
    
    对搜索引擎稍微有点了解的同学,肯定知道分词这个概念,FULLTEXT索引也是按照分词原理建立索引的。西文中,大部分为字母文字,分词可以很方便的按照空格进行分割。但很明显,中文不能按照这种方式进行分词。那又怎么办呢?这个向大家介绍一个Mysql的中文分词插件**Mysqlcft**,有了它,就可以对中文进行分词,想了解的同学请移步[Mysqlcft](http://code.google.com/p/mysqlcft/),当然还有其他的分词插件可以使用。
    
    • HASH
    **Hash**这个词,可以说,自打我们开始码的那一天起,就开始不停地见到和使用到了。其实,hash就是一种(key=>value)形式的键值对,如数学中的函数映射,允许多个key对应相同的value,但不允许一个key对应多个value。正是由于这个特性,hash很适合做索引,为某一列或几列建立hash索引,就会利用这一列或几列的值通过一定的算法计算出一个hash值,对应一行或几行数据(这里在概念上和函数映射有区别,不要混淆)。在java语言中,每个类都有自己的hashcode()方法,没有显示定义的都继承自object类,该方法使得每一个对象都是唯一的,在进行对象间equal比较,和序列化传输中起到了很重要的作用。hash的生成方法有很多种,足可以保证hash码的唯一性,例如在MongoDB中,每一个document都有系统为其生成的唯一的objectID(包含时间戳,主机散列值,进程PID,和自增ID)也是一种hash的表现。额,我好像扯远了-_-!
    
    由于hash索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。那为什么还需要其他的树形索引呢?
    
    在这里愚安就不自己总结了。引用下园子里其他大神的文章:来自 14的路 的[MySQL的btree索引和hash索引的区别](http://www.cnblogs.com/vicenteforever/articles/1789613.html)
    
    (1)Hash 索引仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询。 
    由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样。 
    (2)Hash 索引无法被用来避免数据的排序操作。 
    由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且Hash值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算; 
    (3)Hash 索引不能利用部分索引键查询。 
    对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。 
    (4)Hash 索引在任何时候都不能避免表扫描。 
    前面已经知道,Hash 索引是将索引键通过 Hash 运算之后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从 Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。 
    (5)Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。 
    对于选择性比较低的索引键,如果创建 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。
    
    愚安我稍作补充,讲一下HASH索引的过程,顺便解释下上面的第4,5条:
    
    当我们为某一列或某几列建立hash索引时(目前就只有MEMORY引擎显式地支持这种索引),会在硬盘上生成类似如下的文件:
    
    | hash值  | 存储地址     |
    | 1db54bc745a1 | 77#45b5  |
    | 4bca452157d4 | 76#4556,77#45cc… |
    
    …
    
    hash值即为通过特定算法由指定列数据计算出来,磁盘地址即为所在数据行存储在硬盘上的地址(也有可能是其他存储地址,其实MEMORY会将hash表导入内存)。
    
    这样,当我们进行WHERE age = 18 时,会将18通过相同的算法计算出一个hash值==>在hash表中找到对应的储存地址==>根据存储地址取得数据。
    
    所以,每次查询时都要遍历hash表,直到找到对应的hash值,如(4),数据量大了之后,hash表也会变得庞大起来,性能下降,遍历耗时增加,如(5)。
    
    • BTREE
    BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中,相信学过数据结构的童鞋都对当初学习二叉树这种数据结构的经历记忆犹新,反正愚安我当时为了软考可是被这玩意儿好好地折腾了一番,不过那次考试好像没怎么考这个。如二叉树一样,每次查询都是从树的入口root开始,依次遍历node,获取leaf。
    
    BTREE在MyISAM里的形式和Innodb稍有不同
    
    在 Innodb里,有两种形态:一是primary key形态,其leaf node里存放的是数据,而且不仅存放了索引键的数据,还存放了其他字段的数据。二是secondary index,其leaf node和普通的BTREE差不多,只是还存放了指向主键的信息.
    
    而在MyISAM里,主键和其他的并没有太大区别。不过和Innodb不太一样的地方是在MyISAM里,leaf node里存放的不是主键的信息,而是指向数据文件里的对应数据行的信息.
    
    • RTREE
    RTREE在mysql很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。
    
    相对于BTREE,RTREE的优势在于范围查找.
    各种索引的使用情况
    
    (1)对于BTREE这种Mysql默认的索引类型,具有普遍的适用性
    
    (2)由于FULLTEXT对中文支持不是很好,在没有插件的情况下,最好不要使用。其实,一些小的博客应用,只需要在数据采集时,为其建立关键字列表,通过关键字索引,也是一个不错的方法,至少愚安我是经常这么做的。
    
    (3)对于一些搜索引擎级别的应用来说,FULLTEXT同样不是一个好的处理方法,Mysql的全文索引建立的文件还是比较大的,而且效率不是很高,即便是使用了中文分词插件,对中文分词支持也只是一般。真要碰到这种问题,Apache的Lucene或许是你的选择。
    
    (4)正是因为hash表在处理较小数据量时具有无可比拟的素的优势,所以hash索引很适合做缓存(内存数据库)。如mysql数据库的内存版本Memsql,使用量很广泛的缓存工具Mencached,NoSql数据库redis等,都使用了hash索引这种形式。当然,不想学习这些东西的话Mysql的MEMORY引擎也是可以满足这种需求的。
    
    • RTREE
    待补充
    

    redis的持续化有几种?特点是什么?

    待看
    

    设计模式了解几种?

    策略模式

    策略模式是对象的行为模式,用意是对一组算法的封装。动态的选择需要的算法并使用。

    策略模式指的是程序中涉及决策控制的一种模式。策略模式功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性思想。

    策略模式的三个角色:

    1.抽象策略角色

    2.具体策略角色

    3.环境角色(对抽象策略角色的引用)

    实现步骤:

    1.定义抽象角色类(定义好各个实现的共同抽象方法)

    2.定义具体策略类(具体实现父类的共同方法)

    3.定义环境角色类(私有化申明抽象角色变量,重载构造方法,执行抽象方法)

    就在编程领域之外,有许多例子是关于策略模式的。例如:

    如果我需要在早晨从家里出发去上班,我可以有几个策略考虑:我可以乘坐地铁,乘坐公交车,走路或其它的途径。每个策略可以得到相同的结果,但是使用了不同的资源。
    策略模式的代码实例:

    <?php
        abstract class baseAgent { //抽象策略类
            abstract function PrintPage();
        }
        //用于客户端是IE时调用的类(环境角色)
        class ieAgent extends baseAgent {
            function PrintPage() {
                return 'IE';
            }
        }
        //用于客户端不是IE时调用的类(环境角色)
        class otherAgent extends baseAgent {
            function PrintPage() {
                return 'not IE';
            }
        }
        class Browser { //具体策略角色
            public function call($object) {
                    return $object->PrintPage ();
                }
            }
            $bro = new Browser ();
        echo $bro->call ( new ieAgent () );
    ?>
    

    工厂模式

    工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式。

    使用工厂模式的好处是,如果你想要更改所实例化的类名等,则只需更改该工厂方法内容即可,不需逐一寻找代码中具体实例化的地方(new处)修改了。为系统结构提供灵活的动态扩展机制,减少了耦合。

    header('Content-Type:text/html;charset=utf-8');
    /**
     *简单工厂模式(静态工厂方法模式)
     */
    /**
     * Interface people 人类
     */
    interface  people
    {
        public function  say();
    }
    /**
     * Class man 继承people的男人类
     */
    class man implements people
    {
        // 具体实现people的say方法
        public function say()
        {
            echo '我是男人<br>';
        }
    }
    /**
     * Class women 继承people的女人类
     */
    class women implements people
    {
        // 具体实现people的say方法
        public function say()
        {
            echo '我是女人<br>';
        }
    }
    /**
     * Class SimpleFactoty 工厂类
     */
    class SimpleFactoty
    {
        // 简单工厂里的静态方法-用于创建男人对象
        static function createMan()
        {
            return new man();
        }
        // 简单工厂里的静态方法-用于创建女人对象
        static function createWomen()
        {
            return new women();
        }
    }
    /**
     * 具体调用
     */
    $man = SimpleFactoty::createMan();
    $man->say();
    $woman = SimpleFactoty::createWomen();
    $woman->say();
    

    单例模式

    单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

    单例模式是一种常见的设计模式,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、数据库操作、显卡的驱动程序常被设计成单例。

    单例模式分3种:懒汉式单例、饿汉式单例、登记式单例。

    单例模式有以下3个特点:

    1.只能有一个实例。

    2.必须自行创建这个实例。

    3.必须给其他对象提供这一实例。

    那么为什么要使用PHP单例模式?

    PHP一个主要应用场合就是应用程序与数据库打交道的场景,在一个应用中会存在大量的数据库操作,针对数据库句柄连接数据库的行为,使用单例模式可以避免大量的new操作。因为每一次new操作都会消耗系统和内存的资源。

    class Single {
        private $name;//声明一个私有的实例变量
        private function __construct(){//声明私有构造方法为了防止外部代码使用new来创建对象。
        }
        static public $instance;//声明一个静态变量(保存在类中唯一的一个实例)
            static public function getinstance(){//声明一个getinstance()静态方法,用于检测是否有实例对象
            if(!self::$instance) self::$instance = new self();
                return self::$instance;
        }
        public function setname($n){ $this->name = $n; }
            public function getname(){ return $this->name; }
    }
    $oa = Single::getinstance();
    $ob = Single::getinstance();
    $oa->setname('hello world');
    $ob->setname('good morning');
    echo $oa->getname();//good morning
    echo $ob->getname();//good morning
    

    注册模式

    注册模式,解决全局共享和交换对象。已经创建好的对象,挂在到某个全局可以使用的数组上,在需要使用的时候,直接从该数组上获取即可。将对象注册到全局的树上。任何地方直接去访问。

    class Register
    {
        protected static  $objects;
            function set($alias,$object)//将对象注册到全局的树上
            {
                self::$objects[$alias]=$object;//将对象放到树上
            }
            static function get($name){
            return self::$objects[$name];//获取某个注册到树上的对象
        }
        function _unset($alias)
      {
            unset(self::$objects[$alias]);//移除某个注册到树上的对象。
        }
    }
    

    适配器模式

    将各种截然不同的函数接口封装成统一的API。
    PHP中的数据库操作有MySQL,MySQLi,PDO三种,可以用适配器模式统一成一致,使不同的数据库操作,统一成一样的API。类似的场景还有cache适配器,可以将memcache,redis,file,apc等不同的缓存函数,统一成一致。
    首先定义一个接口(有几个方法,以及相应的参数)。然后,有几种不同的情况,就写几个类实现该接口。将完成相似功能的函数,统一成一致的方法。

    namespace IMooc;
    interface IDatabase
    {
        function connect($host, $user, $passwd, $dbname);
        function query($sql);
        function close();
    }
    

    MYSQL

    namespace IMooc\Database;
    use IMooc\IDatabase;
    class MySQL implements IDatabase
    {
        protected $conn;
            function connect($host, $user, $passwd, $dbname)
            {
                $conn = mysql_connect($host, $user, $passwd);
                mysql_select_db($dbname, $conn);
                $this->conn = $conn;
        }
        function query($sql)
            {
                $res = mysql_query($sql, $this->conn);
                return $res;
        }
        function close()
        {
            mysql_close($this->conn);
        }
    }
    

    MySQLi

    namespace IMooc\Database;
    use IMooc\IDatabase;
    class MySQLi implements IDatabase
    {
        protected $conn;
        function connect($host, $user, $passwd, $dbname)
        {
            $conn = mysqli_connect($host, $user, $passwd, $dbname);
            $this->conn = $conn;
        }
        function query($sql)
        {
            return mysqli_query($this->conn, $sql);
        }
        function close()
        {
            mysqli_close($this->conn);
        }
    }
    

    观察者模式

    1:观察者模式(Observer),当一个对象状态发生变化时,依赖它的对象全部会收到通知,并自动更新。
    2:场景:一个事件发生后,要执行一连串更新操作。传统的编程方式,就是在事件的代码之后直接加入处理的逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件的主体代码。
    3:观察者模式实现了低耦合,非侵入式的通知与更新机制。
    定义一个事件触发抽象类。

    require_once 'Loader.php';
    abstract class EventGenerator{
        private $observers = array();
            function addObserver(Observer $observer){
            $this->observers[]=$observer;
        }
        function notify(){
            foreach ($this->observers as $observer){
                $observer->update();
            }
        }
    }
    

    定义一个观察者接口

    require_once 'Loader.php';
    interface Observer{
        function update();//这里就是在事件发生后要执行的逻辑
    }
    //一个实现了EventGenerator抽象类的类,用于具体定义某个发生的事件
    

    实现

    require 'Loader.php';
    class Event extends EventGenerator{
        function triger(){
            echo "Event<br>";
        }
    }
    class Observer1 implements Observer{
        function update(){
            echo "逻辑1<br>";
        }
    }
    class Observer2 implements Observer{
        function update(){
            echo "逻辑2<br>";
        }
    }
    $event = new Event();
    $event->addObserver(new Observer1());
    $event->addObserver(new Observer2());
    $event->triger();
    $event->notify();
    

    服务器的分布式方案

    
    

    接口和抽象类

    
    

    相关文章

      网友评论

        本文标题:2019-03-08面试记录

        本文链接:https://www.haomeiwen.com/subject/qwyspqtx.html