PHP性能优化解读与补充
目录
前言
性能优化的目标就是使得系统高并发高可用,一般涉及到技术或方案关键字有:缓存、索引、异步、消息队列、分布式、集群、主从等
从该文章中筛选了一部分和优化相关的文章分享解读与补充:
nginx
如果没有上云,可以考虑自己搭建nginx高可用方案,原理基本类似。
代理和反向代理,有几个区别:1、正向代理,后端服务器不知道客户端是谁,反向代理后端服务器知道客户端是谁;2、正向代理一般是代理作为用户客户端的中介跳板服务,并代替访问后端服务器,后端服务器仍然是对外暴露的,而反向代理只是做了转发,并不代替用户客户端,后端服务器一并隐藏在反向代理服务后面,并不会暴露,只有通过发现代理服务才能访问到后端服务
反向代理使得服务更安全,还能提供负载均衡服务,提高性能及资源利用
要优化,首先得知道nginx的工作原理和流程,该文章很详细的介绍了处理php动态请求的全过程,步骤清晰,这个基础知识点很重要,当然一般我们也不建议在nginx上做缓存处理。
限流、缓存、黑白名单这些都是高并发场景经常会遇到的要解决的问题,nginx提供了这些解决方案,当然我们也可以考虑自己编码实现,比如通过redis等内存级缓存实现
nginx限流更加详细的介绍和配置说明
nginx缓存控制详细说明,及图片防盗链设置
redis
这篇文章朴实的文字介绍了redis缓存使用场景的发展,因为要求效率,增加客户端缓存,后来将缓存移到服务端采用redis内存缓存;由于担心redis服务器偶尔抽风缓存数据丢失,考虑redis持久化,保证重启后能恢复缓存数据;再发展到可能redis服务器一挂就是一天,这个时候主从机制(哨兵+复制)就派上用场了,主redis服务挂了,哨兵自动检测并切换到从redis服务;由于业务越来越大,用户量激增,单台redis服务内存不足以支撑用户量,考虑增加内存,但机器的内存也是有个上限的,再考虑横向扩展redis服务(如同数据库分表),这就是redis集群了,可以使用中间件codis进行集群管理,也可以用官方推荐的cluster集群。该文属于介绍方法论,不做具体教学实践,不错的缓存指南。为什么要用redis?点这里
很完整的一个php+redis限流方案(之前我们介绍过nginx的限流方案),该文主要介绍的是令牌桶算法,大概通俗点说:每隔一定时间t会往令牌桶里放一定数量m的令牌(实际代码中表现为令牌数),当请求来时,从令牌桶(redis键值对)检查令牌数(大于0表示拿到令牌,否则表示没有令牌),拿到令牌则继续访问,否则拒绝访问。可能很多开发者自己设计过类似的限流方案,但可能不知道是类令牌桶限流方案,叫什么名字不重要,重要的是有效和正确的思路。
消息队列可用于实现异步处理。在没有采用第三方消息队列中间件或服务时,我们经常采用redis做消息队列,可能是PUB/SUB订阅与发布模式(效果没有很好),可能是基于LIST的push和pop实现,也可能是sset的实现,现在redis5.0新增了stream类型,看起来是对消息队列的进一步完善实现:消息ID序列生成、消息遍历、消息阻塞与非阻塞读取、消息分组消费、消息队列监控等
这篇文章干货较多,主要从redis持久化RDB与AOF入手,介绍了两种持久化方案的优缺点,并介绍了哪些场景下使用什么样的持久化方案会更好(我们觉得这种文章比起只说原理不说如何处理或不给具体案例场景的文章,对新手更友好):
有些场景下redis数据仅仅是缓存数据,丢失也不影响时,都可以不考虑持久化甚至主从(当然不考虑雪崩和穿透问题);而单机环境下,可以接受短时段的部分数据丢失,可以考虑AOF(实时性更好,但恢复较慢、文件较大);如果我们有主从的配置,那么可以不考虑持久化;最完美的方案就是集群+主从,既保证负载均衡又保证数据安全。
另外一篇详细说明AOF与RDB的文章。
该文是一篇基础普及redis使用的文章,可轻松阅读,干货知识甚多。主要介绍了redis的非阻塞IO多路复用是什么(利用送外卖的例子通俗讲解了,更多关于详见:同步-异步-阻塞-非阻塞-IO模型);介绍了redis各种数据类型的常用场景,比如string用于set/get缓存与计数,hash可实现session,list可实现分页,set可实现交集、差集、并集,sorted set可实现排行榜;还介绍了redis的过期策略:定期删除+惰性删除+LRU(最近最少使用淘汰);也谈到了我们之前说到的穿透与雪崩问题/解决方案…
分布式锁
抢购秒杀是redis的一个经典使用场景。
redis中list的push与pop是原子操作,具备
主要探讨内存级数据库redis是否具备数据库的ACID特性
原子性是数据库的事务中的特性。在数据库事务的情景下,原子性指的是:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。
对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。
php
新手phper只要了解php底层基于引用计数实现的垃圾回收机制。
php核心架构、执行流程、opcode、zend引擎、hash table、php变量实现、引用计数、写时拷贝
可能你常会听到别人说四层和七层负载均衡,他们到底是是什么?负载均衡有哪些常用方案?http、dns、反向代理、ip、直接路由等负载均衡方式…简单阅读下本文了解下这些基础知识点
深入理解php的输出缓冲区,这个标题实至名归。在平常是不是经常看到ob函数的实现,但又很迷惑。
这篇文章深入剖析了phps输出缓冲区。 输出层(output layer)就像一个网,它会把所有从PHP”遗漏“的输出圈起来,然后把它们保存到一个大小固定的缓冲区中。当缓冲区被填满了的时,里面的内容会刷新(写入)到下一层(如果有的话),或者是写入到下面的逻辑层:SAPI缓冲区。
很多phper做了好多年开发,都一直没有弄清楚session是怎么回事,可能被996压榨的没时间去整理回顾下,这篇文章简洁快速的让你从996的工作中挤出点时间就可以掌握session。更多关于session文章:如何严格设置php的session过期时间、Session与cookie的运行机制
这个属于安全方面的问题。你的站点使用了https了吗,你的cookie安全吗,是否存在xss漏洞呢,是否有可以随便访问获取php信息的接口等等,这些都有可能暴露信息,让会话被劫持。
由于php是动态解释型脚本,边执行边解释代码(opcode),导致重复的代码也会重新解释执行,显然这样的效率是不高的,开发都知道重复的代码可以封装,重复的数据可以缓存,所以opcode我们也可以通过缓存就能保证重复代码不需要二次解释成opcode,这就有了官方的opcache。
php8最令人期待的应该就是JIT编译了,经过测试比对,php8在使用JIT编译情况下,比PHP7.4提升了45%左右的速度,很满意的。了解JIT编译详见:理解JIT编译
如果不是云服务器,可以考虑搭建php环境监控平台。
数据库-mysql
《高性能MySql》中给出的最佳方案还是限定id查询方案,更多详见:高性能mysql笔记
你知道乐观锁和悲观锁吗,你知道他们用在哪里吗,怎么用?更多关于悲观锁:悲观锁实践
mysql的优化可以围绕锁、索引展开,具体可以学习阿里丁奇老师的《MySql实战45讲》(收费项目,可以搜下,通俗易懂干货很多,打通你在mysql层面的任督二脉)
因为表大,再加上可能有多个索引的情况下,mysql插入多条数据,数据库需要加锁(间隙锁?)、更新索引等复杂操作,耗时较长,文章中采用拼接单条sql的方式,减少不必要的加锁
文中作者也说了:但无论如何,Mysql数据库的性能和效率大家心里都有点abcd数的,在高并发的情况下, 用Mysql做分布式锁,等着跑路吧。
基于XA协议的两阶段提交
高并发方案场景
什么是高并发,高并发有哪些指标数据,一般系统高并发如何演变
抽奖开奖、投票、预约售票、抢购秒杀,这些都是典型经常碰到的高并发场景。文章中针对抽奖环节的优化,从页面展示、抽奖点击操作流程,从nginx入口请求限流、业务逻辑、缓存、数据库等层面作符合实际情况的进一步优化,涉及队列降级和削峰、redis限流和缓存、异步队列消息、锁和事务保证数据安全和一致性、索引优化提高效率。
结合文章我们也备注下相关思路:nginx限流、队列削峰进一步降低请求处理数、数据内存缓存、与抽奖无关的其他业务操作可以异步队列处理、减小业务处理事务(将时间操作长的数据库操作放在事务最后,避免长事务引发更多的等待造成死锁)、数据库读写分离降低数据库压力、本地内存级限流(针对恶意用户)、验证码限流(普通图片验证码适合普通用户、云智能验证平台适合限制恶意用户)
推荐一篇比较完整的关于秒杀系统的设计
幂等性这个概念对于大部分开发,可能不是很熟悉,通俗点说就是保证数据只被处理过一次,不会被多次处理,比如扣款,退款、更新数据等。哪些场景常用到:表单重复提交、支付扣款加锁、不存在则插入数据、某种状态下的订单数据更新、对外提供API更新能力。幂等性还经常要考虑事务、数据一致性、回滚、异常延迟队列处理等,都是为了保证幂等。
如果我们还不明白高并发下像抽奖、预约、支付等这些有库存场景为什么要加入锁的话,我们可以花10分钟看下这篇很简单的关于支付锁的问题分析。更多关于锁与ACID:悲观锁实践、明明白白事务ACID
高并发,我们觉得是根据现有机器数量和QPS来下结论的(狭义上),广义上当然是大厂大项目的高并发。如何评估现有系统的并发数?是否属于高并发?高并发解决思路?
可以通过现有系统一天的流量(PV)结合28定律(80%的访问在20%的时间里)推导出平均并发数(每秒),预测并发数可能峰值,再结合机器是否三高(IO负载、cpu、内存)推断是否快超出现有系统的负载能力(“高并发”),高并发的解决思想:分而治之,不论是堆机器负载均衡分流,还是限流削峰降级、异步队列等优化(总之就是降低每个请求的处理时间即连接时长)。银行柜员如果处理一个客户的业务用时1小时,一天只能处理不到10个客户,如果柜员使用各种智能机器设备工具后,平均处理一个客户的业务用时5分钟,那一天就能处理至少90个客户。提高处理一个客户业务(一次连接请求)效率,降低事务时长,我们能接受处理业务的客户就会更多。
设计过sku的人,才知道sku有多难设计。做电商的必须把sku的设计吃透,不然后续一堆复杂的问题。
以下分享几个比较大型的系统架构设计。
先来看web架构的演变发展:
web架构的演变过程,并不适合所有的网站,实际中网站演进过程与自身业务和不同遇到的问题有密切的关系,没有固定的模式,只有结合网站实际情况,认真的分析和不断地探究,才能发现适合自己网站的架构。
都是因为业务的复杂和用户量的猛增,才会有高并发的场景,高并发场景倒逼架构师要留扩展余地。文章中对异步、缓存、CDN、静态化、集群、分层/分布式、面向服务、冗余与自动化有较深入的解释。
再来看数据库结构的演变,一样根据自身产品实际情况和需要进行探索演进。
虽然可能不是php生态体系的系统,但在高并发场景里,大家考虑的思路是类似,技术思想相近,只是使用不同的中间件或实现方式。
…
其他
持续补充
@tsingchan