最近研究分析了在SWIS上创建的项目的性能。令人惊讶的是,最昂贵的方法之一是由出色的spatie/laravel-permission包引起的。在查阅了更多的资料和研究之后,我发现了一个可能会得到明显改善的性能问题。既然解决方案已经说清楚了,那么编写代码改进和提交pullrequests就很容易了。现在解决方案已经合并发布了,这里分析一下这个性能问题,以及如何在自己的项目中避免它。TL;DR:跳到结论。性能瓶颈如果我们抽象地看spatie/laravel-permission它主要做了两件事:维护属于某个模型的权限列表。检查模型是否具有权限。第一点,性能瓶颈有点牵强。这里的权限数据是保存在数据库中的,需要的时候会读取出来。这个过程有点慢,但只执行一次。结果会被缓存起来,后续的请求可以直接使用。第二点从性能瓶颈的角度来说确实是一个瓶颈。这个瓶颈取决于权限的性质和项目的大小,因为经常检查权限。这个检查过程中的任何一个迟缓都会成为整个项目的性能瓶颈。FilterCollection类过滤权限集合的方法已被确定为性能不佳的原因。它执行以下操作:$permission=$permissions->where('id',$id)->where('guard_name',$guardName)->first();修改后:$permission=$permissions->filter(function($permission)use($id,$guardName){return$permission->id===$id&&$permission->guard_name===$guardName;})->首先();这两个代码段实现了同样的事情,但第二个更快。性能测试我正在开发的应用程序中有大约150种不同的权限。在一个正常的请求中,大约有50个权限需要通过hasPermissionTo方法进行检查。当然,有些页面可能需要检查200个左右的权限。下面是一些用于性能测试的设置。$users=factory(User::class,150)->make();$searchForTheseUsers=$users->shuffle()->take(50);#方法一:whereforeach($searchForTheseUsersas$user){$result=$users->where('id','=',$user->id)->first();}#方法二:过滤,传递一个模型作为回调foreach($searchForTheseUsersas$searchUser){$result=$users->filter(function($user)use($searchUser){return$user->id===$searchUser->id;})->first();}#方法三:过滤,传属性作为回调foreach($searchForTheseUsersas$user){$searchId=$user->id;$result=$users->filter(function($user)use($searchId){return$user->id===$searchId;})->first();}会用到上面三个方法来测试并筛选1个属性、2个属性和3个属性。因此,使用方法1过滤三个属性将如下所示:foreach($searchForTheseUsersas$user){$result=$users->where('id','=',$user->id)->where('firstname','=',$user->firstname)->where('lastname','=',$user->lastname)->first();}结果method#1method#2method#31properties0.1900.139(-27%)0.072(-62%)2个属性0.4990.372(-25%)0.196(-61%)3个属性0.4880.603(+25%)0.198(-59%)结论我们可以得到结论:对于一个项目来说,对一个大集合进行重复过滤会造成严重的性能瓶颈。多个属性的过滤显着增加了计算成本。使用Collection::filter()而不是Collection::where()可以将性能提高60%。警告:将完整模型传递给过滤器回调是昂贵的,最好传递单个属性。致谢感谢Spatie和spatie/laravel-permissions的贡献者创建了这么棒的包,我真的很喜欢使用它!感谢AndruBeldie指出这些性能问题,我有机会调查并纠正这些问题。链接到spatie/laravel-permission包。问题#550解决性能问题。修复问题的拉取请求#710。如需更多现代PHP知识,请访问Laravel/PHP知识社区
