你不知道的 PHP 缓存技巧

前言

相信有一定开发经验的PHP程序员都有缓存的使用经验,包括但不限于redis缓存,模板缓存,路由缓存,配置缓存,数据库缓存,还有opcache(真的很有用),但是今天我不准备炒冷饭了,我要讲点你不知道的缓存。

你不知道的 PHP 缓存技巧

文档链接

  • php源代码
  • php扩展电子书
  • 分析

    首先为了说明问题,我贴出公司的一段代码,我相信你肯定写过,非常简单,就像下面这样。

    $year = date('Y', time());
    $month = date('m', time());

    初看起来,好像没有什么问题,对头,你说的没错,代码是没有问题的,那么问题出在哪里呢?要解释这个问题,我要先给大家讲解下有关知识点,听好咯。

    PHP函数

    php自带的函数,是怎么运行的呢?很简单,php自带的函数都对应底层的一个C语言函数(关于PHP扩展以后会讲到),就以当前的time函数为例。

你不知道的PHP缓存技巧
所有的PHP对应的底层函数,都是这么定义的,PHP_FUNCTION是C语言的一个宏定义,我们看看这个函数,它调用了函数php_time,那么php_time又是啥呢?

你不知道的PHP缓存技巧

这里它调用了系统调用time,什么是系统调用?简单来说,系统调用就是操作系统提供给用户程序访问计算机资源的接口,更要命的是,系统调用极其耗时,说到这里你应该明白了,当你在php脚本里面每一次调用time(),php程序就会系统调用一次。

优化

刚才以time()函数作为例子分析了弊端,那么我们怎么来优化呢?其实很简单,对于时间要求不是那么严格的情况下,我们可以把时间缓存起来,就像下面这样。

class TimeWrapper
{
    private static $now_time = 0;

    /**
     * @param bool $force_refresh 是否强制刷新
     * @return int
     */
    public static function getTime($force_refresh = false)
    {
        if ($force_refresh) {
            self::$now_time = time();
        } else {
            if (!self::$now_time) {
                self::$now_time = time();
            }
        }
        return self::$now_time;
    }
}

定义一个类,然后定义一个静态字段,就完事儿了。

疑惑

你可能会说,我的程序不在乎这么点儿时间。但是php自带的很多函数都会调用系统调用,结果不用我说,你应该也明白了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
微信:okayGoHome
本帖由系统于 3年前 自动加精
Dennis_Ritchie
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 12
Oraoto

缓存思路是好的,但是这个例子举得不好。

  1. Linux下, gettimeofday 系统调用是通过 vDSO 提供的,调用是不需要进入内核态的
  2. PHP静态方法调用、静态变量读取的消耗比直接调用C函数大

上面这两点,就导致了Linux下 time() 其实会比TimeWrapper::getTime()更快。

Nginx和PHP的性能场景是不同的~

4年前 评论
Oraoto (作者) 4年前
Dennis_Ritchie (楼主) 4年前
Dennis_Ritchie (楼主) 4年前
Oraoto (作者) 4年前
Dennis_Ritchie (楼主) 4年前
Dennis_Ritchie (楼主) 4年前

和单例模式获取实例是一个理,不过这样写会不会简洁些?

public static function getTime(bool $force_refresh = false): int
    {
        if (!self::$time || $force_refresh) {
            self::$time = time();
        }

        return self::$time;
    }
4年前 评论
Dennis_Ritchie (楼主) 4年前

直接 $time = time(); 不就行拉

4年前 评论
Dennis_Ritchie (楼主) 4年前

这样子,你反而获得不到真正的当前时间!!!
获得的只是缓存时候的时间
若要这样,何不直接 $_SERVER['REQUEST_TIME']

4年前 评论
Dennis_Ritchie (楼主) 4年前

老哥,测试了吗?我亲测 false,时间貌似更久了~~

4年前 评论
Dennis_Ritchie (楼主) 4年前

这种的话在队列中或者一些长连接中就有问题,队列不重启你的静态变量就不释放

4年前 评论
Dennis_Ritchie (楼主) 4年前
hello-bug (作者) 4年前
Dennis_Ritchie (楼主) 4年前

学过单例模式的同学应该都觉得很熟悉,就laravel的auth里面就有结构一样的代码。
感谢你的输出,大家都晓得,哈哈。
然后他们说的这个时间缓存是没问题的,因为大部分项目都是fpm模式,且业务对时间要求不高,这个函数还是有作用。

4年前 评论
Dennis_Ritchie (楼主) 4年前
L学习不停 (作者) 4年前
lmaster

膜拜大佬+1

4年前 评论
$year = date('Y', time());
$month = date('m', time());

这是啥? 直接下面不行吗? :sweat_smile:

$year = date('Y');
$month = date('m');
4年前 评论
lovecn 4年前
没前途的程序员 4年前
839891627 (作者) 4年前
没前途的程序员 4年前

提到《深入linux内核架构》,视野挺广的

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!