php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。

php面试题

1、写出你能想到的所有HTTP返回状态值,并说明用途(比如:返回404表示找不到页面)

# 200:服务器请求成功
# 301:永久重定向,旧网页已被新网页永久替代
# 302:表示临时性重定向
# 400:错误请求
# 401:未授权,没有权限,未登录
# 403:禁止访问
# 404:找不到页面
# 500:系统错误,服务器错误
# 502:无效响应
# 503:服务不可用,服务器由于维护或者负载过重未能应答
# 504:网关超时(nginx做为反向代理服务器,所连接的应用服务器无响应导致)

2、HTTP中GET,POST和PUT的区别

1、GET在浏览器回退时是无害的(会从缓存中拿结果),而POST会再次提交请求。
2、GET产生的URL地址可以被Bookmark(收藏书签),而POST不可以。
3、GET请求会被浏览器主动cache,而POST不会,除非手动设置。
4、GET请求只能进行url编码,而POST支持多种编码方式。
5、GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
6、GET请求在URL中传送的参数是有长度限制的,2kb,而POST没有,但是根据IIS的配置,传输量也是不同的。
7、对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
8、GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
9、GET参数通过URL传递,POST放在Request body中

# 总结
POST和GET方式的安全性是相对的,另外也要看是从哪个角度来看的。从数据传输过程方面来看,POST方式是更加
安全的,但是从对服务器数据的操作来看,POST方式的安全性又是比较低的。即使是传输过程用POST来执行,
安全性也是相对的,如果了解HTTP协议漏洞,通过拦截发送的数据包,同样可以修改交互数据,
所以这里的安全不是绝对的。

# 1、PUT和POST
PUT和POST都有更改指定URI的语义.但PUT被定义为idempotent的方法,POST则不是.idempotent的方法:如果一
个方法重复执行多次,产生的效果是一样的,那就是idempotent的。也就是说:

PUT请求:如果两个请求相同,后一个请求会把第一个请求覆盖掉。(所以PUT用来改资源)

Post请求:后一个请求不会把第一个请求覆盖掉。(所以Post用来增资源)

# 2、get和post补充

1、HTTP的底层是TCP/IP。HTTP只是个行为准则,而TCP才是GET和POST怎么实现的基本。GET/POST都是TCP链接。
GET和POST能做的事情是一样一样的。但是请求的数据量太大对浏览器和服务器都是很大负担。所以业界有了不成
文规定,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。

2、GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一
并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,
浏览器再发送data,服务器响应200 ok(返回数据)。

3、在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,
两次包的TCP在验证数据包完整性上,有非常大的优点。但并不是所有浏览器都会在POST中发送两次包,
Firefox就只发送一次。

3、php中双引号和单引号的区别

双引号解释变量,单引号不解释变量;所以单引号的执行效率比双引号要高

双引号可以解析所有的转义字符,单引号只能解析 \ 和 ' 本身的转义;

4、echo、print_r、print、var_dump之间的区别

# echo 和 print 区别:

echo - 可以输出一个或多个字符串
print - 只允许输出一个字符串,返回值总为 1
提示:echo 输出的速度比 print 快, echo 没有返回值,print有返回值1# echo和print共同点

都是一个语言结构,使用的时候可以不用加括号,也可以加上括号

# var_dump()和print_r()的区别

共同点:都是函数,两者都可以打印数组,对象之类的复合型变量。

区别:
print_r() 是函数,用于格式化输出数组的结构
print_r() 只能打印一些易于理解的信息
print_r() 在打印数组时,会将把数组的指针移到最后边,使用reset() 可让指针回到开始处。 

var_dump() 不但能打印复合类型的数据,还能打印资源类型的变量
var_dump() 输出的信息则比较详细,一般调试时用得多。
var_dump() 判断一个变量的类型和长度,并输出变量的数值

5、require一个不存在的文件时,如何避免抛出异常错误?

使用 try {
     } catch(Exception $e) 捕获异常

使用 file.exists() 判断文件是否存在,存在引入

6、写一段php demo代码实现单例模式

class Singleton
{
     
	private static $instance;
  
	private function __construct()
	{
     
		// Do nothing.
	}
	//获取实例
	public static function getInstance() 
	{
     
		if (!(self::$instance instanceof self)) {
     
			self::$instance = new self();
		}
	 	return self::$instance;
	}
  
	//防止克隆
	private function __clone() 
	{
     
		// Do nothing.
	}
}

7、请描述一下PHP的自动加载机制

在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class
的文件包含进来。这个在小规模开发的时候,没什么大问题。但在大型的开发项目中,这么做会产生大量的
require或者include方法调用,这样不因降低效率,而且使得代码难以维护,况且require_once的代价很大。

在PHP5之前,各个PHP框架如果要实现类的自动加载,一般都是按照某种约定自己实现一个遍历目录,自动加载
所有符合约定规则的文件的类或函数。 当然,PHP5之前对面向对象的支持并不是太好,类的使用也没有现在频繁。
在PHP5后,当加载PHP类时,如果类所在文件没有被包含进来,或者类名出错,Zend引擎会自动调用__autoload 
函数。此函数需要用户自己实现__autoload函数。 在PHP5.1.2版本后,可以使用spl_autoload_register
函数自定义自动加载处理函数。当没有调用此函数,默认情况下会使用SPL自定义的spl_autoload函数。

1、 __autoload示例:
function __autoload($class_name) {
        
	echo '__autload class:', $class_name, '
'
; } new Demo(); # 以上的代码在最后会输出:__autload class:Demo。 # 并在此之后报错显示: Fatal error: Class ‘Demo’ not found # 我们一般使用_autoload自动加载类如下: function __autoload($class_name) {    require_once ($class_name .class.php”);    } $memo= new Demo(); 我们可以看出_autoload至少要做三件事情,第一件事是根据类名确定类文件名,第二件事是确定类文件所在的 磁盘路径(在我们的例子是最简单的情况,类与调用它们的PHP程序文件在同一个文件夹下),第三件事是将类从 磁盘文件中加载到系统中。第三步最简单,只需要使用include/require即可。要实现第一步,第二步的功能, 必须在开发时约定类名与磁盘文件的映射方法,只有这样我们才能根据类名找到它对应的磁盘文件。 因此,当有大量的类文件要包含的时候,我们只要确定相应的规则,然后在__autoload()函数中,将类名与实际 的磁盘文件对应起来,就可以实现lazy loading的效果。从这里我们也可以看出__autoload()函数的实现中 最重要的是类名与实际的磁盘文件映射规则的实现。 但现在问题来了,假如在一个系统的实现中,假如需要使用很多其它的类库,这些类库可能是由不同的开发工程师 开发,其类名与实际的磁盘文件的映射规则不尽相同。这时假如要实现类库文件的自动加载,就必须__autoload() 函数中将所有的映射规则全部实现,因此__autoload()函数有可能会非常复杂,甚至无法实现。最后可能会导致 __autoload()函数十分臃肿,这时即便能够实现,也会给将来的维护和系统效率带来很大的负面影响。在这种 情况下,在PHP5引入SPL标准库,一种新的解决方案,即spl_autoload_register()函数。 # 2、spl_autoload_register()函数 # 此函数的功能就是把函数注册至SPL的__autoload函数栈中,并移除系统默认的__autoload()函数。 # 下面的例子可以看出: function __autoload($class_name) { echo '__autload class:', $class_name, '
'
; } function classLoader($class_name) { echo 'SPL load class:', $class_name, '
'
; } spl_autoload_register('classLoader'); new Test(); //结果:SPL load class:Test 语法:bool spl_autoload_register ( [callback $autoload_function] ) 接受两个参数:一个是添加到自动加载栈的函数,另外一个是加载器不能找到这个类时是否抛出异常的标志。 第一个参数是可选的,并且默认指向spl_autoload()函数,这个函数会自动在路径中查找具有小写类名和.php 扩展或者.ini扩展名,或者任何注册到spl_autoload_extensions()函数中的其它扩展名的文件。 class CalssLoader { public static function loader($classname){ $class_file = strtolower($classname).".php"; if (file_exists($class_file)){ require_once($class_file); } } } // 方法为静态方法 spl_autoload_register('CalssLoader::loader'); $test = new Test(); 一旦调用spl_autoload_register()函数,当调用未定义类时,系统会按顺序调用注册到 spl_autoload_register()函数的所有函数,而不是自动调用__autoload()函数。 如果要避免这种情况,需采用一种更加安全的spl_autoload_register()函数的初始化调用方法: if(false === spl_autoload_functions()){ if(function_exists('__autoload')){ spl_autoload_registe('__autoload',false); } } spl_autoload_functions()函数会返回已注册函数的一个数组,如果SPL自动加载栈还没有被初始化,它会返回 布尔值false。然后,检查是否有一个名为__autoload()的函数存在,如果存在,可以将它注册为自动加载栈中的 第一个函数,从而保留它的功能。之后,可以继续注册自动加载函数。 还可以调用spl_autoload_register()函数以注册一个回调函数,而不是为函数提供一个字符串名称。如提供一个 如array('class','method')这样的数组,使得可以使用某个对象的方法。 下一步,通过调用spl_autoload_call('className')函数,可以手动调用加载器,而不用尝试去使用那个类。 这个函数可以和函数class_exists('className',false)组合在一起使用以尝试去加载一个类,并且在所有的 自动加载器都不能找到那个类的情况下失败 if(spl_autoload_call('className') && class_exists('className',false)){ } else { } SPL自动加载功能是由spl_autoload() ,spl_autoload_register(), spl_autoload_functions() ,spl_autoload_extensions()spl_autoload_call()函数提供的。 总结:项目过大时,include和require使用过多,php5.2后出现__autoload自动载入,后面出现一个php工程 依赖多个框架,每个框架都有__autoload就会报方法重复定义错误,php5.3之后官方提供 spl_autoload_register('autoload1');,解决了方法重复定义冲突

8、发送POST请求时,application/x-www-form-urlencoded格式和multipart/form-data有什么区别,如果需要发送json格式到后台,发送时Content-Type应该如何设置

application/x-www-form-urlencoded是浏览器默认的编码格式,用于键值对参数,参数之间用&间隔;

multipart/form-data常用于文件等二进制,也可用于键值对参数,最后连接成一串字符传输

设置默认application/x-www-form-urlencoded

9、阐述下你对MVC的理解

Model(模型) - 模型代表一个存取数据的对象。它也可以带有逻辑,在数据变化时更新控制器。

View(视图) - 视图代表模型包含的数据的可视化。

Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。
它使视图与模型分离开。Controller层用来调度View层和Model层,将用户界面和业务逻辑合理的组织在一起,
起粘合剂的效果

# 总结:
  优点:分层,结构清晰,耦合性低,大型项目代码的复用性得到极大的提高,开发人员分工明确,
  提高了开发的效率,维护方便,降低了维护成本。

  缺点:简单的小型项目,使用MVC设计反而会降低开发效率,层和层虽然相互分离,但是之间关联性太强,
  没有做到独立的重用

10、什么是面向对象?主要特征是什么?

面向对象是程序的一种设计方式,
把具体的事物和行为抽象成类,对象有方法,属性值,不可变常量
它利于提高程序的重用性,使程序结构更加清晰。

主要特征:封装、继承、多态。

11、SESSION 与 COOKIE的区别是什么,请从协议,产生的原因与作用说明?

# 产生的背景和原理:
HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的
连接。这就意味着服务器无法从连接上跟踪会话。于是需要引入一种机制,COOKIE于是就顺应而生。

Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。
客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session

# 区别:(位置,大小,安全)
1、cookie 是存放在浏览器端,不同的浏览器存储的cookie数量和数据的大小都不一致。
大多数情况下单个域名限制最多保存20个cookie,每个cookie保存的数据不能超过4K。

2、session存储在服务端,默认是以文件的形式存储,也可以存储在数据库和redis、memcache等缓存内存中。

3、session是占用的服务器内存,所以内存越大,能存的值就越大,原则上讲无上限,一般用于存储对安全要求
较高的重要数据;

# 补充
1、SESSION存储在服务器端,COOKIE保存在客户端。Session比较安全,cookie用某些手段可以修改,不安全。
   Session依赖于cookie进行传递。
2、session为‘会话服务’,在使用时需要开启服务,cookie不需要开启,可以直接用

	禁用cookie后,session不能正常使用。Session的缺点:保存在服务器端,每次读取都从服务器进行读取
对服务器有资源消耗。Session保存在服务器端的文件或数据库中,默认保存在文件中,文件路径由php配置
文件的session.save_path指定。Session文件是公有的。

12、isset() 和 empty() 区别

Isset判断变量是否存在,可以传入多个变量,若其中一个变量不存在则返回假
empty判断变量是否为空为假,只可传一个变量,如果为空为假则返回真

$a = 1;
$b = 2;
echo isset($a,$b) . "
"
; // true echo isset($a,$c) . "
"
; // false echo empty("") . "
"
; // true echo empty("0") . "
"
; // true echo empty(" ") . "
"
; // false echo empty("null") . "
"
; // false echo empty(null) . "
"
; // true

13、请说明 PHP 中传值与传引用的区别。什么时候传值什么时候传引用?

按值传递:函数范围内对值的任何改变在函数外部都会被忽略

按引用传递:函数范围内对值的任何改变在函数外部也能反映出这些修改

优缺点:按值传递时,php必须复制值。特别是对于大型的字符串和对象来说,这将会是一个代价很大的操作。
按引用传递则不需要复制值,对于性能提高很有好处。

14、在PHP中error_reporting这个函数有什么作用?

设置 PHP 的报错级别并返回当前级别。
error_reporting(2047):显示所有错误

15、请用正则表达式(Regular Expression)写一个函数验证电子邮件的格式是否正确。

if(isset($_POST['action']) && $_POST['action']==’submitted’){
     
 $email=$_POST['email'];
 if(!preg_match(/^[0-9a-zA-Z-]+@[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+){
     1,3}$/,$email)){
     
  echo “电子邮件检测失败”;
 }else{
     
  echo “电子邮件检测成功”;
 }
}

16、对于用户输入一串字符串$string,要求string中只能包含大于0的数字和英文逗号,请用正则 表达式验证,对于不符合要求的string返回出错信息

class regx {
     
 public static function check($str) {
     
	if(preg_match("/^([1-9,])+$/",$str)) {
     
		return true;
	}
		return false;
	}
}

$str="12345,6";
if(regx::check($str)) {
     
	echo "suc";
} else {
     
	echo "fail";
}

17、单例模式,创建mysqli数据库链接的单例对象

class Db {
     
	private static $instance;
	public $handle;
	
	private function __construct($host,$username,$password,$dbname) {
     
		$this->handle=NULL;
		$this->getcon($host,$username,$password,$dbname);
	}

	public static function getBb() {
     
		self::$instance=new Db();
		return self::$instance;
	}

	private function getcon($host,$username,$password,$dbname) {
     
		if($this->handle!=NULL){
     
			return true;
		}
  		$this->handle=mysqli_connect($host,$username,$password,$dbname);
  	}
}

18、windows平台, Apache Http Server启动失败, 排错思路是什么?

检查apache使用的80端口是否被占用,如果被占用,先停止占用80端口的服务,然后启动apache服务器

19、PHP session扩展默认将session数据储存在哪里? 答案:D

A) SQLite Database
B) MySQL Database
C) Shared Memory
D) File System
E) Session Server

20、如果你想要自动加载类,下面哪种函数声明是正确的 答案:C

A) function autoload($class_name)
B) function __autoload($class_name, $file)
C) function __autoload($class_name)
D) function _autoload($class_name)
E) function autoload($class_name, $file)

21、PHP程序使用utf-8编码, 以下程序输出结果是什么? 答案:B

$str = ’hello你好世界’;
echo strlen($str);
?>

A) 9
B) 13(gbk)
C) 18
D) 17(utf8)

22、你所知道的php数组相关的函数?

array()----创建数组
array_combine(keys,values);----通过合并两个数组来创建一个新数组
range(low,high,step)----创建并返回一个包含指定范围的元素的数组
compact()----创建一个包含变量名和它们的值的数组。
array_chunk(array,size,preserve_key);----将一个数组分割成多个
array_merge(array1,array2,array3...)----把两个或多个数组合并成一个数组
array_slice(array,start,length,preserve)----在数组中根据条件取出一段值
array_diff(array1,array2,array3...)----返回两个数组的差集数组
array_intersect(array1,array2,array3...)----计算数组的交集
array_search(value,array,strict)----在数组中搜索给定的值
array_splice(array,start,length,array)----移除数组的一部分且替代它
array_key_exists(key,array)----判断某个数组中是否存在指定的key
shuffle(array)----把数组中的元素按随机顺序重新排列
array_flip(array)----交换数组中的键和值
array_reverse(array,preserve)----将原数组中的元素顺序翻转,创建新的数组并返回
array_unique(array)----移除数组中重复的值

23、php读取文件内容的几种方法和函数?

打开文件,然后读取。fopen(filename,mode,include_path,context) fread(file,length)
打开读取一次完成 file_get_contents(path,include_path,context,start,max_length)

24、以下程序,变量str什么值的情况下输入111?

if( ! $str ) {
      echo 111; }

在$str值为:0,’0′,false,null,”",[]0.0

25、你所知道的PHP的一些技术(smarty等)?

Smarty,jquery,ajax,memcache,div+css,js,mysqli,pdo,svn,thinkphp,brophp,yii

26、你所熟悉的PHP论坛系统 有哪些?

Discuz

27、你所熟悉的PHP商城系统 有哪些?

Ecshop

28、你所熟悉的PHP开发框架 有哪些?

laravel,thinkphp

29、常用的魔术方法有哪些?举例说明?

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

<?php
class Person{
     
	public $a = 0;
	public $arr = ['Moe','Larry','Curly'];

	function __construct($name=""){
     
		// unset($this->b);		//__unset() 使用unset()时候会调用
		
		//$this->getB;			//__get() 调用未定义的属性时候调用。
	}

	function __sleep(){
     
		echo '__sleep';
		return [];
	}
	
	public function __wakeup() {
     
        echo "__wakeup";
    }

	function __unset($argu){
     
		echo 'unset';
	}

	function __set($name,$val){
     
		echo $name;
	}

	function __get($name){
     
		echo '__get';
	}

	function __isset($name){
     
		echo '__isset';
	}

	function __call($name,$argument){
     
		echo '__call';
	}

	function __invoke(){
     
		echo 11111;
	}

	function __toString(){
     
		return "__toString";
	}

	function __destruct(){
     
		echo '__destruct';
	}

	function __clone(){
     
		echo '__clone';
	}

	static function __callStatic($name,$argument){
     
		echo '__callStatic';
	}

	static function __set_state(){
     
		echo '__set_state';
	}
}

$person = new Person("cxh");
// $p();						// __invoke() 当尝试把对象当方法调用时调用

// echo $p;						// __toString() 把对象转换成字符串的时候会调用。比如 echo。

// var_dump(isset($p->b));		// __isset() 使用isset()或empty()函数时候会调用。

// $p->test(1);					// __call() 调用一个不存在的方法的时候调用


// Person::test(); 				// __callStatic()调用一个不存在的静态方法是调用

// $ps = clone $p;				// __clone() 当使用clone复制一个对象时候调用。

// echo serialize($p);			// __sleep() 使用serialize序列化时候调用

// var_dump(unserialize(serialize($person)));		// __wakeup() 使用unserialize反序列化的时候调用。

// eval('' . var_export($person, true) . ';'); // __set_state() 当使用var_export()函数时候调用。接受一个数组参数

30、TCP和UDP的的区别

1.TCP基于连接与UDP无连接

2.TCP要求系统资源较多,UDP较少; 

3.UDP程序结构较简单 

4.字节流模式(TCP)与数据报模式(UDP); 

5.TCP保证数据正确性,UDP可能丢包 

6.TCP保证数据顺序,UDP不保证 

# TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;
UDP尽最大努力交付,即不保   证可靠交付

3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,
因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP首部开销20字节;UDP的首部开销小,只有8个字节

6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

31、tcp三次握手

第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;
SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即
SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和
服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

# 四次挥手
1)客户端进程发出连接释放报文,并且停止发送数据。此时,客户端进入FIN-WAIT-1(终止等待1)状态。

2)服务器收到连接释放报文,发出确认报文,此时,服务端就进入了CLOSE-WAIT(关闭等待),半关闭状态,
即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,
也就是整个CLOSE-WAIT状态持续的时间。

3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接
释放报文(在这之前还需要接受服务器发送的最后的数据)。

4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,半关闭状态,等待客户端的确认。

5)客户端收到服务器的连接释放报文后,必须发出确认,当客户端撤销相应的TCB后,才进入CLOSED状态。

6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。
可以看到,服务器结束TCP连接的时间要比客户端早一些。

常见面试题
#【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应的
SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能
先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,
我才能发送FIN报文,因此不能一起发送。故需要四步握手。

#【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,
有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK
回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,
它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一
个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。
所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,
2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,
那么Client推断ACK已经被成功接收,则结束TCP连接。

# 【问题3】为什么不能用两次握手进行连接?

答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方
就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

  现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S
发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功
地建了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,
不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立
成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。
这样就形成了死锁。

# 【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到
一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,
服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为
客户端出了故障,接着就关闭连接。

php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第1张图片
php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第2张图片
32、数据库索引有几类,分别是什么?什么时候该用索引?

普通索引、主键索引、唯一索引,组合索引,外键索引,全文索引

并非所有的数据库都以相同的方式使用索引,作为通用规则,只有当经常查询列中的数据时才需要在表上创建索引。

33、$_REQUEST、_POST、_GET、_COOKIE、_SESSION、_FILES的意思是什么?

# 它们都是PHP预定义变量

$_REQUEST:用来获取post或get方式提交的值

$_POST:用来获取post方式提交的值

$_GET:用来获取get方式提交的值

$_COOKIE:用来获取cookie存储的值

$_SESSION:用来获取session存储的值

$_FILES:用来获取上传文件表单的值

34、Include与require的区别

1、执行的原理不一样: 
include包含文件时,程序遇到include才执行的包含,
require是无条件包含也就是如果一个流程使用了require,无论条件成立与否都会先执行require。
include每次都进行读取和评估,而require只处理一次,

2、报错不一样: include包含失败报warning级别的错误,后续代码继续执行;require报fatal error致命
错误,后续代码不执行

3、返回值:include函数有返回值,require没有返回值。

4、效率:如果可能多次执行代码,require函数效率要高于include函数。

5require()include() 语句是语言结构,不是真正的函数

include_once 与 require_once用法基本同上,唯一的不一样是include_once包含同样的文件时,不管包含
多少次,都只执行一次;通常用于包含函数库、常量等等不能重复定义的。

35、了解XSS攻击吗? 如何防止 ?

XSS是跨站脚本攻击,首先是利用跨站脚本漏洞以一个特权模式去执行攻击者构造的脚本,然后利用不安全的
Activex控件执行恶意的行为。

使用htmlspecialchars()函数对提交的内容进行过滤,使字符串里面的特殊符号实体化。

36、字符串“open_door” 转换成 “OpenDoor”、”make_by_id” 转换成 ”MakeById”。

function str_explode($str){
     

	$str_arr = explode("_",$str);
	$str_implode = implode(" ",$str_arr); 
	//ucwords($str_implode) make by id将每个单词首字母变大写
	$str_implode = implode("",explode(" ",ucwords($str_implode)));

	return $str_implode;
}

$strexplode = str_explode("make_by_id");

37、PHP处理字符串的常用函数?(重点看函数的‘参数’和‘返回值’)

1trim()移除字符串两侧的空白字符和其他字符;
2substr_replace(string,replacement,start,length) 把字符串的一部分替换为另一个字符串,截取替换
3substr_count(string,substring,start,length) 计算子串在字符串中出现的次数;截取计算
4substr(string,start,length)返回字符串的一部分;
5strtolower()把字符串转换为小写字母;
6strtoupper()把字符串转换为大写字母;
7strtr()转换字符串中特定的字符;
8strrchr()查找字符串在另一个字符串中最后一次出现;
9strstr(string,search,before_search)返回第一次出现字符串前/后的字符串,默认false后(大小写敏感)
10strrev()反转字符串;
11strlen()返回字符串的长度;
12str_replace(find,replace,string,count)替换字符串中的一些字符(对大小写敏感);
13print()输出一个或多个字符串;
14explode()把字符串打散为数组;
15is_string()检测变量是否是字符串;
16strip_tags()从一个字符串中去除HTML标签;
17mb_substr()用来截中文与英文的函数

38、nginx和apache的区别

# 1:处理php文件的方式不一样
	apache通过libphp5.so模块处理,nginx通过php-fpm处理
	
# nginx相对于apache的优点
	轻量级,同样是 web 服务,比Apache 占用更少的内存及资源,
	高并发,Nginx 处理请求是异步非塞的,而Apache 则是阻塞型的,
	在高并发下Nginx 能保持低资源低消耗高性能;
	高度模块化的设计
	编写模块相对简单;
	社区活跃,各种高性能模块出品迅速。
	
# apache相对于nginx的优点
	rewrite更强大,模块多,bug少,稳定

总结:需要性能,追求高并发用nginx,只求性能稳定用apache,nginx处理动态请求是弱项,
	 只适合处理静态网页或反向代理

39、php正则两种模式

# 后向引用
$str = 'ccc';
$pattern = '/(.*)<\/b>/';
echo preg_replace($pattern, '\\1', $str);

# 贪婪模式 (?、U 取消贪婪模式)
$str = 'cccaaa';
$pattern = '/.*?<\/b>/';
$pattern = '/.*?<\/b>/U';
echo preg_replace($pattern, '\\1', $str);

40、抽象类和接口的区别

1、抽象类被子类继承,接口被类实现

2、接口只能做方法声明,抽象类可以做方法声明,也可以做方法实现

3、接口里定义的变量只能是公共的,静态的常量,抽象类的变量是普通变量

4、接口是设计的结果,抽象类是重构的结果

5、抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别更高

6、抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量

7、抽象类主要用来抽象类别,接口用来抽象功能

8、都不能实例化,抽象方法必须重载才能使用

41、常见的设计模式

# 工厂模式,单例模式,注册树模式,适配器模式,观察者模式,策略模式

# 单例模式
class Singleton
{
     
	private static $instance;
  
	private function __construct()
	{
     
		// Do nothing.
	}
	//获取实例
	public static function getInstance() 
	{
     
		if (!(self::$instance instanceof self)) {
     
			self::$instance = new self();
		}
	 	return self::$instance;
	}
  
	//防止克隆
	private function __clone() 
	{
     
		// Do nothing.
	}
}

#注册树模式
class Register
{
     
	private static $object;
	
	static function set($alias,$obj)
	{
     
		self::$object[$alias] = $obj;
	}

	static function get($alias)
	{
     
		return self::$object[$alias];
	}

	static function _unset($alias)
	{
     
		unset(self::$object[$alias]);
	}
}

class Database{
     
	static function test(){
     
		echo 'Database';
	}
}

Register::set('db',new Database);

Register::get('db')::test();


# 适配器模式
interface Database
{
     
	function connect();

	function query();
}

class Mysql implements Database{
     
	function connect(){
     

	}

	function query(){
     

	}
}

class PDOs implements Database{
     
	function connect(){
     

	}

	function query(){
     

	}
}

42、三大流行框架优缺点

# ThinkPHP

ThinkPHP(FCS)是一个轻量级的中型框架,是从Java的Struts结构移植过来的中文PHP开发框架。它使用面向对象的
开发结构和MVC模式,并且模拟实现了Struts的标签库,各方面都比较人性化,熟悉J2EE的开发人员相对比较容易上
手,适合php框架初学者。 ThinkPHP的宗旨是简化开发、提高效率、易于扩展,其在对数据库的支持方面已经包括
MySQL、MSSQL、Sqlite、PgSQL、 Oracle,以及PDO的支持。ThinkPHP有着丰富的文档和示例,框架的兼容性较强,
但是其功能有限,因此更适合用于中小项目的开发。

优点:

1.易于上手,有丰富的中文文档;

2.框架的兼容性较强,PHP4和PHP5完全兼容、完全支持UTF8等。

3. 适合用于中小项目的开发

缺点:

1.对Ajax的支持不是很好;

2.目录结构混乱,需要花时间整理;

3.上手容易,但是深入学习较难。

# Yii
Yii 是一个基于组件的高性能php框架,用于开发大型Web应用。Yii采用严格的OOP编写,并有着完善的库引用以及全
面的教程。从 MVC,DAO/ActiveRecord,widgets,caching,等级式RBAC,Web服务,到主题化,I18N和L10N,
Yii提供了 今日Web 2.0应用开发所需要的几乎一切功能。事实上,Yii是最有效率的PHP框架之一。

优点:

纯OOP

用于大规模Web应用

模型使用方便

开发速度快,运行速度也快。性能优异且功能丰富

使用命令行工具。

缺点:

对Model层的指导和考虑较少

文档实例较少

英文太多

要求PHP技术精通,OOP编程要熟练!

View并不是理想view,理想中的view可能只是html代码,不会涉及PHP代码。

# CodeIgniter
优点:
Code Igniter推崇“简单就是美”这一原则。没有花哨的设计模式、没有华丽的对象结构,一切都是那么简单。几行
代码就能开始运行,再加几 行代码就可以进行输出。可谓是“大道至简”的典范。 配置简单,全部的配置使用PHP脚本
来配置,执行效率高;具有基本的路由功能,能够进行一定程度的路 由;具有初步的Layout功能,能够制作一定程度
的界面外观;数据库层封装的不错,具有基本的MVC功能. 快速简洁,代码不多,执行性能高,框架简 单,容易上手,
学习成本低,文档详细;自带了很多简单好用的library,框架适合小型应用.

缺点:
本身的实现不太理想。内部结构过于混乱,虽然简单易用,但缺乏扩展能力。 把Model层简单的理解为数据库操作.
 框架略显简单,只能够满足小型应用,略微不太能够满足中型应用需要.

评价:
总体来说,拿CodeIgniter来完成简单快速的应用还是值得,同时能够构造一定程度的layout,便于模板的复用,
数据操作层来说封装的不 错,并且CodeIgniter没有使用很多太复杂的设计模式,执行性能和代码可读性上都不错。
至于附加的library 也还不错,简洁高效。

# Laravel 框架
优点:
Laravel 的设计思想是很先进的,非常适合应用各种开发模式TDD, DDD 和BDD,作为一个框
架,它准备好了一切,composer 是个php 的未来,没有composer,PHP 肯定要走向没落。
laravel 最大的特点和优秀之处就是集合了php 比较新的特性,以及各种各样的设计模式,
Ioc 容器,依赖注入等。

缺点:
基于组件式的框架,所以比较臃肿

43、在浏览器输入网址到页面显示,期间发生了哪些过程?

# 1、查询DNS,获取域名对应的IP。1)检查本地hosts文件是否有这个网址的映射,如果有,就调用这个IP地址映射,解析完成。

(2)如果没有,则查找本地DNS解析器缓存是否有这个网址的映射,如果有,返回映射,解析完成。

(3)如果没有,则查找填写或分配的首选DNS服务器,称为本地DNS服务器。服务器接收到查询时:
	 如果要查询的域名包含在本地配置区域资源中,返回解析结果,查询结束,此解析具有权威性。
	 如果要查询的域名不由本地DNS服务器区域解析,但服务器缓存了此网址的映射关系,返回解析结果,
	 查询结束,此解析不具有权威性。

(4)如果本地DNS服务器也失效:
	如果未采用转发模式(迭代),本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后,会判断这个域名
	(如.com)是谁来授权管理,并返回一个负责该顶级域名服务器的IP,本地DNS服务器收到顶级域名服务器IP
	信息后,继续向该顶级域名服务器IP发送请求,该服务器如果无法解析,则会找到负责这个域名的下一级DNS
	服务器(如http://baidu.com)的IP给本地DNS服务器,循环往复直至查询到映射,将解析结果返回本地
	DNS服务器,再由本地DNS服务器返回解析结果,查询完成。如果采用转发模式(递归),则此DNS服务器就
	会把请求转发至上一级DNS服务器,如果上一级DNS服务器不能解析,则继续向上请求。最终将解析结果依次
	返回本地DNS服务器,本地DNS服务器再返回给客户机,查询完成。
	
# 2、客户机发送HTTP请求报文:1)应用层:客户端发送HTTP请求报文

(2)传输层:切分长数据,并确保可靠性。

(3)网络层:进行路由

(4)数据链路层:传输数据

(5)物理层:物理传输bit

# 3、服务器端经过物理层→数据链路层→网络层→传输层→应用层,解析请求报文,发送HTTP响应报文。

# 4、客户端解析HTTP响应报文

# 5、浏览器开始显示HTML

# 6、浏览器重新发送请求获取图片、CSS、JS的数据。

# 7、如果有AJAX,浏览器发送AJAX请求,及时更新页面。

44、获取客户端ip、服务端ip、浏览器属性

客户端ip:$_SERVER['REMOTE_ADDR']	//代理服务器除外

服务器ip:$_SERVER["SERVER_ADDR"]

浏览器属性:$_SERVER["HTTP_USER_AGENT"]

45、使用php写一个方法,可以获取指定目录下的所有文件和文件夹

function getDirContent($path){
     
	//检查文件是否是目录
    if(is_dir($path)){
     
        return false;
    }

    $arr = [];

    //列出该目录中的所有文件和目录
    $data = scandir($path);
    foreach ($data as $value){
     
    	//.代表本目录,..代表上级目录
        if($value != '.' && $value != '..'){
     
            $arr[] = $value;
        }
    }

    return $arr;
}

#包含子目录
function searchDir($path,&$files){
     

    if(is_dir($path)){
     

        $opendir = opendir($path);

        while ($file = readdir($opendir)){
     
            if($file != '.' && $file != '..'){
     
                searchDir($path.'/'.$file, $files);
            }
        }
        closedir($opendir);
    }
    if(!is_dir($path)){
     
        $files[] = $path;
    }
}

//$dir:文件目录,$files:文件以及目录数组
$filenames = searchDir($dir, $files);

foreach ($filenames as $value){
     
    echo $value . '
'
; }

46、把html转换为实体的函数是?转义特殊字符的函数是?

# 把html转换为实体:htmlspecialchars()

# 转义特殊字符的函数:addslashes

47、php连接mysql出现乱码的原因有哪些?

1.架设服务器安装MYSQL时的会让你选择一种编码,如果这种编码与你的PHP网页不一致,可能就会造成乱码

2.在PHPMYADMIN或mysql-front等系统 创建数据库时会让你选择一种编码,如果这种编码与你的PHP网页不一致
,也有可能造成PHP页面乱码

3.创建表时会让你选择一种编码,如果这种编码与你的网页编码不一致,也可能造成PHP页面乱码

4.创建表时添加字段是可以选择编码的,如果这种编码与你的网页编码不一致,也可能造成PHP页面乱码

5.用户提交页面的编码与显示数据的页面编码不一致,就肯定会造成PHP页面乱码.
   如用户输入资料的页面是big5码, 显示用户输入的页面却是gb2312,这种100%会造成PHP页面乱码
   
6.PHP页面字符集不正确

48、mysql注入攻击原理,如何预防?

# 注入攻击原理
sql字符串拼接

# 预防
1.开启php的魔术模式,,magic_quotes_gpc = on即可,当一些特殊字符出现在网站前端的时候,就会自动进行
转化,转化成一些其他符号导致sql语句无法执行。

2.网站代码里写入过滤sql特殊字符的代码,对一些特殊字符进行转化,比如单引号,逗号,*,(括号)AND 1=1
反斜杠,select union等查询的sql语句都进行安全过滤,限制这些字符的输入,禁止提交到后端中去。

3.开启网站防火墙,IIS防火墙,apache防火墙,nginx防火墙,都有内置的过滤sql注入的参数,当用户输入参数
get、post、cookies方式提交过来的都会提前检测拦截,也可以向国内专业做网站安全的公司去咨询。

49、php导出excel数据量过大,php excel类效率低,如何优化?

1、切割数据导出
2、优化sql语句
3、建议将php升级到最新的php7版本节省内存,且计算速度要快
4、使用fputcsv()导出csv
5、直接运行sql语句进行导出

50、HTTP与HTTPS有什么区别?

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任
何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,
HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。

为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,
HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。

# HTTP和HTTPS的基本概念

HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW
服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。

HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此
加密的详细内容就需要SSL。

HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站
的真实性。

# HTTP与HTTPS有什么区别?

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是4434、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,
   比http协议安全。

# HTTPS的优点

尽管HTTPS并非绝对安全,掌握根证书的机构、掌握加密算法的组织同样可以进行中间人形式的攻击,但HTTPS
仍是现行架构下最安全的解决方案,主要有以下几个好处:
  
(1)使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;

(2)HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据
	在传输过程中不被窃取、改变,确保数据的完整性。

(3)HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。

(4)谷歌曾在20148月份调整搜索引擎算法,并称“比起同等HTTP网站,采用HTTPS加密的网站在搜索结果中的
	排名将会更高”。
	
# HTTPS的缺点

虽然说HTTPS有很大的优势,但其相对来说,还是存在不足之处的:

(1)HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%,增加10%20%的耗电;

(2)HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗,甚至已有的安全措施也会因此而受到影响;

(3)SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。

(4)SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗。

(5)HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。最关
	键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。

51、在PHP中,当前脚本的名称(不包括路径和查询字符串)记录在预定义变量(1)中;而链接到当前页面的的前一页面URL记录在预定义变量(2)中

//当前脚本的名称
echo $_SERVER['PHP_SELF'] . "
"
; echo $_SERVER['SCRIPT_NAME'] . "
"
; //链接到当前页面的前一页面的 URL 地址: echo $_SERVER['HTTP_REFERER'] . "
"
; //前执行脚本的绝对路径名 echo $_SERVER["SCRIPT_FILENAME"] . "
"
; //查询(query)的字符串(URL 中第一个问号 ? 之后的内容):id=1&bi=2 echo $_SERVER["QUERY_STRING"] . "
"
; //当前运行脚本所在的文档根目录 echo $_SERVER["DOCUMENT_ROOT"] . "
"
;

52、如果返回“找不到文件”的提示,则可用 header 函数,其语句为?

header("HTTP/1.0 404 Not Found");

53、数组排序函数

sort() - 对数组进行升序排列
rsort() - 对数组进行降序排列
asort() - 根据关联数组的值,对数组进行升序排列
ksort() - 根据关联数组的键,对数组进行升序排列
arsort() - 根据关联数组的值,对数组进行降序排列
krsort() - 根据关联数组的键,对数组进行降序排列

54、写出一个正则表达式,过虑网页上的所有JS/VBS脚本(即把script标记及其内容都去掉)

$script="以下内容不显示:<SCRIPT language='javascript'>alert('cc');
</SCRIPT>";
echo preg_replace("/].*?>.*?<\/script>/si", "替换内容", $script);

# .*:任意字符
# ?:可有可无
# [^>]:非 > 字符,^ 取反的意思
# s:忽略换行
# i:忽略大小写

55、php数组合并有几种区别

# 1、“+”运算符
	1:不覆盖,只是追加不存在的键名和对应的值。
	2:键名不重新索引。
	3:无论是全部数字键名还是混合,都只是追加键名和值,如果键名相同则不进行追加,
	   即把最先出现的值作为最终结果返回。
	   
# 2、array_merge
	1:数字索引,不会覆盖,值合并后,键名会连续方式重新索引
	2:字符串键名,则该键名后面的值将覆盖前一个值

# 3、array_merge_recursive
	1:数字索引,不会覆盖,值合并后,键名会连续方式重新索引
	2:字符串键名,将多个相同键名的值递归组成一个数组

56、php如何实现页面跳转

1header('location:http://www.baidu.com');

2、echo '';
	
3、$url = "http://www.baidu.com" ;  
   echo " ";  

57、了解XSS、CSRF、SQL注入原理与防范

1、xss攻击,跨站脚本攻击值得是攻击者在网页中嵌入恶意脚本程序,当用户打开浏览器,脚本程序便开始在客户端
   的浏览器上执行,一盗取客户端cookie等信息,利用系统对用户的信任。
   
防御措施:使用htmlspecialchars()函数把预定义的字符转换为 HTML 实体。

2、csrf攻击是跨站请求伪造,攻击者盗用了你的身份,以你的名义向第三方网站发送恶意请求,利用系统对页面
   浏览器的信任。
防御措施:
	(1):将cookie设置为HttpOnly。如果cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到cookie
		信息,这样能有效的防止XSS攻击,窃取cookie内容,这样就增加了cookie的安全性。
		# 1.PHP 5.2以下支持
		header(“Set-Cookie: hidden=value; httpOnly”);
		# 2.PHP5.2以上支持
		ini_set(“session.cookie_httponly”, 1);
		# 3.兼容全部PHP版本
		setcookie(“name”,”hello”, NULL, NULL, NULL, NULL, TRUE);

	(2):设置token验证身份
	(3):通过refer识别验证来源网站是否合法,不属于本网站拒绝请求
3、
防御措施:
	1):对用户的输入进行过滤处理后,在进行操作,如:addslashes()方法或者开启magic_quotes_gpc方法
	2):完美方法:
		使用Prepared Statement机制的PDO,预处理sql和参数绑定
		MYSQLi一样原理

58、请在表格右侧写出执行每一行后每个变量的值,每行变量的值依赖上一行
php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第3张图片

$a = 1;
$b = 2;
$c = 3;
$d = 4;
$e = 5;

$b = &$a;
echo '$a=' . $a .',$b=' . $b . ',$c=' . $c . ',$d=' . $d . ',$e=' . $e;
echo PHP_EOL . "
"
; $b = "30$a"; echo '$a=' . $a .',$b=' . $b . ',$c=' . $c . ',$d=' . $d . ',$e=' . $e; echo PHP_EOL . "
"
; $e = $c = ($b++); echo '$a=' . $a .',$b=' . $b . ',$c=' . $c . ',$d=' . $d . ',$e=' . $e; echo PHP_EOL . "
"
; $d = ($c > $b) ? ($a+=2) : (--$b); echo '$a=' . $a .',$b=' . $b . ',$c=' . $c . ',$d=' . $d . ',$e=' . $e; echo PHP_EOL . "
"
; $e = implode(',',array($c,$e)); echo '$a=' . $a .',$b=' . $b . ',$c=' . $c . ',$d=' . $d . ',$e=' . $e; echo PHP_EOL . "
"
;

59、以下代码输出是什么?为什么?

$a = 10;
$b = 20;
function multiply(){
     
	$a = $a * 10;
	return $a;
}

function add(){
     
	static $b = 0;
	return $b++;
}

$b++;
multiply();
add();
$b = add();
list($b,$a) = array($a,$b);
var_dump($a,$b);

执行$b++时,$b = 21;

60、多线程和多进程的区别

进程是资源分配的最小单位,线程是CPU调度的最小单位

进程是程序在计算机上的一次执行活动

线程是可执行代码的可分派单元,就是把一个进程分为很多片,每一片都可以是一个独立的流

# 区别

1:进程是资源分配最小单位,线程是程序执行的最小单位;

2:进程有自己独立的地址空间,每启动一个进程,系统都会为其分配地址空间,建立数据表来维护代码段、
堆栈段和数据段,线程没有独立的地址空间,它使用相同的地址空间共享数据;

3:CPU切换一个线程比切换进程花费小;

4:创建一个线程比进程开销小;

5:线程占用的资源要⽐进程少很多。

6:线程之间通信更方便,同一个进程下,线程共享全局变量,静态变量等数据,进程之间的通信需要以通信的方式
(IPC)进行;(但多线程程序处理好同步与互斥是个难点)

7:多进程程序更安全,生命力更强,一个进程死掉不会对另一个进程造成影响(源于有独立的地址空间),
多线程程序更不易维护,一个线程死掉,整个进程就死掉了(因为共享地址空间);

8:进程对资源保护要求高,开销大,效率相对较低,线程资源保护要求不高,但开销小,效率高,可频繁切换;

php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第4张图片
php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第5张图片
61、php常量与变量的区别

1. 常量前面没有美元符号($)
2. 常量只能用 define() 函数定义,而不能通过赋值语句
3. 常量可以不用理会变量范围的规则而在任何地方定义和访问
4. 常量一旦定义就不能被重新定义或者取消定义
5. 常量的值只能是标量

# php常量与变量的作用域:
常量的作用域:常量无论在那定义,都可以直接调用,没有作用域。

变量的作用域:变量定义在那个范围就在那个范围有效,如函数中定义的变量就只在函数中有效。

62、php静态变量和变量的区别

1. 普通变量用 $(美元符号) 表示,静态变量用static表示
2. 普通变量:函数执行完自动销毁;静态变量:不会被销毁保留上次值
3. 普通变量全局会多次初始化,静态变量:static全局变量只初使化一次
4. 静态和公共、受保护、私有这3种属性格式没有关系
5. 普通变量:存在堆内存,静态变量:存在方法区中的静态存储区

mysql面试题

1、varchar与char有什么区别?

# char:
1、表示定长,长度固定
2、插入的长度小于定义长度时,则用空格填充
3、存取速度快,方便程序的存储与查找,空间换取时间
4、最多能存放的字符个数 255 和编码无关
# varchar:
1、表示变长,即长度可变
2、插入多长就存多长
3、速度较慢,时间换空间
4、最多能存放 65532 个字符

尽量避免使用BLOB和TEXT类型,查询会使用临时表,导致严重的性能开销

2、MySQL查询慢有哪些原因?能否给出你的优化建议?为什么会有死锁?

# 查询速度慢的原因很多,常见如下几种:

1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 

2、I/O吞吐量小,形成了瓶颈效应。 

3、没有创建计算列导致查询不优化。 

4、内存不足 

5、网络速度慢 

6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量) 

7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷) 

8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。 

9、返回了不必要的行和列 

10、查询语句不好,没有优化


# 可以通过如下方法来优化查询 :

1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在
支持。数据量(尺寸)越大,提高I/O越重要. 

2、纵向、横向分割表,减少表的尺寸(sp_spaceuse) 

3、升级硬件 

4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用
默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单
一索引如性别字段 

5、提高网速; 

6、扩大服务器的内存

# 为什么会有死锁?
在多道程序环境中,多个进程可以竞争有限数量的资源。当一个进程申请资源时,如果这时没有可用资源,那么这个
进程进入等待状态。有时,如果所申请的资源被其他等待进程占有,那么该等待进程有可能再也无法改变状态。

3、Mysql锁机制

# 表锁:当多个查询同一时刻进行数据修改时,就会产生并发控制的问题

共享锁(读锁):共享的,不堵塞,多个用户可以同时读一个资源,互不干扰

排它锁(写锁):排他的,一个写锁会阻塞其它的写锁和读锁,只允许一个人进行写入,
			 防止其它用户读取正在写入的资源
# 锁粒度:

表锁:系统性能开销最小,会锁定整张表,MyISAM

行锁:最大程度支持并发处理,开销大,InnoDB

4、请写出数据类型(int char varchar datetime text)的意思;请问 varchar 和 char有什么区别?

Int 整数 char 定长字符 Varchar 变长字符 Datetime 日期时间型 Text 文本型 

# Varchar 与char的区别 
char是固定长度的字符类型,分配多少空间,就占用多长空间。 
Varchar是可变长度的字符类型,内容有多大就占用多大的空间,能有效节省空间。 
由于varchar类型是可变的,所以在数据长度改变的时,服务器要进行额外的操作,所以效率比char类型低。

5、MyISAM 和 InnoDB 的基本区别?索引结构如何实现?

# MyISAM:

1、不支持事务
2、表锁
3、易产生碎片,要经常优化
4、读写速度较快
5、拥有全文索引,压缩,空间函数
6、不支持事务和行级锁,不支持崩溃后的安全恢复
7、表存储在两个文件:MYD和MYI
8、设计简单,某些场景下性能很好

# InnoDB:

1、默认事务型引擎,支持事务
2、支持行级锁
3、支持崩溃后的安全恢复
4、读写速度比MyISAM慢
5、数据存储在共享表空间,可以通过配置分开
6、对主键查询的性能高于其它类型的存储引擎
7、内部做了很多优化,从磁盘读取数据时自动在内存创建hash索引,插入数据自动构建插入缓冲区
8、通过一些机制和工具支持真正的热备份
9、支持外键

alter table test add index (`name`);

其它表引擎:Archive、Blackhole、CSV、Memory

6、SQL注入漏洞产生的原因 ? 如何防止?

SQL注入产生的原因:程序开发过程中不注意规范书写sql语句和对特殊字符进行过滤,导致客户端可以通过全局变量
POST和GET提交一些sql语句正常执行。

防止SQL注入:

1、开启配置文件中的magic_quotes_gpc和magic_quotes_runtime设置

2、执行sql语句时使用addslashes进行sql语句转换

3、Sql语句书写尽量不要省略小引号和单引号

4、过滤掉sql语句中的一些关键字:update、insert、delete、select*

5、提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不易被猜到的。

6、Php配置文件中设置register_globals为off,关闭全局变量注册

7、写出三种以上MySQL数据库存储引擎的名称(提示:不区分大小写)

MyISAM、InnoDB、BDB(Berkeley DB)、Merge、Memory(Heap)、Example、Federated、Archive、
CSV、Blackhole、MaxDB 等等十几个引擎

8、在mysql执行一条sql,期间发生了哪些过程?

1、创建连接
2、查询缓存
3、分析器:分析sql语句,语法是否错误
4、优化器:优化sql语句,选择效率高的方案
5、执行器:执行sql语句,验证是否有读/写权限,没有返回权限错误,
   有就打开表调用指定的存储引擎获取执行结果,返回客户端

9、普通索引和唯一索引检索过程中有哪些区别?

# MySQL的查询操作

普通索引:查找到第一个满足条件的记录后,继续向后遍历,直到第一个不满足条件的记录
唯一索引:由于索引定义了唯一性,查找到第一个满足条件的记录后,直接停止继续检索

普通索引会多检索一次,几乎没有影响。因为InnoDB的数据是按照数据页为单位进行读写的,需要读取数据时,
并不是直接从磁盘读取记录,而是先把数据页读到内存,再去数据页中检索

# MySQL的更新操作
普通索引:将数据页从磁盘读入内存,更新数据页
唯一索引:将数据页从磁盘读入内存,判断是否唯一,再更新数据页


由于MySQL中有个changebuffer的机制,会导致普通索引和唯一索引在更新上有一定的区别。
changebuffer的作用是为了降低IO操作,避免系统负载过高。changebuffer将数据写入数据页的过程,叫做merge。
如果需要更新的数据页在内存中时,会直接更新数据页;如果数据不在内存中,会先将更新操作记入changebuffer,
当下一次读取数据页时,顺带merge到数据页中,changebuffer也有定期merge策略。数据库正常关闭的过程中,
也会触发merge。

对于唯一索引,更新前需要判断数据是否唯一(不能和表中数据重复),如果数据页在内存中,就可以直接判断并且
更新,如果不在内存中,就需要去磁盘中读出来,判断一下是否唯一,是的话就更新。changebuffer是用不到的。
即使数据页不在内存中,还是要读出来。

changebuffer用的是bufferpool里的内存,因此不能无限增大。changebuffer的大小,可以通过参数
innodb_change_buffer_max_size来动态设置。这个参数设置为50的时候,表示changebuffer的大小最多只能
占用bufferpool的50%

10、存储过程和触发器

# 存储过程
1、为以后的使用而保存的一条或多条Mysql语句的集合
2、存储过程就是有业务逻辑和流程的集合
3、可以在存储过程中创建表,更新数据,删除等等

使用场景:
1、通过把处理封装在容易使用的单元中,简化复杂的操作
2、保证数据的一致性
3、简化对变动的管理

# 触发器
提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊存储过程

使用场景:
1、通过数据库中的相关表实现级联更改
2、实时监控某张表中的某个字段的更改而需要做出相应的处理
3、生成某些业务编号
滥用会造成数据库及应用程序的维护困难

11、简单描述Mysql中,索引,主键,唯一索引,联合索引的区别,对数据库的性能有什么影响

# 索引:

类似于书籍的目录,要想找到一本书的某个特定主题,需要先查找书的目录,定位对应的页码
存储引擎使用类似的方式进行数据查询,先去索引当中找到对应值,然后根据匹配的索引找到对应的数据行

# 索引对性能的影响

1、大大减少服务器需要扫描的数据量
2、帮助服务器避免排序和临时表
3、将随机IO变成顺序IO
4、大大提高查询速度,降低写的速度,占用磁盘

# 索引类型
1、普通索引:最基本的索引,没有任何约束限制
2、唯一索引:与普通的索引类似,但是具有唯一性
3、主键索引:特殊的唯一索引,不允许有空值
4、组合索引:将多个列组合在一起创建索引,可以覆盖多个列(遵循最左原则)
5、外键索引:只要InnoDB类型的表才可以使用,保证数据的一致性,完整性和实现级联操作
6、全文索引:只能用于MyISAM,并且只能对英文进行检索

# 主键和唯一索引的区别
1、一个表只能有一个主键索引,可以有多个唯一索引
2、主键索引一定是唯一索引,唯一索引不是主键索引
3、主键可以与外键构成参照完整性约束,防止数据不一致

# Mysql索引的创建原则
1、最适合索引的列是出现在where子句中的列,或连接字句中的列而不是出现在select关键字后的列
2、索引列的基数越大,索引的效果越好
3、对字符串索引,应该制定一个前缀长度,可以节省大量的索引空间
4、根据情况创建复合索引,复合索引可以提高查询速度
5、避免创建过多索引,索引会额外占用磁盘空间,降低写操作效率
6、主键尽可能选择较短的数据类型,可以有效减少索引的磁盘占用,提高查询效率

# Mysql索引的注意事项
1、复合索引遵循前缀原则(最左原则)
2、like查询,%不能在前,可以使用全文索引('%字符串%'会导致索引失效,'字符串%'索引有效)
3、column is null 可以使用索引
4、如果msyql估计使用索引比全表扫描更慢,会放弃使用索引
5、如果or前的条件中的列有索引,后面没有,索引都不会被用到
6、列类型是字符串,查询时一定要给值加引号,否则索引失效

12、请简述项目中优化SQL语句执行效率的方法,从哪些方面,SQL语句性能如何分析?

# 1、分析SQL查询慢的方法
	记录慢查询日志,分析查询日志,不要打开慢查询日志进行分析,这样比较浪费时间和精力,
	可以使用pt-query-digest工具进行分析
# 2、使用show profile,进入mysql命令界面操作
	set profiling=1;开启,服务器上执行的所有语句会检测消耗的时间,存到临时表中
	show profile for query Query_ID
# 3、使用show status
	show status会返回一些计数器,show global status查看服务器级别的所有计数
	有时根据这些计数,可以猜测出哪些操作代价较高或者消耗时间多
# 4、使用show processlist
	观察是否有大量线程处于不正常的状态或者特征
# 5、使用explain或desc
	分析单条SQL语句 explain select * from a\G;

# SQL语句优化的一些方法:
1、优化查询过程中的数据访问
#	问题原因:
	1)访问数据太多导致查询性能下降
	2)确定应用程序是否在检索大量超过需要的数据,可能是太多行或列
	3)确认MySQL服务器是否在分析大量不必要的数据行
#	避免使用如下SQL语句:
	1)、查询不需要的记录,使用limit解决
	2)、多表关联返回全部列,指定A.id,A.name,B.age
	3)、总是取出全部列,select * 会让优化器无法完成索引覆盖扫描的优化
	4)、重复查询相同的数据,可以缓存数据,下次直接读取缓存
#	是否在扫描额外的记录:
	使用explain来进行分析,如果发现查询需要扫描大量的数据但只返回少数的行,可以通过如下技巧去优化:
	1)、使用索引覆盖扫描,把所有用的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结果
	2)、改变数据库和表的结构,修改数据表范式,空间换时间
	3)、重写SQL语句,让优化器可以以更优的方式执行查询
	
2、优化长难的查询语句
	MySQL内部每秒能扫描内存中百万行数据,相比之下,响应数据给客户端就要慢的多
	使用尽可能少的查询是好的,但是有时将一个大的查询分解为多个查询是很有必要的
#	1)切分查询
	将一个大的查询分为多个小的相同的查询
	一次性删除1000万的数据要比一次删除一万,暂停一会的方案更加损耗服务器开销
#	2)分解关联查询
	可以将一条关联语句分解成多条SQL来执行
	让缓存的效率更高
	执行单个查询可以减少锁的竞争
	在应用层做关联可以更容易对数据库进行拆分
	查询效率会有大幅提升
	较少冗余记录的查询
	
3、优化特定类型的查询语句
	优化count()查询
	count(*)中的*会忽略所有的列,直接统计所有列数,因此不要使用count(列名)
	MyISAM中,没有任何where条件的count(*)非常快
	当有where条件,MyISAM的count统计不一定比其他表引擎快
	1、可以使用explain查询近似值,用近似值替代count(*)
	2、增加汇总表
	3、使用缓存
	4、优化关联查询
		确定ON或者USING字句的列上有索引
		确保group by和order by中只有一个表中的列,这样MySQL才有可能使用索引
	5、优化子查询,尽可能使用关联查询来替代
	6、优化group by和distinct
		这两种查询均可使用索引优化,是最有效的优化方法
		关联查询中,使用标识列进行分组的效率更高
		如果不需要order by,进行group by时使用order by null,MySQL不会再进行文件排序
		with rollup超级聚合,可以挪到应用程序处理
	7、优化limit分页
		limit偏移量大的时候,查询效率较低
		可以记录上次查询的记录最大id,下次查询时直接根据id来查询
	8、优化union查询
		union all的效率高于union

13、简述MySQL分表操作和分区的工作原理,分别说说分区和分表的使用场景和各自优缺点?

# 考点:分区表的原理,分库分表的原理,延伸:MySQL的复制原理和负载均衡

# 分区表的原理:
	1、对用户而言,分区表是一个独立的逻辑表,但是底层Mysql将其分成了多个物理子表,这对用户来说是透明的
	,每一个分区表都会使用一个独立的表文件
	2、创建表时使用partition by子句定义每个分区存放的数据,执行查询时,优化器会根据分区定义过滤那些
	没有我们需要数据的分区,这样查询只需要查询所需数据在的分区即可
	3、分区的主要目的是将数据按照一个较粗的粒度分在不同的表中,这样可以将相关的数据存放在一起,
	而且如果想一次性删除整个分区的数据也很方便
# 	使用场景
	1、表非常大,无法全部存在内存,或者只在表的最后有热点数据,其他都是历史数据,活跃数据和历史数据分开
	2、分区表的数据更易维护,可以对独立的分区进行独立的操作
	3、分区表的数据可以分布在不同的机器上,从而高效使用资源
	4、可以使用分区表来避免某些特殊的瓶颈
	5、可以备份和恢复独立的分区
# 	限制
	1、一个表最多只能有1024个分区
	25.1版本中,分区表表达式必须是整数,5.5可以使用列分区
	3、分区字段中如果有主键和唯一索引列,那么主键列和唯一列都必须包含进来
	4、分区表中无法使用外键约束
	5、需要对现有表的结构进行修改
	6、所有分区都必须使用相同的存储引擎
	7、分区函数中可以使用的函数和表达式会有一些限制
	8、某些存储引擎不支持分区
	9、对于MyISAM的分区表,不能使用load index into cache
	10、对于MyISAM表,使用分区表时需要打开更多的文件描述符

# 分库分表的原理
	通过一些hash算法或者工具实现将一张数据表垂直或者水平进行物理切分
#	适用场景
	1、单表记录条数达到百万到千万级别时
	2、解决表锁的问题
#	分表方式
	水平分割:表很大,分割后可以降低在查询时需要读的数据和索引的页数,同时也降低了索引的层数,
			 提高查询速度
	使用场景:
		1、表中的数据本身就有独立性,例如表中分别记录各个地区的数据或者不同时期的数据,
		特别是有些数据常用,有些不常用
		2、需要把数据存放在多个介质上
	缺点
	1、给应用增加复杂度,通常查询时需要多个表名,查询所有数据都需union
	2、在许多数据库应用中,这种复杂性会超过它带来的优点,查询时会增加读一个索引层的磁盘次数

	垂直分表:把主键和一些列放在一个表,然后把主键和另外的列放在另一个表中
	使用场景:
	1、如果一个表中某些列常用,而另外一些列不常用
	2、可以使数据行变小,一个数据页能存储更多数据,查询时减少I/O次数
	缺点:
	管理冗余列,查询所有数据需要join操作
# 分表缺点
	有些分表的策略基于应用层的逻辑算法,一旦逻辑算法改变,整个分表逻辑都会改变,扩展性较差
	对于应用层来说,逻辑算法无疑增加开发成本

# MySQL主从复制工作原理
	1、在主库上把数据更改记录到二进制日志
	2、从库将主库的日志复制到自己的中继日志
	3、从库读取中继日志中的事件,将其重放到从库数据中
#	解决的问题:
	1、数据分布:随意停止或开始复制,并在不同地理位置分布数据备份
	2、负载均衡:降低单个服务器的压力
	3、高可用和故障切换:帮助应用程序避免单点失败
	4、升级测试,可以使用高版本的MySQL作为从库

14、现有一统计网站独立访客的需求,流量百万以上,如以 IP 为标识,可以查看当天实时或者指定某天的 IP 数(需要去重),才用 MySQL 来实现,那么:

# 1、你会如何设计表和索引?(文字、sql 均可,方案尽可能高效)
IP 地址转换为整形存储,按日期分表,以 (IP, 日期) 作为联合索引

# 2、数据如何入库,当天实时和某天数据该如何查询?(写出 sql 语句)
redis 队列缓存 -> MySQL 批量入库
select count(ip) from visit_log_{
     yyyymm} where date = {
     yyyy-mm-dd};

15、事务四种隔离级别

对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,
就会导致各种并发问题:

# 脏读:
对于两个事务T1, T2, T1读取了已经被T2更新但还没有被提交的字段。
之后,若T2回滚, T1读取的内容就是临时且无效的。

# 不可重复读:
对于两个事务T1, T2, T1读取了一个字段,然后T2更新了该字段。之后, T1再次读取同一个字段,值就不同了。
		   
# 幻读:
对于两个事务T1,T2,T1从一个表中读取了-个字段,然后T2在该表中插入了一些新的行。
之后,如果T1再次读取同一一个表就会多出几行。

数据库事务的隔离性:数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。

一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,
**隔离级别越高,数据一致性就越好, 但并发性越弱。**

# 四种隔离级别:
# 1、READ UNCOMMITTED(读未提交数据):
允许事务读取未被其他事物提交的变更,脏读、不可重复读和幻读的问题都会出现

# 2、READ COMMITED(读已提交数据):
只允许事务读取已经被其它事务提交的变更,可以避免脏读,但不可重复读和幻读问题仍然可能出现

# 3、REPEATABLE READ(可重复读):
确保事务可以多次从一个字段中读取相同的值在这个事务持续期间,禁止其他事物
对这个字段进行更新可以避免脏读和不可重复读,但幻读的问题仍然存在

# 4、SERIALIZABLE(串行化):
确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新和删除操作,
所有并发问题都可以避免,但性能十分低下.

Oracle支持2种事务隔离级别:1,4,默认1
Mysql支持四种事务隔离级别:默认4

16、mysql中有一个事务频繁出现事务锁等待,结合你的经验,请阐述你分析此问题的方法和步骤?

# 问题描述
1. 在一个事务中同时包括了SELECT,UPDATE语句
2. SELECT和UPDATE涉及到的数据为同一张表中的同一记录
3. 在并发情况下就会触发数据库锁等待和死锁的情况

# 共享锁(S锁)
SELECT 语句时对查询行加的锁类型为共享锁。
共享锁的特性为:不允许其他事务对该记录加排他锁,但是允许加共享锁。
保持时间:可重复度级别中共享锁会保持到事务结束。

# 排他锁(X锁)
MYSQL的默认隔离级别(可重复度)中,UPDATE,INSERT和DELETE语句对操作语句加的锁类型为排他锁
排他锁的特性为:不允许其他事务对该记录加共享锁和排他锁
保持时间:会保持到事务结束。

# 更新锁(U锁)
UPDATE语句在更新前需要对整表上更新锁,在找到记录后对需要操作记录上排他锁并释放更新锁
更新锁特性为:用来预定要对此页施加排他锁,它允许其他事务加共享锁,但不允许再施加更新锁或排他锁
保持时间:会保持到事务结束。

# 分析
在MYSQL中使用的默认隔离接级别为可重复读,那么SELECT语句在执行时会给记录增加共享锁,UPDATE语句会给
数据上更新锁和排他锁,INSERT语句会给数据上排他锁(数据为新增,在事务结束之前是对其他事务不可见的,
可忽略)。

这样我们可以很清晰的看出,假设当前并行执行两个事务A和B。

1. 当A开始执行事务中的SELECT语句时,会给该条记录加共享锁,这样事务B不能够对该记录加排他锁,
   需要等待A事务提交

2. 当A开始执行事务中的UPDATE语句时,首先会给该页记录加更新锁,这样事务B不能够对该页记录加排他锁,
   需要等待A事务提交;同时在定位记录后给记录增加排他锁,这样事务B不能对该记录加共享锁和排他锁,
   需要等待A事务提交。

3. 由于A事务执行时间过长,在整个执行事务中还存在调用其他服务,查询其他表的操作。导致事务提交时间过长
   B事务一直等待,导致B事务锁等待时间过长。引起文章标题中的问题。

# 解决方案
方案一:缩小事务范围,只把DML语句(UPDATE,INSERT,DELETE)包裹在事务中
方案二:尽可能减少事务中包含的DML语句。提高事务的执行时间。

17、mysql的InnoDB和Myisam引擎在使用select count(*)语句时哪个效率更高,为什么?

在 MyISAM 存储引擎中,把表的总行数存储在磁盘上,
当执行 select count(*) from t 时,直接返回总数据。

在 InnoDB 存储引擎中,跟 MyISAM 不一样,没有将总行数存储在磁盘上,
当执行 select count(*) from t 时,会先把数据读出来,一行一行的累加,最后返回总数量。

原因:跟事务的特性有关,由于多版本并发控制(MVCC)的原因,导致行数不确定
优化方法:加一个普通索引,统计会走索引,索引的本质是数据结构,提高效率

18、InnoDB引擎支持行锁,那什么情况下会产生行锁,什么情况下变成表锁?

产生行锁:当通过索引检索的数据,并进行对应操作时,产生行锁
产生表锁:当不通过索引或索引使用不当失效时,检索的数据操作时,产生表锁

19、MySQL优化

1:避免使用 select * 你需要什么信息,就查询什么信息,查询的多了,查询的速度肯定就会慢

2:当你只需要查询出一条数据的时候,要使用 limit 1   比如你要查询数据中是否有男生,只要查询一条含有
	男生的记录就行了,后面不需要再查了,使用Limit 1 可以在找到一条数据后停止搜索

3:建立高性能的索引,索引不是随便加的也不是索引越多越好,更不是所有索引对查询都有效

4:建数据库表时,给字段设置固定合适的大小. 字段不能设置的太大,设置太大就造成浪费,会使查询速度变慢

5:要尽量使用not null   

6:EXPLAIN 你的 SELECT 查询使用EXPLAIN,可以帮助你更了解MySQL是如何处理你的sql语句的,
  你可以查看到sql的执行计划,这样你就能更好的去了解你的sql语句的不足,然后优化语句.

7:在Join表的时候,被用来Join的字段,应该是相同的类型的,且字段应该是被建过索引的,这样,MySQL内部会
  启动为你优化Join的SQL语句的机制。 

8:如果你有一个字段,比如“性别”,“国家”,“民族”, “省份”,“状态”或“部门”,这些字段的取值是有限
  而且固定的,那么,应该使用 ENUM 而不是 VARCHAR。  

  因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。
  这样,我们又可以提高数据库的性能。

9:垂直分割将常用和有关系的字段放在相同的表中,把一张表的数据分成几张表
    这样可以降低表的复杂度和字段的数目,从而达到优化的目的

10:优化where查询

        1): 避免在where子句中对字段进行表达式操作
        	比如:selectfromwhere age*2=36;建议改成selectfromwhere age=36/2;

        2): 应尽量避免在 where 子句中使用 !=<> 操作符,否则将引擎放弃使用索引而进行全表扫描。

        3): 应尽量避免在 where 子句中对字段进行 null 值 判断

        4):应尽量避免在 where 子句中使用 or 来连接条件

11:不建议使用%前缀模糊查询,这种查询会导致索引失效而进行全表扫描
     例如LIKE “%name”或者LIKE “%name%这两种都是不建议的.但是可以使用LIKE “name%”。
     对于LIKE “%name%,可以使用全文索引的形式

12:要慎用in和 not in 
    例如:select id from t where num in(1,2,3)   
    建议改成 select id from t where num between 1 and 3
    对于连续的数值,能用 between 就不要用 in13:理解in和exists, not in和not exists的区别

    很多时候用 exists 代替 in 是一个好的选择:如查询语句使用了not in那么内外表都进行全表扫描,没用
    到索引,而not exists子查询依然能用到表上索引,所以无论哪个表大,用not exists都比not in要快。

    select num from a where num in(select num from b)
    建议改成: select num from a where exists(select 1 from b where num=a.num)

    区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为
    驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于
    外表小而内表大的情况。

    关于not in和not exists,推荐使用not exists,不仅仅是效率问题,not in可能存在逻辑问题

14:理解select Count (*)Select Count(1)以及Select Count(column)区别

    一般情况下,Select Count (*)Select Count(1)两着返回结果是一样的

    假如表沒有主键(Primary key), 那么count(1)count(*)快,

    如果有主键的話,那主键作为count的条件时候count(主键)最快

    如果你的表只有一个字段的话那count(*)就是最快的

   count(*)count(1) 的结果一样,都包括对NULL的统计,而count(column) 是不包括NULL的统计

收藏文章:https://www.cnblogs.com/bypp/p/7755307.html

redis面试题

1、谈谈你对消息队列的理解?除了redis,有用过哪些?

#谈谈你对消息队列的理解?
是一种应用间的通信方式,消息发送后可以立即返回,有消息系统来确保信息的可靠专递,消息发布者只管把消息发
布到MQ中而不管谁来取,消息使用者只管从MQ中取消息而不管谁发布的,这样发布者和使用者都不用知道对方的存在

# 除了redis,有用过哪些?
RabbitMQ

2、使用redis有哪些好处?

(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)

(2) 支持丰富数据类型,支持string,list,set,sorted set,hash

(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行

(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

3、redis相比memcached有哪些优势?

(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型

(2) redis的速度比memcached快很多

(3) redis可以持久化其数据

4、redis常见性能问题和解决方案:

(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件

(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次

(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内

(4) 尽量避免在压力很大的主库上增加从库

(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
	这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,
	可以立刻启用Slave1做Master,其他不变。

5、MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据

相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:

voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

6、Memcache与Redis的区别都有哪些?

1)、存储方式
	Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
	Redis有部份存在硬盘上,这样能保证数据的持久性。
	
2)、数据支持类型
	Memcache对数据类型支持相对简单。
	Redis有复杂的数据类型。
	
3value大小
	redis最大可以达到1GB,而memcache只有1MB

7、Redis 常见的性能问题都有哪些?如何解决?

1).Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,
会间断性暂停服务,所以Master最好不要写内存快照。
 
2).Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,
AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,
特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
 
3).Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,
出现短暂服务暂停现象。
4). Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内

8、redis 最适合的场景

Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,
跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,
那么何时使用Memcached,何时使用Redis呢?
     如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:
     1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
     2 、Redis支持数据的备份,即master-slave模式的数据备份。
     3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
     
(1)会话缓存(Session Cache)
	最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)
	的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,
	大部分人都会不高兴的,现在,他们还会这样吗?

	幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的
	商业平台Magento也提供Redis的插件。

(2)全页缓存(FPC)
	除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有
	磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。再次以Magento为
	例,Magento提供一个插件来使用Redis作为全页缓存后端。此外,对WordPress的用户来说,Pantheon有一个
	非常好的插件  wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

(3)队列
	Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列
	平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
 
(4)排行榜/计数器
	Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得
	我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合
	中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:

	当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
	ZRANGE user_scores 0 10 WITHSCORESAgora Games就是一个很好的例子,用Ruby实现的,它的排行榜
	就是使用Redis来存储数据的。

9、Redis速度快的原因

1、开发语言:Redis就是用C语言开发的,所以执行会比较快

2、纯内存访问:
	Redis将所有数据放在内存中,非数据同步正常工作中,是不需要从磁盘读取数据的,0次IO。
	内存响应时间大约为100纳秒,这是Redis速度快的重要基础

3、单线程
	1)单线程简化算法的实现,并发的数据结构实现不但困难且测试也麻烦。

	2)单线程避免了线程切换以及加锁释放锁带来的消耗,对于服务端开发来说,锁和线程切换通常是性能杀手。
	   当然了,单线程也会有它的缺点,也是Redis的噩梦:阻塞。如果执行一个命令过长,那么会造成其他命
	   令的阻塞,对于Redis是十分致命的,所以Redis是面向快速执行场景的数据库。

除了Redis之外,Node.js也是单线程,Nginx也是单线程,但他们都是服务器高性能的典范

4、非阻塞多路I/O复用机制

	I/O 多路复用模型是利用select、poll、epoll可以同时监察多个流的 I/O 事件的能力,在空闲的时候,
	会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流
	(epoll是只轮询那些真正发出了事件的流),依次顺序的处理就绪的流,这种做法就避免了大量的无用操作
	这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高
	效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的
	操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。

10、redis持久化机制

Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会
因为故障而丢失,这种机制就是 Redis 的持久化机制。

# Redis 的持久化机制有两种:
1、RDB快照:某个时间点的一次全量数据备份,是二进制文件,在存储上非常紧凑
2、AOF 日志:连续的增量备份,日志记录的是内存数据修改的指令记录文本,恢复时间会无比漫长

# RDB的优缺点

# 优点
RDB文件小,非常适合定时备份,用于灾难恢复
Redis加载RDB文件的速度比AOF快很多,因为RDB文件中直接存储的
时内存数据,而AOF文件中存储的是一条条命令,需要重演命令。

# 缺点
RDB无法做到实时持久化,若在两次bgsave间宕机,则会丢失区间(分钟级)的增量数据,不适用于实时性要求较
高的场景,RDB的cow机制中,fork子进程属于重量级操作,并且会阻塞redis主进程,存在老版本的Redis不兼容
新版本RDB格式文件的问题

# AOF的优缺点

# 优点
AOF只是追加写日志文件,对服务器性能影响较小,速度比RDB要快,消耗的内存较少

# 缺点
1、AOF方式生成的日志文件太大,需要不断AOF重写,进行瘦身。
2、即使经过AOF重写瘦身,由于文件是文本文件,文件体积较大(相比于RDB的二进制文件)。
3、AOF重演命令式的恢复数据,速度显然比RDB要慢。

具体持久化机制参考:https://zhuanlan.zhihu.com/p/77646963

11、缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题

# 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间

(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求
都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,
造成整个系统崩溃。

# 解决办法:

大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行
读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。

# 二、缓存穿透

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,
每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,
这也是经常提的缓存命中率问题。

解决办法;

最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这
个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然
把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样
第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

5TB的硬盘上放满了数据,请写一个算法将这些数据进行排重。如果这些数据是一些32bit大小的数据该如何解决?
如果是64bit的呢?

对于空间的利用到达了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)。
Bitmap: 典型的就是哈希表
缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、
时间来完成了。

# 布隆过滤器(推荐)

就是引入了k(k>1)k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。
Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入
几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的
Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。
Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。

# 缓存穿透与缓存击穿的区别

缓存击穿:是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬
间,持续的大并发就穿破缓存,直接请求数据。

解决方案;在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,
访问结束再删除该短期key。

增:给一个我公司处理的案例:背景双机拿token,token在存一份到redis,保证系统在token过期时都只有一个
线程去获取token;线上环境有两台机器,故使用分布式锁实现。

# 三、缓存预热

缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将
相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!
用户直接查询事先被预热的缓存数据!

解决思路:
1、直接写个缓存刷新页面,上线时手工操作下;
2、数据量不大,可以在项目启动的时候自动进行加载;
3、定时刷新缓存;

# 四、缓存更新

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行
自定义的缓存淘汰,常见的策略有两种:
(1)定时去清理过期的缓存;
(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存
失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

# 五、缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务
还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

以参考日志级别设置预案:
(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时
	可以根据情况自动降级或者人工降级;
(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,
可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给
用户。

12、热点数据和冷数据是什么?

# 热点数据,缓存才有价值

对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的
数据,看情况考虑使用缓存

对于上面两个例子,寿星列表、导航信息都存在一个特点,就是信息修改频率不高,读取通常非常高的场景。
对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,
某导航产品,我们将导航信息,缓存以后可能读取数百万次。

**数据更新前至少读取两次,**缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太
大价值了。

那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是
又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,
分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。

13、redis的数据类型,以及每种数据类型的使用场景

# 1、String
	这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数
	功能的缓存。

# 2、hash
	这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这
	种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session
	的效果。
	
# 3、list
	使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于
	redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适—取行情信息。就也是个生产者和消费
	者的场景。LIST可以很好的完成排队,先进先出的原则。
	
# 4、set
	因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为
	我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共
	服务,太麻烦了。
	
	另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

# 5、sorted set
	sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP 
	N操作。

14、Redis 为什么是单线程的?

官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或
者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程
会有很多麻烦!)Redis利用队列技术将并发访问变为串行访问

1)绝大部分请求是纯粹的内存操作(非常快速)2)采用单线程,避免了不必要的上下文切换和竞争条件

2)非阻塞IO优点:
1.速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
2. 支持丰富数据类型,支持string,list,set,sorted set,hash
3.支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
4.丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除如何解决redis的并发竞争key问题

同时有多个子系统去set一个key。这个时候要注意什么呢? 不推荐使用redis的事务机制。因为我们的生产环境,
基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存
储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。

(1)如果对这个key操作,不要求顺序: 准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可

(2)如果对这个key操作,要求顺序: 分布式锁+时间戳。 假设这会系统B先抢到锁,将key1设置为{
     valueB 
3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。

(3)利用队列,将set方法变成串行访问也可以redis遇到高并发,如果保证读写key的一致性,对redis的操作都
   是具有原子性的,是线程安全的操作,你不用考虑并发问题,redis内部已经帮你处理好并发的问题了。

15、Redis 集群方案应该怎么做?都有哪些方案?

1.twemproxy,大概概念是,它类似于一个代理方式,使用时在本需要连接 redis 的地方改为连接twemproxy,
 它会以一个代理的身份接收请求并使用一致性 hash 算法,将请求转接到具体redis,将结果再返回twemproxy
 。缺点: twemproxy 自身单端口实例的压力,使用一致性 hash 后,对 redis 节点数量改变时候的计算值的
 改变,数据无法自动移动到新的节点。

2.codis,目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在 节点数量改变情况下,旧节
点数据可恢复到新 hash 节点

3.redis cluster3.0 自带的集群,特点在于他的分布式算法不是一致性 hash,而是 hash 槽的概念,以及自
身支持节点设置从节点。具体看官方文档介绍。

16、有没有尝试进行多机redis 的部署?如何保证数据一致的?

主从复制,读写分离

一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将
数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数
据库,而一个从数据库只能有一个主数据库。

17、对于大量的请求怎么样处理

redis是一个单线程程序,也就说同一时刻它只能处理一个客户端请求;

redis是通过IO多路复用(select,epoll, kqueue,依据不同的平台,采取不同的实现)来处理多个客户端
请求的

18、为什么Redis的操作是原子性的,怎么保证原子性的?

对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。

Redis的操作之所以是原子性的,是因为Redis是单线程的。

Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。

# 多个命令在并发中也是原子性的吗?

不一定, 将getset改成单命令操作,incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现.

19、Redis事务

Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的
Redis会将一个事务中的所有命令序列化,然后按顺序执行。

1.redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以
保持简单且快速。

2.如果在一个事务中的命令出现错误,那么所有的命令都不会执行;

3.如果在一个事务中出现运行错误,那么正确的命令会被执行。

注:redis的discard只是结束本次事务,正确命令造成的影响仍然存在.

1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,
这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。

2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打
断时,返回空值 nil 。

3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。

4)WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有
一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

20、Redis实现分布式锁

Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系
Redis中可以使用SETNX命令实现分布式锁。

将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作

解锁:使用 del key 命令就能释放锁
解决死锁:
1)通过Redis中expire()给锁设定最大持有时间,如果超过,则Redis来帮我们释放锁。
2) 使用 setnx key “当前系统时间+锁持有的时间”和getset key “当前系统时间+锁持有的时间”组合的命令
就可以实现。

php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第6张图片
21、rabbitmq和redis用作消息队列的区别

# RabbitMQ

RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储
转发消息,在易用性、扩展性、高可用性等方面表现不俗。消息中间件主要用于组件之间的解耦,消息的发送者无需
知道消息使用者的存在,反之亦然。

# Redis

是一个Key-Value的NoSQL数据库,开发维护很活跃,虽然它是一个Key-Value数据库存储系统,但它本身支持MQ
功能,所以完全可以当做一个轻量级的队列服务来使用。

将redis发布订阅模式用做消息队列和rabbitmq的区别:

# 可靠性

redis :没有相应的机制保证消息的可靠消费,如果发布者发布一条消息,而没有对应的订阅者的话,这条消息将
丢失,不会存在内存中;

rabbitmq:具有消息消费确认机制,如果发布一条消息,还没有消费者消费该队列,那么这条消息将一直存放在队
列中,直到有消费者消费了该条消息,以此可以保证消息的可靠消费;

# 实时性

redis:实时性高,redis作为高效的缓存服务器,所有数据都存在在服务器中,所以它具有更高的实时性

# 消费者负载均衡

rabbitmq队列可以被多个消费者同时监控消费,但是每一条消息只能被消费一次,由于rabbitmq的消费确认机制
,因此它能够根据消费者的消费能力而调整它的负载;

redis发布订阅模式,一个队列可以被多个消费者同时订阅,当有消息到达时,会将该消息依次发送给每个订阅者;

 # 持久性
 
redis:redis的持久化是针对于整个redis缓存的内容,它有RDB和AOF两种持久化方式(redis持久化方式,
后续更新),可以将整个redis实例持久化到磁盘,以此来做数据备份,防止异常情况下导致数据丢失。

rabbitmq:队列,消息都可以选择性持久化,持久化粒度更小,更灵活;

# 队列监控

rabbitmq实现了后台监控平台,可以在该平台上看到所有创建的队列的详细情况,良好的后台管理平台可以方面
我们更好的使用;

redis没有所谓的监控平台。

# 总结

redis:   轻量级,低延迟,高并发,低可靠性;
rabbitmq:重量级,高可靠,异步,不保证实时;

rabbitmq:是一个专门的AMQP协议队列,他的优势就在于提供可靠的队列服务,并且可做到异步

redis:主要是用于缓存的,redis的发布订阅模块,可用于实现及时性,且可靠性低的功能。

linux面试题

1、Linux 下建立压缩包,解压缩包的命令

# Tar.gz:

打包: tar -zcvf file.tar.gz file.txt

解压: tar -zxvf file.tar.gz

# Bz2:

打包: bzip2 [-k] 文件

解压: bunzip2 [-k] 文件

# Gzip(只对文件,不保留原文件)

打包: gzip file1.txt

解压: gunzip file1.txt.gz

# Zip: -r 对目录

打包: zip file1.zip file1.txt

解压: unzip file1.zip

2、如何查看linux服务器的当前负载?linux服务器上如何查看php相关的进程有哪些?如何杀死一个指定进程?

# 查看负载
	1)使用top命令查看负载,在top下按“1”查看CPU核心数量,shift+"c"按cpu使用率大小排序,shif+"p"
	   按内存使用率高低排序;
	2)使用iostat -x 命令来监控io的输入输出是否过大
	
# 查看相关进程
ps -aux | grep php 或者 ps -ef | grep php

# 杀死进程
kill 进程id

3、有一个文本1.txt,每行的结构是“姓名 手机号 地址”(用空格分隔,每行手机号可能重复),请用linux相关命令/工具输出去重后的手机号

cut -f2 -d" " 1.txt | uniq -c

# cut -f2 -d" ":以空格" "分割,显示第二字段

# uniq -c 显示该行重复出现的次数,此处相当于去重

php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第7张图片

php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第8张图片
4、如何使用shell脚本统计网站前一天出现40x错误的url总数

awk '{print $9}' access.log | grep -E '404' | wc -l

# awk '{print $9}':逐行读取,默认以空格分隔,打印第9# grep -E '404':以正则形式搜索匹配
# wc -l:统计数量

参考文章:https://blog.csdn.net/enjoyphp/article/details/100024220

docker面试题

1、docker的优势

1、更快速的交付和部署
	Docker在整个开发周期都可以完美的辅助你实现快速交付。Docker允许开发者在装有应用和服务本地容器做开
	发。可以直接集成到可持续开发流程中。

	例如:开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来
	部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容
	易理解应用程序是如何创建和工作的。 Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、
	测试、部署的时间。

2、高效的部署和扩容
	Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。
	这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。

	Docker的兼容性和轻量特性可以很轻松的实现负载的动态管理。你可以快速扩容或方便的下线的你的应用和服
	务,这种速度趋近实时。

3、更高的资源利用率
	Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。容器除了运行其中应用外,
	基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的
	应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可。

4、更简单的管理
	使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新
	,从而实现自动化并且高效的管理。

2、Docker的七种用途

1、简化配置
	这是Docker初始目的,虚拟机VM最大的好处是基于你的应用配置能够无缝运行在任何平台上。Docker提供同样
	类似VM的能力,但是没有任何副作用,它能让你将环境和配置放入代码然后部署,同样的Docker配置能够在各
	种环境中使用,这实际是将应用环境和底层环境实现了解耦。

2、代码管道化管理
	能够对代码以流式pipeline管道化进行管理,从开发者的机器到生产环境机器这个流程中都能有效管理。因为在
	这个流程中会有各种不同的环境,每个都可能有微小的区别,Docker提供了跨越这些异构环境以一致性的微环
	境,从开发到部署实现流畅发布。

3、开发人员的生产化
	在一个开发环境,我们希望我们的开发环境能更加接近于生产环境,我们会让每个服务运行在自己的VM中,这样
	能模拟生产环境,比如有时我们并不总是需要跨越网络连接,这样我们可以将多个Docker装载一系列服务运行在
	单机上最大程度模拟生产分布式部署的环境。

4、应用隔离
	有很多理由你需要在一台机器上运行多个应用,这就需要将原来铁板一块monolithic的应用切分为很多微服务。
	实现应用之间的解耦,将多个应用服务部署在多个Docker中能轻松达到这个目的。

5、服务合并
	使用Docker也能合并多个服务以降低费用,不多的操作系统内存占用,跨实例共享多个空闲的内存,这些技术
	Docker能以更加紧密资源提供更有效的服务合并。

6、多租户
	Docker能够作为云计算的多租户容器,使用Docker能容易为每个租户创建运行应该多个实例,这得益其灵活的
	快速环境以及有效diff命令。
 
7、快速部署
	Docker通过创建流程的容器,不必重新启动操作系统,几秒内能关闭,你可以在数据中心创建或销毁资源,不用
	担心额外消耗。典型的数据中心利用率是30%,通过更积极的资源分配,以低成本方式对一个新的实例实现一个
	更聚合的资源分配,我们很容易超过这个利用率,大大提高数据中心的利用效率。

3、docker解决的问题及原理

# docker理念
	解决了运行环境和配置问题软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术

# 三大要素:镜像,容器,仓库

# 解决的问题
	Docker的出现一定是因为目前的后端在开发和运维阶段确实需要一种虚拟化技术解决开发环境和生产环境环境
	一致的问题,通过 Docker我们可以将程序运行的环境也纳入到版本控制中,排除因为环境造成不同运行结果
	的可能

# 原理
	docker是一个Client-Server结构的系统,docker守护进程运行在主机上,然后通过socket连接从客户端
	访问,守护进程从客户端接受命令并管理运行在主机上的容器,容器:是一个运行时环境
	
	Docker 核心技术与实现原理:http://dockone.io/article/2941
	
# 为什么docker比vm快
	1)docker有着比虚拟机更少的抽象层,由于docker不需要Hypervisor实现硬件资源虚拟化,
	运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在cpu、内存利用率上docker
	在效率上有明显优势
	
	2)docker利用的是宿主机的内核,而不需要Guest OS。因此,当新建一个容器时,docker不需要和虚拟机
	一样重新加载一个操作IT内核。避免引寻,加载操作系统内核返个比较费时费资源的过程,这个过程是分钟
	级别的,而docker由于直接利用宿主机的操作系统,则省略了这个过程,因此只需要几秒钟

php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第9张图片

rabbitmq常见面试题

1、使用RabbitMQ有什么好处?

1.解耦,系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦!

2.异步,将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度

3.削峰,并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常

2、RabbitMQ 中的 broker 是指什么?cluster 又是指什么?

broker 是指一个或多个 erlang node 的逻辑分组,且 node 上运行着 RabbitMQ 应用程序。
cluster 是在 broker 的基础之上,增加了 node 之间共享元数据的约束。

3、RabbitMQ 概念里的 channel、exchange 和 queue 是逻辑概念,还是对应着进程实体?分别起什么作用?

queue 具有自己的 erlang 进程;exchange 内部实现为保存 binding 关系的查找表;
channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给 queue 。
由 AMQP 协议描述可知,channel 是真实 TCP 连接之上的虚拟连接,所有 AMQP 命令都是通过channel发送
的,且每一个 channel 有唯一的 ID。一个 channel 只能被单独一个操作系统线程使用,故投递到
特定 channel 上的 message 是有顺序的。但一个操作系统线程上允许使用多个 channel 。

4、vhost 是什么?起什么作用?

vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ  server。
其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 
vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段
(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。

5、消息基于什么传输?

由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来
传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。

6、消息如何分发?

若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个
订阅的消费者(前提是消费者能够正常处理消息并进行确认)。

7、消息怎么路由?

从概念上来说,消息路由必须有三部分:交换器、路由、绑定。生产者把消息发布到交换器上;绑定决定了消息如何
从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。

1、消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
2、通过队列路由键,可以把队列绑定到交换器上。
3、消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由
  规则)如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进入 “黑洞”。
  
  常用的交换器主要分为一下三种:
	direct:如果路由键完全匹配,消息就被投递到相应的队列
	fanout:如果交换器收到消息,将会广播到所有绑定的队列上
	topic:可以使来自不同源头的消息能够到达同一个队列。 使用topic交换器时,可以使用通配符,比如:
		  “*” 匹配特定位置的任意文本, “.” 把路由键分为了几部分,“#” 匹配所有规则等。特别注意:
		  发往topic交换器的消息不能随意的设置选择键(routing_key),
		  必须是由"."隔开的一系列的标识符组成。

8、什么是元数据?元数据分为哪些类型?包括哪些内容?与 cluster 相关的元数据有哪些?元数据是如何保存的?元数据在 cluster 中是如何分布的?

在非 cluster 模式下,元数据主要分为 Queue 元数据(queue 名字和属性等)、Exchange 元数据
(exchange 名字、类型和属性等)、Binding 元数据(存放路由关系的查找表)、Vhost 元数据(vhost 
范围内针对前三者的名字空间约束和安全属性设置)。在 cluster 模式下,还包括 cluster 中 node 位置
信息和 node 关系信息。元数据按照 erlang node 的类型确定是仅保存于 RAM 中,还是同时保存在 
RAM 和 disk 上。元数据在 cluster 中是全 node 分布的。
下图所示为 queue 的元数据在单 node 和 cluster 两种模式下的分布图。

php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。_第10张图片
9、在单 node 系统和多 node 构成的 cluster 系统中声明 queue、exchange ,以及进行 binding 会有什么不同?

答:当你在单 node 上声明 queue 时,只要该 node 上相关元数据进行了变更,你就会得到 
Queue.Declare-ok 回应;而在 cluster 上声明 queue ,则要求 cluster 上的全部 node 都要进行
元数据成功更新,才会得到 Queue.Declare-ok 回应。另外,若 node 类型为 RAM node 则变更的数据
仅保存在内存中,若类型为 disk node 则还要变更保存在磁盘上的数据。

死信队列&死信交换器:DLX 全称(Dead-Letter-Exchange),称之为死信交换器,当消息变成一个死信之后,
如果这个消息所在的队列存在x-dead-letter-exchange参数,那么它会被发送到x-dead-letter-exchange
对应值的交换器上,这个交换器就称之为死信交换器,与这个死信交换器绑定的队列就是死信队列。

10、如何确保消息正确地发送至RabbitMQ?

RabbitMQ使用发送方确认模式,确保消息正确地发送到RabbitMQ。发送方确认模式:将信道设置成confirm模式
(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。一旦消息被投递到目的队列后,
或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。
如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(not acknowledged,未确认)消息。
发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者
应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。

11、如何确保消息接收方消费了消息?

接收方消息确认机制:消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。
只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。这里并没有用到超时机制,RabbitMQ仅
通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer
足够长的时间来处理消息。

# 下面罗列几种特殊情况:
如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给
下一个订阅的消费者。(可能存在消息重复消费的隐患,需要根据bizId去重)
如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发
更多的消息。

12、如何避免消息重复投递或重复消费?

在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重和幂等的依据(消息投递失
败并重传),避免重复的消息进入队列;在消息消费时,要求消息体中必须要有一个bizId(对于同一业务全局唯一
如支付ID、订单ID、帖子ID等)作为去重和幂等的依据,避免同一条消息被重复消费。

这个问题针对业务场景来答分以下几点:

1.比如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现重复
消费的情况,就会导致主键冲突,避免数据库出现脏数据。

2.再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,
set操作本来就算幂等操作。

3.如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个
全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中
查询有没消费记录即可。

13、如何解决丢数据的问题?

启用手动确认模式可以解决这个问题

1、自动确认模式,消费者挂掉,待ack的消息回归到队列中。消费者抛出异常,消息会不断的被重发,直到处理
成功。不会丢失消息,即便服务挂掉,没有处理完成的消息会重回队列,但是异常会让消息不断重试。

2、手动确认模式,如果消费者来不及处理就死掉时,没有响应ack时会重复发送一条信息给其他消费者;如果监听
程序处理异常了,且未对异常进行捕获,会一直重复接收消息,然后一直抛异常;如果对异常进行了捕获,但是
没有在finally里ack,也会一直重复发送消息(重试机制)。

3、不确认模式,acknowledge="none" 不使用确认机制,只要消息发送完成会立即在队列移除,无论客户端
异常还是断开,只要发送完就移除,不会重发。

14、死信队列和延迟队列的使用

# 死信消息:
1、消息被拒绝(Basic.Reject或Basic.Nack)并且设置 requeue 参数的值为 false
2、消息过期了
3、队列达到最大的长度
# 过期消息:
    在 rabbitmq 中存在2种方可设置消息的过期时间,第一种通过对队列进行设置,这种设置后,该队列中所有
    的消息都存在相同的过期时间,第二种通过对消息本身进行设置,那么每条消息的过期时间都不一样。如果
    同时使用这2种方法,那么以过期时间小的那个数值为准。当消息达到过期时间还没有被消费,那么那个消息
    就成为了一个 死信 消息。

    队列设置:在队列申明的时候使用 x-message-ttl 参数,单位为 毫秒

    单个消息设置:是设置消息属性的 expiration 参数的值,单位为 毫秒

延时队列:在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间和死信队列来模拟出延时队列。
消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。

有了以上的基础知识,我们完成以下需求:

需求:用户在系统中创建一个订单,如果超过时间用户没有进行支付,那么自动取消订单。
分析:
   1、上面这个情况,我们就适合使用延时队列来实现,那么延时队列如何创建

   2、延时队列可以由 过期消息+死信队列 来时间

   3、过期消息通过队列中设置 x-message-ttl 参数实现

   4、死信队列通过在队列申明时,给队列设置 x-dead-letter-exchange 参数,然后另外申明一个队列绑定
   x-dead-letter-exchange对应的交换器。

15、使用了消息队列会有什么缺点?

1.系统可用性降低:你想啊,本来其他系统只要运行好好的,那你的系统就是正常的。
现在你非要加个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性降低

2.系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息
可靠传输。因此,需要考虑的东西更多,系统复杂性增大。

16、工作模式

1、简单模式:一个生产者,一个消费者

2Work queues工作队列模式:一个生产者,多个消费者,每个消费者获取到的消息唯一。

2、Pub/Sub订阅模式:一个生产者发送的消息会被多个消费者获取。

3、 Routing路由模式:发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key

4、Topics通配符模式:将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,
				   “*”只匹配一个词。

17、AMQP协议

1.什么是AMQP协议

即高级消息队列协议,规范客户端与消息中间件服务器之间的通信,并能相互操作。

2.AMQP协议的作用

降低应用程序之间的耦合度,这样不同应用之间的集成的难度将变得更小,并开发出更有用的应用程序 。

3.AMQP协议的模型

包含三个成员:Exchange,Message Queue,Binding

Exchange:转换成员,是接收应用程序发过来的信息,但通过Binding这个路由规则,
路由到对应的Message Queue中

Message Queue:这个是存储实际队列的内容

Binding:定义Exchange与Message Queue这间绑定的路由规则

其他面试题

1、WebSocket 是一种网络通信协议?

初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来
什么好处?

答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。

举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务
器主动向客户端推送信息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":
每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。

轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,
有没有更好的方法。WebSocket 就是这样发明的。

WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等
对话,属于服务器推送技术的一种。

# 特点包括:1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80443,并且握手阶段采用 HTTP 协议,因此握手时不
	容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

具体参考:http://www.ruanyifeng.com/blog/2017/05/websocket.html

2、高并发解决方案案例

流量优化:防盗链处理

前端优化:减少HTTP请求,合并css或js,添加异步请求,启用浏览器缓存和文件压缩,CDN加速,
		 建立独立图片服务器,
		 
服务端优化:页面静态化,并发处理,队列处理

数据库优化:数据库缓存,分库分表,分区操作,读写分离,负载均衡

web服务器优化:负载均衡,nginx反向代理,7,4层LVS软件

具体参考:https://www.cnblogs.com/cn-sbo/p/10853469.html

3、JWT token 认证

# JWT的构成
1、头部(header)

{
     
  "alg": "HS256",		//签名算法(HMAC、SHA256、RSA)
  "typ": "JWT"			//令牌的类型(即JWT)
}

2、载荷(payload, 类似于飞机上承载的物品)

{
     
  "sub": "1",												// 签发人
  "iss": "http://localhost:8000/auth/login",				// 主题
  "iat": 1451888119,										// 受众
  "exp": 1454516119,										// 过期时间
  "nbf": 1451888119,										// 生效时间,在此之前是无效的
  "jti": "37c107e4609ddbcc9c096ea5ee76c667",				// 签发时间
  "aud": "dev"												// 编号
}

3、签名(Signature)

HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    secret
)

# 优点
因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以
使用。

因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。

便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。

它不需要在服务端保存会话信息, 所以它易于应用的扩展

# JWT有什么好处?

1、支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息
通过HTTP头传输.

2、无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有
登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.

4、更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的
服务端只要提供API即可.

5、去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,
你可以进行Token生成调用即可.

6、更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持
的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。

7、CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。

8、性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析
要费时得多.

9、不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理.

10、基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, 
Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)

# 安全相关
不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。

保护好secret私钥,该私钥非常重要。

如果可以,请使用https协议

具体查看:https://www.jianshu.com/p/576dbf44b2ae
https://learnku.com/articles/10885/full-use-of-jwt

4、斐波那契数几种算法

// 普通递归
function func1($n = 1)
{
     
	if($n < 1){
     
		return 0;
	}
	
    if ($n < 3) {
     
        return 1;
    }
    // 递归计算前两位
    return func1($n - 1) + fib($n - 2);
}

// 递归优化
function func2($n = 1, $a = 1, $b = 1)
{
     
    if ($n > 2) {
     
        // 存储前一位,优化递归计算
        return func2($n - 1, $a + $b, $a);
    }
    return $a;
}

// 循环
function func3($n){
     
	if($n < 1){
     
		return 0;
	}

	$arr = [];

	for($i = 0; $i <= $n; $i++){
     
		if($i < 2){
     
			$arr[] = $i;
		}else{
     
			$arr[] = $arr[$i - 1] + $arr[$i - 2];
		}
	}


	return $arr[$n];
}

function func4($n = 1)
{
     
    if ($n <= 0) {
     
        return 0;
    }
    
    if ($n < 3) {
     
        return 1;
    }
    
    $a = 0;
    $b = 1;
    
    // 循环计算
    for ($i = 2; $i < $n; $i++) {
     
        $b = $a + $b;
        $a = $b - $a;
    }
    
    return $b;
}

// 公式法
function func5($n = 1)
{
     
    // 黄金分割比
    $radio = (1 + sqrt(5)) / 2;
    
    // 斐波那契序列和黄金分割比之间的关系计算
    $num = intval(round(pow($radio, $n) / sqrt(5)));
    
    return $num;
}

5、什么是nginx?

nginx是一个高性能的HTTP和反向代理web服务器,同时提供了IMAP/POP3/SMTP服务

正向代理:代理的是客户端
反向代理:代理的是服务器

6、nginx负载均衡的几种模式

# 轮询分派
upstream loop{
     
    server 127.0.0.1:8080
    server 127.0.0.1:7080
    server 127.0.0.1:6305
}
按照默认轮询的方式进行负载,
假设后端server down掉,能自己剔除。
缺点:可靠性低,负载不均衡,机器性能可能不一致

# 权重分派
upstream loopweight{
     
    server 127.0.0.1:8080 weight = 5;
    server 127.0.0.2:7080 weight = 5;
    server 127.0.0.3:6305 weight = 10;
}
考虑12的机器配置低,或者12的性能不如3的时候
这样将3的权重设置大一些,更多的请求会被分配到3上。
为12分担更多的请求。

# IP哈希
upstream iphash{
     
    ip_hash;
    server 127.0.0.1:8080;
    server 127.0.0.2:7080;
    server 127.0.0.3:6305;
}
这里的IP说的是客户端的出口IP,这样经过 des_server_ip = hash(ip)
相应的ip在没有down掉的情况下,肯定会hash到固定的ip上。

# URL哈希
upstream urlhash{
     
    server 127.0.0.1:8080;
    server 127.0.0.2:7080;
    server 127.0.0.3:6305;
    hash $request_uri;
    hash_method crc32;
}
按照URI进行哈希,固定的URI Hash到固定的server上。

# 性能,相应时间分派
upstream iphash{
     
    server 127.0.0.1:8080;
    server 127.0.0.2:7080;
    server 127.0.0.3:6305;
    fair;
}
性能,相应时间分派:
按后端服务器的响应时间来分配请求。响应时间短的优先分配。

down And backup
upstream iphash{
     
     server 127.0.0.1:8080 down; #当前server不参与负载
     server 127.0.0.2:7080;
     server 127.0.0.3:6305 backup; #非backup的机器(参与负载的机器),down掉或者忙的时候,
     								请求backup机器。备用机
     fair;
}

# 应用
listen 80;
server_name www.domain.com
location ~^ /api{
     
    proxy_pass http://loopweight    #选择一种你喜欢的负载策略
}

你可能感兴趣的:(面试题,php,php,面试题)