PHP面试/笔试题杂记 VII

一个PHP高级工程师 需要具备哪些知识?

准备

    1. 自我介绍: 说上家公司负责的项目或者浓缩简历。
    1. 简历项目经验层次
    • 利用什么技术
    • 实现了什么功能
    • 遇到了什么问题
    • 达到了什么结果
    1. 期望薪资
    1. 应用型的问题:要记得站高看远、架构分层
    1. 及管理经验及自身对项目管理的理解

知识点列表

PHP

  • 代码解释过程(大多的非编译语言)
    • lexical 词法分析, 输入为源代码, 输出为token
    • 语法分析工具为文法(LALR), 输出为表达式, 7.0为AST, 涉及
      • 注释
      • 分号 & 分隔符
      • 变量
      • 常量
      • 操作数
    • 类型检查、关键字处理、导入, 输出为中间代码.工具为选定的编译器优化工具
      • 中间代码生成(Opcodes)
      • 机器码生成 (编译语言)
  • Session共享配置
  • PHPUnit用法
  • Cookie购物车和Session购物车的实现
  • 弱类型实现
    • zval(不仅是变量名) & zend_val 变量值
  • 代码规范
    • 自动化: sonarquebe+jenkins
    • 单元测试
  • PHP进程间如何通信
    • 信号量(消息同步|互斥)
    • 信号(信号触发事件)(pcntl_signal, pcntl_wait*)
    • 消息队列(msg_*)
    • 管道(pipe)
    • socket | unix_*.sock
    • 共享内存(shm_,shmop_)
  • PHP并发模型
  • PHP执行流程
  • 变量底层存储结构
  • 常用的数组函数(列出10个)
    • array_combine(前面数组作为其键,后面数组做为其值)
    • array_merge(合并两个数组,后面覆盖前面,但数字索引会重新索引,不会覆盖)
  • PHP垃圾回收机制(gc)
    • zend.enable_gc (php.ini)
    • gc_enable() (function)
    • 引入计数(zval指向zend_value个数为0时) + 写时拷贝 (copy on write)
    • 循环引用问题 (arrayobject引用自身成员), 垃圾回收器将收集于一个buffer(_zend_gc_global -> gc_root_buffer) 后启动垃圾鉴定程序
  • Session放入Redis里面还会触发类似文件的state session
    • session.gc_probability (default 1)
    • session.gc_divisor (default 100)
    • session.gc_maxlifetime (单位秒)
    • session.cookie_lifetime (单位秒, 0表示直到关闭浏览器)
    • session.save_path
    • session.write_close (显示关闭, 后期使用需要显示开启)
  • 内存模型
    • 整型、浮点、boolNULL、内部字符串、不可变数组都是通过zval直接保存,不会用到引用计数
    • stringarray都会使用引入计数(支持复制cow), objectresource本身可以理解为引用
  • fpm三种配置及场景
    • dynamic
      • pm.start_servers
      • pm.max_children
      • pm.max_spare_servers
      • pm.min_spare_servers
    • static
      • pm.max_children
    • ondaemon
      • pm.process_idle_timeout
  • 数组底层
    • 如何保证有序: 又加了一层映射表 (与bucket大小相同)
    • 如何解决hash冲突: 拉链法(头插)
    • 扩容: 逻辑删除 (考虑unset内存情况, 是否需要重建索引)

MySQL

  • 索引
    • 物理存储
      • 聚簇索引
      • 非聚簇索引
    • 数据结构
      • B + 树
      • hash
      • fulltext
      • R-tree
    • 逻辑角度
      • 唯一索引 unique
      • 普通索引 index
      • 主键索引 primary key
      • 全文索引 full index (myisam)
      • 复合索引 (最左前缀原则)
        • 类似 where a and b and c a b c 问题
        • 联合索引(a,b,c) 能够正确使用索引的有 (a=1), (a=1 and b=1), (a=1 and b=1 and c=1) (b=1 and c =1)
  • 引擎类型
    • myisam
    • innodb
    • 区别
        1. myisam采用非聚簇索引, innodb采用聚簇索引
        1. myisam索引myi与数据myd文件分离, 索引文件仅保存数据记录指针地址
        1. myisam的主索引与辅助索引在结构上没区别, 而innodb不一样: innodb的所有辅助索引都引用主索引为data域
        1. innodb支持事务, 行级锁, myisam不行
        1. innodb必须有主键, 而myisam可以没有
  • 事务
    • 原子性 atomicity
    • 一致性 consistency
    • 隔离性 isolation
    • 持久性 durability
  • 分表数量级
    • 单表在500w左右, 性能最佳, BTREE索引树在3-5之间
  • 隔离级别
    • 事务的隔离性是数据库处理数据的基础之一,隔离级别是提供给用户在性能和可靠性做除选择和权衡的配置项目,以下四种情况都有一个前提(在同一个事务中)
    • read_uncommited: 未提交读(脏读)
    • read_commit: 已提交读 (不可重复读)
    • repeatable_read: 可重复读
    • serialize: 可串行化
  • 索引机制(算法)
    • hash
    • b+tree
    • b-tree(不要念成b减tree,-只是个符号)
    • 种类
      • optimistic lock 乐观锁
        • 特点: 不会真的死锁
      • pessimistic lock 悲观锁
        • 为了保证事务的隔离性, 就需要一致性锁定读.读的时候要加锁,防止其他事务再次更改,修改的时候也要加锁,其他事务无法读取。主要就是依靠数据库的锁机制来实现,同时缺点很明显,就是会带来性能的开销,并发的减少
    • innodb的MVCC(Multi-Version Concurrency Control)
      • 多版本并发控制, 适用于行锁的、事务性的数据库模型
      • 适用于innodb的rcrr级别, 因为可串行化涉及到锁表
      • 实现思想是在每行增加一个create_verisondelete_version字段
      • update 是插入一个新行,先保存当前版本号到旧行的delete_version,且新建行的new_create_version也就是delete_version
      • delete操作就是直接标记delete_version
      • insert的时候,就是保存至create_version
      • select的时候可以这样
        • delete_version为空的
        • 大于当前事务版本号的
        • 创建版本号 <= 当前事务版本号的
    • 粒度划分
      • 行锁
      • 表锁
    • 意向锁 intention lock (表级锁)
      • 场景:A对表中一行进行修改,B对整个表修改。如果没有以下的两个锁,B将对全表扫描是否被锁定。反之,A可以对某行添加意向互斥锁(表级),然后再添加互斥锁(行级),然后B只需要等待意向互斥锁释放)
      • 意向共享锁
      • 意向互斥锁
    • 共享锁 shard lock 读锁(行锁)
    • 排它锁 exclusive lock 写锁(行锁)
    • 关于innodb必须要知道的
      • 可以通过 SELECT \* FROM products WHERE id='3' FOR UPDATE 进行锁,但是必须在事务中
      • 上述语句必须是命中索引才会行锁,否则是 table lock
    • 锁的算法
      • record lock:加到索引记录上的锁,如果通过where条件上锁,而不知道具体哪行,这样会锁定整个表
      • gap lock:间隙锁某个区间的锁定,对索引记录中的一段连续区域的锁。
      • next-key lock:行锁和GAP(间隙锁)的合并,next-key锁是解决RR级别中 幻读问题的主要方案。可以搜索关键字 快照读(snapshot read)和当前读(current read)去了解
    • 分库分表
    • 主从复制 读写分离
    • ACID
    • 覆盖索引(复合索引)
      • 定义:包含两个或多个属性列的索引称为复合索引。如果查询字段是普通索引,或者是联合索引的最左原则字段,查询结果是联合索引的字段或者是主键。这种就不必通过主键(聚集索引再次查询)
      • 目的: 减少磁盘IO, 不用回表
      • b+树索引

Nginx

  • Nginx
    • worker_connections
    • upstream weight
    • 负责均衡实现方式
      • 轮询
      • IP哈希
      • 指定权重
      • 第三方
        • fair
        • url_hash

Linux

  • Linux
    • epoll select
    • netstat 查看tcp udp unixsock网络
    • 查看负载 cat /proc/loadavg | w | top
    • df
    • lstat: strace的时候常常可见它
    • top: shift+M
    • free
    • lsof: 查看当前进程ID, 进程名等占用的文件描述符
    • ipstat
    • strace
    • grep [-A ,-B, -C]'HTTP/1.1" 200' access.log | wc -l
    • socket和管道(pipe)的区别: socket全双工, pipe半双工*2
    • awk & sed
      • awk '{print $1}' access.log | sort | uniq | wc -l

Redis

  • 类型/应用场景
    • string: cache, incr
    • hash: key为key, value为Hashmap
    • set: 去重(中奖只一次 sismember), 交/并/差 (如微博社交关系), 内部实现为value为null的hashmap
    • zset: (sorted set), 既去重又能保证按照score排序, 比如按照帖子的关注个数排序,value为帖子id,个数为score。
    • list: (阻塞rpop) 消息队列、列表旋转(常用于监控, rpoplpush)
    • HyperLogLog: 大量统计 (非精确)
    • bitmaps
  • OBJ_ENCODING
    • string
      • OBJ_ENCODING_RAW, 代表sds, 原生string类型
      • OBJ_ENCODING_INT, long类型
      • OBJ_ENCODING_EMBSTR, 嵌入
    • OBJ_HASH
      • OBJ_ENCODING_HT, 表示成dict
      • OBJ_ENCODING_ZPILIST, hash用ziplist表示
    • OBJ_SET
      • OBJ_ENCODING_INTSET, 表示成intest
      • config
        • set-max-intset-entries 512
    • OBJ_ZSET
      • OBJ_ENCODING_SKIPLIST, 表示成skiplist
    • OBJ_LIST
      • OBJ_ENCODING_QUICKLIST
      • config
        • list-max-ziplist-size-2
        • list-compress-depth 0
  • 扩展问题
    • zset如何根据两个属性排序? 比如根据id和age
      • 可以用位操作, 把两个属性合成一个double
      • 用zunionstore合并存储为新的key, 再zrange
    • redis是如何保证原子性操作的?
      • 因为它是单线程的! (MySQL是多线程)
      • 在并发脚本中的 get set 等不是原子的
      • 在并发的原子命令 incr setnx 等是原子的
      • 事务是保证批量操作的原子性
    • 主从复制过程
      • 从服务器向主服务器发送sync
      • 主服务器收到sync命令执行BGSAVE, 且在这期间新执行的命令到一个缓冲区
      • 主执行(BGSAVE) 完毕后,将.rdb文件发送给从服务器, 从服务器将文件载入内存
      • BGSAVE期间到缓冲区的命令会以redis命令协议的方式, 将内容发送给从服务器
  • 特性
    • 单线程, 自实现(event driver库, 见下面四个io多路复用函数)
    • io多路复用, 最常用调用函数: select (epoll, kquene, avport等), 同时监控多个文件描述符的可读可写
    • reactor方式实现文件处理器 (每一个网络连接对应一个文件描述符), 同时监听多个fd的accept, read (from client), write (to client), close文件事件
  • 备份与持久化
    • rdb (fork进程dump到file)
      • 手动: save(阻塞) & bgsave (fork 子进程), 但是这两个不会同时进行
      • 自动触发: conf:save 900 1 save 300 10 save 60 10000 dbfilename dump.rdb
      • rdb优点: 对服务进程影响小, 记录原数据文件方式便于管理还原
      • rdb缺点: 可能数据不完整
      • rdb为纯文本文件, 可以用 od -c dump.rdb 分析
    • aof (类似binlog)
      • 三种写入同步方式
        • appendfsync no
        • appendfsync everysec (每个事件循环写入缓冲区, 但是每隔一秒同步到磁盘文件)
        • appendfsync always (每执行一个命令, 每个事件循环都会执行写入aof 缓冲区并同步到磁盘文件,效率最慢,但是最安全)
      • aof优点: 数据最完整, 可以通过数据重写rewrite来减少体积, 存储内存为redis的纯文本协议
      • aof缺点: 文件相对rdb更大, 导入速度比rdb慢
      • 一般有了aof就不rdb, 因为aof更新频率更高
  • 过期策略
    • 定时过期: 时间到了立即删除, cpu不友好, 内存友好
    • 惰性过期: 访问时判断是否过期, cpu友好, 内存不友好
    • 定期过期: expires dict中scan, 清除已过期的key, cpu和内存最优解
  • 内存淘汰机制
    • noeviction: 新写入时会报错
    • allkeys-lru: 移除最近最少使用的key
    • allkeys-random: 随机移除某些key
    • volatile-lru: 设置了过期时间的key中, 移除最近最少使用
    • volatile-random: 随机移除某些key
    • volatile-ttl: 设置类过期时间的键中, 有更早过期时间的key优先移除
  • redis队列特殊关注之处
    • 队列可能丢东西
      • 比如redis挂了, producer没有停止, 但是队列数据无法写入
    • 队列的consumer需要手动处理commit协议
      • 如果consumer处理完, 表示真正完成
      • 如果没有处理完?放回队列?直接丢弃?
    • 事件重放机制不支持
      • 比如consumer消费错了, 那能不能将队列回放呢?再次处理呢?
    • 队列最大长度及过期时间
      • 如果producer远大于consumer, 撑爆了怎么办
      • 如果comsumer一直没有处理, producer的数据如何处理
    • exactly once
    • 单机锁setnx或者基于set众多参数没有问题, 集群下可利用tag机制
    • 如何保证业务执行时间超过锁的过期时间, 而引起误删除操作, 答案是可以加一个唯一标识
  • vs memcache
    • memcached
      • 优势
        • 多线程(listen & worker), 利用多核
        • round robin
        • cas (check and set, compare and swap)
      • 劣势
        • cache coherency、 锁
        • key大小有限制 (1M)
      • 特点
        • 内存预分配: slab + trunk
    • redis
      • 优势
        • 自己封装了一个AEEvent (epoll + select + kqueue), io多路复用
        • 丰富的数据结构 (对内 + 对外)
        • 良好的持久化策略 (rdb + aof)
      • 劣势
        • 排序、聚会cpu密集操作会影响吞吐量
        • key 大小最大为 1g
  • redis的事务机制
    • 基于乐观锁的watch multi exec
    • redis call lua 脚本 (比如get+del一起)
    • 2.6.12后set命令支持(setnx + expire就不需要写lua script了)
    • redis 下的分布式锁,当主从不同步或者主重新被选举需要多想想,主从情况下一般采用从节点的大多数 (es也是这样)
  • redis 主从哨兵配置,copy三份redis.conf文件,以下设置一主二从一哨兵
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    vim redis01.conf
    port 63791

    vim redis02.conf
    port 63792
    slaveof 127.0.0.1 63791

    vim redis03.conf
    port 63793
    slaveof 127.0.0.1 63791

    vim sentinel.conf
    daemonize yes
    port 26379
    sentinel monitor mymaster 127.0.0.1 63791 1
    // mymaster为自定义命名,127.0.0.1 63791为master,1为选举主节点的时候投票数目的同意个数,1代表有一个哨兵同意就行。
  • redis cluster
    • 集群会将数据自动按照算法分割在不同节点负责的槽上 (data sharding)

算法&数据结构

  • 最小堆: 根节点为最小值, 且节点比其他子节点小
  • 平衡树 (avl红黑树)
  • 最大堆: 根节点为最大值, 且节点比其他子节点大
  • sikplist
  • hash
    • hash 碰撞原因
    • hash 碰撞解决方案
      • 拉链, 塞到链表里. 有点是相对简单, 但是需要附加空间
      • 开发寻址, 有点是空间利用率高, 一直找
        • 线性探测
        • 二次探测再散列函数
        • 伪随机数
  • 给定数组n, 判断n是斐波那契数列的第几项? 写算法
  • 反转列表如A->B->C->D 到 A->D->C->B
  • 插入排序
  • 数组与链表区别与联系
  • 链表操作
    • 单链表删除
    • 单链表插入
      • 快慢指针判断环路、找链表中点
  • 应用问题
    • 如何实现一个LRU功能 (双向链表)
    • 如何实现浏览器前进后退功能 (两个栈)

网络协议

  • HTTP
    • 构成: 起始行(Get=>200), 首部头(Accept=>Content-Type), 主体(name => tongbo)
    • 版本
      • 1.0
      • 1.1
      • 2.0 多路复用, 流量控制
  • 长连接
    • 在一个连接上发送多个数据包
    • 心跳、如何发送心跳
  • HTTP DNS
    • 定义: 用http协议代替元素的udp dns协议, 可以绕过运营商的local dns
    • 解决问题: 避免local dns造成的域名劫持问题和调度不精确问题
    • 其他解决方案
      • 客户端dns缓存
      • 热点域名解析
      • 懒更新策略 (ttl过期后再同步)
  • Post请求分割head和body
  • Get vs Post
    • Get
      • 安全幂等, 请求实体资源
      • 参数只能url编码, 且参数长度有限制
      • 浏览器会自动加cache
    • Post
      • 附加请求实体于服务器
      • 产生两个TCP数据包
      • 数据支持多种编码格式
  • Resultful
    • get: 获取资源
    • post: 新建资源
    • put: 跟新完整资源
    • delete: 删除资源
    • patch: 更新部分资源
  • RPC
    • RPC框架涉及基本组件服务
      • 客户端、服务端自动代码生成、多语言支持
      • 消息序列化、反序列化
      • 连接池、负载、故障、队列、超时、异步
    • 常见协议
      • soap (http jsonrpc)
      • GRPC
      • thrift(tcp)
  • TCP
    • 面向连接, 先建立(握手), 然后释放(挥手确认拜拜)
    • 只能点对点
    • 可靠交付, 全双工, 接收和发送端都设有发送和接收cache
    • 面向字节流
  • 特性协议
    • 停等
    • 超时重传
    • 慢启动
    • 滑动窗口
    • 快速重传
  • UDP
    • 无连接、best effort、面向报文(不合并、不拆分、保留边界)
    • 无拥塞控制、流量控制、首部开销小(8个字节, 而TCP有20个首部)
    • 支持一对一、一对多、多对一
  • 自定义协议

PHP框架

  • laravel
    • AppServerProvider register:服务提供者注册
    • IocContainer: (IoC容器, 工厂模式的升华)
      • 控制反转 (inversion of control) 可以降低计算机代码直接的耦合, 其中最常见的方式叫做依赖注入 (Dependence Injection), 还有一种方式叫依赖查找
      • 实现方式
        • 基于接口: 实现特定接口以供外部容器注入所依赖类型的对象
        • 基于Set方法:
        • 基于构造函数: 实现特定参数的构造函数
      • 管理类依赖
      • 执行 (依赖注入DI): 通过构造函数或者某些情况下通过Setter方法将类依赖注入到类中, 容器并不需要被告知如何构建对象,因为他会使用PHP的反射服务自动解析出具体的对象
  • swoole
  • thinkphp
  • ci
  • yii
  • easyswoole

操作系统

  • 操作系统
    • 多线程
    • 多进程
    • 协程的理解
    • socket和管道的区别
    • 进程间通信手段
      • 共享内存
      • rpc
      • 管道
    • 线程间通信手段
      • 读写进程数据段

设计模式

  • 单例模式 (static, construct)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

static private $instance;

private $config;

private function __construct($config)
{
$this->config = $config;
}

static public function instance($config)
{
if (! self::$instance instanceof self) {
self::$instance = new self($config);
}

return self::$instance;
}
  • 简单工厂 (switch case include new return)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php

{
public function makeModule($moduleName, $options)
{
switch ($moduleName) {
case 'Fight':
return new Fight($options[0], $options[1]);
case 'Force':
return new Force($options[0]);
case 'Shot':
return new Shot($options[0], $options[1], $options[2]);
}
}
}

# 使用工厂方式 001
class Superman
{
protected $power;

public function __construct()
{
// 初始化工厂
$factory = new SuperModuleFactory;

// 通过工厂提供的方法制造需要的模块
$this->power = $factory->makeModule('Fight', [9, 100]);
// $this->power = $factory->makeModule('Force', [45]);
// $this->power = $factory->makeModule('Shot', [99, 50, 2]);
/*
$this->power = array(
$factory->makeModule('Force', [45]),
$factory->makeModule('Shot', [99, 50, 2])
);
*/
}
}

# 使用工厂方式 002
class Superman
{
protected $power;

public function __construct(array $modules)
{
// 初始化工厂
$factory = new SuperModuleFactory;

// 通过工厂提供的方法制造需要的模块
foreach ($modules as $moduleName => $moduleOptions){
$this->power[] = $factory->makeModule($moduleName, $moduleOptions);
}
}
}
// 创建超人
$superman = new Superman([
'Fight' => [9, 100],
'Shot' => [99, 50, 2
];
  • 门面模式
    • 对客户屏蔽子系统组件, 减少子系统与客户之间的松耦合关系
  • 依赖注入(DI) 和 AOP思想

大前端

  • js
    • 百度统计的实现
      • 基于cookie, 引入js脚本及百度个人账户ID, 读取当前信息, 适当节点发送请求给百度服务
    • 前后端分离

运维

  • 运维&架构
    • 服务器CPU99%如何分析
    • MySQL占CPU如何分析
    • PHP占CPU较高如何分析
    • SSO实现方法
    • MySQL优化方法
    • 如何提高监测数据的准确性
    • Dcoker原理及引用及编排管理

分布式|微服务

  • 分布式
    • redis分布式锁问题
    • cap及常见应用关注cap哪两点
  • 微服务
    • 优点
      • 相对于单体服务更简单, 注重单一功能
      • 每个服务可以独立开发打包测试部署, 且语言环境无关
      • 可以水平、高效扩展
    • 缺点
      • 运营成本, 服务发现, 治理, 降级, 熔断
      • 网络信息传输、安全、延迟
      • 服务调用排查追踪
    • 最佳原则
      • 高内聚: 修改了一个功能, 只需要改一个服务
      • 低耦合: 修改了一个地方, 不需要改其他的地方
      • 业务内原则
        • 新服务用新的微服务, 确定无误后保留推进, 否则调整
        • 老的保留, 直到新服务稳定再切换
        • 必须的监控与日志 | 生产 - 订阅 - 消费模型
        • 尝试对外不可见的服务先做是试点, 错误邮件, 日志, 系统内调用, API内部分成熟接口
  • 考虑问题
    • 分布式数据一致性问题?CAP如何权衡
    • 调用链追踪(基于OpenTracing协议的JeagerORZipkin)

其他

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2023 Keep It Simple And Stupid All Rights Reserved.

访客数 : | 访问量 :