vlambda博客
学习文章列表

PHP 面试服务器优化和大数据

服务器配置优化
系统参数调整
Linux 系统内核参数优化
vim /etc/sysctl.conf 
 net.ipv4.ip_local_port_range = 1024 65535 # 用户端口范围 
 net.ipv4.tcp_max_syn_backlog = 4096 
 net.ipv4.tcp_fin_timeout = 30 
 fs.file-max=65535 # 系统最大文件句柄,控制的是能打开文件最大数量
数据库参数优化
实例整体
thread_concurrency   #并发线程数量个数 
sort_buffer_size     #排序缓存 
read_buffer_size     #顺序读取缓存 
read_rnd_buffer_size #随机读取缓存 
key_buffer_size      #索引缓存 
thread_cache_size    #线程缓存(1G—>8, 2G—>16, 3G—>32, >3G—>64)
连接层(基础优化)

设置合理的连接客户和连接方式

max_connections        #最大连接数,看交易笔数设置 
max_connect_errors     #最大错误连接数,能大则大 
connect_timeout        #连接超时 
max_user_connections   #最大用户连接数 
skip-name-resolve      #跳过域名解析 
wait_timeout           #等待超时 
back_log               #可以在堆栈中的连接数量
SQL 层(基础优化)

query_cache_size:查询缓存
OLAP 类型数据库,需要重点加大此内存缓存.
但是一般不会超过 GB.
对于经常被修改的数据,缓存会立马失效。
我们可以实用内存数据库(redis、memecache),替代他的功能。

存储引擎层

innodb 基础优化参数

default-storage-engine 
innodb_buffer_pool_size # 没有固定大小,50%测试值,看看情况再微调。但是尽量设置不要超过物理内存70% innodb_file_per_table=(1,0) 
innodb_flush_log_at_trx_commit=(0,1,2) #1是最安全的,0是性能最高,2折中 
binlog_sync 
Innodb_flush_method=(O_DIRECT, fdatasync) 
innodb_log_buffer_size      #100M以下 
innodb_log_file_size        #100M 以下 
innodb_log_files_in_group   #5个成员以下,一般2-3个够用(iblogfile0-N) 
innodb_max_dirty_pages_pct  #达到百分之75的时候刷写 内存脏页到磁盘。log_bin
max_binlog_cache_size       #可以不设置 
max_binlog_size             #可以不设置 
innodb_additional_mem_pool_size     #小于2G内存的机器,推荐值是20M。32G内存以上100M
大数据的一些操作
大数据的分页
直接用 limit start, count 分页语句

当起始页较小时,查询没有性能问题,随着起始记录的增加,时间也随着增大

发现

1)limit 语句的查询时间与起始记录的位置成正比
2)mysql 的 limit 语句是很方便,但是对记录很多的表并不适合直接使用

对 limit 分页问题的性能优化方法

如果一个索引包含 (或覆盖) 所有需要查询的字段的值,称为‘覆盖索引’。即只需扫描索引而无须回表。

覆盖索引优点

1.索引条目通常远小于数据行大小,只需要读取索引,则mysql会极大地减少数据访问量。
2.因为索引是按照列值顺序存储的,所以对于IO密集的范围查找会比随机从磁盘读取每一行数据的IO少很多。
3.一些存储引擎如myisam在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用
4.innodb的聚簇索引,覆盖索引对innodb表特别有用。
覆盖索引必须要存储索引列的值,而哈希索引、空间索引和全文索引不存储索引列的值,所以 mysql 只能用 B-tree 索引做覆盖索引。
例子

select id from product limit 866613, 20 
SELECT * FROM product WHERE ID > =(select id from product limit 866613, 1) limit 20
SELECT * FROM product a JOIN (select id from product limit 866613, 20) b ON a.ID = b.id
百万级的数据的导出

一。对于数据超过了 65535 行的问题,很自然的就会想到将整个数据分块,利用 excel 的多 sheet 页的功能,将超出 65535 行后的数据写入到下一个 sheet 页中,即通过多 sheet 页的方式,突破了最高 65535 行数据的限定,具体做法就是,单独做一个链接,使用 JSP 导出,在 JSP 上通过程序判断报表行数,超过 65535 行后分 SHEET 写入。这样这个问题就得以解决了

二。在这种大数据量的报表生成和导出中,要占用大量的内存,尤其是在使用 TOMCAT 的情况下,JVM 最高只能支持到 2G 内存,则会发生内存溢出的情况。此时的内存开销主要是两部分,一部分是该报表生成时的开销,另一部分是该报表生成后写入一个 EXCEL 时的开销。由于 JVM 的 GC 机制是不能强制回收的,因此,对于此种情形,我们要改变一种方式:

将该报表设置起始行和结束行参数,在 API 生成报表的过程中,分步计算报表,比如一张 20 万行数据的报表,在生成过程中,可通过起始行和结束行分 4-5 次进行。这样,就降低了报表生成时的内存占用,在后面报表生成的过程中,如果发现内存不够,即可自动启动 JVM 的 GC 机制,回收前面报表的缓存
导出 EXCEL 的过程,放在每段生成报表之后立即进行,改多个 SHEET 页为多个 EXCEL,即在分步生成报表的同时分步生成 EXCEL,则通过 POI 包生成 EXCEL 的内存消耗也得以降低。通过
多次生成,同样可以在后面 EXCEL 生成所需要的内存不足时,有效回收前面生成 EXCEL 时占用的内存
再使用文件操作,对每个客户端的导出请求在服务器端根据 SESSIONID 和登陆时间生成唯一的临时目录,用来放置所生成的多个 EXCEL,然后调用系统控制台,打包多个 EXCEL 为 RAR
或者 JAR 方式,最终反馈给用户一个 RAR 包或者 JAR 包,响应客户请求后,再次调用控制台删除该临时目录。
通过分段运算和生成,有效降低了报表从生成结果到生成 EXCEL 的内存开销。其次是通过使用压缩包,响应给用户的生成文件体积大大缩小,降低了多用户并
发访问时服务器下载文件的负担,有效减少多个用户导出下载时服务器端的流量,从而达到进一步减轻服务器负载的效果

php 相关
cgi ,fastcgi,php-fpm 区别
cgi

CGI 的英文是(COMMON GATEWAY INTERFACE)公共网关接口,它的作用就是帮助服务器与语言通信,这里就是 nginx 和 php 进行通信,因为 nginx 和 php 的语言不通,因此需要一个沟通转换的过程,而 CGI 就是这个沟通的协议。
nginx 服务器在接受到浏览器传递过来的数据后,如果请求的是静态的页面或者图片等无需动态处理的则会直接根据请求的 url 找到其位置然后返回给浏览器,这里无需 php 参与,但是如果是一个动态的页面请求,这个时候 nginx 就必须与 php 通信,这个时候就会需要用到 cgi 协议,将请求数据转换成 php 能理解的信息,然后 php 根据这些信息返回的信息也要通过 cgi 协议转换成 nginx 可以理解的信息,最后 nginx 接到这些信息再返回给浏览器。

fast-cgi

传统的 cgi 协议在每次连接请求时,会开启一个进程进行处理,处理完毕会关闭该进程,因此下次连接,又要再次开启一个进程进行处理,因此有多少个连接就有多少个 cgi 进程,这也就是为什么传统的 cgi 会显得缓慢的原因,因此过多的进程会消耗资源和内存。
而 fast-cgi 每次处理完请求后,不会 kill 掉这个进程,而是保留这个进程,使这个进程可以一次处理多个请求。这样每次就不用重新 fork 一个进程了,大大提高效率。

php-cgi

php-cgi 是 php 提供给 web serve 也就是 http 前端服务器的 cgi 协议接口程序,当每次接到 http 前端服务器的请求都会开启一个 php-cgi 进程进行处理,而且开启的 php-cgi 的过程中会先要重载配置,数据结构以及初始化运行环境,如果更新了 php 配置,那么就需要重启 php-cgi 才能生效,例如 phpstudy 就是这种情况。

php-fpm

php-fpm 是 php 提供给 web serve 也就是 http 前端服务器的 fastcgi 协议接口程序,它不会像 php-cgi 一样每次连接都会重新开启一个进程,处理完请求又关闭这个进程,而是允许一个进程对多个连接进行处理,而不会立即关闭这个进程,而是会接着处理下一个连接。它可以说是 php-cgi 的一个管理程序,是对 php-cgi 的改进。
php-fpm 会开启多个 php-cgi 程序,并且 php-fpm 常驻内存,每次 web serve 服务器发送连接过来的时候,php-fpm 将连接信息分配给下面其中的一个子程序 php-cgi 进行处理,处理完毕这个 php-cgi 并不会关闭,而是继续等待下一个连接,这也是 fast-cgi 加速的原理,但是由于 php-fpm 是多进程的,而一个 php-cgi 基本消耗 7-25M 内存,因此如果连接过多就会导致内存消耗过大,引发一些问题,例如 nginx 里的 502 错误。
同时 php-fpm 还附带一些其他的功能:
例如平滑过渡配置更改,普通的 php-cgi 在每次更改配置后,需要重新启动才能初始化新的配置,而 php-fpm 是不需要,php-fpm 分将新的连接发送给新的子程序 php-cgi,这个时候加载的是新的配置,而原先正在运行的 php-cgi 还是使用的原先的配置,等到这个连接后下一次连接的时候会使用新的配置初始化,这就是平滑过渡。
参考链接:https://blog.csdn.net/belen_xue/article/de…

PHP5 跟 php7 的区别

PHP7.0 号称是性能提升上革命性的一个版本。面对 Facebook 家的 HHVM 引擎带来的压力,开发团队重写了底层的 Zend Engine,名为 Zend Engine 2。

底层内核解析

PHP7 中最重要的改变就是 zval 不再单独从堆上分配内存并且不自己存储引用计数。需要使用 zval 指针的复杂类型(比如字符串、数组和对象)会自己存储引用计数。这样就可以有更少的内存分配操作、更少的间接指针使用以及更少的内存分配。在 PHP7 中的 zval, 已经变成了一个值指针,它要么保存着原始值,要么保存着指向一个保存原始值的指针。也就是说现在的 zval 相当于 PHP5 的时候的 zval . 只不过相比于 zval , 直接存储 zval, 我们可以省掉一次指针解引用,从而提高缓存友好性.

参考链接:https://www.jb51.net/article/76732.htm

PHP7 为什么比 PHP5 性能提升了

1、变量存储字节减小,减少内存占用,提升变量操作速度
2、改善数组结构,数组元素和 hash 映射表被分配在同一块内存里,降低了内存占用、提升了 cpu 缓存命中率
3、改进了函数的调用机制,通过优化参数传递的环节,减少了一些指令,提高执行效率

安全

函数修改

preg_replace () 不再支持 /e 修饰符,,同时官方给了我们一个新的函数 preg_replace_callback
create_function () 被废弃,实际上它是通过执行 eval 实现的。
mysql* 系列全员移除,如果你要在 PHP7 上面用老版本的 mysql* 系列函数需要你自己去额外装了,官方不在自带,现在官方推荐的是 mysqli 或者 pdo_mysql。
unserialize () 增加一个可选白名单参数,其实就是一个白名单,如果反序列数据里面的类名不在这个白名单内,就会报错。
assert () 默认不在可以执行代码
语法修改

foreach 不再改变内部数组指针
8 进制字符容错率降低,在 php5 版本,如果一个八进制字符如果含有无效数字,该无效数字将被静默删节。在 php7 里面会触发一个解析错误。
十六进制字符串不再被认为是数字
移除了 ASP 和 script PHP 标签
超大浮点数类型转换截断,将浮点数转换为整数的时候,如果浮点数值太大,导致无法以整数表达的情况下, 在 PHP5 的版本中,转换会直接将整数截断,并不会引发错误。在 PHP7 中,会报错。

总体

性能提升:PHP7 比 PHP5.0 性能提升了两倍。
全面一致的 64 位支持。
以前的许多致命错误,现在改成 [抛出异常]。
PHP 7.0 比 PHP5.0 移除了一些老的不在支持的 SAPI([服务器端] 应用编程端口)和扩展。
.PHP 7.0 比 PHP5.0 新增了空接合操作符。
PHP 7.0 比 PHP5.0 新增加了结合比较运算符。
PHP 7.0 比 PHP5.0 新增加了函数的返回类型声明。
PHP 7.0 比 PHP5.0 新增加了标量类型声明。
PHP 7.0 比 PHP5.0 新增加匿名类。

多进程同时读写一个文件

PHP 是支持进程的而不支持多线程(这个先搞清楚了),如果是对于文件操作,其实你只需要给文件加锁就能解决,不需要其它操作,PHP 的 flock 已经帮你搞定了。
用 flock 在写文件前先锁上,等写完后解锁,这样就实现了多线程同时读写一个文件避免冲突。

流程

flock 参数说明:file 必需,规定要锁定或释放的已打开的文件,lock 必需。规定要使用哪种锁定类型。block 可选。若设置为 1 或 true,则当进行锁定时阻挡其他进程。
LOCK_SH 要取得共享锁定(读取的程序)
LOCK_EX 要取得独占锁定(写入的程序)
LOCK_UN 要释放锁定(无论共享或独占)
LOCK_NB 如果不希望 flock () 在锁定时堵塞
在 PHP 中,flock 似乎工作的不是那么好!在多并发情况下,似乎是经常独占资源,不即时释放,或者是根本不释放,造成死锁,从而使服务器的 cpu 占用很高,甚至有时候会让服务器彻底死掉。好所以使用 flock 之前,一定要慎重考虑。

解决方案

对文件进行加锁时,设置一个超时时间,超时设置为 1ms,如果这里时间内没有获得锁,就反复获得,直接获得到对文件操作权为止,当然。如果超时限制已到,就必需马上退出,让出锁让其它进程来进行操作。
不使用 flock 函数,借用临时文件来解决读写冲突的问题
将需要更新的文件考虑一份到我们的临时文件目录,将文件最后修改时间保存到一个变量,并为这个临时文件取一个随机的,不容易重复的文件名。
当对这个临时文件进行更新后,再检测原文件的最后更新时间和先前所保存的时间是否一致。
如果最后一次修改时间一致,就将所修改的临时文件重命名到原文件,为了确保文件状态同步更新,所以需要清除一下文件状态。
但是,如果最后一次修改时间和先前所保存的一致,这说明在这期间,原文件已经被修改过,这时,需要把临时文件删除,然后返回 false, 说明文件这时有其它进程在进行操作。
对操作的文件进行随机读写,以降低并发的可能性。
先前需要定义一个随机空间,空间越大,并发的的可能性就越小,这里假设随机读写空间为 [1-500], 那么我们的日志文件的分布就为 log1~ 到 log500 不等。每一次用户访问,都将数据随机写到 log1~log500 之间的任一文件。在同一时刻,有 2 个进程进行记录日志,A 进程可能是更新的 log32 文件,而 B 进程呢?则此时更新的可能就为 log399. 要知道,如果要让 B 进程也操作 log32, 概率基本上为 1/500, 差不多约等于零。在需要对访问日志进行分析时,这里我们只需要先将这些日志合并,再进行分析即可。使用这种方案来记录日志的一个好处时,进程操作排队的可能性比较小,可以使进程很迅速的完成每一次操作。
将所有要操作的进程放入一个队列中。
队列中的每一个排除的进程相当于第一个具体的操作,所以第一次我们的服务只需要从队列中取得相当于具体操作事项就可以了,如果这里还有大量的文件操作进程,没关系,排到我们的队列后面即可,只要愿意排,队列的多长都没关系。