自从 Laravel出身以来,没有一个 PHP 开拓职员不受她的影响。他们是喜好 Laravel 供应的快速开拓的低级或中级开拓职员,或者是由于市场压力而被迫学习 Laravel 的高等开拓职员。
不管若何,不可否认的是,Laravel 已经振兴了 PHP 生态系统(我确定,如果没有 Laravel,早就离开了 PHP 天下了).
点击查看->30G-PHP进阶资料,助力大家达到30K

但是,由于 Laravel 不遗余力让您的事情变得大略,这意味着它在底层做了大量事情,以确保您作为开拓职员能有一个舒适的编程体验。 Laravel 所有看似「神奇」的功能都有一层又一层的代码,每当运行一个功能时都须要启动这些代码层。乃至是一个大略的非常都会穷究到底层 (从缺点那里开始,一贯到内核):
对付一个视图中彷佛是编译缺点的情形,有 18 个函数调用要跟踪。我个人碰着过 40 个的,如果您利用其他库和插件,则可能会更多。
重点是,默认情形下,这样层层嵌套的代码,使得 Laravel 速率很慢。
Laravel 有多慢?说实话,这个问题根本无法回答,缘故原由有几个。首先,目前还没有公认的、客不雅观的、合理的标准来衡量网络运用的速率。与什么比较更快或更慢?在什么条件下?第二,一个 Web 运用取决于很多东西(数据库、文件系统、网络、缓存等),以是评论辩论速率是很屈曲的。一个非常快的 Web 运用,如果有一个非常慢的数据库,那么它便是一个非常慢的 Web 运用。但这种不愿定性正是基准测试受欢迎的缘故原由。只管它们毫无意义(拜会 这里 和 这里),但它们供应了一些 参考框架,帮助我们避免生气。因此,最好有所保留,让我们对 PHP 框架之间的速率有一个缺点的、粗略的认识。根据这个相称值得尊敬的 GitHub 源码,以下是 PHP 框架的比拟情形。
你可能根本不会把稳到 Laravel 在这里 (纵然你真的很努力地眯着眼睛), 除非你把你的目光投到最尾部。是的,亲爱的朋友们,Laravel 排在末了!
现在,天经地义的,这些「框架」中的大多数都不是很实用,乃至没有什么用途,但它确实见告我们,与其他更盛行的框架比较,Laravel 是多么的慢。常日情形下,这种「慢」在运用中不会涌现, 由于我们日常的 Web 运用很少达到很高的数据量。但是一旦达到了(比如高达 200-500 以上的并发量),做事器就会开始壅塞而去世。这时候纵然扔再多的硬件也办理不了问题,根本架构用度迅速攀升,你对云打算的崇高空想轰然倒塌。
不过,嘿嘿,振作起来吧!
这篇文章并不是讲什么不能做, 而是讲什么可以做。
好是, 你可以做很多事情来让你的 Laravel 运用更快。几倍的速率。 是的,不是开玩笑。你可以让同样的代码库变得快速,每个月节省几百美元的根本举动步伐 / 托管用度。 怎么做?让我们开始吧。
很多人在刚打仗这个行业的时候或者是在碰着瓶颈期的时候,总会碰着一些问题,比如学了一段韶光觉得没有方向感,不知道该从那里入手去学习,对此我整理了一些资料,须要的可以免费分享给大家(点击此处加入php高等互换群一起学习互换,11年架构师带你解读年薪50万口试通关秘籍。)
四种类型的优化在我看来,优化可以在四个不同的层面上进行(当涉及到 PHP 运用时,便是):
措辞层面:这意味着你利用更快的措辞版本,并避免措辞中特定的功能 / 编码风格,使你的代码速率变慢。框架层面:这些是我们在本文中要涉及的内容。根本举动步伐层面:调度你的 PHP 进程管理器、Web 做事器、数据库等。硬件层面:转向更好、更快、更强大的硬件主机供应商。所有这些类型的优化都有其存在的意义(例如,php-fpm 的优化是非常关键和强大的)。但本文的重点是纯粹的第 2 类优化:那些与框架干系的优化。顺便说一下,这些编号背后没有任何情由,也不是一个公认的标准。我只是编了这些。请千万不要引用我的话说:「我们的做事器须要 type-3 优化」,否则你的团队卖力人会杀了你,找到我,然后把我也杀了。现在,我们终于到了应许之地。
要把稳 n+1 数据库查询n+1 查讯问题是利用 ORM 时常见的问题。Laravel 有其强大的 ORM,叫 Eloquent,它是如此的俊秀,如此的方便,以至于我们常常忘却了看是怎么回事。考虑一个非常常见的场景:显示指定客户列表下的所有订单。这在电子商务系统和任何必要显示与某些实体干系的所有实体的列表中非常常见,
我们可以想象有这样一个掌握器:
class OrdersController extends Controller{// …public function getAllByCustomers(Request $request, array $ids) { $customers = Customer::findMany($ids); $orders = collect(); // new collection foreach ($customers as $customer) { $orders = $orders->merge($customer->orders); } return view('admin.reports.orders', ['orders' => $orders]);}}
太好了!
更主要的是,优雅,俏丽。????????不幸的是,用 Laravel 编写这样的代码是一种灾害性的方法。缘故原由如下。当我们利用 ORM 查找给定的客户实体时,会天生这样一个 SQL 查询语句:
SELECT FROM customers WHERE id IN (22, 45, 34, . . .);
这与预期的完备同等。结果,所有返回的行都被存储在掌握器函数中的凑集 $customers 中。现在我们逐一循环处理每个客户,并获取他们的订单。这将实行下面的查询……
SELECT FROM orders WHERE customer_id = 22;
…… 有多少客户就有多少次。换句话说,如果我们须要获取 1000 个客户的订单数据,那么实行的数据库查询总数将是 1(用于获取所有客户的数据)+1000(用于获取每个客户的订单数据)=1001。这便是 n+1 这个名字的由来。我们可以做得更好吗?当然可以!
通过利用预加载,我们可以逼迫 ORM 实行 JOIN,并在一次查询中返回所有须要的数据!
就像这样:
$orders = Customer::findMany($ids)->with(‘orders’)->get();
由此产生的数据构造是一个嵌套构造,当然,但订单数据可以很随意马虎地提取出来。在这种情形下,产生的单个查询是这样的。
SELECT FROM customers INNER JOIN orders ON customers.id = orders.customer_id WHERE customers.id IN (22, 45, …);
当然,一次查询比多查询一千次要好。想象一下,如果有一万个客户要处理,会发生什么情形!
或者说,如果我们还想显示每个订单中包含的项目,那切实其实便是天方夜谭!
记住,这个技能的名字叫预加载,它险些在任何时候都能派上用场。
Laravel 的灵巧性的缘故原由之一是它有大量的配置文件, 这些文件是框架的一部分。想要改变图片的存储办法 / 位置?好吧,只要修正 config/filesystems.php 文件就可以了(至少写到这里)。想要利用多个行列步队驱动?可以在 config/queue.php 中随意描述。我刚刚统计了一下,创造针对框架的不同方面有 13 个配置文件,担保你无论想改什么都不会失落望。
鉴于 PHP 的特性,每当一个新的 Web 要求进来,Laravel 就会被唤醒, 启动所有的东西, 并解析所有的配置文件来找出这次该如何做不同的事情。 如果这几天什么都没变,那就太傻了!
每次要求都要重修配置文件是一种摧残浪费蹂躏,这是可以 (实际上,必须) 避免的,办理的办法是 Laravel 供应的一个大略的命令:
php artisan config:cache
这样做的目的是把所有可用的配置文件合并成一个文件,并缓存在某个地方以便快速检索。 下一次有 Web 要求的时候,Laravel 会大略地读取这个单一的文件并开始事情。也便是说,配置缓存是一个极其奇妙的操作,可能会在你的面前炸开。最大的陷阱是一旦你发出这个命令,除了配置文件之外,其他地方的 env() 函数调用都会返回 null!
仔细想想确实有道理。如果你利用配置缓存,你便是在见告框架:「你知道吗,我以为我已经把东西设置得很好了,我 100% 确定我不肯望它们改变。」 换句话说,你希望环境保持静态,这便是 .env 文件的浸染。
说到这里,这里有一些铁定的、神圣的、不可违背的配置缓存规则:
只在生产系统上做。只有在你非常非常确定要冻结配置的情形下才做。万一出了问题,用 php artisan cache:clear 撤销设置。祈祷对企业造成的丢失不是很大!减少自动加载的做事
为了帮助大家,Laravel 在唤醒时加载了大量的做事,这些做事在 config/app.php 文件中作为 ‘providers’ 数组键的一部分。让我们来看看我的情形:
/|————————————————————————–| Autoloaded Service Providers|————————————————————————–|| The service providers listed here will be automatically loaded on the| request to your application. Feel free to add your own services to| this array to grant expanded functionality to your applications.|/'providers' => [ / Laravel Framework Service Providers... / Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, Illuminate\Cache\CacheServiceProvider::class, Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Cookie\CookieServiceProvider::class, Illuminate\Database\DatabaseServiceProvider::class, Illuminate\Encryption\EncryptionServiceProvider::class, Illuminate\Filesystem\FilesystemServiceProvider::class, Illuminate\Foundation\Providers\FoundationServiceProvider::class, Illuminate\Hashing\HashServiceProvider::class, Illuminate\Mail\MailServiceProvider::class, Illuminate\Notifications\NotificationServiceProvider::class, Illuminate\Pagination\PaginationServiceProvider::class, Illuminate\Pipeline\PipelineServiceProvider::class, Illuminate\Queue\QueueServiceProvider::class, Illuminate\Redis\RedisServiceProvider::class, Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, Illuminate\Session\SessionServiceProvider::class, Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, / Package Service Providers... / / Application Service Providers... / App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class,],
我再一次数了数,一共列出了 27 项做事!
现在,你可能须要所有的做事,但不太可能。例如,我现在恰好在构建一个 REST API,这意味着我不须要 Session Service Provider、View Service Provider 等。而且由于我是按照自己的办法来做一些事情,而不是按照框架的默认值来做,以是我也可以禁用 Auth Service Provider、Pagination Service Provider、Translation Service Provider 等。总而言之,对付我的用例来说,这些险些有一半是不必要的。仔细核阅一下你的运用吧。它是否须要所有这些做事供应者?但是看在上帝的份上,请不要盲目地注释掉这些做事,然后推送莅临盆中去!
运行所有的测试,在开拓机和暂存机上手动检讨,并且在扣动扳机之前要非常非常偏执。
当你须要对传入的 Web 要求进行一些自定义处理时,创建一个新的中间件便是答案。现在,打开 app/Http/Kernel.php 并将中间件粘在 web 或 api 堆栈中是很有诱惑力的;这样一来,它就会在全体运用程序中变得可用,而且如果它没有做一些侵入性的事情(例如,像日志或关照)。然而,随着运用程序的增长,如果所有(或大多数)这些全局中间件都存在于每个要求中,那么这个全局中间件的凑集可能会成为运用程序的一个无声包袱,纵然没有业务缘故原由。换句话说,要小心你在哪里添加 / 运用新的中间件。在全局范围内添加一些东西可能会更方便,但从长远来看,性能惩罚是非常高的。我知道如果每次有新的变革都要有选择地运用中间件,你要承受的痛楚,但这是我心甘情愿承受的痛楚,也是我所推举的!
虽然 Eloquent 让 DB 交互的很多方面变得愉悦,但它因此速率为代价的。作为一个映射器,ORM 不仅要从数据库中获取记录,还要实例化模型工具,并用列数据对其进行添补。以是,如果你做一个大略的 $users = User::all(),比如有 10000 个用户,框架会从数据库中获取 10000 行记录,并在内部做 10000 个 new User(),并用干系数据添补他们的属性。这是大量的事情在幕后进行,如果数据库是你的运用成为瓶颈的地方,绕过 ORM 有时是个好主张。这对付繁芜的 SQL 查询来说尤其如此,在这种情形下,你必须跳很多的圈子,写一个又一个的闭包,但终极还是能得到一个高效的查询。在这种情形下,最好做一个 DB::raw(),然夹帐工写查询。
根据 这个 的性能研究,纵然是大略的插入, Eloquent 也会随着记录数量的增加而变慢:
只管即便利用缓存
Web 运用优化中最守旧的秘密之一便是缓存。对付新手来说,缓存的意思是预先打算和存储昂贵的结果 (昂贵的 CPU 和内存利用量),并在重复相同的查询时大略地返回。例如,在一个电商商店里,可能会碰着,在 200 万种产品中,大多数时候人们都会对那些新鲜出炉的、在一定价格范围内的、针对特定年事段的产品感兴趣。在数据库中查询这些信息是很摧残浪费蹂躏的 —— 由于查询的内容不会常常变革,以是最好把这些结果存储在我们可以快速访问的地方。Laravel 内置支持多种类型的缓存。除了利用缓存驱动和从底层构建缓存系统外,你可能还想利用一些 Laravel 包,方便模型缓存、查询缓存等。但是请把稳,在一定的简化用例之外,预制的缓存包可能会带来更多的问题,而不是办理这些问题.
优先选择内存缓存当你在 Laravel 中缓存一些东西时, 你有几个选项可以选择将须要缓存的打算结果存储在哪里。这些选项也被称为 缓存驱动。以是, 虽然利用文件系统来存储缓存结果是可能的,也是完备合理的,但这并不是缓存的真正目的。空想情形下,你希望利用内存中(完备活在 RAM 中)的缓存,比如 Redis、Memcached、MongoDB 等,这样在较高的负载下,缓存就能起到至关主要的浸染,而不是自己成为瓶颈。现在,你可能会认为拥有 SSD 磁盘和利用 RAM 棒险些是一样的,但还差得远。纵然是非正式的 基准测试也显示,在速率方面,RAM 优于 SSD 的 10-20 倍。在缓存方面,我最喜好的系统是 Redis。它的速率 快得离谱 (每秒 10 万次读取操作是很常见的),对付非常大的缓存系统,可以很随意马虎地演化成一个 集群。
缓存路由就像运用程序的配置一样,路由不会随着韶光的推移而改变,是缓存的空想选择。如果你像我一样无法忍受大文件,并且终极把你的 web.php 和 api.php 分割成几个文件的话,这一点尤实在用。 一个大略的 Laravel 命令就可以把所有可用的路由打包并保存起来, 方便往后的访问:
php artisan route:cache
而当你终极要增加或改变路由时,只需这样做即可。
php artisan route:clear
图像优化和 CDN
图片是大多数网络运用的核心和灵魂。巧合的是,它们也是最大的带宽花费者,也是导致运用程序 / 网站速率慢的最大缘故原由之一。如果你只是大略地将上传的图片天真地存储在做事器上,然后以 HTTP 相应的办法发送回来,你就会让一个巨大的优化机会溜走。我的第一个建议是不要在本地存储图片 —— 有数据丢失的问题要处理,而且取决于你的客户在哪个地理区域,数据传输可能会非常缓慢。相反,选择像 Cloudinary 这样的办理方案,它可以自动动态调度和优化图像的大小。如果这不可能,利用类似 Cloudflare 的东西来缓存和做事图像,同时它们存储在你的做事器上。如果连这一点都做不到,调度一下你的网络做事器软件,压缩资产并勾引访问者的浏览器去缓存东西,就会有很大的不同。下面是一个 Nginx 配置的片段。
server {# file truncated# gzip compression settingsgzip on;gzip_comp_level 5;gzip_min_length 256;gzip_proxied any;gzip_vary on;# browser cache controllocation ~ .(ico|css|js|gif|jpeg|jpg|png|woff|ttf|otf|svg|woff2|eot)$ {expires 1d;access_log off;add_header Pragma public;add_header Cache-Control “public, max-age=86400”;}}
我知道图片优化与 Laravel 无关, 但这是一个如此大略而强大的技巧 (而且常常被忽略), 以是我忍不住了。
自动加载器优化自动加载是 PHP 中一个整洁的、并不古老的功能,它可以说是拯救了这门措辞的末日。只管如此,通过破译给定的命名空间字符串来探求和加载干系类的过程是须要韶光的,在生产支配中,如果须要高性能,可以避免这个过程。 再一次,Laravel 有一个单一命令的办理方案来办理这个问题:
composer install –optimize-autoloader –no-dev
与行列步队交朋友
行列步队 是指当有很多事情时,你如何处理这些事情,而且每件事情都须要几毫秒才能完成。一个很好的例子是发送电子邮件 —— 在网络运用中,一个广泛的用例是当用户实行一些操作时,发出几封关照邮件。
例如,在一个新推出的产品中,你可能希望每当有人下单超过一定值时,公司领导层(大约 6-7 个电子邮件地址)就会收到关照。假设你的邮件网关能在 500ms 内相应你的 SMTP 要求,那么在订单确认启动之前,用户须要等待 3-4 秒。一个非常糟糕的用户体验,我相信你会赞许。
补救的办法是在任务进来的时候就把它们存储起来,见告用户统统都很顺利,然后再处理它们(几秒钟)。如果涌现缺点,在宣告失落败之前,排队的任务可以重试几次。
虽然行列步队系统使设置繁芜化了一些 (并增加了一些监控开销), 但它在当代 Web 运用中是不可短缺的。
资源优化 (Laravel Mix)对付你的 Laravel 运用中的任何前端资源,请确保有一个管道可以编译和最小化所有的资源文件。 那些对 Webpack,Gulp,Parcel 等打包器系统很熟习的人不须要费心,但如果你还没有这样做,Laravel Mix 是一个可靠的推举。Mix 是一个轻量级的 (诚笃说,很讨人喜好!
) 环绕 Webpack 的打包器,它可以处理你所有的 CSS,SASS,JS 等文件。 一个范例的 .mix.js 文件可以像这样小,但仍旧可以发挥出巨大的浸染。
const mix = require(‘laravel-mix’).mix.js(‘resources/js/app.js’, ‘public/js’);mix.js(‘resources/js/app.js’, ‘public/js’).sass(‘resources/sass/app.scss’, ‘public/css’);
当您准备支配生产环境并运行 npm run production 时,它将自动处理导入,最小化,优化以及全体事情流程。 Mix 不仅关心传统的 JS 和 CSS 文件,而且还关心您在运用程序事情流程中可能利用的 Vue 和 React 组件。
更多信息参考 这里 !
结论性能优化与其说是科学,不如说是艺术 —— 知道如何做以及做多少比做什么更主要。也便是说,在 Laravel 运用中可以优化的内容和数量是无限的。但无论您做什么,我都希望留给您一些临别的建议 —— 优化该当在有充分的情由时进行,而不是由于它听起来不错,也不是由于您对 超过 100,000 个用户的运用程序的性能抱有偏执,而实际上只有 10 个用户。如果你不愿定是否须要优化你的运用,那你就不要去捅这个马蜂窝。一个能正常运转的运用,虽然有时觉得很无趣,但却做了它必须做的事情,这比一个优化成突变体稠浊型超级机器却时时时会失落败的运用要可取十倍。