PHP面试题
一、cookie和session的区别
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。
4、cookie 支持字符串
session 支持所有数据类型,比较常见的有数组,对象;
5、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
6、所以个人建议:
将登陆信息等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中
7、session不能区分路径,同一个用户在访问一个网站期间,所有的session在任何一个地方都可以访问到。而cookie中如果设置了路径参数,那么同一个网站中不同路径下的cookie互相是访问不到的。
8、session需要借助cookie才能正常工作。如果客户端完全禁止cookie,session将失效。
9、两个都可以用来存私密的东西,同样也都有有效期的说法。
session是放在服务器上的,过期与否取决于服务期的设定,cookie是存在客户端的,过期与否可以在cookie生成的时候设置进去。
10、session文件过期时间,默认的话是1440秒,也就是24分钟,这个24分钟是session的发呆时间,如果在24分钟内没有对session进行操作,那么session文件就会过期,如果在23分钟的时候操作了session,那么就会又有24分钟的过期时间,如果过期了,该session被服务器认为是垃圾。
服务器每次session_start的时候,都有可能启动垃圾回收机制去删除垃圾文件,这个概率为session.gc_probability / session.gc_divisor。默认的1000分之1的概率
二、如何实现单点登录和唯一登录
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
1、所有应用系统共享一个身份认证系统。认证系统的主要功能是将用户的登录信息和用户信息库相比较,对用户进行登录认证;认证成功后,认证系统应该生成统一的认证标志(ticket),返还给用户。另外,认证系统还应该对ticket进行效验,判断其有效性。
2、所有应用系统能够识别和提取ticket信息。要实现SSO的功能,让用户只登录一次,就必须让应用系统能够识别已经登录过的用户。应用系统应该能对ticket进行识别和提取,通过与认证系统的通讯,能自动判断当前用户是否登录过,从而完成单点登录的功能。
3、统一的认证系统并不是说只有单个的认证服务器。认证服务器之间要通过标准的通讯协议,互相交换认证信息,就能完成更高级别的单点登录。如下图,当用户在访问应用系统1时,由第一个认证服务器进行认证后,得到由此服务器产生的ticket。当他访问应用系统4的时候,认证服务器2能够识别此ticket是由第一个服务器产生的,通过认证服务器之间标准的通讯协议(例如SAML)来交换认证信息,仍然能够完成SSO的功能。
同一个域但是不同的子域如何进行单点登录
单一登录,单账号单ip登录策略
实现:
登录sub1.onmpw.com系统
登录成功以后,设置cookie信息。将用户名和密码存到cookie中,但是在设置的时候必须将这cookie的所属域设置为顶级域 .onmpw.com。
访问sub2.onmpw.com系统,浏览器会将cookie中的信息username和password附带在请求中一块儿发送到sub2.onmpw.com系统。这时该系统会先检查session是否登录,如果没有登录则验证cookie中的username和password从而实现自动登录。
sub2.onmpw.com 登录成功以后再写session信息。以后的验证就用自己的session信息验证就可以了
使sub1和sub2有共同的sessionId,那必须在session_start()之前设置sessionId所属域
ini_set('session.cookie_path', '/');
ini_set('session.cookie_domain', '.onmpw.com');
ini_set('session.cookie_lifetime', '0');
唯一登录:多个客户端只允许用户登录一次
每次登录的时候,都会重新设置一个code,code保存入库,并且保存进session中。
利用拦截器,每次请求都会查一次数据库,比较session中的code和数据库中的code。
如果相同,那么说明没有被踢掉。如果不相同,则说明已被踢掉。
三、MySQL事务的四个属性以及四种隔离级别
事务有四个属性:
1.原子性 事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。
2.一致性 在事务处理时,无论执行成功还是失败,都要保证数据库系统处于一致的状态
3.持久性 事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
4.隔离性 当一个事务在执行时,不会受到其他事务的影响。当多个用户并发访问数据库时,数据库为每个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
四种隔离级别:
第1级别:Read Uncommitted(读取未提交内容)
(1)所有事务都可以看到其他未提交事务的执行结果
(2)本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
(3)该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据
第2级别:Read Committed(读取提交内容)
(1)这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)
(2)它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
(3)这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
导致这种情况的原因可能有:(1)有一个交叉的事务有新的commit,导致了数据的改变;(2)一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit
第3级别:Repeatable Read(可重读)
(1)这是MySQL的默认事务隔离级别
(2)它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行
(3)此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行
(4)InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题
第4级别:Serializable(可串行化)
(1)这是最高的隔离级别
(2)它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。
(3)在这个级别,可能导致大量的超时现象和锁竞争
四、MySQL锁的使用场景,表锁和行锁的区别
行锁
特点:锁的粒度小,发生锁冲突的概率低、处理并发的能力强;开销大、加锁慢、会出现死锁
加锁的方式:自动加锁。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁。
表锁
特点:开销小、加锁快、无死锁;锁粒度大,发生锁冲突的概率高,高并发下性能低
加锁的方式:自动加锁。查询操作(SELECT),会自动给涉及的所有表加读锁,更新操作(UPDATE、DELETE、INSERT),会自动给涉及的表加写锁。
InnoDB引擎既有表锁又有行锁,表锁的应用和MyISAM表锁用法一样,行锁只有通过有索引的字段作为条件检索的时候,才会使用行级锁,反之则是表锁。
五、ajax的同步异步的使用实例
AJAX中根据async的值不同分为同步(async = false)和(async = true)两种执行方式;
异步:在异步模式下,当我们使用AJAX发送完请求后,可能还有代码需要执行。这个时候可能由于种种原因导致服务器还没有响应我们的请求,但是因为我们采用了异步执行方式,所有包含AJAX请求代码的函数中的剩余代码将继续执行。如果我们是将请求结果交由另外一个JS函数去处理的,那么,这个时候就好比两条线程同时执行一样。
同步:在同步模式下,当我们使用AJAX发送完请求后,后续还有代码需要执行,我们同样将服务器响应交由另一个JS函数去处理,但是这时的代码执行情况是:在服务器没有响应或者处理响应结果的JS函数还没有处理完成return时,包含请求代码的函数的剩余代码是不能够执行的。就好比单线程一样,请求发出后就进入阻塞状态,知道接触阻塞余下的代码才会继续执行。
我们在发送AJAX请求后,还需要继续处理服务器的响应结果,如果这时我们使用异步请求模式同时未将结果的处理交由另一个JS函数进行处理。这时就有可能发生这种情况:异步请求的响应还没有到达,函数已经执行完了return语句了,这时将导致return的结果为空字符串。
六、什么是跨域?跨域如何解决
跨域,浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域
解决办法:
1、JSONP:
JSONP(JSON with Padding:填充式JSON),应用JSON的一种新方法,
JSON、JSONP的区别:
1、JSON返回的是一串数据、JSONP返回的是脚本代码(包含一个函数调用)
2、JSONP 只支持get请求、不支持post请求
(类似往页面添加一个script标签,通过src属性去触发对指定地址的请求,故只能是Get请求)
JSONP有什么弊端:1、服务器需要改动代码支持(因为如果调用的是别的人就没法改了)2、只支持给方法 3、发送的不是XHR请求(XHR有很多特性:异步等等)
2、代理:
例如www.123.com/index.html需要调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。
3、PHP端修改header(XHR2方式)
CORS,又称跨域资源共享,英文全称Cross-Origin Resource Sharing
在php接口脚本中加入以下两句即可:
header('Access-Control-Allow-Origin:*'
);//允许所有来源访问
header('Access-Control-Allow-Method:POST,GET');//允许访问的方式
七、如何设计一个数据库
1.需求分析阶段
准确了解与分析用户需求(包括数据与处理)
是整个设计过程的基础,是最困难、最耗费时间的一步
2.概念结构设计阶段
是整个数据库设计的关键
通过对用户需求进行综合、归纳与抽象,形成一个独立于具体DBMS的概念模型
3.逻辑结构设计阶段
将概念结构转换为某个DBMS所支持的数据模型
对其进行优化
4.数据库物理设计阶段
为逻辑数据模型选取一个最适合应用环境的物理结构(包括存储结构和存取方法)
5.数据库实施阶段
运用DBMS提供的数据语言、工具及宿主语言,根据逻辑设计和物理设计的结果
建立数据库,编制与调试应用程序,组织数据入库,并进行试运行
6.数据库运行和维护阶段
数据库应用系统经过试运行后即可投入正式运行。
在数据库系统运行过程中必须不断地对其进行评价、调整与修改
存储引擎
一、InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。
二、MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用
三、InnoDB支持外键,MyISAM不支持
五、InnoDB不支持FULLTEXT类型的索引
六、InnoDB中不保存表的行数,如select count(*
) from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*
)语句包含where条件时MyISAM也需要扫描整个表
七、对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引
八、清空整个表时,InnoDB是一行一行的删除,效率非常慢。MyISAM则会重建表
九、InnoDB支持行锁(某些情况下还是锁整表,如 update table set a=1 where user like '%lee%'
命名必须规范
3大范式
第一范式(1NF):数据表中的每一列(每个字段)必须是不可拆分的最小单元,也就是确保每一列的原子性;
第二范式(2NF):满足1NF后,要求表中的所有列,都必须依赖于主键,而不能有任何一列与主键没有关系,也就是说一个表只描述一件事情;
第三范式(3NF):必须先满足第二范式(2NF),要求:表中的每一列只与主键直接相关而不是间接相关,(表中的每一列只能依赖于主键);
数据类型
完整性约束
NOT NULL:非空约束C,指定的列不允许为空值
UNIQUE:唯一约束U,指定的列中没有重复值,或该表中每一个值或者每一组值都将是唯一的
PRIMARY KEY:主键约束P,唯一的标识出表的每一行,且不允许空值,一个表只能有一个主键约束
FOREIGN KEY:外键约束R,一个表中的列引用了其它表中的列,使得存在依赖关系,可以指向引用自身的列
CHECK:条件约束C,指定该列是否满足某个条件
创建索引
1、通过唯一性索引(unique)可确保数据的唯一性;
2、加快数据的检索速度;
3、加快表之间的连接;
4、减少分组和排序时间;
5、使用优化隐藏器提高系统性能。
视图
为了在数据库和应用程序代码之间提供另一层抽象,可以为应用程序建立专门的视图而不必非要应用程序直接访问数据表。这样做还等于在处理数据库变更时给你提供了更多的自由。
八、什么是面向对象编程、什么是面向切面编程(AOP)
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP 达到了软件工程的三个主要目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其它对象发送信息。
一.封装
通过访问修饰符:public(公有),protected(受保护)或 private(私有),将类中不需要外部访问的属性和方法进行私有化处理,以实现访问控制。
二.继承
继承就是子类继承父类的一切,使用extends关键字,只能单继承(就是子类只能有一个父类,但一个父类可以有多个子类)
三.多态
当父类引用指向子类实例的时候,由于子类对父类的方法进行了重写,该引用在调用方法的时候表现出的不同,叫做多态。
AOP(Aspect Oriented Programming),面向切面编程其实是对业务逻辑又进行了进一步的抽取,将多种业务逻辑中的公用部分抽取出来做成一种服务(比如日志记录,性能统计,安全验证等),从而实现代码复用。另外这种服务通过配置可以动态的给程序添加统一控制,利用AOP可以对业务逻辑的各个部分进行分离,从而使得业务逻辑各部分之间的耦合度降低。
简单的总结:将各个业务中的公共部分抽取出来,实现代码的复用的一种编程方法
九、数据库优化方向有哪些?具体如何操作
1、选取最适用的字段属性
MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。
例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。
另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOTNULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
2、使用连接(JOIN)来代替子查询(Sub-Queries)
如果使用连接(JOIN)速度将会快很多,连接(JOIN)..之所以更有效率一些,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
3、使用联合(UNION)来代替手动创建的临时表
union查询,它可以把需要使用临时表的两条或更多的select查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要想同。
4、事务
可以保持数据库中数据的一致性和完整性。
5、锁定表
尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。
其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。
6、使用外键
锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。
7、使用索引
索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。
8、优化的查询语句
首先,最好是在相同类型的字段间进行比较的操作。
在建有索引的字段上尽量不要使用函数进行操作。
在搜索字符型字段时,我们有时会使用LIKE关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。
注意避免在查询中让MySQL进行自动类型转换,因为转换过程也会使索引变得不起作用。
根据场景合理地反规范化
A:分割表
B:保留冗余字段。当两个或多个表在查询中经常需要连接时,可以在其中一个表上增加若干冗余的字段,以避免表之间的连接过于频繁,一般在冗余列的数据不经常变动的情况下使用。
C:增加派生列。派生列是由表中的其它多个列的计算所得,增加派生列可以减少统计运算,在数据汇总时可以大大缩短运算时间。
代码优化
定位慢SQL,并优化
由自带的慢查询日志或者开源的慢查询系统定位到具体的出问题的SQL,然后使用explain、profile等工具来逐步调优,最后经过测试达到效果后上线。
A:尽量少用(或者不用)sqlserver 自带的函数
B:连续数值条件,用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5
D:尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型
E:不建议使用 select * from t
,用具体的字段列表代替*
,不要返回用不到的任何字段
F:表与表之间通过一个冗余字段来关联,要比直接使用JOIN有更好的性能
G:select count(*) from table
;这样不带任何条件的count会引起全表扫描
合理使用索引
A:尽量不要在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
B:应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
C:应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
分库分表
水平分割(按行)、垂直分割(按列)
读写分离
主从同步:数据库最终会把数据持久化到磁盘,集群必须确保每个数据库服务器的数据是一致的。从库读主库写,从库从主库上同步数据。
读写分离:使用负载均衡实现,写操作都往主库上写,读操作往从服务器上读。
缓存
短时间内相同数据重复查询多次且数据更新不频繁
本地缓存:Cache
缓存服务:Redis/Tair/Memcache等
硬件优化
十、三种或三种以上设计模式的使用场景
mvc
Model-View-Controller(模型-视图-控制器)模式。这种模式用于应用程序的分层开发。
模型Model – 管理大部分的业务逻辑和所有的数据库逻辑。模型提供了连接和操作数据库的抽象层。
控制器Controller - 负责响应用户请求、准备数据,以及决定如何展示数据。
视图View – 负责渲染数据,通过HTML方式呈现给用户。
一个典型的Web MVC流程:
Controller截获用户发出的请求;
Controller调用Model完成状态的读写操作;
Controller把数据传递给View;
View渲染最终结果并呈献给用户。
单例模式
单例就是单个实例,单个对象的意思。节省内存分配开支
将构造函数__construct
设置为私有的private,对外提供一个静态方法.一般是用getInstance获取实例。在getInstance方法里做统一判断是否有new一个实例。存在的话就直接返回。没有就new一个。
工厂模式
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
观察者模式
十一、redis和memcache的区别,rdb和aof的区别
redis和memcache的区别
- Redis和Memcache都是将数据存放在内存中,都是内存数据库
- Redis不仅仅支持简单的字符串类型的数据,同时还提供list,set,hash等数据结构的存储。
- redis支持数据的持久化
- 在100k以上的数据中,Memcache性能要高于Redis。
- memcache支持多线程,redis支持单线程。对于cpu利用率 memcache会更好
6.MemCached可以修改最大内存,采用LRU算法。Redis增加了VM的特性,突破了物理内存的限制。
7.Memcached单个key-value大小有限,一个value最大只支持1MB,而Redis最大支持512MB
8.从本质上讲,Memcached只是一个单一key-value内存Cache;而Redis则是一个数据结构内存数据库,支持五种数据类型,因此Redis除单纯缓存作用外,还可以处理一些简单的逻辑运算,Redis不仅可以缓存,而且还可以作为数据库用
Redis 提供了两种持久化方式:RDB(默认) 和AOF
2.1 RDB(Redis DataBase)
功能核心函数, rdbSave rdbLoad
把当前进程的快照数据保存到硬盘中
优点
代表Redis某个时间点上的数据快照。
用户备份,全量复制
Redis 加载RDB恢复数据远远快于AOF
缺点
无法做到实时持久化,秒级持久化
二进制格式,存在兼容问题
2.2 AOF Append-only file
Append-only File(AOF),Redis 每次接收到一条改变数据的命令时,它将把该命令写到一个 AOF 文件中(只记录写操作,读操作不记录),当 Redis 重启时,它通过执行 AOF 文件中所有的命令来恢复数据。
WRITE: 根据条件,将aof_buf中的缓存写入AOF文件
SAVE: 根据条件,调用fsync fdatasync 命令,把AOF文件保存到磁盘中
存储结构:内容是Redis通讯协议格式的命令文本存储。
2.3 AOF 和 RDB对比
AOF 比 RDB 更新频率更高,优先使用AOF还原数据
AOF 比 RDB更安全,更大
RDB性能比AOF好
优先加载AOF
Redis 事务
Redis 中的事务(transaction)是一组命令的集合,至少是两个或两个以上的命令,redis 事务保证这些命令被执行时中间不会被任何其他操作打断。
MySQL:
BEGIN:显式地开启一个事务;
COMMIT:提交事务,将对数据库进行的所有修改变成为永久性的;
ROLLBACK:结束用户的事务,并撤销正在进行的所有未提交的修改;
Redis:
MULTI:标记事务的开始;
EXEC:执行事务的commands队列;
DISCARD:结束事务,并清除commands队列;
redis:
事务执行 exec 之前,入队命令错误(语法错误;严重错误导致服务器不能正常工作(例如内存不足)),放弃事务。
事务执行 exec 命令后,执行队列命令,无法回滚,会出现部分成功,部分失败情况
十二、redis有几种数据类型、分别用于什么场景
-
string类型
string为最简单类型,一个key对应一个value
常用命令:get、set、incr、decr
比如想知道什么时候封锁一个IP地址(访问超过几次)。INCRBY命令让这些变得很容易,通过原子递增保持计数。
计数器 incr decr 访问量
微博数、粉丝数
缓存功能 -
Hash
hash是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
常用命令:hget,hset,hgetall 等。
用户ID,为查找的key,存储的value用户对象包含姓名name,年龄age,生日birthday 等信息 -
List
列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)
常用命令:lpush,rpush,lpop,rpop,lrange,BLPOP(阻塞版),llen等。
消息队列,抢购 -
set
Set 是 string 类型的无序集合,集合成员是唯一的,即集合中不能出现重复的数据
常用命令:sadd,srem,spop,sdiff ,smembers,sunion 等。
比如在微博应用中,每个人的好友存在一个集合(set)中,这样求两个人的共同好友的操作,可能就只需要用求交集命令即可。
Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现
共同爱好,共同好友 -
Sort Set
有序集合zset和集合set一样也是string类型元素的集合,且不允许重复的成员。不同的是 zset 的每个元素都会关联一个分数(分数可以重复),redis 通过分数来为集合中的成员进行从小到大的排序。
常用命令:zadd,zrange,zrem,zcard等
以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
全班同学成绩的SortedSets,value可以是同学的学号,而score就可以是其考试得分,这样数据插入集合的,就已经进行了天然的排序。
排行榜
十三、抢购的实施方案3种或以上
- 事务锁机制
for update 加排他锁 - 文件锁
if(!flock($fp,LOCK_EX | LOCK_NB)) { echo "系统繁忙,请稍后再试"; return; }
- redis队列
十四、tp5框架中的中间件的使用
中间件主要用于拦截或过滤应用的HTTP请求,并进行必要的业务处理。
也就是说,降低了系统的耦合;【在http请求阶段,执行中间件的入口执行方法(handle)--tp5.1】----减少了系统的一些if/else判断,因此降低了系统的耦合
十五、API接口设计以及安全需要考虑到哪些因素
-
接口版本化
生产环境中,如果没有版本控制的程序变更会导致调用接口的相关方频繁的跟着变更,假设相关方没有及时的跟着变更,那么系统就会报错,从而影响到用户的使用及体验,使其对整个系统的运营都是不利的,接口对接的难度也会不断的加大。
如果接口能够有版本的控制,则升级系统的主动权就掌握在相关方,这样当有新版本的程序发布时旧版本的业务逻辑不会受到影响,从用户感知上也受到的影响就比较小,相关方也可以根据自身的条件是否要升级版本。 -
接口面向应用场景
在对接口进行设计时,我们还要考虑接口是面向web前端开发还是手机app开发,或者服务端开发。不同的应用场景接口总体规划是不同的(例如:当我们的接口是提供给web前端或APP端使用时,接口安全验证我们可以采用jwt、oauth2.0等,如果我们的接口是提供给后端服务使用时,那么我可以采用token机制)。 -
请求参数的规范性及处理的统一性
请求参数的规范性意思就是说,接口要以什么样的方式来接收参数。是统一使用json的方式接收呢还是xml或者form表单方式接收。
在开发接口应该统一在一个地方进行对参数的接收、校验等操作。为了保证参数的完整性,我们可以考虑新增签名验证等处理。 -
返回数据类型、返回码及信息提示的规范性
我们以统一的格式json,
制定统一的返回码
便于后期迅速调整问题 -
接口安全验证及权限的控制
接口并不是每个操作者都能请求访问的,所以我们要为接口提供一个安全验证,就像用户登录系统一样,没有在我们系统注册的合法用户我们是不允许请求访问的
权限的控制是指针对不同的用户群体,我们要分配不同的权限(例如:超级管理员可以操作所有接口,普通用户只允许操作部分接口),这里除了针对用户群体我们可以针对不同的调用接口的相关方(这里的相关方是指调用接口的应用)进行权限控制。 -
接口调用频率的控制
了接口的安全性及接口的可维护性 -
请求接口日志的记录
后期维护接口时则会大大降低维护成本 -
详细使用文档
总结
- 路由重写
- 长地址优化
- 版本控制
- 请求凭证
- 安全验证,白名单
- 返回状态码,数据格式
- 权限控制,访问频率
- 详细文档
十六、常见的三种以上缓存实现方案
HTTP缓存机制
浏览器存在一个缓存数据库,用于存储缓存信息。在客户端第一次请求数据时,此时缓存数据库中没有对应的缓存数据,需要请求服务器,服务器返回后,将数据存储至缓存数据库中。
- 强制缓存
服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,过期才后重新请求服务器
//用Cache-Control告诉浏览器有效期15秒 header("Cache-Control:max-age=15"); //使用响应头Expires控制缓存,设置10秒的有效期,时间格式是GMT $expires = gmdate('D, d M Y H:i:s', time() + 10) . ' GMT'; header("Expires:$expires");
- 对比缓存
访问服务器,根据服务器响应来获取内容。Last-Modified,Etag,must-revalidate 等有些特殊,不直接受浏览器影响,它们必须访问服务器后,再由服务器判断是直接发送新的资源,还是发送一个304 Not Modfied让浏览器使用缓存中的资源
ETag全称Entity Tag,用来标识一个资源。ETag可以是一个hash值或是一个内部维护的版本号。ETag能反映出资源内容的变化,这是http缓存可以正常工作的基础。
Last-Modified的原理其实和ETag差不多,Last-Modified通过时间来标识资源。通过这样的方式可以不必每次都获取全部的资源达到更新的目的,能极大的节省服务器的开销,更有利于搜素引擎的抓取
<?php header("Content-type:text/html;charset=utf-8"); //强制每次请求直接发送给源服务器,而不经过本地缓存版本的校验 header("Cache-Control:no-cache"); //响应头Last-Modified $lastmodified = filemtime('demo_cache.php'); $lastmodifiedGMT = gmdate('D, d M Y H:i:s',$lastmodified). ' GMT'; header("Last-Modified:$lastmodifiedGMT"); //响应头ETag $etag = md5_file('demo_cache.php'); header("ETag:$etag"); if (@$_SERVER['HTTP_IF_MODIFIED_SINCE'] == $lastmodifiedGMT || @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { header("HTTP/1.1 304 Not Modified"); exit; } $data = require('demo_cache.php'); foreach( $data as $key=>$value ) { echo $key.'---'.$value; echo '<hr>'; } $curr_time = date('Y-m-d H:i:s'); echo '服务器时间:<span style="color:blue">'.$curr_time.'</span>'; ?> <a href="index.php" >点击在新的页面打开</a>
ob缓存
解决:页面静态化,header头前有输出的问题
php.ini 中的相关配置项
output_buffering 当设置为 on 时,则在所有脚本自动打开输出缓冲区,也就相当于在每个脚本都自动执行了 ob_start() 这个函数,而不用再显示的调用该函数。也可以设置为一个整型的数字,代表缓冲区可以存储的最大字节数
<?php $file_name = 'index.html'; $lifttime = 10; if(file_exists( $file_name ) && filemtime( $file_name ) - time() < $lifttime ) { require_once( $file_name );//引入文件 }else{ ob_start( ); //开启缓存 echo '<p>我是要生成的静态内容</p>'; $content = ob_get_contents(); //输出到浏览器 file_put_contents($file_name, $content); }
内存式缓存缓存
使用redis,memcached等nosql数据库设置PHP缓存,通过缓存查询结果,来减少数据库的访问次数,从而提高网站的响应速度。
CDN缓存
各地部署多套静态存储服务,本质上是空间成本换时间
CDN是域名和真实服务器中间的一个环节,添加cdn节点后,用户访问时,自动选择最近的节点内容,不存在再请求原始服务器
CDN本质上是一种文件分发类系统,适合存储更新很少的静态内容,文件更新慢
十七、索引的分类、索引失效情况
- 索引分类
- 普通索引 MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。key或index定义
- 唯一索引 索引列中的值必须是唯一的,但是允许为空值。unique index
- 主键索引 是一种特殊的唯一索引,不允许有空值。primary key
- 全文索引 fulltext key 只能在CHAR,VARCHAR,TEXT类型字段上使用全文索引
- 空间索引 spatial index 对空间数据类型的字段建立的索引
- 复合索引 在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时遵循最左前缀集合.
- 使用索引需遵循一些基本的原则
- 查询条件中频繁使用的字段
- 数字型的字段适合建立索引 主键id
- 存储空间较小的适合建立索引 varchar char(5)
- 重复值较高的字段不适合建立索引 性别
- 更新频率的字段不适合建立索引
- 索引失效
- 查询时保证字段的独立 索引字段在查询时要保证该字段在关系运算符(=,>)的一侧,不能是表达式的一部分或函数的参数
where id+2 > 3 => where id > 3-2 - 模糊查询中通配符的使用 通配符%在左侧,会导致全表扫描 ‘笔记%’使用索引
- MySQL的优化器在查询时会判断全表扫描是否比使用索引慢,若是使用索引慢,则会选做全表扫描
- where 子句中使用 or 来连接条件
- 字符串索引 不能不加引号(查询时需要类型转换)存在索引列的数据类型隐形转换,则用不上索引,比如列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
- 复合索引未用左侧字段;
- mysql估计使用全表扫描要比使用索引快,则不使用索引
- 索引字段上使用 is null / is not null 判断时,会导致索引失效而转向全表扫描
- 查询时保证字段的独立 索引字段在查询时要保证该字段在关系运算符(=,>)的一侧,不能是表达式的一部分或函数的参数
十八、http协议与websocket协议
HTTP协议为单向协议,即浏览器只能向服务器请求资源,服务器才能将数据传送给浏览器,而服务器不能主动向浏览器传递数据。
WebSocket一种双向通信协议,在建立连接后,WebSocket服务器和Browser/UA都能主动的向对方发送或接收数据,就像Socket一样,不同的是WebSocket是一种建立在Web基础上的一种简单模拟Socket的协议;
WebSocket需要通过握手连接,类似于TCP它也需要客户端和服务器端进行握手连接,连接成功后才能相互通信。
WebSocket在建立握手连接时,数据是通过http协议传输的,“GET/chat HTTP/1.1”,这里面用到的只是http协议一些简单的字段。但是在建立连接之后,真正的数据传输阶段是不需要http协议参与的。
建立在TCP之上,同http一样通过TCP来传输数据
十九、Linux常用20个命令
ls (list)显示当前目录下的文件或目录
- a 列出全部的文件,连同隐藏文件
ll(ls -l) 除了文件名之外,还将文件的权限、所有者、文件大小等信息详细列出来
cd 进入指定目录
- cd / 进入根目录
- cd ~ 进入家目录
- cd - 进入上一次工作路径
pwd 当前工作目录的绝对路径名称
mkdir 创建文件夹
cat 由第一行开始显示文件内容
cp –r test/ newtest(将当前目录"test/"下的所有文件复制到新目录"newtest"下)
touch 创建一个文件,重复touch不会覆盖,时间会更新
mv 移动文件、目录或更名(如目录,则移动文件;如为文件则重命令该文件)
- mv test.log test1.txt 将文件test.log重命名为test1.txt
- mv llog1.txt log2.txt log3.txt /test3 将文件log1.txt,log2.txt,log3.txt移动到test3目录中
rm 删除文件或目录
- rm -rf test 删除test目录及子目录中所有档案删除,并且不用确认
find查找文件
- find / -name file1 从 '/' 开始进入根文件系统搜索文件和目录
vi/vim 文本编辑器
tar -czvf 包名字.tar.gz 被压缩的目录或文件
tar –xvf file.tar 解压 tar包
clear 清屏
history 列出历史命令
ps aux 查看系统进程
kill 杀死进程
netstat -lnp 查看网络状况
man 查看帮助信息
ifconfig 用于查看和配置Linux系统的网络接口
ping 一个远程主机
logout 让用户退出系统
useradd 用户名 添加用户
passwd 用户名 设置密码
chgrp 改变文件所属用户组
- chgrp users -R ./dir # 递归地把dir目录下中的所有文件和子目录下所有文件的用户组修改为users
chown 改变文件的所有者
-chown user1 file1 改变一个文件的所属者
chmod 改变文件访问权限
-chmod a+x t.log 增加文件t.log所有用户可执行权限
date 显示系统日期
reboot 重启
poweroff 关机
shutdown -h now 关闭系统并立即关机
tree 以树状图列出目录的内容
wget 从网上下载文件资源
yum 查找、安装、删除某一个、一组甚至全部软件包
make 调试安装文件
make install 调试并安装文件
二十、xss、csrf、SQL注入原理
一、XSS
- XSS (Cross-Site Scripting),跨站脚本攻击,因为缩写和 CSS重叠,所以只能叫 XSS。跨站脚本攻击是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。
- XSS 的原理是恶意攻击者往 Web 页面里插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中 Web 里面的脚本代码会被执行,从而可以达到攻击者盗取用户信息或其他侵犯用户安全隐私的目的。
- 为了防止出现非持久型 XSS 漏洞,需要确保这么几件事情:
Web 页面渲染的所有内容或者渲染的数据都必须来自于服务端。
尽量不要从 URL,document.referrer,document.forms 等这种 DOM API 中获取数据直接渲染。
尽量不要使用 eval, new Function(),document.write(),document.writeln(),window.setInterval(),window.setTimeout(),innerHTML,document.createElement() 等可执行字符串的方法。
如果做不到以上几点,也必须对涉及 DOM 渲染的方法传入的字符串参数做 escape 转义。
前端渲染的时候对任何的字段都需要做 escape 转义编码。
4.CSP 本质上就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。
5.转义字符
6.HttpOnly Cookie。
这是预防XSS攻击窃取用户cookie最有效的防御手段。Web应用程序在设置cookie时,将其属性设为HttpOnly,就可以避免该网页的cookie被客户端恶意JavaScript窃取,保护用户cookie信息。
二、SQL
- SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。 攻击者通过在应用程序预先定义好的SQL语句结尾加上额外的SQL语句元素,欺骗数据库服务器执行非授权的查询,篡改命令。
SQL注入的必备条件:
- 可以控制输入的数据
- 服务器要执行的代码拼接了控制的数据。
严格限制Web应用的数据库的操作权限
后端代码检查输入的数据是否符合预期,严格限制变量的类型,例如使用正则表达式进行一些匹配处理。
对进入数据库的特殊字符(’,",\,<,>,&,*
; 等)进行转义处理,或编码转换。
所有的查询语句建议使用数据库提供的参数化查询接口
重要的信息一定要加密
三、CSRF
CSRF(Cross Site Request Forgery),即跨站请求伪造,是一种常见的Web攻击,它利用用户已登录的身份,在用户毫不知情的情况下,以用户的名义完成非法操作。
完成 CSRF 攻击必须要有三个条件:
用户已经登录了站点 A,并在本地记录了 cookie
在用户没有登出站点 A 的情况下(也就是 cookie 生效的情况下),访问了恶意攻击者提供的引诱危险站点 B (B 站点要求访问站点A)。
站点 A 没有做任何 CSRF 防御
防范 CSRF 攻击可以遵循以下几种规则:
Get 请求不对数据进行修改
不让第三方网站访问到用户 Cookie
阻止第三方网站请求接口请求时附带验证信息,比如验证码或者 Token