成航先森 成航先森

成都航院计算机系一个学生的个人博客

 

随便看看:

分段图 anylinkaria2CDNkeepalivedLinuxmysqlnginxpycharmpythonredisrsyncUEditorWordPressWordPress插件七牛九价体育部信息志愿服务队光猫免流全体例会向党组织靠拢团学会学习部宣传部影视相关心理部成都航院报社招生信息教学资源文艺部毕业就业流量活动生活杂谈社会实践部站长经历系部介绍系部动态纪检部组织部经验分享老师腾讯云资源下载路由器轻松时刻高考录取分数线

标签:redis

修复anylink增加Redis缓存后存在的bug

脚本编程, WordPress技巧修复anylink增加Redis缓存后存在的bug

标签:, , | 4 条评论 | 2022-04-04

先森最近更新了一篇文章,发布后先森自己没有去看,今天去看了一眼,结果突然发现这篇文章中所有的外链没有自动转成内链。以前优化SEO的时候,看到的优化建议基本都有不要直接跳转到外链,会导致权重降低,所以先森一直都有用anylink这个插件来实现这个功能。对于anylink这个插件,先森的使用体验还是非常好的,之前一共还发过三篇博文:WordPress为anylink插件外链跳转添加漂亮的跳转页面WordPress:WPJAM BASIC插件与anylink冲突WordPress优化:为anylink插件增加缓存既然发现了问题,那么就得解决问题,正好是清明节放假期间,托疫情的福先森哪里也不好去,所以来会会这个bug。排查过程首先,出现没有转内链的文章只有先森最新发布的那一篇,之前的文章都是正常的。1、怀疑缓存可能是CDN有缓存,先森将本机hosts改成源站,直接访问源站,测试依旧正常,pass。除了CDN,先森WordPress还有缓存,先森用的是插件wp-super-cache进行缓存。先森清理了插件缓存后,问题依旧;然后想是不是插件有问题,直接将wp-super-cache插件停用了,问题依旧。2、怀疑anylink插件排除了缓存的问题,先森又想是不是anylink插件自己出了什么问题,先森将anylink插件停用后再启用,发现问题依旧,插件运行问题pass。然后先森考虑是不是插件在本篇文章的执行有问题,开debug看一看。先森在WordPress的wp-config.php中打开了dubug:define( 'WP_DEBUG', true );define( 'SAVEQUERIES', true );先森开debug主要是想看本篇文章中的SQL查询,所以在后台开启了Debug Queries插件,结果开启后并没有看到anylink的查询,然后先森想到了为anylink添加redis缓存这篇文章中,已经将相关的SQL查询优化了,这样debug确实已经看不到去MySQL的查询了。此时先森已经开始怀疑是不是当时优化有什么bug了,但还是需要先确认一下是不是MySQL、Redis数据库有问题。3、怀疑数据库有问题先森的MySQL是云数据库,Redis是部署在源站本机的。MySQL运行肯定没有问题, 不然就不只是外链有问题了;Redis检查后发现也是正常运行的。先森再去看anylink的MySQL表,发现wp_al_urls表里是有出问题这篇文章的外链转内链对应表的,这就奇怪了,到这里MySQL的问题排除了,先森就怀疑是自己之前的优化存在问题了。4、怀疑优化存在BUG先森本来想直接在redis里查有问题文章的缓存,但是redis的key太多了,先森已经忘了key是什么规则了,所以就先去研究了一下anylink的代码,先搞清楚之前先森的优化。首先确认到,先森优化的是插件的classes/al_filter.php文件,优化了getAllLnks()和get_slug_by_url()这两个类中的函数,增加了先查redis缓存,没有再查数据库并将结果存到redis的代码。修改的部分这两个函数先森研究了一下,get_slug_by_url()这个函数是用在把留言者的链接转换成内链的,所以出现问题的不可能是这个函数,那只能是getAllLnks()这个函数了。getAllLnks()这个函数保存缓存的key是‘getAllLnks:’开头的,后面跟的是文章的id,先森就去redis查相关key。应该是redis的key不支持冒号,所以实际存储的key是‘getAllLnks-’开头的。先森发现有问题的文章ID是1968,所以它的key是‘getAllLnks-1968’,先森先查的是正常文章的key,查出来是一大堆的内容,而查到1968就发现明显有问题:getAllLnks有问题这个key的内容简直就没有内容,按逻辑来想起码有两个内容:外链地址、内链地址,将这个key删除,然后重新访问有问题的文章,发现外链就已经自动转为内链了,此时再来查redis的key查询结果,正常多了:getAllLnks正常结果至此,问题已经临时得到了解决,但是原因还需要分析一下。出现bug的原因一开始,先森以为这个问题原因是anylink在管理员登录时不会处理外链,先森预览文章被缓存了,但是找了一圈anylink的代码,没有找到判断管理员是否登录的代码,所以应该不是这个原因。好好想了一下,发现bug的原因应该是这个:当文章还在编辑的过程中,预览文章,由于此时文章还没有外链,所以数据库查询结果为空,但是这个空结果被先森的代码保存到Redis中了,由于Redis的缓存没有设置过期或到期时间未到,先森也没有设置redis缓存更新,导致后续anylink插件来查询这篇文章的外链时一直是空的。解决方案问题原因找到了,就好解决了,先森想到了两个解决方案:方法1:管理员登录时anylink不处理链接方法2:文章发布、变更时更新anylink的缓存第一种方法,管理员登录时,尤其是正在编辑文章时,预览文章不会造成错误的key被redis缓存。但此方法还是有bug,管理员未来更新文章时新增了其他外链,此时由于Redis已经缓存了这篇文章的外链查询情况,如果改缓存未过期,新增的外链在文章中会出现未被转换的情况。且这种方法容易造成管理员视角和访客视角不同,先森用的其它插件有类似设定,实际使用中会让人比较头疼,所以先森不太希望使用这种方式进行修复。相较而言,第二种方法就要合理的多,只要文章有变动,那就把anylink该文章的redis缓存删了,有人访问该文章时再缓存。实现也比较简单,在自己的主体function.php最后加一个publish_post钩子,实现这个需求:/** * 更新或发布文章清理Redis缓存 * 增加时间:2022-04-04 12:43:00 * By:https://www.capjsj.cn/ */function Clean_Redis($post_ID){ wp_cache_delete( 'getAllLnks:'.$post_ID );}add_action('publish_post', 'Clean_Redis', 0);来测试一下,在管理后台随便更新了一篇已发布的文章,发布前查询该文章的redis缓存,刷新后再去查询,该文章的key已经查不到数据了。测试hook删除redis缓存2020年埋下的bug终于在2022年被修复了,还好期间先森新发布的文章只有2021两篇(有点惭愧),且不涉及外链,所以影响不大。按理说也会影响到旧文章的更新,但是先森最近几年对博客这边的关注实在太少,影响着实不大。无论怎样,写代码还是的考虑周到一点,还好先森不是程序员,只是一枚小运维。

WordPress优化:为anylink插件增加缓存

WordPress技巧WordPress优化:为anylink插件增加缓存

标签:, , , | 2 条评论 | 2020-11-24

先森最近在梳理网站的代码,想办法为网站加速,主要从代码、软件、网络层面进行优化,这一切都是从网站切换到HTTPS开始的。先森已经连续观察了多日的CDN了,目前也就到了查缺补漏的阶段了。先森还将整个网站目前备份到了另一台服务器,将网站在本地解析到这台服务器上,开始了对代码的检查。开启了debug,把大的一些问题都处理了,然后也把主题的代码理了一遍,并且网站也加上了Redis缓存。但是先森发现,即使加上了Redis,有时候网页打开生成时间还是得一秒多,先森就很纳闷,一直想搞明白到底是什么情况。排查工具很早以前,先森就在主题的footer.php末尾,添加了下面的代码,以便于登录之后可以看到当前网页的查询次数,生成时间:<?php if (is_user_logged_in()){    echo "<pre>".get_num_queries().'次查询,用时';    timer_stop(3);    echo '秒</pre>';?>这个代码网络上到处都是,相信很多人都添加的有。先森网站加Redis之前,网页的查询次数都是150+次,生成时间2-3秒,甚至更多。用上之后减少到50+次,但是有时还是会需要1秒多,让先森百思不得其解。然后先森把主题代码该优化的都优化后,查询次数30+次,生成时间降到1秒左右,但是先森还是不太满足,所以想看看到底是执行了哪些查询,然后就在网上找到了这段代码,和上面的有些类似。首先需要先在WordPress的根目录配置文件wp-config.php中添加保存查询的代码:define('SAVEQUERIES', true);然后也是在footer.php的网页最后部分添加打印代码:<?phpif (current_user_can('administrator')){    global $wpdb;    echo "<pre>";    print_r($wpdb->queries);    echo "</pre>";}?>但是先森添加后看了一下,差点当场去世,这样打印出来的是一个很大的多维数组,看的人眼花缭乱,重点是太长了还显示不全。先森将打印复制出来,拿到NotePad++里面打开,依旧显得很乱。查询的打印不过大概看了一下,大数组的每一个键值表示一个查询,然后一个查询数组了,第一个值是执行的SQL,第二个值是使用的时间,第三个值是调用的代码位置。其实可以用循环做一个网格,让前端显示看着方便一点,但是先森很懒,网上看了一下,有插件可以做到相关功能,且不用修改wp-config.php,即Debug Queries,所以先森就懒得自己写了,直接装了一个来进行排查。需要注意的是,Debug Queries很久没有更新了,安装可能会报错,不过还是可以正常使用的。其实Debug Queries介绍页也推荐使用Debug Objects插件,但是先森试了一下Debug Queries可以用,也就懒得再试另一个插件了。排查问题工具准备好了,先森就来好好排查到底是哪里查询比较慢了。插件装好了,再去看打印出来的查询信息,就比较清晰了。先森对比了一下,大部分的查询都是0.00x秒的,就是几毫秒的,但是只要涉及到wp_al_urls的查询,就会是即时甚至上百毫秒。wp_al_urls的查询先森看了一下,这个表是插件anylink的,这个插件主要是将网站上的外链全都转化成内链,点击后可以跳转到外链。对于anylink,先森这里也发过两篇相关的文章:WordPress为anylink插件外链跳转添加漂亮的跳转页面WordPress:WPJAM BASIC插件与anylink冲突这个插件先森也是从建站伊始就在用了,是一个很好用的插件,但没有想到这个插件会出现慢查询。通过上面的截图可以看到,对wp_al_urls的查询条件是网站链接,看了一下数据库,这个SQL是为了去拿到内链的slug记录:anylink获取slug这里去查的,实际上是网页正文里的外链、各位评论大佬的网址对应的内链地址。先森看了一下这个表,没想到竟然有10M的大小,接近10万条数据,而且这里查询的是al_origURL字段,先森看了一下,这个字段是没有索引的。解决问题对于MySQL的查询,先森能想到的优化方法就是加索引,所以先森直接就操作加索引,但是报错了:给al_origURL字段加索引报错看报错是跟字段格式有关的,看了一下这个字段的类型是mediumtext的,这个字段是存URL的,有些URL非常的长,如果该varchar的话,可能会出问题,varchar最长255个字符。网上找了一阵子解决方案,都是说text相关的类型无法加索引。先森本来就对数据库索引什么的不太了解,所以只能放弃这条路。先森还能想到的办法,就是看下这个查询的代码,想办法把结果存到Redis上缓存起来。至于怎么找到实际执行的代码,先森看了一下,直接找调用的最后一段就可以了。找到慢查询的调用代码可以看到,两个SQL实际上是一样的,查的是同个网址,结果执行的时间竟然都比较长,所以确实得把结果缓存起来。为了优化代码,先森把整个网站都作为了一个PhpStorm里的一个项目,不得不说一个好的IDE工具写起代码来是真的舒服。直接全局搜索,寻找get_slug_by_url这个函数,顺利找到了代码所在。搜索get_slug_by_url函数这里有两个结果,第一个是原本的函数,已经被先森注释起来了,第二个是先森改了之后的。可以看到这个函数就是调用$wpdb来执行SQL语句,将得到的结果再返回一下。函数比较简单,也很利于先森修改。然后先森又找了一下WordPress如何添加缓存,结果找到一下,发现非常简单。WordPress操作缓存WordPress 为我们提供了使用对象缓存的函数,方便我们使用对象缓存。wp_cache_add() :添加数据到缓存中,如果数据已存在,返回 flasewp_cache_set() :添加数据到缓存中,如果数据已存在,会覆盖数据wp_cache_get() :获取缓存中的数据,如果数据不存在,返回 falsewp_cache_delete() : 从缓存中删除数据wp_cache_replace() :替换缓存中的数据,类似 wp_cache_set,但是如果数据不存在,不自动添加wp_cache_flush():清除所有缓存如果没有装redis缓存插件,上面的这些函数是在./wp-includes/cache.php里。如果装了Redis Object Cache等插件,就会自动增加一个./wp-content/object-cache.php文件,这些函数也会存在于这个文件中,用于存入缓存。WordPress 对象缓存使用使用示例$result = wp_cache_get( 'my_result' );if ( false === $result ) {    $result = $wpdb->get_results( $query );    wp_cache_set( 'my_result', $result );}对anylink的get_slug_by_url函数改造有了上面这个案例,先森为anylink的函数增加缓存就很方便了。示例很简单,get_slug_by_url函数本身也简单,所以改造后如下:public function get_slug_by_url( $url ) { $arr_slug = wp_cache_get( $url ); if ( false === $arr_slug ) { global $wpdb; $arr_slug = array(); $arr_slug = $wpdb->get_row($wpdb->prepare( "SELECT * FROM " . ANYLNK_DBTB . "  WHERE al_origURL = %s", $url ), ARRAY_A); wp_cache_set( $url, $arr_slug ); } return $arr_slug;}因为示例和函数太契合了,所以这个函数几乎就和示例结构一样。首先去Redis获取缓存数据,获取不到就去MySQL查询,查到后再存到Redis。2022年4月4日更新:此代码存在亿点点bug,修复参考此文章:修复anylink增加Redis缓存后存在的bug检查效果代码修改后,同步到测试服务器,访问了两次之前访问的页面,查询次数和用时都降下来了。然后看到查询次数还是有33次,其中有部分是查询wp_al_urls_index这个表的,虽然速度不慢,但是次数比较多,先森重复上面的方法也修改了一下相关函数,最终效果如下图:最终效果查看Redis的keys上面改造的代码中,是拿url地址去做的key名称,那么先森也来看下缓存数据在Redis里的情况:redis的缓存上面改造的代码比较简陋,直接拿的URL做的key名称,如果URL比较短还好,如果长的话就可能出现问题,key名称其实可以做一下长度限制。提示:Redis最好不要使用默认端口6379,除非安全做的非常好。使用Redis时注意以下几点:1、一定要配置强密码;2、安全组、防火墙一定要最小范围放通Redis端口,即针对指定IP放通访问;3、尽量不要使用默认端口。因为Redis而导致服务器中木马病毒的保障,先森这边经常遇到。总结先森以前以为给WordPress配上Redis很麻烦,实际使用发现真香,建议有能力的朋友都上一下,毕竟生命不止,折腾不息。本文最主要的还是记录一下排查网页查询慢的过程和解决方法,希望能够给其他朋友提供思路。

官方微信
返回顶部