# php
**Repository Path**: hackjiyi/php
## Basic Information
- **Project Name**: php
- **Description**: php
- **Primary Language**: PHP
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 0
- **Created**: 2021-06-11
- **Last Updated**: 2024-10-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# php知识
## 协议
#### TCP协议
- 什么是TCP
TCP是面向连接的、可靠的流协议。面向连接是指通信开始之前先做好两端质检的准备工作.
- TCP的作用
互联网进行通信时, 需要响应的网络协议, TCP就是为了使用互联网而开发定制的协议族
- TCP的优缺点
优点:
安全、可靠
缺点:
相比于UDP协议传输效率低
- 三次握手
所谓的三次握手是指建立一个TCP连接时需要客户端和服务端总共发送三个包以确认连接的建立。在stock协议编程中, 这一过程由客户端执行connect来触发。
为什么要要三次握手
1. 假如只进行一次握手, 客户端发送连接请求后, 没有收到服务端的应答, 没有办法判断连接是否成功。
2. 假如只进行两次握手, 客户端发送连接请求后, 会等待服务端的应答, 但是会出现的问题是, 假如客户端的SYN迟迟没有到达服务端, 此时客户端超时后, 会重新发送一次连接, 假如重发的这次服务端收到了, 且应答服务端了, 连接建立了。但是建立后, 第一个SYN也到达了服务端, 这时服务端会认为这时个新的连接, 会再给客户端发送一个ACK, 这个ACK当然会被客户端丢弃, 但此时服务端已经为这个连接分配资源了, 而且服务端会一直维持这个资源, 会造成浪费。
3. 三次握手, 两次握手的问题是服务端不知道SYN的有效性, 所以如果三次握手, 服务端会等待客户端的第三次握手, 如果第三次迟迟不来, 服务端就会释放相关的资源。但是有人会问, 假如第三次握手没有达到服务器呢?但这时客户端认为连接已经建立了。但是其实这种情况下, 只要客户端想服务端写数据, 就会收到服务端的RST应答, 这时客户端就能知道出现问题了。

- 四次挥手
1. 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
2. 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1,Server进入CLOSE_WAIT状态。
3. 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
4. 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

#### HTTP协议
- 什么是HTTP
HTTP是超文本传输协议(HyperText Transfer Protocol)的缩写, 是用于万维网服务器传输超文本到浏览器客户端的传输协议, 基于TCP/IP通信协议来传递数据。
- HTTP的特点
- [ ] 简单快速:客户向服务器请求服务时, 只需发送请求方法和路径。由于HTTP协议简单, 使得HTTP服务器的程序规模小, 因而通信速度很快
- [ ] 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
- [ ] 无连接: 无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求, 并受到客户的应答后, 即断开连接。采用这种方式可以节省传输的时间。
- [ ] 无状态:HTTP协议是无状态的协议。无状态协议是指对于事务处理没有记忆能力。缺失状态意味着如果候选处理需要前面的信息,则它必须重传, 这样可能导致每次连接传输的数据量增大, 另一方面, 在服务器不需要先前信息时它的应答较快。
- [ ] 支持B/S及C/S模式
- HTTP的URL解析
`http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name`
`协议:http`
`域名:www.aspxfans.com`
`端口:8080`
`虚拟目录: news`
`文件名称: index.asp`
`参数部分:从号开始到#井号之间`
`锚部分:从“#”开始到最后,都是锚部分`
- URI和URL的区别
URL指定要使用的协议类型,而URI不涉及协议规范。
- 格式

- HTTP请求示例
```
POST / HTTP1.1
Host:www.wrox.com
User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Content-Type:application/x-www-form-urlencoded
Content-Length:40
Connection: Keep-Alive
```
- HTTP请求方法:
- [ ] GET: 请求指定页面信息, 并返回实体
- [ ] HEAD: 类似GET请求, 只不过响应中没有具体的内容, 用于获取报头
- [ ] POST: 向指定的资源提交数据进行处理请求
- [ ] PUT: 从客户端向服务器传送的数据渠道指定文档的内容
- [ ] DELETE: 请求服务器删除指定的页面
- [ ] CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器
- [ ] OPTIONS:允许客户端查看服务器的性能
- [ ] TRACE: 回显服务器收到的请求, 主要用于测试或诊断
- HTTP状态码
- [ ] 200:服务器成功处理了请求
- [ ] 301: 请求的URL已经移走, Response中应该包含一个Location URL, 说明资源现在所处的位置
- [ ] 302:与301类似, 但这里的移除是临时性的, 客户端会使用Location中给出的URL,重新发送新的HTTP request
- [ ] 400:客户端发送了个错误的请求
- [ ] 401:未授权, 需要客户端对自己认证
- [ ] 403:请求被服务器拒绝
- [ ] 404:未找到资源
- [ ] 500:服务器遇到一个错误, 使其无法为请求提供服务
- HTTP的工作原理:
1. 客户端连接到Web服务器
一个HTTP客户端, 通常是浏览器, 与Web服务器的HTTP端口建立一个TCP套接字连接。
2. 发送HTTP请求
通过TCP套接字, 客户端向Web服务器发送一个文本的请求报文, 一个请求报文由请求行、请求头部、空行、请求数据4部分组成。
3. 服务器接受请求并返回HTTP响应
Web服务器解析请求, 定位请求资源。服务器将资源复本写到TCP套接字, 由客户端读取, 一个响应由状态行、响应头部、空行和响应数据4部分组成。
4. 释放TCP连接
诺connection模式为close, 则服务器主动关闭TCP连接, 客户端被动关闭连接, 释放TCP连接, 诺connection模式为keepalive, 则该连接会保持一段时间, 在该时间内可以继续接受请求
- GET和POST请求的区别
- [ ] GET提交的数据会放在URL之后, 以?分割URL和传输数据, 参数之间用&相连, 而POST是提交的实体
- [ ] GET提交的数据大小有限制(因为浏览器对URL的长度有限制), 而POST没有限制
- [ ] GET方式提交数据, 会带来安全问题, 比如一个登陆界面, 通过GET方式提交数据时, 用户名和密码将出现在URL上
- SESSION和COOKIE
- [ ] 产生
HTTP是一种无状态协议, 一但数据交换完毕, 客户端和服务端的连接就会断开, 再次交换数据需要建立新的连接, 这就意味着服务器无法从连接上跟踪回话, 于是需要引入一种机制, COOKIE于是顺应而生。
SESSION是另一种记录客户端状态的机制, 不同的是COOKIE保存在客户端的浏览器中, 而SESSION保存在服务器上, 客户端浏览器访问服务器时, 服务器把客户端信息以某种形式记录在服务器上, 这就是SESSION
- [ ] 区别
COOKIE保存在浏览器客户端中, 不同的浏览器存储的cookie数量和大小限制是不一样的
Session是存储在服务器的, 默认是文件形式存储, 可以存在在其他软件中, 如redis
- [ ] 安全性
SESSION要比COOKIE高
## PHP
#### 面向对象
- 什么是面向对象
- [ ] 在面向对象程序设计(Object oriented programming, OOP)中, 对象是一个由信息及对信息进行处理的秒数所组成的整体, 是对现实世界的抽象
- 什么是类,什么是对象,类和对象之间的关系
- [ ] 类的概念:类是具有相同属性和服务的一组对象的集合。
- [ ] 对象的概念:对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。
- [ ] 类与对象的关系就如模具和铸件的关系,类的实力化的结果就是对象,而对对象的抽象就是类,类描述了一组有相同特性(属性)和相同行为的对象。
- 封装
- [ ] 权限修饰符

- [ ] 方法重载
在PHP一个类中, 根本就不可以定义多个同名的函数, 这是语法错误。
- [ ] 方法重写
子类中的覆盖方法不能使用比父类中被覆盖的方法更严格的访问权限。
子类可以拥有与父类不同的参数数量。
- 继承
- [ ] 说明
当扩展一个类,子类就会继承父类所有公有的和受保护的方法
- 多态
- [ ] 说明
多态性是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息将可以产生不同的结果,这种现象称为多态性。

- 关键字
- [ ] this
当一个方法在类定义内部被调用时,有一个可用的伪变量 $this。$this 是一个到当前对象的引用
- [ ] parent
本质上就是代表父类这个“类”,而不是父类的“对象”
- [ ] static
声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。
- [ ] self
静态成员函数内不能用this调用非成员函数,但可以用self调用静态成员函数/变量/常量
其他成员函数可以用self调用静态成员函数以及非静态成员函数
self就是写在哪个类里面, 实际调用的就是这个类.
static代表使用的这个类, 就是你在父类里写的static,然后被子类覆盖,使用的就是子类的方法或属性
```php
class ParentClass
{
static $age = 18;
public function getAge()
{
return static::$age; // static 20, self 18
}
}
class ChildClass extends ParentClass
{
static $age = 20;
}
$obj = new ChildClass();
echo $obj->getAge();
```
- [ ] instanceof
用于确定一个 PHP 变量是否属于某一类 或者其子类的示例
- [ ] const
一个常量一旦被定义,就不能再改变或者取消定义
不能在函数内,循环内以及 `if` 或 `try`/`catch` 语句之内用 `const` 来定义常量
使用 `const` 关键字定义的常量总是大小写敏感的,而使用 define()不区分大小写。
- [ ] final
如果父类中的方法被声明为final, 则子类无法覆盖该方法
如果一个类被声明为final, 则不能被继承
属性不能被定义为 final,只有类和方法才能被定义为 final
- [ ] clone
因为 clone 的方式实际上是对整个对象的内存区域进行了一次复制并用新的对象变量指向新的内存,因此赋值后的对象和原对象之间是相互独立,并且调用对象的__clone(), 当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)

- 魔术方法
- [ ] __tostring()
用于一个类被当成字符时应怎么回应
- [ ] __invoke()
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

- [ ] __sleep()
[serialize()](https://www.php.net/manual/zh/function.serialize.php) 函数会检查类中是否存在一个魔术方法 [__sleep()](https://www.php.net/manual/zh/language.oop5.magic.php#object.sleep)。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组
- [ ] __wakeup()
[unserialize()](https://www.php.net/manual/zh/function.unserialize.php) 会检查是否存在一个 [__wakeup()](https://www.php.net/manual/zh/language.oop5.magic.php#object.wakeup) 方法。如果存在,则会先调用 `__wakeup` 方法,预先准备对象需要的资源。
- [ ] __construct()
有构造函数的类会在每次创建新的对象时先调用此方法, 如果子类定义了构造函数则不会隐式调用其父类的构造函数。
- [ ] __destruct()
析构函数在会某个对象的所有引用都被删除或者对象呗显式销毁时执行。如果子类定义了析构函数则不会隐式调用其父类的析构函数。
- [ ] __clone()
新创建的对象(复制生成的对象)中的 __clone() 方法会被调用。
- [ ] __call()
在对象调用一个不可访问的方法时, __call()会被调用
- [ ] __callStatic()
在静态上下文中调用一个不可访问方法时, __callStatic()会被调用
- [ ] __isset()
当对不可访问属性调用isset()或者empty()时, __isset()会被调用
- [ ] __get()
读取不可访问属性的值时, __get()会被调用
- [ ] __set()
当给不可访问的属性赋值时, __set()会被调用
- [ ] __unset()
当对不可访问的属性调用unset()时, __unset()会被调用
- [ ] __autoload()
PHP在实例化一个对象时, 首先会在系统中查找该类或该结构是否存在, 如果不存在的话就尝试使用autoload机制来加载该类
- 深浅拷贝
- [ ] 浅拷贝
对象有着原始对象属性值的一份精确拷贝。
若属性是基本类型,拷贝的就是基本类型的值;若属性是引用类型,拷贝就是存地址, 数组属于浅拷贝。
- [ ] 深拷贝
会拷贝所有属性,并拷贝属性指向的动态分配的内存,当对象和它所引用的对象一起拷贝时,发生深拷贝。
深拷贝与浅拷贝相比,速度较慢,花销较大。拷贝后两对象互不影响。
- [ ] PHP赋值
赋值时, 普通对象是深拷贝, 但对对象来说, 是浅拷贝。如果是对象属性还是对象, 哪怕用clone克隆出来的对象属性还是浅拷贝, 可以通过以下方法解决:
在__clone()方法中clone对象属性
使用serialize()和unserialize执行进行彻底的深拷贝, 内部是递归深拷贝
使用json_encode()和json_decode()执行进行彻底的深拷贝, 内部是递归深拷贝
- 写时复制
PHP执行的是写时复制, 在需要复制内存时,将复杂对象分解为最小粒度来处理。 这样做就使内存中复杂对象中某一部分做修改时,不必将该对象的所有元素全部“分离”出一份内存拷贝, 从而节省了内存的使用
#### 超全局变量
- #### $GLOBALES
在PHP中,定义在函数体外的全局变量,函数内部是不能直接获取的,所以需要global声明或者$GLOBALS[]来获取。

- $_SERVER
包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组
- $_GET
通过 URL 参数(又叫 query string)传递给当前脚本的变量的数组
- $_POST
能获取POST的参数
- $_COOKIE
获取传递给当前脚本的变量的数组。
- $_FILES
通过 HTTP POST 方式上传到当前脚本的项目的数组
- $_SESSION
当前脚本可用 SESSION 变量的数组
- $_REQUEST
包含了$_GET和$_POST的信息
- $_ENV
通过环境方式传递给当前脚本的变量的数组。
#### 垃圾回收机制
- 什么是垃圾
新的GC负责清理的垃圾是指变量的容器还存在, 但是又没有任何变量名指向此zval。因此GC判断是否为垃圾的一个重要标准是有没有变量名指向变量容器zval
- 回收机制
- [ ] 简单类型的引用计数
我们说的简单类型是指:bool(true/false), null, long,double、char
对于这些类型的变量值,直接使用zval结构就可以记录,无需额外的内存。所以,也就没有引用计数。
更深层的原因是,php7开始,zval是在栈空间分配的,可自动释放,不需要垃圾回收(堆上的内存才需要主动管理回收),也就不需要引用计数了。
- [ ] 复杂类型
对于复杂类型的变量(string,array,object,resource等),我们会将其具体的值记录在单独的内存区域,再由zend_value中相应的指针指向该内存区域。指向该内存区域的指针数量,即为引用计数。
- [ ] 数据结构(5.3)

- [ ] 循环引用
```php
class AT
{
public $test;
}
$a = new AT();
$a->test = $a;
$b = $a;
xdebug_debug_zval($a);
```
- [ ] 垃圾回收逻辑
1.在5.2版本或之前版本,PHP会根据refcount值来判断是不是垃圾
如果refcount值为0,PHP会当做垃圾释放掉
这种回收机制有缺陷,对于环状引用的变量无法回收
2.在5.3之后版本改进了垃圾回收机制
如果发现一个zval容器中的refcount在增加,说明不是垃圾
如果发现一个zval容器中的refcount在减少,如果减到了0,直接当做垃圾回收
如果发现一个zval容器中的refcount在减少,并没有减到0,PHP会把该值放到缓冲区,当做有可能是垃圾的怀疑对象。
当缓冲区达到了临界值,PHP会自动调用一个方法去遍历每一个值,如果发现是垃圾就清理
#### fastcgi和php-fpm
- 什么是CGI
CGI是为了保证Web Server传递过来的数据是标准格式的,方便CGI程序的编写者。
- 什么是FASTCGI
Fastcgi是CGI的升级版,一种语言无关的协议。
- 什么php-fpm
是一个实现了FastCGI(协议)的程序。
php-fpm是一种master/worker多进程架构,master主要负责CGI及PHP环境初始化、事件监听、子进程状态等等, worker进程负责处理php请求。
php-fpm实现了平滑启动(处理完的worker就重启)。
PHP-FPM由1个master进程和N个worker进程组成。
- 进程管理模式
Static:静态模式, 启动是分配固定的进程, 后续不在动态增减worker的进程。
Dynamic: 动态模式, 启动时分配固定的进程, 伴随着请求数的增加, 在设定的范围内动态调整worker进程。
Ondemand: 按需分配, 当收到用户请求时fork worker进程。初始化时不生产worker进程, 当lister_socket收到request, 先检查是否存在已生成的空闲的worker进程, 诺存在则使用这个进程, 否则fork 一个新的进程, 每个一秒触发心跳事件, 会kill掉空闲超过设定时间的worker进程。
#### 线程安全和非线程安全的
- 什么是线程安全和非线程安全
None-Thread Safe就是非线程安全,在执行时不进行线程(thread)安全检查;Thread Safe就是线程安全,以FastCGI方式安装PHP时,PHP拥有独立的进程,并且FastCGI是单一线程的,不存在多个线程之间可能引发的相互干扰(这种干扰通常都是由于全局变量和静态变量导致的)。由于省去了线程安全的检查,因此使用FastCGI方式比ISAPI方式的效率更高一些
- 选择
- [ ] Non Thread Safe(非线程安全)版本php适用在使用CGI以及fastCGI的web服务器上,如nginx,lighttpd以及IIS的CGI模式下
- [ ] Thread Safe(线程安全)版本php适用在使用ISAPI或者module的web服务器上,如IIS的ISAPI模式或者apache module模式
windows+ISS+fastCGI 使用非线程安全版本
windows + IIS + ISAPI :使用线程安全版本
windows+Apache+PHP(模块) 使用线程安全版本
windows+Apache+PHP(ISAPI) 使用线程安全版本
windows + Apache + PHP(FastCGI) 使用非线程安全版本
#### PHP7的新特性
性能
匿名类
## 正则
#### 普通字符
- [ABC]
匹配 **[...]** 中的所有字符
- [^ABC]
匹配除了 **[...]** 中字符的所有字符
- [A-Z]
[A-Z] 表示一个区间,匹配所有大写字母,[a-z] 表示所有小写字母
- .
批量除了换行符(\n、\r)之外的任何单个字符
- [\s\S]
匹配所有。\s匹配所有空白字符, 包括换行, \S非空白字符, 不包括换行
- \w
匹配所有字母、数字、下划线。
#### 特殊字符
- $
匹配输入字符串的结尾位置
- ( )
标记一个子表达式的开始和结束的位置, 子表达式可以获取供以后使用
- *
匹配签名的子表达式零次或多次
- +
匹配前面子表达式一次或多次
- .
匹配除换行符之外的任何单字符
- [
标记一个中括号表达式的开始
- ?
匹配签名的子表达式零次或多次, 或指明一个非贪婪限定符
- \
将下一个字符表标记为特殊字符
- ^
匹配字符开始的位置
- {
标记现代表达式的开始
- |
指明两项之间的一个选择
- 限定符
- [ ] *
匹配签名的子表达式零次或多次
- [ ] +
匹配前面子表达式一次或多次
- [ ] ?
匹配前面子表达式零次或一次
- [ ] {n}
匹配N次
- [ ] {n,}
匹配至少N次
- [ ] {n,m}
匹配最少N次且最多M次
#### 定位符
- \b
匹配一个单词的边界, 即字与空格间的位置
- \B
非单词边界匹配
#### 贪婪和非贪婪
- *和 + 限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配
#### 匹配示例
- 手机号码
`/^(136|138|152|189|199)\d{8}$/`
- 邮箱
`/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]\.[a-zA-Z0-9_-]$/`
## 设计模式
#### 单例模式
- 特点
三私一公: 私有静态属性, 私有构造方法, 私有__clone()方法, 公有静态方法,
- 实现
```php
Class Test
{
private static $_instace;
private function __construct() {
}
private function __clone() {
}
public function test() {
echo "test";
}
public static function getInstace()
{
if (self::$_instace == null) {
self::$_instace = new Test();
}
return self::$_instace;
}
}
$obj = Test::getInstace();
$obj->test();
```
#### 工厂模式
- 特点
将调用对象与创建对象分离, 调用者直接向工厂请求, 减少代码的耦合, 提高系统的可维护性
- 实现
```php
class ClassA
{
public function methodA()
{
echo "class A";
}
}
class ClassB
{
public function methodB()
{
echo "class B";
}
}
class ClassC
{
public function methodC()
{
echo "class C";
}
}
class Factory
{
const CLASSA = "ClassA";
const CLASSB = "ClassB";
const CLASSC = "ClassC";
public static function getInstance($class)
{
return new $class;
}
}
$obj = Factory::getInstance(Factory::CLASSB);
$obj->methodB();
```
#### 注册树模式
- 特点
注册树模式通过将对象示例注册到一颗全局的对象树上, 需要的时候从对象树上采摘的模式设计方法
- 实现
```php
methodA();
```
#### 策略模式
- 特点
定义一系列算法封装起来, 让他们可以相互替代,策略模式提供了管理相关算法族的办法, 提供了可以体会继承关系的棒法, 避免使用多重条件转移语句
- 实现
```php
_stratege = $stratege;
}
public function goSchool()
{
$this->_stratege->goSchool();
}
}
$traget = new Run();
$obj = new GoSchoolContext($traget);
$obj->goSchool();
```
#### 适配器模式
- 特点
需要的东西在面前,但却不能用,而短时间又无法改造它,于是就想办法适配
- 实现
```php
// 适配器
interface Charget
{
public function putCharget();
}
class China implements Charget
{
private $v = 220;
public function putCharget()
{
return $this->v;
}
}
class Adper extends China
{
public function putCharget() {
return parent::putCharget() / 2 + 10;
}
}
class Phone
{
public function charge(Charget $charge)
{
if ($charge->putCharget() != "120") {
echo "不能充电";
} else {
echo "能充电";
}
}
}
$china = new China();
$adper = new Adper();
$phone = new Phone();
$phone->charge($adper);
```
#### 观察者模式
- 特点
当一个对象状态发生变化时, 依赖他的对象全部收到通知, 并主动更新。观察者模式实现了低耦合, 非侵入式的通知与更新机制。
- 实现
```php
_observers[] = $observer;
}
public function notify()
{
foreach($this->_observers as $object) {
$object->watch();
}
}
}
// 观察者
class Cat1 implements Observer{
public function watch(){
echo "Cat1 watches TV
";
}
}
class Dog1 implements Observer{
public function watch(){
echo "Dog1 watches TV
";
}
}
class People implements Observer{
public function watch(){
echo "People watches TV
";
}
}
$action = new WatchAction();
$action->register(new Cat1());
$action->register(new People());
$action->register(new Dog1());
$action->notify();
```
## 算法
#### 冒泡排序
- 思路

- 实现
```php
$arr[$j +1]) {
$temp = $arr[$j];
$arr[$j] = $arr[$j + 1];
$arr[$j + 1] = $temp;
}
}
}
return $arr;
}
$arr = [66,65,17,7,45,0,9];
$new_arr = bubbleSort($arr);
var_dump($new_arr);exit;
```
#### 二分查找
- 思路

- 实现
```php
$array[$index]) {
$min_index = $index + 1;
} else {
$max_index = $index -1;
}
}
return -1;
}
$index = search($arr, 130);
var_dump($index);
```
#### 快速排序
- 思路
找到一个元素作为基准比较值(key),分别从两个方向进行比较。从后往前找,比key小元素放在数组前面,然后从前往后找,比key大的元素放在数组后面。最终两个方向交汇到中间,让key交换到数组的中间位置
- 实现
```php
$arr[$j]) {
$min = $j;
}
}
// 假定最小值和实际最小值不符,交换位置
if ($min != $i) {
$temp = $arr[$i];
$arr[$i] = $arr[$min];
$arr[$min] = $temp;
}
}
return $arr;
}
var_dump(select($arr));
```
#### 插入排序
- 思路
从第一个元素开始,左边视为已排序数组,右边视为待排序数组,从左往右依次取元素,插入左侧已排序数组,对插入新元素的左侧数组重新生成有序数组
- 实现
```php
= 0 && $arr[$j] > $temp; $j--) {
$arr[$j + 1] = $arr[$j];
}
$arr[$j + 1] = $temp;
}
return $arr;
}
var_dump(select($arr));
```
## Mysql
#### 范式
- 第一范式
只要求表中字段不可再拆分, 即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项
- 第二范式
主键必须最小, 主键是这样的属性:对某个关系中所有n元组来说,主键必须是没有重复的,所以依靠它可以唯一定位某个n元组。
- 第三方式
所有非主属性对主键的依赖应当是直接的,不容许是间接的。也就是说,所有非主属性不容许依赖主键之外的属性
#### 存储引擎
- MyISAM
不支持外建、事务, 表锁, 适合select多的情况
索引和数据用两个不同的文件存储, 主键索引和非主键索引都存储了数据的文件指针
- INNODB
默认存储引擎, 支持外键、行锁、事务
索引和数据都用一用一个文件存储,辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录
#### 事务四个特性
- 原子性
要么全部成功, 要么全部失败
- 一致性
执行前和执行后的状态一致
- 隔离性
两个用户同时提交的事务, 两个事务之间互不干扰, 跟隔离级别有关
- 持久性
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的
#### 字段类型
- 数值类型

- 字符类型

注意:char(n) 和 varchar(n) 中括号中 n 代表字符的个数,并不代表字节个数,比如 CHAR(30) 就可以存储 30 个字符
字符类型若为gbk,每个字符最多占2个字节,最大长度不能超过32766
字符类型若为utf8,每个字符最多占3个字节,最大长度不能超过21845
VARCHAR(4),最多存储4个字符,有几个字符存储几个, 最大255个字符
CHAR(4),最多存储4个字符,不足4个尾部用空格填满
#### MVCC
- 特点
MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较
- SELECT
- [ ] InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的
- [ ] 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除
- INSERT
- [ ] InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
- UPDATE
- [ ] InnoDB执行UPDATE,实际上是新插入了一行记录,并保存其创建时间为当前事务的ID,同时保存当前事务ID到要UPDATE的行的删除时间
- DELETE
- [ ] InnoDB为删除的每一行保存当前系统版本号作为行删除标识。
#### 事务隔离级别
- Read Uncommitted-读未提交
- [ ] 特点
一个事务在处理过程中读取了另外一个事务未提交的数据
- [ ] 图解

- 读提交(Read Committed)
- [ ] 特点
读取的数据是其它事务提交后的数据, 在同一事务过程中,多次读取到的数据是不一致的
- [ ] 图解

- 可重复读(Repeatable Read)
- [ ] 特点
在同一事务中,每次读取的数据是一致的
REPEATABLE READ是不能防止幻读产生的。INNODB使用了next-key locks实现了防止幻读的发生。
RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),不存在幻读现象。
- [ ] 图解

- 串行化
- [ ] 特点
各事务间互斥。即使是读操作也会互斥。所有的读都是当前读, 都加锁。
- [ ] 图解


- 脏读
- [ ] 特点
脏读发生在一个事务A读取了被另外一个事务B修改, 但是还未提交的数据。假如B回退, 则事务A读取的数据是无效的数据
- [ ] 图解

- 不可重复读
- [ ] 特点
事务A前后读取到的数据不一致(主要针对修改)
- [ ] 图解

- 幻读
- [ ] 特点
幻读发生在当两个完全相同的查询执行时,第二次查询所返回的结果集跟第一次查询不相同。(主要针对新增)
- [ ] 图解

#### 索引
- [ ] 索引类型
- [ ] 普通索引
辅助索引
- [ ] 唯一索引
与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值(注意和主键不同)。如果是组合索引,则列值的组合必须唯一,创建方法和普通索引类似。
- [ ] 主键索引
聚集索引
- [ ] 单列索引、多列索引
多个单列索引与单个多列索引的查询效果不同,因为执行查询时,MySQL只能使用一个索引,会从多个索引中选择一个限制最为严格的索引。
- [ ] 组合索引(最左前缀)
联合索引
- [ ] 聚集和非聚集索引
概括
聚集索引就是以**主键**创建的索引
非聚集索引就是以**非主键**创建的索引
区别
聚集索引在叶子子节点上存储的是表中的数据
非聚集索引在叶子中存储的是主键和索引列
使用非聚集索引查询出数据时, 拿到叶子上的主键再去查到想要查找的数据(拿到主键再查找这个过程叫做**回表**)
覆盖索引就是把要**查询出的列和索引是对应的**,不做回表操作
- [ ] mysql查询时只会用到一个索引, 一个限制最为严格的索引
#### 锁
- 锁类型

- 表锁
读读不堵塞、读写堵塞、写写阻塞
- 写锁(X锁、排它锁)
允许获得排它锁的事务更新数据, 阻止其他事务取得相同数据集的共享锁和排它锁。
- 读锁(S锁、共享锁)
允许一个事务去读一行, 阻止其他事务获取相同数据集的排他锁。
- 乐观锁
乐观锁是一种思想, 具体实现的是, 表中有个版本字段, 第一次读取的时候, 获取这个字段, 处理完业务逻辑开始更新的时候, 需要再次查看该字段的值是否和第一次是一样的, 如果一样的则更新, 反之拒绝。
- 悲观锁
悲观锁思想就是从一开始就认为更新会有冲突, 所以先手动获取排他锁, 再做更新。
- 记录锁
记录锁就是为某行加锁, 条件必须为主键列或者唯一索引列
- 间隙锁(GAP)
- [ ] 间隙锁锁定的区域(两边都是开区间)
根据检索条件向下寻找最靠近检索条件的记录值A作为左区间,向上寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。
- [ ] 间隙锁使用的条件
必须在RR级别下
检索条件必须有索引(没有索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)
- [ ] 不包括自己本身的记录
- Next-Key Lock(临键锁、前开后闭)
行锁和间隙锁组合起来就是 Next-Key Lock。
#### 快照读和当前读
- 快照读
- [ ] 说明
读取的是记录的可见版本(有可能是历史版本), 不用加锁
- [ ] 例子
```sql
SELECT * FROM table WHERE ?;
```
- 当前读
- [ ] 说明
特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。
所有以下的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加**S锁 (共享锁)外**,其他的操作,都加的是**X锁 (排它锁)**
- [ ] 例子
```sql
SELECT * FROM table WHERE ? lock in share mode;
SELECT * FROM table WHERE ? for update;
INSERT INTO table values(...)
UPDATE table SET ? WHERE ?;
DELETE FROM table WHERE ?;
```
#### 分表
- 分表
- [ ] 垂直分表
也就是“大表拆小表”,基于列字段进行的。一般是表中的字段较多,将不常用的, 数据较大,长度较长(比如text类型字段)的字段数据拆分到“扩展表“
- [ ] 水平分表
当一个应用难以再细粒度的垂直切分,或切分后数据量行数巨大,存储性能瓶颈,这时候就需要进行水平切分了, 可以按照一定的算法拆分数据, 如时间、租户ID等等
#### 主从
- 原理

- 同步类型
- [ ] 异步复制
特点
主库在执行完客户端提交的时候会立即将结果返回给客户端, 并不关心从库是否已经接收并处理。
二进制日志复制
基于二进制日志的复制是 MySQL 最早使用的复制技术,因此 MySQL 对其的支持比较完善,对执行修改的 SQL 语句几乎没有任何限制。其主要的缺点是在一主多从的高可用复制架构中,如果主库发生宕机,此时想要自动通过从库的日志和偏移量来确定新的主库比较困难
GTID
从服务器连接到主服务器之后,把自己执行过的GTID (Executed_Gtid_Set: 即已经执行的事务编码) 、获取到的GTID (Retrieved_Gtid_Set: 即从库已经接收到主库的事务编号) 发给主服务器,主服务器把从服务器缺少的GTID及对应的transactions发过去补全即可

- [ ] 全同步复制
当主库只希望一个事务, 所有的从库都执行了该事物, 才返回给客户端。
- [ ] 半同步复制
介于异步复制和全同步复制之间, 主库在执行完客户端提交的时候后不是立即返回给客户端, 而是至少等待至少一个从库接收并写到relay log 才返回给客户端。
#### 分布式事务
- 特点
跨库跨机器保证ACID
- 实现
```php
query("XA START '$grid'");//准备事务1$mysqlObj2->query("XA START '$grid'");//准备事务2try {
$return = $mysqlObj2->query("UPDATE question SET author='高海峰' WHERE id=10") ;//第一个分支事务准备做的事情,通常他们会记录进日志
if($return == false) { throw new Exception("202数据库更新失败!");
}
$return = $mysqlObj1->query("UPDATE question SET author='高海峰22' WHERE id=11");//第二个分支事务准备做的事情,通常他们会记录进日志
if($return == false) { throw new Exception("78数据库更新失败!");
}
$mysqlObj2->query("XA END '$grid'");
$mysqlObj2->query("XA PREPARE '$grid'");
$mysqlObj1->query("XA END '$grid'");
$mysqlObj1->query("XA PREPARE '$grid'");//通知是否准备提交
$mysqlObj1->query("XA COMMIT '$grid'");//这两个基本同时执行
$mysqlObj2->query("XA COMMIT '$grid'");
} catch (Exception $e) {
$mysqlObj1->query("XA ROLLBACK '$grid'");
$mysqlObj2->query("XA ROLLBACK '$grid'"); print $e->getMessage();
}
```
#### 优化
- 字段
- [ ] VARCHAR长度只分配真正需要的空间
- [ ] 使用TINYINT代替枚举
- [ ] 尽量使用TIMESTAMP而非DATETIME
- [ ] 单表不要太多字段
- [ ] 避免使用NULL字段, 很难查询优化, 且NULL是个很特殊的存在, 可能会导致查询结果不是自己想要的
- 索引
- [ ] 使用EXPLAIN来分析查询语句
- [ ] 考虑在WHERE和ORDER BY 查询涉及的列建立索引
- [ ] 值分布很稀少的字段不是和建立索引, 如性别等
- [ ] 字符字段只建前缀索引
- [ ] 字符串字段不要做主键
- [ ] 使用多列索引注意顺序和查询条件保持一致, 同时删除不必要索引
- 查询SQL
- [ ] 不做列运算
- [ ] 不要SELECT *
- [ ] OR 改成IN(or 只有两边的字段都有索引才会走索引)
- [ ] 避免%XXX
- [ ] 少连表
- [ ] 尽量避免在字句中使用!=或者<>等操作符, 否则会放弃使用索引
- [ ] 对于连续的数值, 使用BETWEEN 不使用IN
- [ ] 大量数据也需要分页查询, 那边只查一条也加上LIMIT 1
- [ ] 连接尽量遵循小表驱动大表
- 其他
不使用函数
不使用存储过程
不是使用触发器
不使用外键
开启慢日志查询
读写分离
禁止嵌套事务
尽量保证小事务
#### 5.7新特性
- 多线程复制
Mysql 5.7 对 “多线程复制” 进行了改善,可以按照逻辑时钟的方式来分配线程,大大提高了复制性能
- 性能
3倍更快的性能
- JSON
原生 JSON 支持
## Redis
#### Redis是什么
redis是高速的内存键值数据库, 常被用来做分布式的高速缓存和队列
#### 为什么快
- 读写全部在内存
Redis最大的特点在数据数据的读写全部在内存中进行, 进而带来了极大的效率优势
- 单线程处理请求
由于Redis都是在内存里面进行读写的, 如果使用多线程对数据进行读写, 那么同时就会带来锁开销, 大量写入的情况下, 过多的锁带来的时间消耗比多线程带来的多核利用优势更大
- I/O多路复用技术
I/O多路复用我们又称之为事件驱动,Redis基于epoll等技术实现了网络部分响应的同步非阻塞I/O模式
#### 数据结构
String(字符串)
key-value形式存储, 一个键最大能存储512M
Hash(哈希)
是一个键值(key=>value)对集合
LIST(列表)
简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
SET(集合)
Set 是 string 类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
ZSET(有序集合)
一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复
#### 淘汰策略
- 说明
当 Redis 内存满了,在进行 set 的时候,就会触发淘汰策略
- volatile-lru
从已设置的过期时间的数据集中挑选最近最少使用的数据淘汰
- valatile-ttl
从已设置过期时间的数据集中挑选将要过期的数据淘汰
- votile-random
从已设置过期时间的数据集中任意选择数据淘汰
- volatile-lfu
从已设置过期时间的数据集挑选使用频率最低的数据淘汰
- allkeys-lru
从数据集中挑选最近最少使用的数据淘汰
- allkeys-lfu
从数据集中挑选使用频率最低的数据淘汰
- allkey-random
从数据集中任意选择数据淘汰
- no-enviction(驱逐)
禁止驱逐, 默认策略, 意思是当内存不足以容纳新数据时, 新的写入操作就会报错, 读取可以继续进行
#### 删除策略(过期策略)
- 说明
当有 key 设置了过期时间,就会有删除策略, **Reids** 采用的是**惰性删除 + 定期删除**两种策略结合使用
- 惰性删除
当键被读取时如果发现他已经失效, 那么就删除它, 确点时如果key迟迟不被访问, 就会占用很多空间
- 定时删除
在设置键的过期时间的同时,创建一个定时器( timer ). 让定时器在键的过期时间来临时,立即执行对键的删除操作
- 定期删除
key的定期删除会在Redis的周期性执行任务(serverCron,默认每100ms执行一次)中进行
#### 持久化策略
- 说明
当 Redis 启动时, 如果 RDB 持久化和 AOF 持久化都被打开了, 那么程序会优先使用 AOF 文件来恢复数据集, 因为 AOF 文件所保存的数据通常是最完整的。
- rdb(redis database、快照持久化)
- [ ] 说明
redis将某一时间段的数据全部打包生成一个.rdb的文件, 保存在磁盘中, 当我们重启redis服务的时候, 会读取该rdb文件恢复数据库中的数据
- [ ] 配置

aof(append only file, 只追加文件持久化)
- [ ] 说明
redis会将执行的写命令添加到aof文件的末尾, 该文件被保存在磁盘中, 当重启redis服务的时候回优先(相对于rdb文件而已)读取aof文件, 完成对redis的恢复。
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
- [ ] 配置

#### 事务
- 说明
- [ ] Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
批量操作在发送 EXEC 命令前被放入队列缓存。
收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
- 原子性
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的
#### 复制
- 复制过程
1.slave连接到master后, 会发送一条SYNC命令
2.模式:
全量模式: master收到命令后, 将内存的中的数据整理好, 一次性发送给slave
增量模式: master收到命令后, 将内存的中的数据整理好, 一次性发送给slave, 且后续数据有更新不断的同步给slave
#### 雪崩、击穿、穿透
- 雪崩
- [ ] 理解
某一时刻大量的数据同时失效, 导致大量的请求打到数据库上
- [ ] 解决
避免缓存集中失效, 不同的key设置不同的时间
- 击穿
- [ ] 理解
大量的请求同时访问同一条数据, 刚好这条数据缓存过期, 导致所有的请求都到数据库
- [ ] 解决
可以互斥锁, 当缓存不存在的时候, 只有一个请求去生成缓存
- 穿透
- [ ] 理解
恶意用户故意访问redis和mysql中不存在的数据
- [ ] 解决
做数据过滤或者hash存储数据是否存在
#### 集群模式
- 主从模式
- [ ] 采用异步复制
- [ ] 从服务器在与主服务器初始化连接时, 数据库中原有所有数据都将丢失, 并被替换为主服务器发来的数据
- 哨兵模式

- [ ] 哨兵集群模式是基于主从模式的,所有主从的优点,哨兵模式同样具有。
- [ ] 主从可以切换, 故障可以转移, 系统可用性更好
- [ ] 独立运行的哨兵线程, 监控服务器是否正常
- [ ] 选举的逻辑配置的优先级>偏移量最大(数据最完整)>启动最早的节点
- Cluster集群模式

- [ ] 集群不支持那些需要同时处理多个键的 `Redis` 命令
- [ ] 将数据自动切分`split`到多个节点的能力
- [ ] redis cluster为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉
## Mongodb
#### NoSql是什么
- 说明
泛指非关系型的数据库
#### MongoDB是什么
- 说明
基于分布式文件存储的非关系型数据库
- 优点
文档结构的存储方式,能够更便捷的获取数据
分片简单
海量数据下, 性能优越
支持自动故障恢复
- 缺点
不支持事务
占用空间大
无法链表
模式自由, 自由灵活文件存储格式带来的数据错误
删除记录不释放空间
#### 数据类型
- String(字符串)
- Integer(整数)
- Boolean(布尔值)
- Double(双精度)
- Min/Max keys
- Arrays(数组)
- Timestamp(时间戳)
- Object(内嵌文档)
- Null(空值)
- Symbol(符号)
- Date(日期时间)
- Object ID(对象Id)
- Binary Data(二进制)
- Code(代码类型)
- Regular expression(正则表达式)
#### 数据库
- 创建数据库(默认数据库为test)
```
use runoob
```
- 查看数据库
```
#查看当前所有数据库
show dbs;
#查看当前使用的数据库
db;
```
- 切换数据库
```
use runoob;
```
- 删除数据库
```
#切换到要删除的数据库下
use runoob;
#删除当前数据库
db.dropDatabase();
```
#### 集合(表)
- 创建集合
- [ ] mogodb不需要手动创建集合, 当插入文档时, 会自动创建集合
- [ ] 示例
```sql
db.createCollection('name', []);
```
- 查看已有集合
```sql
show collections;
```
- 删除集合
```sql
db.COLLECTION_NAME.drop();
```
#### 文档
- 插入文档
```
#如果插入主键已存在, 提示错误
db.COLLECTION_NAME.insert(document);
#如果_id主键存在则更新数据, 不存在则插入数据
db.COLLECTION_NAME.save(document);
#插入一条
#writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求
#document:要写入的文档。
db.COLLECTION_NAME.insertOne(document, {
writeConcern:
});
#插入多条数据
#writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求
#document:要写入的文档。
#ordered: 指定是否按顺序写入,默认 true,按顺序写入
db.COLLECTION_NAME.insertMany(
[ , , ... ],
{
writeConcern: ,
ordered:
}
)
```
- 更新文档
```
# 参数说明
# query: 更新的查询条件
# update: update的对象
# upsert: 可选, 如果不存在update的记录, 是否插入记录, 默认为false
# multi: 可选, 这更新找到的第一条记录
# writeConcern: 抛出异常的级别
db.COLLECTION_NAME.update(
,
,
{
upsert: ,
multi: ,
writeConcern:
}
);
```
- 删除文档
```
# 参数说明
# query: 更新的查询条件
# justOne: 是否只删除一条记录, 默认为false
db.COLLECTION_NAME.remove(
,
);
```
- 查询文档
```
# 参数说明
# query: 更新的查询条件
# projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)
db.COLLECTION_NAME.find(query, projection).limit(1).skip(1).sort({KEY:1});
```
- type操作符
```
db.col.find({"title" : {$type : 2}})
或
db.col.find({"title" : {$type : 'string'}})
```

#### 聚合操作
- 聚合表达式

- 管道
- [ ] $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
- [ ] $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
- [ ] $limit:用来限制MongoDB聚合管道返回的文档数。
- [ ] $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
- [ ] $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
- [ ] $group:将集合中的文档分组,可用于统计结果。
- [ ] $sort:将输入文档排序后输出。
- [ ] $geoNear:输出接近某一地理位置的有序文档。
#### 索引
- 创建索引
```
#参数说明
#background: 建立索引会堵塞其他数据库操作, true为在后台创建, 默认为false
#unique: 建立的索引为唯一键, 默认值为false
#name: 索引的名称
#sparse: 对索引不存在的字段不启用索引, 默认值为false
#expireAfterSeconds: 指定一个以秒为的数值, 设定集合的生存时间
#weights: 索引权重值, 1到99999之间
#dufault_language 默认语言英语
#language_overiride 默认值为languaage
db.COLLECTION_NAME.createIndex(keys, options)
```
- 查看索引
```
db.COLLECTION_NAME.getIndexes()
```
- 删除索引
```
db.COLLECTION_NAME.dropIndex(name)
```
#### 复制
- 说明
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
- 主从
主从复制模式的集群中只能有一个主节点,主节点提供所有的增、删、查、改服务,从节点不提供任何服务,但是可以通过设置使从节点提供查询服务,这样可以减少主节点的压力。
- 副本集
副本集与主从复制的区别在于:当集群中主节点发生故障时,副本集可以自动投票,选举出新的主节点,并引导其余的从节点连接新的主节点。
可以说,MongoDB 的副本集是自带故障转移功能的主从复制。
#### 分片
- 说明
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
## Eastsearch
#### 倒排索引

#### 索引(Index)
- 说明
类似mysql的数据库
- 创建索引
```
#参数说明
#number_of_shards: 分片数量, 默认为5, 索引一创建就无法#改变分片的数量, es已实现了分片数据的管理, 用户无感知, 将数据拆分成多台节点进行存放, shard = hash(routing) % number_of_primary_shards
#number_of_replicas: 副本数, 默认为1
#
PUT /index_name
{
"setting": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
```
- 查看索引
```
#指定索引
GET /index_name?pretty
#所有索引
GET /cat/indices?v
```
- 删除索引
```
#删除索引
DELETE /index_name?pretty
```
#### 类型(Type)
- 说明
由于在ES的index定义中, 两个不同的type如果大家都有相同名的字段, 则其的定义必须完全一致, 也就是没有完全区分开, 分离不够彻底, 将只允许单个类型单个索引, 比较推荐的类型名字为 _doc, 7.X完全移除type
#### 文档(Document)
- 说明
默认情况下, 插入一条文档, 当index和type,不需要提前创建,而且es默认会对document每个field都建立倒排索引,让其可以被搜索(可以配置禁用自动创建索引, 因为可能会导致mapping类型错误)
#### Mapping(映射)
- 说明
类似于mysql的表结构, 用于定义type中有多少字段且每个字段的类型
- 索引字段类型
- [ ] text
存储前先进行分词, 存储的是分词后的结果,而不是完整的字段。text类型的字段不适合做排序和聚合
- [ ] keyword
用于存储一体化的内容(例如电子邮箱, 主机名, 状态代码, 邮箱或者标签)的字段, 这些字段被分词后没有啥意义, 存储的是分词前的结果
- [ ] date
支持排序, 且可以通过format字段对时间进行格式化
- [ ] object
mapping中不用特意知道field为Object类型, 因为这就是它的默认类型
- [ ] nest
一种特殊的Object类型, 它允许Object可以以数组的形式被索引,而且数组中某一项都可以被独立索引
```
PUT my_index/_doc/1
{
"group" : "fans",
"user" : [
{
"first" : "John",
"last" : "Smith"
},
{
"first" : "Alice",
"last" : "White"
}
]
}
```
上面格式的对象会被按照下列格式进行索引,因此会发现一个user中的两个属性值不再匹配,`alice`和`white`失去了联系
```
{
"group" : "fans",
"user.first" : [ "alice", "john" ],
"user.last" : [ "smith", "white" ]
}
```
- [ ] range

```
PUT range_index
{
"settings": {
"number_of_shards": 2
},
"mappings": {
"properties": {
"age_range": {
"type": "integer_range"
},
"time_frame": {
"type": "date_range",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
}
}
}
}
```
### RabbitMQ
- Direct
- Fanout
- Topic
- Headers
## 高可用
#### 什么是高可用
- 说明
高可用的含义就是当一台服务器宕机后,服务可以继续使用以及数据不会丢失。
#### 什么是负载均衡
- 说明
负责均衡服务器根据负载均衡算法(轮询,随机,哈希,权重等)来分发请求到不同的主服务器, 增加容错.
#### 什么是Keepalive
- 说明
一种检查服务器状态的软件, 且可以自动剔除故障机器
#### Nginx
- 说明
Nginx的upstream本身就已经实现了负载均衡, 可以为每台机器分配权重, 当某台机器挂了, 会自动踢掉该机器, 缺点是本身NGINX服务器为单点
- 轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器, 如果后端服务器down掉, 自动剔除
- ip_hash
每个请求按访问IP的hash结果分配, 这样每个访客固定访问一个后端服务器, 可以解决session的问题
#### DNS域名实现负载均衡
- 说明
在域名解析的使用同一个域名配置到不同的IP,以实现DNS域名负载均衡, 支持按权重配置
#### LVS+Keepalive
- 说明
LVS是基于内核的, 只做IP和端口的转发, 不建立连接

- 主备模式

- 双主模式(两个VIP互为主备)

#### Nginx+Keepalive
- 说明
如果单单使用NGINX的upstream实现负载均衡, 但本身NGINX就是单点, 所以可以使用Nginx+Keepalive解决

## 其他
#### 秒杀
- 说明
利用redis内存运算且为原子性的情况下实现乐观锁, 先判断数量后同步/异步处理秒杀成功的逻辑, 核心就是不查询关系型数据库, 将绝大部分请求挡在处理逻辑之前


#### 正向代理
- 说明
正向代理类似一个跳板机,代理访问外部资源,正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端.

#### 反向代理
- 说明
实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端

#### 网站优化
- 静态化
- 动静分离
- 静态文件版本号
- 缓存
- 减少请求数(合并CSS、javascript、图片等)
- 压缩静态资源(CSS、javascript)
- 异步请求资源
- 懒加载图片
- 缩略图
- CDN