1、觅蓝
1、数据库索引,复合索引,唯一索引,哪些地方用到?
建立索引:
格式:
create index 索引名 on 表名 (列名);
例子:
create index index_name on users (name);
复合索引:
- 单一索引是指索引列为一列的情况,即新建索引的语句只实施在一列上;
- 用户可以在多个列上建立索引,这种索引叫做复合索引(组合索引);
- 复合索引在数据库操作期间所需的开销更小,可以代替多个单一索引;
参考:复合索引的优点和注意事项
唯一索引:
Unique Index 就是额外添加的唯一性约束。该约束严格的保证索引列的取值唯一性。例如:用户邮箱、用户的手机号需要唯一索引
聚簇索引:
聚簇索引的索引顺序与数据库的物理存储顺序一致,并且一个表只能有唯一的一条聚簇索引。
非聚簇索引:
非聚簇索引的索引顺序与数据库的物理存储顺序没有必然联系,一个表可以有多个非聚簇索引。
用到索引的地方:
- 根据商品的分类来查询商品时,在商品表里面的商品分类字段建立索引
- 根据商品的发布时间来查询商品
根据订单的状态来查询订单时(在出现大量重复值的字段建立索引将不会有什么帮助;相反,还有可能降低数据库的性能)- 根据文章的分类来查询文章时,在文章表里面的文章分类字段建立索引
- 根据文章的发布时间来查询文章时
用到复合索引的地方:
- 用户登录时,在数据库中查询账号和密码,用到复合索引。
- 同时根据商品的分类和商品的发布时间来查询商品
- 同时根据文章的分类和文章的发布时间来查询文章
2、数据库 char,varchar 的区别
char 的长度是固定的。而 varchar 的长度是自动调整的,也就是说,定义一个 char(10) 和 varchar(10),如果存进去的是 ‘csdn’,那么 char 所占的长度依然为 10,除了字符 ‘csdn’ 外,后面跟六个空格,而 varchar 就立马把长度变为 4 了,取数据的时候,char 类型的要用 trim() 去掉多余的空格,而 varchar 是不需要的,尽管如此,char 的存取速度还是要比 varchar 要快得多,因为其长度固定,方便程序的存储与查找;但是 char 也为此付出的是空间的代价,因为其长度固定,所以难免会有多余的空格占位符占据空间,可谓是以空间换取时间效率,而 varchar 是以空间效率为首位的。
例如:
- 存储用户的手机号,用 char(11)
- 存储用户的真实姓名,用 char(10)
- 邮政编码,用 char(6)
3、各种数据库引擎的区别
- ISAM:查询速度快
- MyISAM:在 ISAM 的基础上增加了索引功能
- InnoDB:支持事务、支持外键。查询速度比前面两个引擎慢
在使用 MySQL 的时候,你所面对的每一个挑战几乎都源于 ISAM 和 MyISAM 数据库引擎不支持事务处理(transaction process)也不支持外来键。尽管要比 ISAM 和 MyISAM 引擎慢很多,但是InnoDB包括了对事务处理和外键的支持,这两点都是前两个引擎所没有的。
MyISAM与InnoDB的区别
InnoDB 和 MyISAM 基本的差别为:MyISAM 类型的表强调的是性能,其执行速度比 InnoDB 类型更快,但是不支持事务、外键等功能,而 InnoDB 支持事务、外键等功能。
MyISAM适合:
- 做很多 count 的计算
- 查询非常频繁、没有事务
- 例如:用户表、文章表、文章分类表
InnoDB适合:
- 要求事务、外键的地方
- 例如:订单表,订单的创建和订单的取消,需要用到事务
4、用到数据库事务的地方
很多地方都用到事务,数据的增删改都需要用到事务。
例如:
- 例如订单的创建、订单的取消。
- 生成订单、更新库存,如果失败了,需要回滚事务。
5、数据库的优化
- 使用索引
- 使用 char
- 适当的使用数据库引擎
6、数据库保存手机号用什么类型的字段
用 char(11)
7、Redis 有哪些数据类型?
- string
- list
- set
- zset (sorted set)
- hash (field和value的映射表,适合用来存储对象)
8、项目中哪些数据保存在 Redis ?
例如商品分类这种经常被查询又不经常改变的数据,保存在 Redis
9、ThinkPHP5 安全相关措施
- 预防 SQL 注入:使用 addslashes 函数(将
'
转为\'
,将"
转为\"
) - 预防 XSS:使用 htmlspecialchars 函数将脚本标签的小于号转为
<
大于号转为>
- 预防 CSRF:from 表单提交数据,使用 csrf token 验证
- public 目录是唯一的对外访问的目录
- 使用 TP 框架提供的 Request 类的 param 方法或者使用 input 助手函数来获取请求参数,而不是原生系统变量获取用户输入的数据
参考:官方文档-输入变量
10、秒杀用 Redis 队列?原子性
假设有10件库存,就往队列中push10个数。抢购开始后,每到来一个用户,就从队列中pop一个数,表示用户抢购成功。当列表为空时,表示已经被抢光了。因为 Redis 列表的pop操作是原子的,即使有很多用户同时到达,也是依次执行的。
上面的会导致一个用户抢多个,思路:
需要一个排队队列(比如:queue:1,以user_id为值的列表)和抢购结果队列(比如:order:1,以user_id为值的列表)及库存队列(比如上面的goods_store:1)。高并发情况,先将用户进入排队队列,用一个线程循环处理从排队队列取出一个用户,判断用户是否已在抢购结果队列,如果在则已抢购,否则未抢购,接着执行库存减1,写入数据库,将此user_id用户同时也进入结果队列。
11、PHP 用存储过程会出现的问题
优点:
- 执行速度快
- 减少网络传输
缺点:
- 调试不方便
- 不能应用缓存
- 不能分库,存储过程不知道数据保存在哪个库中
2、好品汇
1、索引是什么?主键和唯一索引的区别?索引的缺点?
索引就相当于一本书的目录,可以加快数据库的查询速度。
一张表只能有一个主键,但可以有多个唯一索引。
缺点:
- 索引在加快查询速度的同时也让插入和更新数据变慢,因为插入和更新数据时要修改索引结构
- 索引使用不当,有可能让速度变慢。例如:数据量很少的表就不用建立索引;
- 重复数据较多的字段就不应该建立索引,否则不仅没有加快查询速度,反而有可能让速度变慢。
2、什么是跨域?怎么解决跨域问题?
跨域是浏览器的一种安全机制(同源策略),是指网站的前端 javascript 禁止请求 不同域名、不同端口、不同协议的链接。
解决:
- 通过 jsonp 跨域 (原理:在页面上引用跨域的 js 脚本文件)
- 通过 php 等后端语言跨域
3、php 实现一个双向队列
class Deque {
private $queue=array();
function addFirst($item){ //头入队
return array_unshift($this->queue, $item);
}
function removeFirst(){ //头出队
return array_shift($this->queue);
}
function addLast($item){ //尾入队
return array_push($this->queue, $item);
}
function removeLast(){ //尾出队
return array_pop($this->queue);
}
function show(){ //显示
echo implode(" ", $this->queue);
}
function clear(){ //清空
unset($this->queue);
}
function getFirst(){
return array_shift($this->queue);
}
function getLast(){
return array_pop($this->queue);
}
function getLength(){
return count($this->queue);
}
}
4、写出工作中遇到的一个难搞的 Bug,怎么解决?
Laravel 打开路由信息缓存时(php artisan route:cache
),报错
解决:
- 打开路由信息缓存之后,路由文件里面不能有闭包的形式,要用控制器的形式
- 排查路由文件中的重复路由并修改。尤其要注意
resource
方法很可能导致与其他路由重复。
5、消息队列 RabbitMQ 的相关概念,初始化配置
- Broker:简单来说就是消息队列服务器实体。
- Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
- Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
- Binding:绑定,它的作用就是把 exchange 和 queue 按照路由规则绑定起来。
- Routing Key:路由关键字,exchange 根据这个关键字进行消息投递。
- vhost:虚拟主机,一个 broker 里可以开设多个 vhost,用作不同用户的权限分离。
- producer:消息生产者,就是投递消息的程序。
- consumer:消息消费者,就是接受消息的程序。
- channel:消息通道,在客户端的每个连接里,可建立多个 channel,每个 channel 代表一个会话任务。
消息队列的使用过程大概如下:
- 客户端连接到 Broker 消息队列服务器,打开一个 channel。
- 客户端声明一个exchange,并设置相关属性。
- 客户端声明一个queue,并设置相关属性。
- 在exchange和queue之间建立好绑定关系。
- 客户端投递消息到exchange。
- exchange接收到消息后,就根据消息的key和已经设置好的绑定,进行消息路由。
消息队列的持久化包括:
- exchange持久化,在声明时指定durable => 1
- queue持久化,在声明时指定durable => 1
- 消息持久化,在投递时指定delivery_mode => 2(1是非持久化)
如果exchange和queue都是持久化的,那么它们之间的绑定也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。
6、RabbitMQ 消息队列的应用场景、使用注意
应用场景:
- 对实时性要求不高、并且比较耗时的操作,放到消息队列里
- 用户注册时,发送注册短信、发送激活邮件 的消息放到消息队列里,异步处理
- 生成订单之后,更新库存的消息放到消息队列里
使用注意:
- 要把 no_ack 设置为 false,这样在接收到处理成功的 ack 通知之后才把消息队列里面的消息删除
- 消息队列 要设置持久化,防止丢失数据
参考:
初识RabbitMQ,附RabbitMQ+PHP演示实例
RabbitMq应用一的补充(RabbitMQ的应用场景)
7、kafka、RabbitMQ、ActiveMQ 的区别
- kafka 的性能比较高、但是有可能会丢数据,所以在稳定性方面有所欠缺
- RabbitMQ 比较重量级、支持多种网络协议、支持数据持久化
- ActiveMQ 跟 RabbitMQ 类似,Java 语言用它用的比较多
8、Redis 秒杀 ,商品库存队列用一个 String 存一个数字,通过数字的递减来减少库存
思路:查询 Redis 中是否有保存库存,如果有就直接更新 Redis。如果 Redis 没有,才去数据库中取出库存并缓存到 Redis 中。
public function buy($goods_id = 0){
if(!$goods_id){
die("商品不存在!");
}
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$stock = 0;
if(!$redis->get("gid".$goods_id)){
$stock = get_stock($goods_id); // 从数据库获取实际库存
$redis->set("gid".$goods_id, $stock);
}else{
$stock = $redis->get("gid".$goods_id);
}
if($stock > 0){
//逻辑操作(生成订单等操作)
//coding here...
// 更新库存
$redis->set("gid".$goods_id, $stock-1);
}else{
die("已卖完!");
}
}
对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。
增加库存、减少库存的 Redis 命令:
INCR key
将 key 中储存的数字值增一。DECR key
将 key 中储存的数字值减一。
9、Redis 的 List 数据类型是链表结构?
在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。
从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是非常低效的。
10、Redis 有哪些数据类型?
- String
- List
- Set
- Sorted Set
- Hash
11、Memcached 和 Redis 的区别?
- Memcached 相对来说速度比较快,但只能存储 key / value 类型
- Redis 支持多种数据类型的存储:String、List、Set、Sorted Set、Hash
- Memcached 是存储在内存中的,如果机器断电,数据就会丢失
- Redis 的数据是可以持久化到硬盘中的
12、数据库引擎 MyISAM 为什么比 InnoDB 快?MyISAM 的存储结构?
- ISAM:查询速度快
- MyISAM:在 ISAM 的基础上增加了索引功能
- InnoDB:支持事务、支持外键。查询速度比前面两个引擎慢
MyISAM 为什么比 InnoDB 快?
- InnoDB 要缓存整个数据块,MyISAM 只缓存索引块
- InnoDB 寻址要映射到数据块,再到行。MyISAM 记录的直接是文件的 offset,定位比较快
- InnoDB 还需要维护 MVCC 一致。MVCC (Multi-Version Concurrency Control)多版本并发控制
13、多进程之间通讯的几种方式
- 管道
- 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端
- 它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)
- 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中
- FIFO(也称为命名管道,它是一种文件类型)
- FIFO可以在无关的进程之间交换数据
- FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中
- 消息队列
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
- 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取
- 信号量(semaphore):与前面介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作
- 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数
- 支持信号量组
- 共享内存(Shared Memory):指两个或多个进程共享一个给定的存储区
- 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取
- 因为多个进程可以同时操作,所以需要进行同步(或加锁)
- 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问
- Socket
参考:进程间的五种通信方式介绍
14、分布式和集群的概念?区别?
- 分布式:将业务拆分,不同的业务部署在不同的机器上
- 分布式的各个模块之间通过接口通信
- 需要做好分布式事务
- 集群:同一个业务,部署在多台机器上
- 集群需要做好 session 共享
- 使用 cookie 来保存 session,从而实现不同服务器之间 session 同步
- session 保存在数据库中
- session 保存在缓存中(Redis 或者 Memcached)
- 集群需要做好 session 共享
15、微服务?
微服务:是将模块拆分成一个独立的服务单元通过接口来实现数据的交互。微服务与分布式的细微差别是,微服务的应用不一定是分散在多个服务器上,它也可以在同一个服务器。
- 微服务框架:PHP-MSF
- Java 微服务框架:Dubbo、Spring Cloud
16、ThinkPHP 和 Laravel 的比较?
- ThinkPHP 的体积比较小,Laravel 的体积比较大
- ThinkPHP 的性能比 Laravel 高(使用 siege 测试,每秒处理数据的速度,ThinkPHP 比 Laravel 快了 4 倍左右)
- Laravel 的路由配置比较方便、比较优雅
- Larave 还提供了中间件(可以实现访问前后的处理)、数据迁移文件等功能
17、Laravel 的中间件 和 过滤器的区别?
Laravel 的中间件和 Java 的过滤器的功能类似,都是可以拦截请求的方法,来做一些前置或者后置操作。
18、Laravel 中间件 的前置处理,后置处理
参考:前置 & 后置中间件
19、Laravel 的 IoC 和 DI ,DI 的实现原理
IoC(控制反转):
- 让 IoC 容器来管理对象的生成和销毁
- 启动容器后,所有对象直接取用,不用手动 new 对象
DI(依赖注入)
- 自动建立对象之间的依赖关系
参考:
PHP程序员如何理解IoC/DI
DI 的实现原理:
- 依赖注入原理其实就是利用类方法反射,取得参数类型,然后利用容器构造好实例。然后再使用回调函数调起。
- 注入对象构造函数不能有参数。否则会报错。Missing argument 1
- 依赖注入故然好,但它必须要由 Router 类调起,否则直接用 new 方式是无法实现注入的。所以这就为什么只有 Controller 、Job 类才能用这个特性了。
20、MySQL 的范式和反范式
- 第一范式(1NF):数据库表中的列具有原子性,不能再分解。只要数据库是关系型数据库(mysql/oracle/db2/informix/sysbase/sql server),就自动的满足1NF。
- 第二范式(2NF):要求数据库表中的每一行具备唯一性。通常需要一个主键来区分它的唯一性
- 第三范式(3NF):要求一张表中不包含已在其它表中的非主键字段。例如商品和商品分类应该设计成两张表,而不是放在一张表里面
反范式:
没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。降低范式就是增加字段,减少了查询时的关联,提高查询效率。
21、Swoole 有哪些功能?
- HTTP Server
- 异步 IO
- 异步 Redis
- 多进程 / 进程池
参考:swoole 官方文档
22、Laravel 中间件实现原理
中间件的基本工作原理:接收 HTTP 请求,让请求经过定义好的路由中间件,可以进行前置处理或者后置处理。
中间件的实现使用了管道。
23、分库分表
垂直拆分:将不同的表拆分到不同的机器上
- 优点:拆分规则明确、数据维护简单
- 缺点:无法通过 join 连接查询、存在单库大数据
水平拆分:将同一张表的数据拆分到不同的机器上
- 优点:可以用 join 连接查询、不存在单库大数据
- 缺点:拆分规则难以抽象、数据维护比较难
- 水平拆分的规则:
- 根据 id 值取余,将余数不同的数据分散到不同的机器上
- 根据特定的范围段分散到不同的库中
- 将不同日期的数据分散到不同的机器上
3、点盛
1、怎样测试一条 SQL 语句的效率(性能)?
- 放入数据库查询一下,如果数据量很大,可以先拿一小部分数据来测试。
- 使用 explain
2、支付宝的回调流程?
简单来说:
- 客户端发起支付请求,在自己的服务器端生成订单并且使用私钥给订单签名
- 客户端使用拿到服务器端生成的字符串(订单字符串+签名字符串)交给支付宝SDK进行支付
- 支付宝会回调我们的服务器端,根据订单信息中传递的回调地址来访问我们的服务器,并且将订单信息和支付宝的签名字符串发送过来,我们可以在这个时候验证支付是否成功。
3、微信支付的流程?
- H5页面发起支付请求,请求生成支付订单
- 调用统一下单API,生成预付单
- 生成JSAPI页面调用的支付参数并签名
- 微信浏览器自动调起支付JSAPI接口支付
- 确认支付
- 异步通知商户支付结果,商户收到通知返回确认信息
- 返回支付结果,展示支付信息给用户
参考:微信支付 时序图
4、高并发、只剩 1 个库存,2个人去抢,怎么处理?
使用排队队列
5、八皇后算法?
6、有没有用过 MongoDB ?
- MongoDB 是一种文档型数据库,即可以存放 xml、json 等类型的数据
- MongoDB 支持持久化
- MongoDB 在操作方面最类似于 MySQL 这种关系型数据库
4、天启
1、定时任务
使用 crontab -e
,按照 分 时 日 月 周 命令
来编辑定时任务。
/数字
代表每隔多久执行一次
假如在 时
的位置直接写数字 10
,就代表每天 10 点去执行一个任务。如下图:
2、UEditor 后端 保存草稿功能
在前端自动保存草稿时,通过 Ajax 调用后端的接口,从而把草稿文章保存到数据库。
3、UEditor 手机和电脑的预览界面适配
前端这方面使用 Bootstrap 之类的 CSS 框架,就可以做界面适配。
4、谈谈对 Vue.js 的了解
vue.js 组件化,把模板、css 样式、js 封装在一个组件里(跟 jQuery),方便相同功能的重用
5、谈谈对 ThinkPHP 的了解
- ThinkPHP 的体积比较小,Laravel 的体积比较大
- ThinkPHP 的性能比 Laravel 高(使用 siege 测试,每秒处理数据的速度,ThinkPHP 比 Laravel 快了 4 倍左右)
- Laravel 的路由配置比较方便、比较优雅
- Larave 还提供了中间件(可以实现访问前后的处理)、数据迁移文件等功能
6、哪里用到 Redis
- 例如 商品分类 这种经常需要使用的,又不经常改变的数据,可以放在 Redis 里面
- 还有高并发的场景,例如做商品的秒杀,可以利用 Redis 原子性的特性,防止商品超卖。
7、Laravel 的 Notifications
Laravel 内置的 Notifications ,通过 notify() 方法,设置相关的配置信息(通过邮件或者数据库),给用户发送邮件通知、或者站内信等等。
8、Laravel 的 Auth 模块
Laravel 框架内置的 Auth 用来验证用户是否登录,底层也是使用 session 保存用户信息,来判断用户是否登录。
9、使用 C/C++
的扩展自己编译的好处?
可以使用例如 PECL 这些工具来安装扩展,也可以自己编译安装。一般自己编译安装的可操控性更强。
5、要出发
笔试题如下图:
选择题参考答案:
- B
- D
- B
- C
- C
- D
- A(类名首字母要大写)
- B
1、请简述常见的 HTTP 状态码及其对应的出现和使用情况
状态码被分为五大类:
1xx
用于指定客户端应相应的某些动作。2xx
用于表示请求成功。3xx
用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。4xx
用于指出客户端的错误。5xx
用于指出服务器错误。
参考:常见HTTP状态码大全
2、请简述至少 5 个 php web 应用中常见的安全问题及解决办法
- 关闭 php 错误提示功能(在 php.ini 中把 display_errors 改成
display_errors = OFF
) - 严格配置文件权限(比如包含上传图片的文件不能有执行权限,只能读取)
- 预防 SQL 注入:使用 addslashes 函数(将
'
转为\'
,将"
转为\"
) - 预防 XSS:使用 htmlspecialchars 函数将脚本标签的小于号转为
<
大于号转为>
- 预防 CSRF:from 表单提交数据,使用 csrf token 验证
- public 目录是唯一的对外访问的目录
3、参考电商系统的常见的商品中心,设计相关表结构(考虑扩展性):
4、请根据你最熟悉的业务,画出清晰完整的架构图:
5、简述 PHP 数组的实现原理,PHP 如何解决 hash 冲突
6、有 1000 亿条记录,每条记录由 url、ip、时间 组成,设计一个系统能够快速查询以下的内容:
1、给定 url 和时间段(精确到分钟)统计 url 的访问次数
2、给定 ip 和时间段(精确到分钟)统计 ip 的访问次数
请描述你们的解决方案!