[关闭]
@lifei6671 2018-05-31T08:37:38.000000Z 字数 14408 阅读 462

PHP常见面试题目

PHP


一、mysql_real_escape_string mysql_escape_string 有什么本质的区别,有什么用处,为什么被弃用?

两者都是过滤字符串,防止sql注入,但两者有一些区别

mysql_real_escape_string:

  1. 具有两个参数,其中第二个为选填参数,默认为上一个数据库链接connection

  2. 使用之前要先连接上数据库,否则会出错

  3. 在过滤字符串的时候,会考虑当前链接connection字符集,(set names 'utf-8')

mysql_escapte_string是处理单个字符串函数.

不过从PHP5.4开始,PHP官方就鼓励使用mysqli和PDO来操作数据库了。

二、一台电脑配置无限好,可以同时打开多少个网页?

65535-1024 = 64511(端口数)

三、ip地址能被伪造吗?

http头部可以被篡改,但是只能修改X_FORWARDED_FOR,真实ip地址(REMOTE_ADDR)很难修改(除非是路由器去修改),因为真实ip是底层会话ip地址,而且因为TCP 3次握手的存在,连接无法建立,伪造的意义不大,至于UDP的话,一般是内网才使用UDP通信。

四、有100万个奖品,每个人可以中奖3次,先到先得,怎么控制并发,不能发超,并保证完全的先到先得模式?

如果奖品相同,则在redis中初始化一个值为100万的KV值,每当一个用户抽奖时,先在redis判断该用户的抽奖记录,如果抽奖记录小于3则可去抽奖,并增加抽奖记录。否则重定向到静态页面。直到100W奖品抽完为止。
如果奖品不同,需要根据奖品种类初始化奖品种类数量的KV值,重复上面过程。

五、索引为什么是用B树呢?

一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。而B-/+/*Tree,经过改进可以有效的利用系统对磁盘的块读取特性,在读取相同磁盘块的同时,尽可能多的加载索引数据,来提高索引命中效率,从而达到减少磁盘IO的读取次数。

不了解磁盘相关知识的可以查看 硬盘基本知识(磁头、磁道、扇区、柱面)

下面通过示意图来看一下,B-tree、B+tree、B*tree

B-tree

从图中可以看出,B-tree 利用了磁盘块的特性进行构建的树。每个磁盘块一个节点,每个节点包含了很关键字。把树的节点关键字增多后树的层级比原来的二叉树少了,减少数据查找的次数和复杂度。

B-tree巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页(每页为4K),这样每个节点只需要一次I/O就可以完全载入。
B-tree 的数据可以存在任何节点中。

此处输入图片的描述

B+tree 是 B-tree 的变种,B+tree数据只存储在叶子节点中。这样在B树的基础上每个节点存储的关键字数更多,树的层级更少所以查询数据更快,所有指关键字指针都存在叶子节点,所以每次查找的次数都相同所以查询速度更稳定;

如果每个节点能存放M个数据,每个节点的数据在M/2到M之间。预留出空间可以插入新的数据。

B*treeB*树

B*tree 每个磁盘块中又添加了对下一个磁盘块的引用。这样可以在当前磁盘块满时,不用扩容直接存储到下一个临近磁盘块中。当两个邻近的磁盘块都满时,这两个磁盘块各分出1/3的数据重新分配一个磁盘块,这样这三个磁盘块的数据都为2/3。

如果每个节点能存放M个数据,每个节点的数据在2M/3到M之间。预留出空间可以插入新的数据。

在B+树的基础上因其初始化的容量变大,使得节点空间使用率更高,而又存有兄弟节点的指针,可以向兄弟节点转移关键字的特性使得B*树额分解次数变得更少;

六、Memcache 和 Redis的区别?

  1. Redis中,并不是所有的数据都一直存储在内存中的,这是和Memcache相比一个最大的区别。
  2. Redis在很多方面具备数据库的特征,或者说就是一个数据库系统,而Memcache只是简单的K/V缓存。
  3. 他们的扩展都需要做集群;实现方式:master-slave、Hash。
  4. 在100k以上的数据中,Memcache性能要高于Redis。
  5. 如果要说内存使用效率,使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcache。当然,这和你的应用场景和数据特性有关。
  6. 如果你对数据持久化和数据同步有所要求,那么推荐你选择Redis,因为这两个特性Memcache都不具备。即使你只是希望在升级或者重启系统后缓存数据不会丢失,选择Redis也是明智的。
  7. Redis和Memcache在写入性能上面差别不大,读取性能上面尤其是批量读取性能上面Memcache更强
  8. Redis支持多种数据结构,如string,list,dict,set,zset,hyperloglog

七、父类方法是 protected,子类重构为 private,会发生什么?

会发生fatal错误,因为继承的方法或属性只能维持或放大权限,不能缩小,比如protected重载为public是可行的。

八、 一个网页从输入地址回车,到完整展示网页内容这段时间里,做了哪些工作,越详细越好。

  1. 浏览器本地缓存匹配;
  2. 本地hosts映射对比;
  3. 本地dns缓存解析;
  4. 远程dns解析获得服务器ip地址;
  5. 浏览器发送tcp连接请求包(syn);
  6. 请求包经过传输层、网络层、数据链路层封装通过网卡到达路由器;
  7. 路由器转发数据包到所属运营商服务器;
  8. 运营商服务器通过寻址最短路径通过中继节点到达指定ip地址;
  9. 服务器端可能存在反向代理或者负载均衡,都是直接转发请求至上游服务器,当然也可以制定安全防御规则直接丢弃请求包;
  10. 上游服务器收到连接请求,在自身可用的情况下,返回(syn+ack);
  11. 浏览器校验ack,再次发送(syn+ack);
  12. 服务器校验ack切换连接状态至established,然后根据请求传输数据包;
  13. 当transform-encoding为chunked时,浏览器开始渲染页面; 四次挥手,连接关闭;
  14. 渲染数据完成。

九、 HTTP Keep-Alive是什么?

HTTP协议采用“请求-应答”模式,当使用普通模式,即非KeepAlive模式时,每个请求/应答客户和服务器都要新建一个连接,完成 之后立即断开连接(HTTP协议为无连接的协议);当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服 务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。

http 1.0中默认是关闭的,需要在http头加入"Connection: Keep-Alive",才能启用Keep-Alive;http 1.1中默认启用Keep-Alive,如果加入"Connection: close ",才关闭。目前大部分浏览器都是用http1.1协议,也就是说默认都会发起Keep-Alive的连接请求了,所以是否能完成一个完整的Keep- Alive连接就看服务器设置情况。

十、myisam跟innodb有什么区别?

  1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
  2. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
  3. InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
  4. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
  5. Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高;

十一、php进程死锁产生的原因是什么?怎么自动排查与自动恢复?

死锁产生的原因:

(1)竞争资源;

(2)进程推进顺序不当。

死锁产生的必要条件:

(1)互斥条件:一个资源一次只能被一个进程所使用,即是排它性使用。

(2)不剥夺条件:一个资源仅能被占有它的进程所释放,而不能被别的进程强占。

(3)请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源要求,而该资源又已被其它进程占有,此时请求进程阻塞,但又对已经获得的其它资源保持不放。

(4)环路等待条件:当每类资源只有一个时,在发生死锁时,必然存在一个进程-资源的环形链。

死锁的解除:

(1)方法1:强制性地从系统中撤消一个或多个死锁的进程以断开循环等待链,并收回分配给终止进程的全部资源供剩下的进程使用。

(2)方法2:使用一个有效的挂起和解除机构来挂起一些死锁的进程,其实质是从被挂起的进程那里抢占资源以解除死锁。

十二、php5.2->php7.1的各版本演进历史,新增特性等?

PHP 5.3

PHP 5.4

PHP5.5

PHP5.6

PHP7.0

PHP7.1

十三、编写一个PHP函数,求任意n个正负整数里面最大的连续和,要求算法时间复杂度尽可能低。

  1. function continuousMaxSum(array $array,$len){
  2. $curSum = 0;
  3. $maxSum = 0;
  4. for($i = 0; $i < $len; $i ++){
  5. if($curSum + $array[$i] < 0){
  6. $curSum = 0;
  7. }else{
  8. $curSum += $array[$i];
  9. $maxSum = max($curSum,$maxSum);
  10. }
  11. }
  12. return $maxSum;
  13. }

十四、PHP解决多进程同时写一个文件的问题。

  1. function writeFile($fileName,$content){
  2. if($fp=fopen($fileName,'a')){
  3. $startTime=microtime();
  4. do{
  5. $canWrite=flock($fp,LOCK_EX);
  6. if(!$canWrite){
  7. usleep(round(rand(0,100)*1000));
  8. }
  9. }while((!$canWrite)&&((microtime()-$startTime)<1000));
  10. if($canWrite){
  11. fwrite($fp,$content);
  12. }
  13. fclose($fp);
  14. }
  15. }

十五、PHP实现冒泡排序

冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,依次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

步骤:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

代码示例

  1. function bubbleSort ($arr)
  2. {
  3. $len = count($arr);
  4. //该层循环控制 需要冒泡的轮数
  5. for ($i=1; $i<$len; $i++) {
  6. //该层循环用来控制每轮 冒出一个数 需要比较的次数
  7. for ($k=0; $k<$len-$i; $k++) {
  8. if($arr[$k] > $arr[$k+1]) {
  9. $tmp = $arr[$k+1]; // 声明一个临时变量
  10. $arr[$k+1] = $arr[$k];
  11. $arr[$k] = $tmp;
  12. }
  13. }
  14. }
  15. return $arr;
  16. }

十六、选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。

  1. function selection_sort($array)
  2. {
  3. $len = count($array);
  4. for($i = 0;$i < $len - 1; $i++){
  5. $m = $i;
  6. for($j = $i + 1; $j < $len; $j++){
  7. if($this->array[$m] > $this->array[$j]){
  8. $m = $j;
  9. }
  10. }
  11. if($m != $i){
  12. $temp = $this->array[$m];
  13. $this->array[$m] = $this->array[$i];
  14. $this->array[$i] = $temp;
  15. }
  16. }
  17. return $this->array;
  18. }

十七、插入排序

插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

步骤:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置中
  6. 重复步骤2

参考代码

  1. function sort($array)
  2. {
  3. for ($i=1,$len=count($array);$i < $len;$i++){
  4. $temp = $array[$i];
  5. for($j = $i - 1;$j >= 0 && $temp < $array[$j]; $j --){
  6. $array[$j + 1] = $array[$j];
  7. }
  8. $array[$j + 1] = $temp;
  9. }
  10. }

十八、快速排序

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

步骤:

  1. 从数列中挑出一个元素,称为 “基准”(pivot),
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

示例代码

  1. function quickSort($arr)
  2. {
  3. if(!is_array($arr)){
  4. return false;
  5. }
  6. if(count($arr) <= 1){
  7. return $arr;
  8. }
  9. $left = $right = array();
  10. for($i = 1,$len = count($arr);$i < $len;$i++){
  11. if($arr[$i] < $arr[0]){
  12. $left[] = $arr[$i];
  13. }else{
  14. $right[] = $arr[$i];
  15. }
  16. }
  17. $left = $this->quickSort($left);
  18. $right = $this->quickSort($right);
  19. $left[] = $arr[0];
  20. return array_merge($left,$right);
  21. }

十九、实现随机打乱一个数组

Fisher–Yates随机置乱算法也被称做高纳德置乱算法,通俗说就是生成一个有限集合的随机排列。Fisher-Yates随机置乱算法是无偏的,所以每个排列都是等可能的,当前使用的Fisher-Yates随机置乱算法是相当有效的,需要的时间正比于要随机置乱的数,不需要额为的存储空间开销。

  1. function array_shuffle(array $array){
  2. $i = 0;
  3. $len = count($array);
  4. while ($i < $len){
  5. $index = rand($i,$len - 1);
  6. $temp = $array[$index];
  7. $array[$index] = $array[$i];
  8. $array[$i] = $temp;
  9. $i ++;
  10. }
  11. return $array;
  12. }

二十、数组的全排列

从n个数中选取m(m<=n)个数按照一定的顺序进行排成一个列,叫作从n个元素中取m个元素的一个排列。由排列的定义,显然不同的顺序是一个不同的排列。从n个元素中取m个元素的所有排列的个数,称为排列数。从n个元素取出n个元素的一个排列,称为一个全排列。全排列的排列数公式为n!,通过乘法原理可以得到。

例如: 输出一组数组的全排列。 [1,2,3] 输出 [1,2,3] [1,3,2] [2,1,3] [2,3,1] [3,1,2] [3,2,1]

  1. function fullPermutation(array $array,array &$result, $index, $end)
  2. {
  3. //递归最重要的是输出或者结束条件,当index到达最后,说明所有值以交换一遍
  4. if ($index == $end) {
  5. $result[] = $array;
  6. }
  7. for ($i = $index; $i <= $end; $i++) {
  8. //index位置和i位置交换
  9. list($array[$i], $array[$index]) = array($array[$index], $array[$i]);
  10. fullPermutation($array, $result,$index + 1, $end);
  11. //换回,不影响其他递归的继续
  12. list($array[$i], $array[$index]) = array($array[$index], $array[$i]);
  13. }
  14. }

二十一、什么是 CSRF 攻击 ?XSS 攻击?如何防范?

XSS定义的主语是“脚本”,是一种跨站执行的脚本,也就是javascript脚本,指的是在网站上注入我们的javascript脚本,执行非法操作。
CSRF定义的主语是”请求“,是一种跨站的伪造的请求,指的是跨站伪造用户的请求,模拟用户的操作。

防御XSS攻击可以通过以下两方面操作:

  1. 对用户表单输入的数据进行过滤,对javascript代码进行转义,然后再存入数据库;
  2. 在信息的展示页面,也要进行转义,防止javascript在页面上执行。

CSRF攻击的防御可以通过以下两方面操作:

  1. 所有需要用户登录之后才能执行的操作属于重要操作,这些操作传递参数应该使用post方式,更加安全;
  2. 为防止跨站请求伪造,我们在某次请求的时候都要带上一个csrf_token参数,用于标识请求来源是否合法,csrf_token参数由系统生成,存储在SESSION中。

二十二、函数内部 static 和 global 关键字的作用

static 是静态变量,在局部函数中存在且只初始化一次,使用过后再次使用会使用上次执行的结果; 作为计数,程序内部缓存,单例模式中都有用到。

global 关键字,引用全局变量,wordpress中大量用到,如面向过程开发。

static 静态方法,是类的成员方法,但不需要实例化类可直接使用

使GLOBAL['a']。

二十三、HTTP协议中 Transfer-Encoding: Chunked 适用于哪些应用场景,这个与使用Content-Length: xxx在收到的报文包上有哪些区别?

一、背景:

  1. 持续连接的问题:对于非持续连接,浏览器可以通过连接是否关闭来界定请求或响应实体的边界;而对于持续连接,这种方法显然不奏效。有时,尽管我已经发送完所有数据,但浏览器并不知道这一点,它无法得知这个打开的连接上是否还会有新数据进来,只能傻傻地等了。
  2. 用Content-length解决:计算实体长度,并通过头部告诉对方。浏览器可以通过 Content-Length 的长度信息,判断出响应实体已结束
  3. Content-length引入的新问题:由于 Content-Length 字段必须真实反映实体长度,但是对于动态生成的内容来说,在内容创建完之前,长度是不可知的。这时候要想准确获取长度,只能开一个足够大的 buffer,等内容全部生成好再计算。但这样做一方面需要更大的内存开销,另一方面也会让客户端等更久。
  4. 我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界——分块编码(Transfer-Encoding: chunked)

二、分块编码(Transfer-Encoding: chunked)

  1. Transfer-Encoding,是一个 HTTP 头部字段(响应头域),字面意思是「传输编码」。最新的 HTTP 规范里,只定义了一种编码传输:分块编码(chunked)。
  2. 分块传输编码(Chunked transfer encoding)是超文本传输协议(HTTP)中的一种数据传输机制,允许HTTP由网页服务器发送给客户端的数据可以分成多个部分。分块传输编码只在HTTP协议1.1版本(HTTP/1.1)中提供。
  3. 数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小。
  4. 具体方法
    1. 在头部加入 Transfer-Encoding: chunked
    2. 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。
    3. 每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的 CRLF(\r\n),也不包括分块数据结尾的 CRLF。
    4. 最后一个分块长度值必须为 0,对应的分块数据没有内容,表示实体结束。
      5
  5. 例:
  1. HTTP/1.1 200 OK
  2. Content-Type: text/plain
  3. Transfer-Encoding: chunked
  4. 25\r\n
  5. This is the data in the first chunk\r\n
  6. 1C\r\n
  7. and this is the second one\r\n
  8. 3\r\n
  9. con\r\n
  10. 8\r\n
  11. sequence\r\n
  12. 0\r\n
  13. \r\n
  1. Content-Encoding 和 Transfer-Encoding 二者经常会结合来用,其实就是针对 Transfer-Encoding 的分块再进行 Content-Encoding压缩。

二十四、描述epoll和poll、select的区别,为什么epoll会具备性能优势?

Poll和Select和Epoll都是事件触发机制,当等待的事件发生就触发进行处理,多用于Linux实现的服务器对客户端连接的处理。

Poll和Select都是这样的机制:可以阻塞地同时探测一组支持非阻塞的IO设备,是否有事件发生(如可读,可写,有高优先级的错误输出,出现错误等等),直至某一个设备触发了事件或者超过了指定的等待时间——也就是它们的职责不是做IO,而是帮助调用者寻找当前就绪的设备。

epoll相关系统调用是在Linux2.5.44开始引入的。该系统调用针对传统的select/poll系统调用的不足,设计上作了很大的改动。select/poll的缺点在于:

1. 每次调用时要重复地从用户态读入参数。
2. 每次调用时要重复地扫描文件描述符。
3. 每次在调用开始时,要把当前进程放入各个文件描述符的等待队列。在调用结束后,又把进程从各个等待队列中删除。

在实际应用中,select/poll监视的文件描述符可能会非常多,如果每次只是返回一小部分,那么,这种情况下select/poll显得不够高效。

epoll的设计思路,是把select/poll单个的操作拆分为1个epoll_create+多个epoll_ctrl+一个wait。此外,内核针对epoll操作添加了一个文件系统”eventpollfs”,每一个或者多个要监视的文件描述符都有一个对应的eventpollfs文件系统的inode节点,主要信息保存在eventpoll结构体中。而被监视的文件的重要信息则保存在epitem结构体中。所以他们是一对多的关系。

由于在执行epoll_create和epoll_ctrl时,已经把用户态的信息保存到内核态了所以之后即使反复地调用epoll_wait,也不会重复地拷贝参数,扫描文件描述符,反复地把当前进程放入/放出等待队列。这样就避免了以上的三个缺点。

select、poll、epoll_wait参数及实现对比

  1. int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

    select的第一个参数nfds为fdset集合中最大描述符值加1,fdset是一个位数组,其大小限制为__FD_SETSIZE(1024),位数组的每一位代表其对应的描述符是否需要被检查。

    select的第二三四个参数表示需要关注读、写、错误事件的文件描述符位数组,这些参数既是输入参数也是输出参数,可能会被内核修改用于标示哪些描述符上发生了关注的事件。所以每次调用select前都需要重新初始化fdset。

    timeout参数为超时时间,该结构会被内核修改,其值为超时剩余的时间。

    select对应于内核中的sys_select调用,sys_select首先将第二三四个参数指向的fd_set拷贝到内核,然后对每个被SET的描述符调用进行poll,并记录在临时结果中(fdset),如果有事件发生,select会将临时结果写到用户空间并返回;当轮询一遍后没有任何事件发生时,如果指定了超时时间,则select会睡眠到超时,睡眠结束后再进行一次轮询,并将临时结果写到用户空间,然后返回。

    select返回后,需要逐一检查关注的描述符是否被SET(事件是否发生)。

  2. poll与select不同,通过一个pollfd数组向内核传递需要关注的事件,故没有描述符个数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组只需要被初始化一次。

    poll的实现机制与select类似,其对应内核中的sys_poll,只不过poll向内核传递pollfd数组,然后对pollfd中的每个描述符进行poll,相比处理fdset来说,poll效率更高。

    poll返回后,需要对pollfd中的每个元素检查其revents值,来得指事件是否发生。

  3. epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

    epoll通过epoll_create创建一个用于epoll轮询的描述符,通过epoll_ctl添加/修改/删除事件,通过epoll_wait检查事件,epoll_wait的第二个参数用于存放结果。

    epoll与select、poll不同,首先,其不用每次调用都向内核拷贝事件描述信息,在第一次调用后,事件信息就会与对应的epoll描述符关联起来。另外epoll不是通过轮询,而是通过在等待的描述符上注册回调函数,当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。

    epoll返回后,该参数指向的缓冲区中即为发生的事件,对缓冲区中每个元素进行处理即可,而不需要像poll、select那样进行轮询检查。

二十五、PHP中常用的几个预定义的全局数组是哪些?

有9大预处理的内置数组变量 $_GET$_POST$_REQUSET$_SESSION$_COOKIE$_FILES$_SERVER$_ENV$GLOBALS

二十六、 对于大流量的网站,你采用什么样的方法支持当前流量?

(1)确认服务器硬件是否支持当前的流量。
(2)优化数据表。
(3)控制外部的盗链。
(4)控制文件的下载。
(5)使用不同主机分流主要流量。
(6)数据库读写分离。

二十七、PHP常见的魔术方法

__construct() 实例化类时自动调用。
__destruct() 类对象使用结束时自动调用。
__set() 在给未定义的属性赋值的时候调用。
__get() 调用未定义的属性时候调用。
__isset() 使用isset()或empty()函数时候会调用。
__unset() 使用unset()时候会调用。
__sleep() 使用serialize序列化时候调用。
__wakeup() 使用unserialize反序列化的时候调用。
__call() 调用一个不存在的方法的时候调用。
__callStatic()调用一个不存在的静态方法是调用。
__toString() 把对象转换成字符串的时候会调用。比如 echo。
__invoke() 当尝试把对象当方法调用时调用。
__set_state() 当使用var_export()函数时候调用。接受一个数组参数。
__clone() 当使用clone复制一个对象时候调用。

二十八、MySQL事物隔离级别

SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

Read Uncommitted(读取未提交内容)

   在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

Repeatable Read(可重读)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

**Serializable(可串行化) **

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

这四种隔离级别采取不同的锁类型来实现,若读取的是同一个数据的话,就容易发生问题。例如:

脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

在MySQL中,实现了这四种隔离级别,分别有可能产生问题如下所示:

此处输入图片的描述

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注