要知道一个访问速度快的网站可以让用户喜欢它,帮助网站提高在谷歌上的排名,帮助网站提高转化率。如果你读过关于网站性能优化的文章,比如设置服务器的最佳实现、杀死慢速代码、使用CDN加载图片,你会认为你的WordPress网站已经足够快了。但事实真的如此吗?对于动态数据库驱动的网站(如WordPress),您的网站可能仍有一个问题需要解决:数据库查询会减慢您的网站速度。在本文中,我将向您展示如何识别导致性能问题的查询、如何找出问题所在、如何快速解决这些问题以及其他加快查询速度的方法。我将以门户网站deliciousbrains.com作为减慢查询的实际示例。定位慢速SQL查询的第一步是找到它们。Ashley在之前的博文中称赞了调试插件QueryMonitor,该插件的数据库查询特性使其成为定位慢速SQL查询的宝贵工具。插件会在页面请求时上报所有的数据库请求,并可以通过调用这些查询代码或原始组件(插件、主题、WordPress核心)来过滤这些查询,突出重复查询和慢查询。如果不想在生产环境安装调试插件(出于性能开销的原因),也可以开启MySQLSlowQueryLog,这样在特定时间执行的所有查询都会被记录下来。这种方法配置和设置存储查询位置比较简单。由于这是一项服务级别的调整,因此对性能的影响将小于使用调试插件,但在不使用时应将其关闭。理解一旦找到花费大量资金寻找的查询,下一步就是尝试理解它并找出导致查询变慢的原因。最近,在我们开发网站时,我们发现一个查询需要8秒才能执行。我们使用WooCommerce和自定义版本的WooCommerce软件插件来运行我们的插件商店。此查询的目的是获取我们知道其客户编号的那些客户的所有订阅。WooCommerce有一个稍微复杂的数据模型,即使订单存储在自定义类型中,用户的ID(商店为每个用户创建WordPress)也没有存储在post_author中,而是作为发布数据的一部分。订阅软件插件会创建一对指向自定义表格的链接。让我们深入了解有关查询的更多信息。MySQL是你的好朋友MySQL有一个非常方便的语句DESCRIBE,它可以输出表结构的信息,比如字段名、数据类型等等。所以,当你执行DESCRIBEwp_postmeta;您将看到以下结果:您可能已经知道此语句。但是您知道DESCRIBE语句可以用在SELECT、INSERT、UPDATE、REPLACE和DELETE语句之前吗?它以其同义词EXPLAIN而广为人知,并将提供有关如何执行语句的详细信息。这是我们的发现:乍一看,很难解释。幸运的是,SitePoint的人们整理了一份综合指南来理解该声明。最重要的字段是type,它描述了表的结构。如果你想看到全部内容,那就意味着MySQL会从内存中读取整个表,提高I/O的速度并加载到CPU上。这称为“全表浏览”——稍后会详细介绍。rows字段也是MySQL将要做什么的一个很好的指标,它显示了在结果中找到了多少行。Explain也给了我们很多可以优化的信息。比如pm2表(wp_postmeta),告诉我们它是Usingfilesort,因为我们使用ORDERBY语句对结果进行排序。如果我们要对查询结果进行分组,这会增加执行的开销。可视化研究对于这种类型的研究,MySQLWorkbench是另一个方便免费的工具。用MySQL5.6及以上打开数据库,EXPLAIN的结果可以JSON格式输出,MySQLWorkbench将JSON转换成可视化的执行语句:它会自动高亮显示查询问题提醒用户注意。我们可以立即看到连接wp_woocommerce_software_licences(别名l)表有严重问题。要解决查询你应该避免这种全表视图,因为它使用非-索引字段order_id加入wp_woocommerce_software_licences表和wp_posts表,这是慢查询的通病,也是一个容易解决的问题。索引order_id是表中非常重要的符号数据。如果我们要这样查询,就需要在列上建立索引。另外,MySQL会逐字扫描表的每一行,直到找到我们想要的。行为停止。让我们添加一个索引,看看它是如何工作的:CREATEINDEXorder_idONwp_woocommerce_software_licences(order_id)哇,干得好!我们设法添加了索引并将查询时间缩短了5秒。KnowyourqueryCheckthequery——查看每一个连接,每一个子查询。他们在做不该做的事吗?有什么可以在这里做的优化吗?在此示例中,我们通过order_id将licenses表与posts表连接起来,同时将帖子类型限制为shop_order。这是为了通过维护数据完整性来确保我们只使用正确的订单记录,但实际上这在查询中是多余的。我们知道可以肯定的是,posts表中的软件许可行通过order_id与WooCommerce订单相关联,这是在PHP插件代码中强制执行的。让我们删除连接以查看是否有任何改进:改进不是很大,但现在查询时间不到3秒。缓存所有内容如果您的服务器默认不使用MySQL查询缓存,那么您应该启用缓存。启用缓存意味着MySQL会保存所有语句和语句执行结果。如果有一条语句与缓存中的语句完全相同,则MySQL将返回缓存的结果。缓存不会过时,因为MySQL会在表数据更新后刷新缓存。QueryMonitor发现我们的查询在加载单个页面时执行了四次,虽然MySQL查询缓存很好,但应该完全避免在一次请求中从数据库中重新读取数据。PHP代码中的静态缓存很简单,可以非常有效地解决这个问题。基本上就是在请求***的时候从数据库中取出查询结果,保存在类的静态属性中,然后后面的查询语句调用会从静态属性中返回结果:classWC_Software_Subscription{protectedstatic$subscriptions=array();publicstaticfunctionget_user_subscriptions($user_id){if(isset(static::$subscriptions[$user_id])){returnstatic::$subscriptions[$user_id];}global$wpdb;$sql='...';$results=$wpdb->get_results($sql,ARRAY_A);static::$subscriptions[$user_id]=$results;return$results;}}缓存是有生命周期的,具体来说就是实例化对象有生命周期.如果您正在跨请求查看查询结果,那么您需要实现持久对象缓存。但是,您的代码应该负责设置缓存并在基础数据更改时使其失效。跳出框框思考不仅仅是调整查询或添加索引,还有其他方法可以加快查询执行速度。我们查询中最慢的部分是从客户id到产品id再到连接表单所做的工作,我们必须为每个客户做这些工作。我们可以在需要时获取客户数据吗?如果是这样,那么我们只需要加入一次。您可以通过创建数据表来存储许可证数据,以及所有许可证用户ID和产品标识符,并查询特定客户,从而对数据进行反规范化(denormalize)。您需要在INSERT/UPDATE/DELETE上使用MySQL触发器来重建表(取决于数据正在更改的表),这将显着提高查询数据的性能。同样,如果某些连接减慢了MySQL中的查询速度,将查询分解为两个或更多语句并在PHP中分别执行它们可能会更快,然后可以在代码中收集和过滤结果。Laravel通过预加载在Eloquent中做了类似的事情。如果您有大量数据和许多不同的自定义帖子类型,WordPress可能会减慢对wp_posts表的查询。如果您发现查询帖子类型很慢,请考虑从自定义帖子类型的存储模型转移到自定义表-稍后的帖子中会详细介绍。
