Repository files navigation

LinkCache - 一个灵活高效的PHP缓存工具库

LinkCache 是一个PHP编写的灵活高效的缓存工具库,提供多种缓存驱动支持,包括Memcache、Memcached、Redis、SSDB、文件缓存、APC、YAC等。通过LinkCache可以使不同缓存驱动实现操作统一,同时又可发挥不同缓存驱动各自的优势。LinkCache支持缓存 objectarray,同时为防止产生惊群现象做了优化。

环境要求

安装

composer 安装

LinkCache 可以通过 composer 安装,使用以下命令从 composer 下载安装 LinkCache

$ composer require dongnan/linkcache

手动下载安装

下载地址

安装方法

在你的入口文件中引入

<?php//引入 LinkCache 的自动加载文件include("path_to_linkcache/autoload.php");

如何使用

(提示:如果以下锚点定位的链接错误,导致页面找不到等问题,请访问 https://github.com/dongnan/LinkCache 查看文档)

  • config - 配置信息
  • instance - 缓存实例化
  • getDriver - 获取缓存驱动实例
  • set - 将参数中的 value设置为 key 的值
  • setnx - 当缓存中不存在 key 时,将参数中的 value 设置为 key 的值
  • setDE - 将参数中的 value设置为 key 的值,key 将自动延迟过期
  • get - 获取 key 对应的值
  • getDE - 获取 key 对应的值,与 setDE 配合使用
  • del - 删除 key
  • has - 判断 key 是否存在
  • hasDE - 判断延迟过期的 key 理论上是否存在
  • ttl - 获取 key 的生存时间(单位:s)
  • ttlDE - 获取延迟过期的 key 理论上的生存时间(单位:s)
  • expire - 设置一个 key 的生存时间(单位:s)
  • expireDE - 以延迟过期的方式设置一个 key 的生存时间(单位:s)
  • expireAt - 用UNIX时间戳设置一个 key 的过期时间
  • expireAtDE - 以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间
  • persist - 删除一个 key 的生存时间,使其永不过期
  • lock - 对 key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)
  • isLock - 判断 key 是否有锁标记
  • unLock - 移除 key 的锁标记
  • incr - 设置 key 的值按整数递增
  • incrByFloat - 设置 key 的值按浮点数递增
  • decr - 设置 key 的值按整数递减
  • mSet - 批量设置多个 key 对应的值
  • mSetNX - 当缓存中不存在 key 时,批量设置多个 key 对应的值
  • mGet - 获取所有给定 key 的值
  • mHas - 批量判断 key 是否存在
  • mDel - 批量删除 key

config

配置信息

<?php//设置缓存配置, 使用 array_merge 的方式合并到默认配置中
\linkcache\Cache::setConfig($config);
//获取配置信息$config = \linkcache\Cache::getConfig();
//获取指定缓存驱动的配置信息$config = \linkcache\Cache::getConfig('redis');
//默认配置信息
[
//默认使用的缓存驱动'default' => 'files',
//当前缓存驱动失效时,使用的备份驱动'fallback' => 'files',
'memcache' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1, 'persistent' => true, 'timeout' => 1, 'retry_interval' => 15, 'status' => true],
],
'compress' => ['threshold' => 2000, 'min_saving' => 0.2],
],
'memcached' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1],
],
//参考 Memcached::setOptions'options' => [],
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'database' => '',
'timeout' => ''
],
'ssdb' => [
'host' => '127.0.0.1',
'port' => 8888,
'password' => '',
'timeoutms' => ''
],
]

instance

实例化缓存对象

<?php//直接new一个$cache = new \linkcache\Cache();
//通过getInstance获取$cache = \linkcache\Cache::getInstance();
//根据驱动类型实例化,支持的驱动:redis,memcache,memcached,ssdb,files,apc,yac$cache = new \linkcache\Cache('files');
//或者$cache = \linkcache\Cache::getInstance('files');
//传入配置参数实例化$cache = new \linkcache\Cache('redis', ['host' => '127.0.0.1', 'port' => 6379]);

getDriver

获取缓存驱动实例

所有缓存驱动都必须实现 linkcache\interfaces\driver\Base 接口的方法,获取到缓存驱动实例后,可直接使用缓存驱动的方法,包括缓存驱动中没有定义但缓存驱动扩展对象中已定义的方法。

set

将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->set($key, $value);
//设置有过期时间的缓存$status = $cache->set($key, $value, $time);

setnx

当缓存中不存在 key 时,将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setnx($key, $value);
//设置有过期时间的缓存$status = $cache->setnx($key, $value, $time);

setDE

将参数中的 value设置为 key 的值,key 将自动延迟过期

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - key 的生存时间,单位是秒(s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setDE($key, $value);
//设置有过期时间的缓存$status = $cache->setDE($key, $value, $time);
//设置有过期时间的缓存并指定延迟过期时间$status = $cache->setDE($key, $value, $time, $delayTime);

get

获取 key 对应的值

参数

  • key - 字符串

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->get($key);

getDE

获取 key 对应的值,与 setDE 配合使用

参数

  • key - 字符串
  • isExpired - key 理论上是否已经过期,这是一个引用变量,不需要初始化

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->getDE($key, $isExpired);
//如果已经过期,处理其他逻辑if ($isExpired) {
//TODO
}

del

删除 key

参数

  • key - 字符串

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false注意:key 不存在时,也会返回 true

例子

<?php//删除key$status = $cache->del($key);

has

判断 key 是否存在

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->has($key);

hasDE

判断延迟过期的 key 理论上是否存在。设置自动延期的 key,过期时间实际会被修改,用方法 has 判断的话,可能 key 仍然存在,但是理论上已经过期了

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->hasDE($key);

ttl

获取 key 的生存时间(单位:s)

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取key的生存时间$ttl = $cache->ttl($key);

ttlDE

获取延迟过期的 key 理论上的生存时间(单位:s)。

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取延迟过期的 key 理论上的生存时间(单位:s)$ttl = $cache->ttlDE($key);

expire

设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//设置一个key的生存时间$status = $cache->expire($key, $time);

expireDE

以延迟过期的方式设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//以延迟过期的方式设置一个 key 的生存时间(单位:s)$status = $cache->expireDE($key, $time);
//自定义延迟过期时间$status = $cache->expireDE($key, $time, $delayTime);

expireAt

用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAt($key, $time);

expireAtDE

以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAtDE($key, $time);
//自定义延迟过期时间$status = $cache->expireAtDE($key, $time, $delayTime);

persist

删除一个 key 的生存时间,使其永不过期

参数

  • key - 字符串

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//删除一个key的生存时间,使其永不过期$status = $cache->persist($key);

lock

key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)

参数

  • key - 字符串
  • time - 整数,key锁标记 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果 key 已存在或设置失败,返回 false

例子

<?php//对key设置锁标记$status = $cache->lock($key, $time);

isLock

判断 key 是否有锁标记

参数

  • key - 字符串

返回值

Boolean - 如果有锁标记,返回 true; 如果没有锁标记或判断失败,返回 false

例子

<?php//判断key是否有锁标记$status = $cache->isLock($key);

unLock

移除 key 的锁标记

参数

  • key - 字符串

返回值

Boolean - 如果移除成功,返回 true; 如果失败,返回 false

例子

<?php//移除key的锁标记$status = $cache->unLock($key);

incr

设置 key 的值按整数递增

参数

  • key - 字符串
  • step - (可选参数) 整数,递增步长,默认值为 1,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 step 设置新值

例子

<?php//设置key的值按整数递增$value = $cache->incr($key, $step);

incrByFloat

设置 key 的值按浮点数递增

参数

  • key - 字符串
  • float - 浮点数,递增步长,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 float 设置新值

例子

<?php//设置key的值按浮点数递增$value = $cache->incrByFloat($key, $float);

decr

设置 key 的值按整数递减

参数

  • key - 字符串
  • step - (可选参数) 整数,递减步长,默认值为 1,可以为负值

返回值

Mixed - 递减后的值,失败返回 false,如果 key 不存在,则按 -step 设置新值

例子

<?php//设置key的值按整数递减$value = $cache->decr($key, $step);

mSet

批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//批量设置多个key对应的值$status = $cache->mSet($sets);

mSetNX

当缓存中不存在 key 时,批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//当缓存中不存在key时,批量设置多个key对应的值$status = $cache->mSetNX($sets);

mGet

获取所有给定 key 的值

参数

  • keys - 多个 key 组成的数组

返回值

array - 参数 keys 中的所有 key 与对应的 value 组成的数组,如果 key 不存在或是获取失败,对应的 value 值为 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//获取所有给定key的值$status = $cache->mGet($keys);

mHas

批量判断 key 是否存在

参数

  • keys - 多个 key 组成的数组

返回值

array - 返回存在的 key 的数组,如果判断失败返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量判断key是否存在$hasKeys = $cache->mHas($keys);

mDel

批量删除 key

参数

  • keys - 多个 key 组成的数组

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量删除key$status = $cache->mDel($keys);

如何防止惊群

什么是惊群

首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了缓存的高并发的系统下,如果缓存突然都消失了,会发生什么?首先数据库的压力必然骤增,接着负载迅速升高,响应时间变慢,进程/线程由于响应时间变慢导致集压并剧增,这就是惊群。惊群的后果就是系统不可用甚至宕机。当然这是一个非常极端的例子,但是能很好的解释惊群现象。真实环境中,可能会有个一些页面是访问非常频繁的,如果这些页面的缓存在同一时间或相近的时间突然过期了,在高并发的情况下也会产生惊群现象。

如何避免惊群

知道了什么是惊群,就不难防止惊群了。首先,避免缓存在同一时间过期;其次,如果缓存过期,不要让所有并发的进程/线程都去触发更新同一缓存数据。

例子

LinkCache 为此专门做了优化,使用 LinkCache 提供的一些特殊方法,可以有效的防止惊群现象。以下是简单的示例(伪代码):

<?php/* LinkCache专为防止惊群现象设计的方法包括: * setDE,getDE,hasDE,ttlDE,expireDE,expireAtDE,lock,isLock,unLock * 比较常用的是:setDE,getDE,lock *///设置缓存$cache->setDE($key,$value,$time);
//获取缓存$value = $cache->getDE($key,$isExpired);
//情况1,$value存在,$isExpired=false,不需要更新缓存if($value !== false){
//your code
}
//情况2,$value存在,$isExpired=true,需要更新缓存if($value !== false){
//your code
}
//更新缓存if($isExpired === true){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
}
//情况3,$value不存在,需要更新缓存if($value === false){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
//如果失败,说明已经有进程/线程对key加了锁标记,正在更新缓存else{
//如果并发大,建议此处做特殊处理,不要去更新缓存//特殊处理的代码
}
}

默认情况说明

  • Cache - \linkcache\Cache
  • drivers
    • files - \linkcache\drivers\Files
    • memcache - \linkcache\drivers\Memcache
    • memcached - \linkcache\drivers\Memcached
    • redis - \linkcache\drivers\Redis
    • ssdb - \linkcache\drivers\Ssdb
    • apc - \linkcache\drivers\Apc
    • yac - \linkcache\drivers\Yac

cache

  • 默认使用的缓存驱动: files
  • 默认使用的备用驱动: files
  • memcacheredisssdb 等缓存驱动的默认配置均参考官方默认配置
  • 自定义配置支持非驱动类型的key,但配置信息中需要有 driver_type 属性,否则会抛异常,示例如下:
<?phpuse \linkcache\Cache;
$config = [
'redis_m_db_1' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 1
],
'redis_m_db_2' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 2
],
'redis_s_db' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6381
]
];
Cache::setConfig($config);
//根据自定义配置实例化$redisM1 = newCache('redis_m_db_1');
$redisM2 = newCache('redis_m_db_2');
$redisS0 = newCache('redis_s_db');

drivers

  • 所有驱动默认使用备用缓存
  • 备用缓存优先使用实例化时构造函数传入的配置 config 中的 fallback 定义,如果没有定义,则使用 \linkcache\Cache::$config 中的 fallback 定义

files

  • 默认保存路径:优先使用上传文件临时目录,未定义则使用系统临时目录,并在目录下创建linkcache目录,作为 files 驱动的默认保存路径,代码如下: (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . '/linkcache'

memcache

  • memcache 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

memcached

  • memcached 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

redis

  • redis 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

ssdb

  • ssdb 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

apc

yac

  • 可自定义缓存 key 前缀,默认无 key 前缀

开发

如果你觉得LinkCache还不错,但又不支持你想用的缓存驱动,不妨尝试在LinkCache新增该缓存驱动的支持。

1.增加新的缓存驱动

目前有两种方式可以方便的开发新的缓存驱动支持

  • 继承 linkcache\abstracts\DriverSimple 抽象类

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\abstracts\DriverSimple;
      class Example extends DriverSimple {
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key * @param string $value * @return boolean */protectedfunctionsetOne($key, $value) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key * @return mixed */protectedfunctiongetOne($key) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key * @return boolean */protectedfunctiondelOne($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\abstracts\DriverSimple 抽象类中的抽象方法
  • 实现接口 linkcache\interfaces\driver\Base (Base接口是必须的,你也可以实现更多的接口:Incr,Lock,Multi)

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\interfaces\driver\Base;
      class Example implements Base {
      use \linkcache\traits\CacheDriver;
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionset($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 当键名不存在时设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionsetnx($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 设置键值,将自动延迟过期;<br> * 此方法用于缓存对过期要求宽松的数据;<br> * 使用此方法设置缓存配合getDE方法可以有效防止惊群现象发生 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,<=0则设置为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionsetDE($key, $value, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key 键名 * @return mixed|false 键值,失败返回false */publicfunctionget($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值,与setDE配合使用;<br> * 此方法用于获取setDE设置的缓存数据;<br> * 当isExpired为true时,说明key已经过期,需要更新;<br> * 更新数据时配合isLock和lock方法,防止惊群现象发生 * @param string $key 键名 * @param boolean $isExpired 是否已经过期 * @return mixed|false 键值,失败返回false */publicfunctiongetDE($key, &$isExpired = null) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key 键名 * @return boolean 是否成功 */publicfunctiondel($key) {
      //TODO 实现这个方法
      }
      /** * 是否存在键值 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhas($key) {
      //TODO 实现这个方法
      }
      /** * 判断延迟过期的键值理论上是否存在 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhasDE($key) {
      //TODO 实现这个方法
      }
      /** * 获取生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttl($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值理论生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttlDE($key) {
      //TODO 实现这个方法
      }
      /** * 设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @return boolean 是否成功 */publicfunctionexpire($key, $time) {
      //TODO 实现这个方法
      }
      /** * 以延迟过期的方式设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionexpireDE($key, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 移除指定键值的过期时间 * @param string $key 键名 * @return boolean 是否成功 */publicfunctionpersist($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\interfaces\driver\Base 接口中的方法

2.测试新增的驱动

为了保证代码的可靠性,不妨对新增的驱动做个测试吧。 我使用的是PHPUnit做的测试,版本:4.8.15

  • 测试步骤
    1. tests 目录新增测试类 TestDriverExample.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcacheTests;
      /** * TestDriverExample */class TestDriverExample extends TestDriverFiles
      {
      protected$cacheDriver = 'example';
      }
    2. 进入LinkCache项目目录,执行测试命令
      phpunit --bootstrap autoload_dev.php tests/TestDriverExample
      
      显示以下信息测试就通过啦~
      PHPUnit 4.8.15 by Sebastian Bergmann and contributors.
      ....................
      Time: 8.24 seconds, Memory: 8.75Mb
      OK (26 tests, 105 assertions)
      

LICENSE

使用非常灵活宽松的 New BSD License 协议

About

一个灵活高效的PHP缓存工具库

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

LinkCache - 一个灵活高效的PHP缓存工具库

LinkCache 是一个PHP编写的灵活高效的缓存工具库,提供多种缓存驱动支持,包括Memcache、Memcached、Redis、SSDB、文件缓存、APC、YAC等。通过LinkCache可以使不同缓存驱动实现操作统一,同时又可发挥不同缓存驱动各自的优势。LinkCache支持缓存 objectarray,同时为防止产生惊群现象做了优化。

环境要求

安装

composer 安装

LinkCache 可以通过 composer 安装,使用以下命令从 composer 下载安装 LinkCache

$ composer require dongnan/linkcache

手动下载安装

下载地址

安装方法

在你的入口文件中引入

<?php//引入 LinkCache 的自动加载文件include("path_to_linkcache/autoload.php");

如何使用

(提示:如果以下锚点定位的链接错误,导致页面找不到等问题,请访问 https://github.com/dongnan/LinkCache 查看文档)

  • config - 配置信息
  • instance - 缓存实例化
  • getDriver - 获取缓存驱动实例
  • set - 将参数中的 value设置为 key 的值
  • setnx - 当缓存中不存在 key 时,将参数中的 value 设置为 key 的值
  • setDE - 将参数中的 value设置为 key 的值,key 将自动延迟过期
  • get - 获取 key 对应的值
  • getDE - 获取 key 对应的值,与 setDE 配合使用
  • del - 删除 key
  • has - 判断 key 是否存在
  • hasDE - 判断延迟过期的 key 理论上是否存在
  • ttl - 获取 key 的生存时间(单位:s)
  • ttlDE - 获取延迟过期的 key 理论上的生存时间(单位:s)
  • expire - 设置一个 key 的生存时间(单位:s)
  • expireDE - 以延迟过期的方式设置一个 key 的生存时间(单位:s)
  • expireAt - 用UNIX时间戳设置一个 key 的过期时间
  • expireAtDE - 以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间
  • persist - 删除一个 key 的生存时间,使其永不过期
  • lock - 对 key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)
  • isLock - 判断 key 是否有锁标记
  • unLock - 移除 key 的锁标记
  • incr - 设置 key 的值按整数递增
  • incrByFloat - 设置 key 的值按浮点数递增
  • decr - 设置 key 的值按整数递减
  • mSet - 批量设置多个 key 对应的值
  • mSetNX - 当缓存中不存在 key 时,批量设置多个 key 对应的值
  • mGet - 获取所有给定 key 的值
  • mHas - 批量判断 key 是否存在
  • mDel - 批量删除 key

config

配置信息

<?php//设置缓存配置, 使用 array_merge 的方式合并到默认配置中
\linkcache\Cache::setConfig($config);
//获取配置信息$config = \linkcache\Cache::getConfig();
//获取指定缓存驱动的配置信息$config = \linkcache\Cache::getConfig('redis');
//默认配置信息
[
//默认使用的缓存驱动'default' => 'files',
//当前缓存驱动失效时,使用的备份驱动'fallback' => 'files',
'memcache' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1, 'persistent' => true, 'timeout' => 1, 'retry_interval' => 15, 'status' => true],
],
'compress' => ['threshold' => 2000, 'min_saving' => 0.2],
],
'memcached' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1],
],
//参考 Memcached::setOptions'options' => [],
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'database' => '',
'timeout' => ''
],
'ssdb' => [
'host' => '127.0.0.1',
'port' => 8888,
'password' => '',
'timeoutms' => ''
],
]

instance

实例化缓存对象

<?php//直接new一个$cache = new \linkcache\Cache();
//通过getInstance获取$cache = \linkcache\Cache::getInstance();
//根据驱动类型实例化,支持的驱动:redis,memcache,memcached,ssdb,files,apc,yac$cache = new \linkcache\Cache('files');
//或者$cache = \linkcache\Cache::getInstance('files');
//传入配置参数实例化$cache = new \linkcache\Cache('redis', ['host' => '127.0.0.1', 'port' => 6379]);

getDriver

获取缓存驱动实例

所有缓存驱动都必须实现 linkcache\interfaces\driver\Base 接口的方法,获取到缓存驱动实例后,可直接使用缓存驱动的方法,包括缓存驱动中没有定义但缓存驱动扩展对象中已定义的方法。

set

将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->set($key, $value);
//设置有过期时间的缓存$status = $cache->set($key, $value, $time);

setnx

当缓存中不存在 key 时,将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setnx($key, $value);
//设置有过期时间的缓存$status = $cache->setnx($key, $value, $time);

setDE

将参数中的 value设置为 key 的值,key 将自动延迟过期

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - key 的生存时间,单位是秒(s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setDE($key, $value);
//设置有过期时间的缓存$status = $cache->setDE($key, $value, $time);
//设置有过期时间的缓存并指定延迟过期时间$status = $cache->setDE($key, $value, $time, $delayTime);

get

获取 key 对应的值

参数

  • key - 字符串

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->get($key);

getDE

获取 key 对应的值,与 setDE 配合使用

参数

  • key - 字符串
  • isExpired - key 理论上是否已经过期,这是一个引用变量,不需要初始化

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->getDE($key, $isExpired);
//如果已经过期,处理其他逻辑if ($isExpired) {
//TODO
}

del

删除 key

参数

  • key - 字符串

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false注意:key 不存在时,也会返回 true

例子

<?php//删除key$status = $cache->del($key);

has

判断 key 是否存在

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->has($key);

hasDE

判断延迟过期的 key 理论上是否存在。设置自动延期的 key,过期时间实际会被修改,用方法 has 判断的话,可能 key 仍然存在,但是理论上已经过期了

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->hasDE($key);

ttl

获取 key 的生存时间(单位:s)

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取key的生存时间$ttl = $cache->ttl($key);

ttlDE

获取延迟过期的 key 理论上的生存时间(单位:s)。

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取延迟过期的 key 理论上的生存时间(单位:s)$ttl = $cache->ttlDE($key);

expire

设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//设置一个key的生存时间$status = $cache->expire($key, $time);

expireDE

以延迟过期的方式设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//以延迟过期的方式设置一个 key 的生存时间(单位:s)$status = $cache->expireDE($key, $time);
//自定义延迟过期时间$status = $cache->expireDE($key, $time, $delayTime);

expireAt

用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAt($key, $time);

expireAtDE

以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAtDE($key, $time);
//自定义延迟过期时间$status = $cache->expireAtDE($key, $time, $delayTime);

persist

删除一个 key 的生存时间,使其永不过期

参数

  • key - 字符串

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//删除一个key的生存时间,使其永不过期$status = $cache->persist($key);

lock

key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)

参数

  • key - 字符串
  • time - 整数,key锁标记 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果 key 已存在或设置失败,返回 false

例子

<?php//对key设置锁标记$status = $cache->lock($key, $time);

isLock

判断 key 是否有锁标记

参数

  • key - 字符串

返回值

Boolean - 如果有锁标记,返回 true; 如果没有锁标记或判断失败,返回 false

例子

<?php//判断key是否有锁标记$status = $cache->isLock($key);

unLock

移除 key 的锁标记

参数

  • key - 字符串

返回值

Boolean - 如果移除成功,返回 true; 如果失败,返回 false

例子

<?php//移除key的锁标记$status = $cache->unLock($key);

incr

设置 key 的值按整数递增

参数

  • key - 字符串
  • step - (可选参数) 整数,递增步长,默认值为 1,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 step 设置新值

例子

<?php//设置key的值按整数递增$value = $cache->incr($key, $step);

incrByFloat

设置 key 的值按浮点数递增

参数

  • key - 字符串
  • float - 浮点数,递增步长,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 float 设置新值

例子

<?php//设置key的值按浮点数递增$value = $cache->incrByFloat($key, $float);

decr

设置 key 的值按整数递减

参数

  • key - 字符串
  • step - (可选参数) 整数,递减步长,默认值为 1,可以为负值

返回值

Mixed - 递减后的值,失败返回 false,如果 key 不存在,则按 -step 设置新值

例子

<?php//设置key的值按整数递减$value = $cache->decr($key, $step);

mSet

批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//批量设置多个key对应的值$status = $cache->mSet($sets);

mSetNX

当缓存中不存在 key 时,批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//当缓存中不存在key时,批量设置多个key对应的值$status = $cache->mSetNX($sets);

mGet

获取所有给定 key 的值

参数

  • keys - 多个 key 组成的数组

返回值

array - 参数 keys 中的所有 key 与对应的 value 组成的数组,如果 key 不存在或是获取失败,对应的 value 值为 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//获取所有给定key的值$status = $cache->mGet($keys);

mHas

批量判断 key 是否存在

参数

  • keys - 多个 key 组成的数组

返回值

array - 返回存在的 key 的数组,如果判断失败返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量判断key是否存在$hasKeys = $cache->mHas($keys);

mDel

批量删除 key

参数

  • keys - 多个 key 组成的数组

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量删除key$status = $cache->mDel($keys);

如何防止惊群

什么是惊群

首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了缓存的高并发的系统下,如果缓存突然都消失了,会发生什么?首先数据库的压力必然骤增,接着负载迅速升高,响应时间变慢,进程/线程由于响应时间变慢导致集压并剧增,这就是惊群。惊群的后果就是系统不可用甚至宕机。当然这是一个非常极端的例子,但是能很好的解释惊群现象。真实环境中,可能会有个一些页面是访问非常频繁的,如果这些页面的缓存在同一时间或相近的时间突然过期了,在高并发的情况下也会产生惊群现象。

如何避免惊群

知道了什么是惊群,就不难防止惊群了。首先,避免缓存在同一时间过期;其次,如果缓存过期,不要让所有并发的进程/线程都去触发更新同一缓存数据。

例子

LinkCache 为此专门做了优化,使用 LinkCache 提供的一些特殊方法,可以有效的防止惊群现象。以下是简单的示例(伪代码):

<?php/* LinkCache专为防止惊群现象设计的方法包括: * setDE,getDE,hasDE,ttlDE,expireDE,expireAtDE,lock,isLock,unLock * 比较常用的是:setDE,getDE,lock *///设置缓存$cache->setDE($key,$value,$time);
//获取缓存$value = $cache->getDE($key,$isExpired);
//情况1,$value存在,$isExpired=false,不需要更新缓存if($value !== false){
//your code
}
//情况2,$value存在,$isExpired=true,需要更新缓存if($value !== false){
//your code
}
//更新缓存if($isExpired === true){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
}
//情况3,$value不存在,需要更新缓存if($value === false){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
//如果失败,说明已经有进程/线程对key加了锁标记,正在更新缓存else{
//如果并发大,建议此处做特殊处理,不要去更新缓存//特殊处理的代码
}
}

默认情况说明

  • Cache - \linkcache\Cache
  • drivers
    • files - \linkcache\drivers\Files
    • memcache - \linkcache\drivers\Memcache
    • memcached - \linkcache\drivers\Memcached
    • redis - \linkcache\drivers\Redis
    • ssdb - \linkcache\drivers\Ssdb
    • apc - \linkcache\drivers\Apc
    • yac - \linkcache\drivers\Yac

cache

  • 默认使用的缓存驱动: files
  • 默认使用的备用驱动: files
  • memcacheredisssdb 等缓存驱动的默认配置均参考官方默认配置
  • 自定义配置支持非驱动类型的key,但配置信息中需要有 driver_type 属性,否则会抛异常,示例如下:
<?phpuse \linkcache\Cache;
$config = [
'redis_m_db_1' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 1
],
'redis_m_db_2' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 2
],
'redis_s_db' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6381
]
];
Cache::setConfig($config);
//根据自定义配置实例化$redisM1 = newCache('redis_m_db_1');
$redisM2 = newCache('redis_m_db_2');
$redisS0 = newCache('redis_s_db');

drivers

  • 所有驱动默认使用备用缓存
  • 备用缓存优先使用实例化时构造函数传入的配置 config 中的 fallback 定义,如果没有定义,则使用 \linkcache\Cache::$config 中的 fallback 定义

files

  • 默认保存路径:优先使用上传文件临时目录,未定义则使用系统临时目录,并在目录下创建linkcache目录,作为 files 驱动的默认保存路径,代码如下: (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . '/linkcache'

memcache

  • memcache 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

memcached

  • memcached 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

redis

  • redis 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

ssdb

  • ssdb 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

apc

yac

  • 可自定义缓存 key 前缀,默认无 key 前缀

开发

如果你觉得LinkCache还不错,但又不支持你想用的缓存驱动,不妨尝试在LinkCache新增该缓存驱动的支持。

1.增加新的缓存驱动

目前有两种方式可以方便的开发新的缓存驱动支持

  • 继承 linkcache\abstracts\DriverSimple 抽象类

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\abstracts\DriverSimple;
      class Example extends DriverSimple {
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key * @param string $value * @return boolean */protectedfunctionsetOne($key, $value) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key * @return mixed */protectedfunctiongetOne($key) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key * @return boolean */protectedfunctiondelOne($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\abstracts\DriverSimple 抽象类中的抽象方法
  • 实现接口 linkcache\interfaces\driver\Base (Base接口是必须的,你也可以实现更多的接口:Incr,Lock,Multi)

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\interfaces\driver\Base;
      class Example implements Base {
      use \linkcache\traits\CacheDriver;
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionset($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 当键名不存在时设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionsetnx($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 设置键值,将自动延迟过期;<br> * 此方法用于缓存对过期要求宽松的数据;<br> * 使用此方法设置缓存配合getDE方法可以有效防止惊群现象发生 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,<=0则设置为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionsetDE($key, $value, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key 键名 * @return mixed|false 键值,失败返回false */publicfunctionget($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值,与setDE配合使用;<br> * 此方法用于获取setDE设置的缓存数据;<br> * 当isExpired为true时,说明key已经过期,需要更新;<br> * 更新数据时配合isLock和lock方法,防止惊群现象发生 * @param string $key 键名 * @param boolean $isExpired 是否已经过期 * @return mixed|false 键值,失败返回false */publicfunctiongetDE($key, &$isExpired = null) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key 键名 * @return boolean 是否成功 */publicfunctiondel($key) {
      //TODO 实现这个方法
      }
      /** * 是否存在键值 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhas($key) {
      //TODO 实现这个方法
      }
      /** * 判断延迟过期的键值理论上是否存在 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhasDE($key) {
      //TODO 实现这个方法
      }
      /** * 获取生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttl($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值理论生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttlDE($key) {
      //TODO 实现这个方法
      }
      /** * 设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @return boolean 是否成功 */publicfunctionexpire($key, $time) {
      //TODO 实现这个方法
      }
      /** * 以延迟过期的方式设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionexpireDE($key, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 移除指定键值的过期时间 * @param string $key 键名 * @return boolean 是否成功 */publicfunctionpersist($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\interfaces\driver\Base 接口中的方法

2.测试新增的驱动

为了保证代码的可靠性,不妨对新增的驱动做个测试吧。 我使用的是PHPUnit做的测试,版本:4.8.15

  • 测试步骤
    1. tests 目录新增测试类 TestDriverExample.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcacheTests;
      /** * TestDriverExample */class TestDriverExample extends TestDriverFiles
      {
      protected$cacheDriver = 'example';
      }
    2. 进入LinkCache项目目录,执行测试命令
      phpunit --bootstrap autoload_dev.php tests/TestDriverExample
      
      显示以下信息测试就通过啦~
      PHPUnit 4.8.15 by Sebastian Bergmann and contributors.
      ....................
      Time: 8.24 seconds, Memory: 8.75Mb
      OK (26 tests, 105 assertions)
      

LICENSE

使用非常灵活宽松的 New BSD License 协议

About

一个灵活高效的PHP缓存工具库

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

LinkCache - 一个灵活高效的PHP缓存工具库

LinkCache 是一个PHP编写的灵活高效的缓存工具库,提供多种缓存驱动支持,包括Memcache、Memcached、Redis、SSDB、文件缓存、APC、YAC等。通过LinkCache可以使不同缓存驱动实现操作统一,同时又可发挥不同缓存驱动各自的优势。LinkCache支持缓存 objectarray,同时为防止产生惊群现象做了优化。

环境要求

安装

composer 安装

LinkCache 可以通过 composer 安装,使用以下命令从 composer 下载安装 LinkCache

$ composer require dongnan/linkcache

手动下载安装

下载地址

安装方法

在你的入口文件中引入

<?php//引入 LinkCache 的自动加载文件include("path_to_linkcache/autoload.php");

如何使用

(提示:如果以下锚点定位的链接错误,导致页面找不到等问题,请访问 https://github.com/dongnan/LinkCache 查看文档)

  • config - 配置信息
  • instance - 缓存实例化
  • getDriver - 获取缓存驱动实例
  • set - 将参数中的 value设置为 key 的值
  • setnx - 当缓存中不存在 key 时,将参数中的 value 设置为 key 的值
  • setDE - 将参数中的 value设置为 key 的值,key 将自动延迟过期
  • get - 获取 key 对应的值
  • getDE - 获取 key 对应的值,与 setDE 配合使用
  • del - 删除 key
  • has - 判断 key 是否存在
  • hasDE - 判断延迟过期的 key 理论上是否存在
  • ttl - 获取 key 的生存时间(单位:s)
  • ttlDE - 获取延迟过期的 key 理论上的生存时间(单位:s)
  • expire - 设置一个 key 的生存时间(单位:s)
  • expireDE - 以延迟过期的方式设置一个 key 的生存时间(单位:s)
  • expireAt - 用UNIX时间戳设置一个 key 的过期时间
  • expireAtDE - 以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间
  • persist - 删除一个 key 的生存时间,使其永不过期
  • lock - 对 key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)
  • isLock - 判断 key 是否有锁标记
  • unLock - 移除 key 的锁标记
  • incr - 设置 key 的值按整数递增
  • incrByFloat - 设置 key 的值按浮点数递增
  • decr - 设置 key 的值按整数递减
  • mSet - 批量设置多个 key 对应的值
  • mSetNX - 当缓存中不存在 key 时,批量设置多个 key 对应的值
  • mGet - 获取所有给定 key 的值
  • mHas - 批量判断 key 是否存在
  • mDel - 批量删除 key

config

配置信息

<?php//设置缓存配置, 使用 array_merge 的方式合并到默认配置中
\linkcache\Cache::setConfig($config);
//获取配置信息$config = \linkcache\Cache::getConfig();
//获取指定缓存驱动的配置信息$config = \linkcache\Cache::getConfig('redis');
//默认配置信息
[
//默认使用的缓存驱动'default' => 'files',
//当前缓存驱动失效时,使用的备份驱动'fallback' => 'files',
'memcache' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1, 'persistent' => true, 'timeout' => 1, 'retry_interval' => 15, 'status' => true],
],
'compress' => ['threshold' => 2000, 'min_saving' => 0.2],
],
'memcached' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1],
],
//参考 Memcached::setOptions'options' => [],
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'database' => '',
'timeout' => ''
],
'ssdb' => [
'host' => '127.0.0.1',
'port' => 8888,
'password' => '',
'timeoutms' => ''
],
]

instance

实例化缓存对象

<?php//直接new一个$cache = new \linkcache\Cache();
//通过getInstance获取$cache = \linkcache\Cache::getInstance();
//根据驱动类型实例化,支持的驱动:redis,memcache,memcached,ssdb,files,apc,yac$cache = new \linkcache\Cache('files');
//或者$cache = \linkcache\Cache::getInstance('files');
//传入配置参数实例化$cache = new \linkcache\Cache('redis', ['host' => '127.0.0.1', 'port' => 6379]);

getDriver

获取缓存驱动实例

所有缓存驱动都必须实现 linkcache\interfaces\driver\Base 接口的方法,获取到缓存驱动实例后,可直接使用缓存驱动的方法,包括缓存驱动中没有定义但缓存驱动扩展对象中已定义的方法。

set

将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->set($key, $value);
//设置有过期时间的缓存$status = $cache->set($key, $value, $time);

setnx

当缓存中不存在 key 时,将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setnx($key, $value);
//设置有过期时间的缓存$status = $cache->setnx($key, $value, $time);

setDE

将参数中的 value设置为 key 的值,key 将自动延迟过期

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - key 的生存时间,单位是秒(s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setDE($key, $value);
//设置有过期时间的缓存$status = $cache->setDE($key, $value, $time);
//设置有过期时间的缓存并指定延迟过期时间$status = $cache->setDE($key, $value, $time, $delayTime);

get

获取 key 对应的值

参数

  • key - 字符串

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->get($key);

getDE

获取 key 对应的值,与 setDE 配合使用

参数

  • key - 字符串
  • isExpired - key 理论上是否已经过期,这是一个引用变量,不需要初始化

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->getDE($key, $isExpired);
//如果已经过期,处理其他逻辑if ($isExpired) {
//TODO
}

del

删除 key

参数

  • key - 字符串

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false注意:key 不存在时,也会返回 true

例子

<?php//删除key$status = $cache->del($key);

has

判断 key 是否存在

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->has($key);

hasDE

判断延迟过期的 key 理论上是否存在。设置自动延期的 key,过期时间实际会被修改,用方法 has 判断的话,可能 key 仍然存在,但是理论上已经过期了

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->hasDE($key);

ttl

获取 key 的生存时间(单位:s)

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取key的生存时间$ttl = $cache->ttl($key);

ttlDE

获取延迟过期的 key 理论上的生存时间(单位:s)。

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取延迟过期的 key 理论上的生存时间(单位:s)$ttl = $cache->ttlDE($key);

expire

设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//设置一个key的生存时间$status = $cache->expire($key, $time);

expireDE

以延迟过期的方式设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//以延迟过期的方式设置一个 key 的生存时间(单位:s)$status = $cache->expireDE($key, $time);
//自定义延迟过期时间$status = $cache->expireDE($key, $time, $delayTime);

expireAt

用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAt($key, $time);

expireAtDE

以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAtDE($key, $time);
//自定义延迟过期时间$status = $cache->expireAtDE($key, $time, $delayTime);

persist

删除一个 key 的生存时间,使其永不过期

参数

  • key - 字符串

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//删除一个key的生存时间,使其永不过期$status = $cache->persist($key);

lock

key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)

参数

  • key - 字符串
  • time - 整数,key锁标记 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果 key 已存在或设置失败,返回 false

例子

<?php//对key设置锁标记$status = $cache->lock($key, $time);

isLock

判断 key 是否有锁标记

参数

  • key - 字符串

返回值

Boolean - 如果有锁标记,返回 true; 如果没有锁标记或判断失败,返回 false

例子

<?php//判断key是否有锁标记$status = $cache->isLock($key);

unLock

移除 key 的锁标记

参数

  • key - 字符串

返回值

Boolean - 如果移除成功,返回 true; 如果失败,返回 false

例子

<?php//移除key的锁标记$status = $cache->unLock($key);

incr

设置 key 的值按整数递增

参数

  • key - 字符串
  • step - (可选参数) 整数,递增步长,默认值为 1,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 step 设置新值

例子

<?php//设置key的值按整数递增$value = $cache->incr($key, $step);

incrByFloat

设置 key 的值按浮点数递增

参数

  • key - 字符串
  • float - 浮点数,递增步长,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 float 设置新值

例子

<?php//设置key的值按浮点数递增$value = $cache->incrByFloat($key, $float);

decr

设置 key 的值按整数递减

参数

  • key - 字符串
  • step - (可选参数) 整数,递减步长,默认值为 1,可以为负值

返回值

Mixed - 递减后的值,失败返回 false,如果 key 不存在,则按 -step 设置新值

例子

<?php//设置key的值按整数递减$value = $cache->decr($key, $step);

mSet

批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//批量设置多个key对应的值$status = $cache->mSet($sets);

mSetNX

当缓存中不存在 key 时,批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//当缓存中不存在key时,批量设置多个key对应的值$status = $cache->mSetNX($sets);

mGet

获取所有给定 key 的值

参数

  • keys - 多个 key 组成的数组

返回值

array - 参数 keys 中的所有 key 与对应的 value 组成的数组,如果 key 不存在或是获取失败,对应的 value 值为 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//获取所有给定key的值$status = $cache->mGet($keys);

mHas

批量判断 key 是否存在

参数

  • keys - 多个 key 组成的数组

返回值

array - 返回存在的 key 的数组,如果判断失败返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量判断key是否存在$hasKeys = $cache->mHas($keys);

mDel

批量删除 key

参数

  • keys - 多个 key 组成的数组

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量删除key$status = $cache->mDel($keys);

如何防止惊群

什么是惊群

首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了缓存的高并发的系统下,如果缓存突然都消失了,会发生什么?首先数据库的压力必然骤增,接着负载迅速升高,响应时间变慢,进程/线程由于响应时间变慢导致集压并剧增,这就是惊群。惊群的后果就是系统不可用甚至宕机。当然这是一个非常极端的例子,但是能很好的解释惊群现象。真实环境中,可能会有个一些页面是访问非常频繁的,如果这些页面的缓存在同一时间或相近的时间突然过期了,在高并发的情况下也会产生惊群现象。

如何避免惊群

知道了什么是惊群,就不难防止惊群了。首先,避免缓存在同一时间过期;其次,如果缓存过期,不要让所有并发的进程/线程都去触发更新同一缓存数据。

例子

LinkCache 为此专门做了优化,使用 LinkCache 提供的一些特殊方法,可以有效的防止惊群现象。以下是简单的示例(伪代码):

<?php/* LinkCache专为防止惊群现象设计的方法包括: * setDE,getDE,hasDE,ttlDE,expireDE,expireAtDE,lock,isLock,unLock * 比较常用的是:setDE,getDE,lock *///设置缓存$cache->setDE($key,$value,$time);
//获取缓存$value = $cache->getDE($key,$isExpired);
//情况1,$value存在,$isExpired=false,不需要更新缓存if($value !== false){
//your code
}
//情况2,$value存在,$isExpired=true,需要更新缓存if($value !== false){
//your code
}
//更新缓存if($isExpired === true){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
}
//情况3,$value不存在,需要更新缓存if($value === false){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
//如果失败,说明已经有进程/线程对key加了锁标记,正在更新缓存else{
//如果并发大,建议此处做特殊处理,不要去更新缓存//特殊处理的代码
}
}

默认情况说明

  • Cache - \linkcache\Cache
  • drivers
    • files - \linkcache\drivers\Files
    • memcache - \linkcache\drivers\Memcache
    • memcached - \linkcache\drivers\Memcached
    • redis - \linkcache\drivers\Redis
    • ssdb - \linkcache\drivers\Ssdb
    • apc - \linkcache\drivers\Apc
    • yac - \linkcache\drivers\Yac

cache

  • 默认使用的缓存驱动: files
  • 默认使用的备用驱动: files
  • memcacheredisssdb 等缓存驱动的默认配置均参考官方默认配置
  • 自定义配置支持非驱动类型的key,但配置信息中需要有 driver_type 属性,否则会抛异常,示例如下:
<?phpuse \linkcache\Cache;
$config = [
'redis_m_db_1' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 1
],
'redis_m_db_2' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 2
],
'redis_s_db' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6381
]
];
Cache::setConfig($config);
//根据自定义配置实例化$redisM1 = newCache('redis_m_db_1');
$redisM2 = newCache('redis_m_db_2');
$redisS0 = newCache('redis_s_db');

drivers

  • 所有驱动默认使用备用缓存
  • 备用缓存优先使用实例化时构造函数传入的配置 config 中的 fallback 定义,如果没有定义,则使用 \linkcache\Cache::$config 中的 fallback 定义

files

  • 默认保存路径:优先使用上传文件临时目录,未定义则使用系统临时目录,并在目录下创建linkcache目录,作为 files 驱动的默认保存路径,代码如下: (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . '/linkcache'

memcache

  • memcache 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

memcached

  • memcached 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

redis

  • redis 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

ssdb

  • ssdb 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

apc

yac

  • 可自定义缓存 key 前缀,默认无 key 前缀

开发

如果你觉得LinkCache还不错,但又不支持你想用的缓存驱动,不妨尝试在LinkCache新增该缓存驱动的支持。

1.增加新的缓存驱动

目前有两种方式可以方便的开发新的缓存驱动支持

  • 继承 linkcache\abstracts\DriverSimple 抽象类

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\abstracts\DriverSimple;
      class Example extends DriverSimple {
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key * @param string $value * @return boolean */protectedfunctionsetOne($key, $value) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key * @return mixed */protectedfunctiongetOne($key) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key * @return boolean */protectedfunctiondelOne($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\abstracts\DriverSimple 抽象类中的抽象方法
  • 实现接口 linkcache\interfaces\driver\Base (Base接口是必须的,你也可以实现更多的接口:Incr,Lock,Multi)

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\interfaces\driver\Base;
      class Example implements Base {
      use \linkcache\traits\CacheDriver;
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionset($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 当键名不存在时设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionsetnx($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 设置键值,将自动延迟过期;<br> * 此方法用于缓存对过期要求宽松的数据;<br> * 使用此方法设置缓存配合getDE方法可以有效防止惊群现象发生 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,<=0则设置为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionsetDE($key, $value, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key 键名 * @return mixed|false 键值,失败返回false */publicfunctionget($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值,与setDE配合使用;<br> * 此方法用于获取setDE设置的缓存数据;<br> * 当isExpired为true时,说明key已经过期,需要更新;<br> * 更新数据时配合isLock和lock方法,防止惊群现象发生 * @param string $key 键名 * @param boolean $isExpired 是否已经过期 * @return mixed|false 键值,失败返回false */publicfunctiongetDE($key, &$isExpired = null) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key 键名 * @return boolean 是否成功 */publicfunctiondel($key) {
      //TODO 实现这个方法
      }
      /** * 是否存在键值 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhas($key) {
      //TODO 实现这个方法
      }
      /** * 判断延迟过期的键值理论上是否存在 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhasDE($key) {
      //TODO 实现这个方法
      }
      /** * 获取生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttl($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值理论生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttlDE($key) {
      //TODO 实现这个方法
      }
      /** * 设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @return boolean 是否成功 */publicfunctionexpire($key, $time) {
      //TODO 实现这个方法
      }
      /** * 以延迟过期的方式设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionexpireDE($key, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 移除指定键值的过期时间 * @param string $key 键名 * @return boolean 是否成功 */publicfunctionpersist($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\interfaces\driver\Base 接口中的方法

2.测试新增的驱动

为了保证代码的可靠性,不妨对新增的驱动做个测试吧。 我使用的是PHPUnit做的测试,版本:4.8.15

  • 测试步骤
    1. tests 目录新增测试类 TestDriverExample.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcacheTests;
      /** * TestDriverExample */class TestDriverExample extends TestDriverFiles
      {
      protected$cacheDriver = 'example';
      }
    2. 进入LinkCache项目目录,执行测试命令
      phpunit --bootstrap autoload_dev.php tests/TestDriverExample
      
      显示以下信息测试就通过啦~
      PHPUnit 4.8.15 by Sebastian Bergmann and contributors.
      ....................
      Time: 8.24 seconds, Memory: 8.75Mb
      OK (26 tests, 105 assertions)
      

LICENSE

使用非常灵活宽松的 New BSD License 协议

About

一个灵活高效的PHP缓存工具库

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

LinkCache - 一个灵活高效的PHP缓存工具库

LinkCache 是一个PHP编写的灵活高效的缓存工具库,提供多种缓存驱动支持,包括Memcache、Memcached、Redis、SSDB、文件缓存、APC、YAC等。通过LinkCache可以使不同缓存驱动实现操作统一,同时又可发挥不同缓存驱动各自的优势。LinkCache支持缓存 objectarray,同时为防止产生惊群现象做了优化。

环境要求

安装

composer 安装

LinkCache 可以通过 composer 安装,使用以下命令从 composer 下载安装 LinkCache

$ composer require dongnan/linkcache

手动下载安装

下载地址

安装方法

在你的入口文件中引入

<?php//引入 LinkCache 的自动加载文件include("path_to_linkcache/autoload.php");

如何使用

(提示:如果以下锚点定位的链接错误,导致页面找不到等问题,请访问 https://github.com/dongnan/LinkCache 查看文档)

  • config - 配置信息
  • instance - 缓存实例化
  • getDriver - 获取缓存驱动实例
  • set - 将参数中的 value设置为 key 的值
  • setnx - 当缓存中不存在 key 时,将参数中的 value 设置为 key 的值
  • setDE - 将参数中的 value设置为 key 的值,key 将自动延迟过期
  • get - 获取 key 对应的值
  • getDE - 获取 key 对应的值,与 setDE 配合使用
  • del - 删除 key
  • has - 判断 key 是否存在
  • hasDE - 判断延迟过期的 key 理论上是否存在
  • ttl - 获取 key 的生存时间(单位:s)
  • ttlDE - 获取延迟过期的 key 理论上的生存时间(单位:s)
  • expire - 设置一个 key 的生存时间(单位:s)
  • expireDE - 以延迟过期的方式设置一个 key 的生存时间(单位:s)
  • expireAt - 用UNIX时间戳设置一个 key 的过期时间
  • expireAtDE - 以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间
  • persist - 删除一个 key 的生存时间,使其永不过期
  • lock - 对 key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)
  • isLock - 判断 key 是否有锁标记
  • unLock - 移除 key 的锁标记
  • incr - 设置 key 的值按整数递增
  • incrByFloat - 设置 key 的值按浮点数递增
  • decr - 设置 key 的值按整数递减
  • mSet - 批量设置多个 key 对应的值
  • mSetNX - 当缓存中不存在 key 时,批量设置多个 key 对应的值
  • mGet - 获取所有给定 key 的值
  • mHas - 批量判断 key 是否存在
  • mDel - 批量删除 key

config

配置信息

<?php//设置缓存配置, 使用 array_merge 的方式合并到默认配置中
\linkcache\Cache::setConfig($config);
//获取配置信息$config = \linkcache\Cache::getConfig();
//获取指定缓存驱动的配置信息$config = \linkcache\Cache::getConfig('redis');
//默认配置信息
[
//默认使用的缓存驱动'default' => 'files',
//当前缓存驱动失效时,使用的备份驱动'fallback' => 'files',
'memcache' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1, 'persistent' => true, 'timeout' => 1, 'retry_interval' => 15, 'status' => true],
],
'compress' => ['threshold' => 2000, 'min_saving' => 0.2],
],
'memcached' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1],
],
//参考 Memcached::setOptions'options' => [],
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'database' => '',
'timeout' => ''
],
'ssdb' => [
'host' => '127.0.0.1',
'port' => 8888,
'password' => '',
'timeoutms' => ''
],
]

instance

实例化缓存对象

<?php//直接new一个$cache = new \linkcache\Cache();
//通过getInstance获取$cache = \linkcache\Cache::getInstance();
//根据驱动类型实例化,支持的驱动:redis,memcache,memcached,ssdb,files,apc,yac$cache = new \linkcache\Cache('files');
//或者$cache = \linkcache\Cache::getInstance('files');
//传入配置参数实例化$cache = new \linkcache\Cache('redis', ['host' => '127.0.0.1', 'port' => 6379]);

getDriver

获取缓存驱动实例

所有缓存驱动都必须实现 linkcache\interfaces\driver\Base 接口的方法,获取到缓存驱动实例后,可直接使用缓存驱动的方法,包括缓存驱动中没有定义但缓存驱动扩展对象中已定义的方法。

set

将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->set($key, $value);
//设置有过期时间的缓存$status = $cache->set($key, $value, $time);

setnx

当缓存中不存在 key 时,将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setnx($key, $value);
//设置有过期时间的缓存$status = $cache->setnx($key, $value, $time);

setDE

将参数中的 value设置为 key 的值,key 将自动延迟过期

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - key 的生存时间,单位是秒(s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setDE($key, $value);
//设置有过期时间的缓存$status = $cache->setDE($key, $value, $time);
//设置有过期时间的缓存并指定延迟过期时间$status = $cache->setDE($key, $value, $time, $delayTime);

get

获取 key 对应的值

参数

  • key - 字符串

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->get($key);

getDE

获取 key 对应的值,与 setDE 配合使用

参数

  • key - 字符串
  • isExpired - key 理论上是否已经过期,这是一个引用变量,不需要初始化

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->getDE($key, $isExpired);
//如果已经过期,处理其他逻辑if ($isExpired) {
//TODO
}

del

删除 key

参数

  • key - 字符串

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false注意:key 不存在时,也会返回 true

例子

<?php//删除key$status = $cache->del($key);

has

判断 key 是否存在

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->has($key);

hasDE

判断延迟过期的 key 理论上是否存在。设置自动延期的 key,过期时间实际会被修改,用方法 has 判断的话,可能 key 仍然存在,但是理论上已经过期了

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->hasDE($key);

ttl

获取 key 的生存时间(单位:s)

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取key的生存时间$ttl = $cache->ttl($key);

ttlDE

获取延迟过期的 key 理论上的生存时间(单位:s)。

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取延迟过期的 key 理论上的生存时间(单位:s)$ttl = $cache->ttlDE($key);

expire

设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//设置一个key的生存时间$status = $cache->expire($key, $time);

expireDE

以延迟过期的方式设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//以延迟过期的方式设置一个 key 的生存时间(单位:s)$status = $cache->expireDE($key, $time);
//自定义延迟过期时间$status = $cache->expireDE($key, $time, $delayTime);

expireAt

用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAt($key, $time);

expireAtDE

以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAtDE($key, $time);
//自定义延迟过期时间$status = $cache->expireAtDE($key, $time, $delayTime);

persist

删除一个 key 的生存时间,使其永不过期

参数

  • key - 字符串

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//删除一个key的生存时间,使其永不过期$status = $cache->persist($key);

lock

key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)

参数

  • key - 字符串
  • time - 整数,key锁标记 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果 key 已存在或设置失败,返回 false

例子

<?php//对key设置锁标记$status = $cache->lock($key, $time);

isLock

判断 key 是否有锁标记

参数

  • key - 字符串

返回值

Boolean - 如果有锁标记,返回 true; 如果没有锁标记或判断失败,返回 false

例子

<?php//判断key是否有锁标记$status = $cache->isLock($key);

unLock

移除 key 的锁标记

参数

  • key - 字符串

返回值

Boolean - 如果移除成功,返回 true; 如果失败,返回 false

例子

<?php//移除key的锁标记$status = $cache->unLock($key);

incr

设置 key 的值按整数递增

参数

  • key - 字符串
  • step - (可选参数) 整数,递增步长,默认值为 1,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 step 设置新值

例子

<?php//设置key的值按整数递增$value = $cache->incr($key, $step);

incrByFloat

设置 key 的值按浮点数递增

参数

  • key - 字符串
  • float - 浮点数,递增步长,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 float 设置新值

例子

<?php//设置key的值按浮点数递增$value = $cache->incrByFloat($key, $float);

decr

设置 key 的值按整数递减

参数

  • key - 字符串
  • step - (可选参数) 整数,递减步长,默认值为 1,可以为负值

返回值

Mixed - 递减后的值,失败返回 false,如果 key 不存在,则按 -step 设置新值

例子

<?php//设置key的值按整数递减$value = $cache->decr($key, $step);

mSet

批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//批量设置多个key对应的值$status = $cache->mSet($sets);

mSetNX

当缓存中不存在 key 时,批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//当缓存中不存在key时,批量设置多个key对应的值$status = $cache->mSetNX($sets);

mGet

获取所有给定 key 的值

参数

  • keys - 多个 key 组成的数组

返回值

array - 参数 keys 中的所有 key 与对应的 value 组成的数组,如果 key 不存在或是获取失败,对应的 value 值为 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//获取所有给定key的值$status = $cache->mGet($keys);

mHas

批量判断 key 是否存在

参数

  • keys - 多个 key 组成的数组

返回值

array - 返回存在的 key 的数组,如果判断失败返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量判断key是否存在$hasKeys = $cache->mHas($keys);

mDel

批量删除 key

参数

  • keys - 多个 key 组成的数组

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量删除key$status = $cache->mDel($keys);

如何防止惊群

什么是惊群

首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了缓存的高并发的系统下,如果缓存突然都消失了,会发生什么?首先数据库的压力必然骤增,接着负载迅速升高,响应时间变慢,进程/线程由于响应时间变慢导致集压并剧增,这就是惊群。惊群的后果就是系统不可用甚至宕机。当然这是一个非常极端的例子,但是能很好的解释惊群现象。真实环境中,可能会有个一些页面是访问非常频繁的,如果这些页面的缓存在同一时间或相近的时间突然过期了,在高并发的情况下也会产生惊群现象。

如何避免惊群

知道了什么是惊群,就不难防止惊群了。首先,避免缓存在同一时间过期;其次,如果缓存过期,不要让所有并发的进程/线程都去触发更新同一缓存数据。

例子

LinkCache 为此专门做了优化,使用 LinkCache 提供的一些特殊方法,可以有效的防止惊群现象。以下是简单的示例(伪代码):

<?php/* LinkCache专为防止惊群现象设计的方法包括: * setDE,getDE,hasDE,ttlDE,expireDE,expireAtDE,lock,isLock,unLock * 比较常用的是:setDE,getDE,lock *///设置缓存$cache->setDE($key,$value,$time);
//获取缓存$value = $cache->getDE($key,$isExpired);
//情况1,$value存在,$isExpired=false,不需要更新缓存if($value !== false){
//your code
}
//情况2,$value存在,$isExpired=true,需要更新缓存if($value !== false){
//your code
}
//更新缓存if($isExpired === true){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
}
//情况3,$value不存在,需要更新缓存if($value === false){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
//如果失败,说明已经有进程/线程对key加了锁标记,正在更新缓存else{
//如果并发大,建议此处做特殊处理,不要去更新缓存//特殊处理的代码
}
}

默认情况说明

  • Cache - \linkcache\Cache
  • drivers
    • files - \linkcache\drivers\Files
    • memcache - \linkcache\drivers\Memcache
    • memcached - \linkcache\drivers\Memcached
    • redis - \linkcache\drivers\Redis
    • ssdb - \linkcache\drivers\Ssdb
    • apc - \linkcache\drivers\Apc
    • yac - \linkcache\drivers\Yac

cache

  • 默认使用的缓存驱动: files
  • 默认使用的备用驱动: files
  • memcacheredisssdb 等缓存驱动的默认配置均参考官方默认配置
  • 自定义配置支持非驱动类型的key,但配置信息中需要有 driver_type 属性,否则会抛异常,示例如下:
<?phpuse \linkcache\Cache;
$config = [
'redis_m_db_1' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 1
],
'redis_m_db_2' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 2
],
'redis_s_db' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6381
]
];
Cache::setConfig($config);
//根据自定义配置实例化$redisM1 = newCache('redis_m_db_1');
$redisM2 = newCache('redis_m_db_2');
$redisS0 = newCache('redis_s_db');

drivers

  • 所有驱动默认使用备用缓存
  • 备用缓存优先使用实例化时构造函数传入的配置 config 中的 fallback 定义,如果没有定义,则使用 \linkcache\Cache::$config 中的 fallback 定义

files

  • 默认保存路径:优先使用上传文件临时目录,未定义则使用系统临时目录,并在目录下创建linkcache目录,作为 files 驱动的默认保存路径,代码如下: (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . '/linkcache'

memcache

  • memcache 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

memcached

  • memcached 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

redis

  • redis 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

ssdb

  • ssdb 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

apc

yac

  • 可自定义缓存 key 前缀,默认无 key 前缀

开发

如果你觉得LinkCache还不错,但又不支持你想用的缓存驱动,不妨尝试在LinkCache新增该缓存驱动的支持。

1.增加新的缓存驱动

目前有两种方式可以方便的开发新的缓存驱动支持

  • 继承 linkcache\abstracts\DriverSimple 抽象类

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\abstracts\DriverSimple;
      class Example extends DriverSimple {
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key * @param string $value * @return boolean */protectedfunctionsetOne($key, $value) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key * @return mixed */protectedfunctiongetOne($key) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key * @return boolean */protectedfunctiondelOne($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\abstracts\DriverSimple 抽象类中的抽象方法
  • 实现接口 linkcache\interfaces\driver\Base (Base接口是必须的,你也可以实现更多的接口:Incr,Lock,Multi)

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\interfaces\driver\Base;
      class Example implements Base {
      use \linkcache\traits\CacheDriver;
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionset($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 当键名不存在时设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionsetnx($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 设置键值,将自动延迟过期;<br> * 此方法用于缓存对过期要求宽松的数据;<br> * 使用此方法设置缓存配合getDE方法可以有效防止惊群现象发生 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,<=0则设置为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionsetDE($key, $value, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key 键名 * @return mixed|false 键值,失败返回false */publicfunctionget($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值,与setDE配合使用;<br> * 此方法用于获取setDE设置的缓存数据;<br> * 当isExpired为true时,说明key已经过期,需要更新;<br> * 更新数据时配合isLock和lock方法,防止惊群现象发生 * @param string $key 键名 * @param boolean $isExpired 是否已经过期 * @return mixed|false 键值,失败返回false */publicfunctiongetDE($key, &$isExpired = null) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key 键名 * @return boolean 是否成功 */publicfunctiondel($key) {
      //TODO 实现这个方法
      }
      /** * 是否存在键值 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhas($key) {
      //TODO 实现这个方法
      }
      /** * 判断延迟过期的键值理论上是否存在 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhasDE($key) {
      //TODO 实现这个方法
      }
      /** * 获取生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttl($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值理论生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttlDE($key) {
      //TODO 实现这个方法
      }
      /** * 设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @return boolean 是否成功 */publicfunctionexpire($key, $time) {
      //TODO 实现这个方法
      }
      /** * 以延迟过期的方式设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionexpireDE($key, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 移除指定键值的过期时间 * @param string $key 键名 * @return boolean 是否成功 */publicfunctionpersist($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\interfaces\driver\Base 接口中的方法

2.测试新增的驱动

为了保证代码的可靠性,不妨对新增的驱动做个测试吧。 我使用的是PHPUnit做的测试,版本:4.8.15

  • 测试步骤
    1. tests 目录新增测试类 TestDriverExample.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcacheTests;
      /** * TestDriverExample */class TestDriverExample extends TestDriverFiles
      {
      protected$cacheDriver = 'example';
      }
    2. 进入LinkCache项目目录,执行测试命令
      phpunit --bootstrap autoload_dev.php tests/TestDriverExample
      
      显示以下信息测试就通过啦~
      PHPUnit 4.8.15 by Sebastian Bergmann and contributors.
      ....................
      Time: 8.24 seconds, Memory: 8.75Mb
      OK (26 tests, 105 assertions)
      

LICENSE

使用非常灵活宽松的 New BSD License 协议

About

一个灵活高效的PHP缓存工具库

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

LinkCache - 一个灵活高效的PHP缓存工具库

LinkCache 是一个PHP编写的灵活高效的缓存工具库,提供多种缓存驱动支持,包括Memcache、Memcached、Redis、SSDB、文件缓存、APC、YAC等。通过LinkCache可以使不同缓存驱动实现操作统一,同时又可发挥不同缓存驱动各自的优势。LinkCache支持缓存 objectarray,同时为防止产生惊群现象做了优化。

环境要求

安装

composer 安装

LinkCache 可以通过 composer 安装,使用以下命令从 composer 下载安装 LinkCache

$ composer require dongnan/linkcache

手动下载安装

下载地址

安装方法

在你的入口文件中引入

<?php//引入 LinkCache 的自动加载文件include("path_to_linkcache/autoload.php");

如何使用

(提示:如果以下锚点定位的链接错误,导致页面找不到等问题,请访问 https://github.com/dongnan/LinkCache 查看文档)

  • config - 配置信息
  • instance - 缓存实例化
  • getDriver - 获取缓存驱动实例
  • set - 将参数中的 value设置为 key 的值
  • setnx - 当缓存中不存在 key 时,将参数中的 value 设置为 key 的值
  • setDE - 将参数中的 value设置为 key 的值,key 将自动延迟过期
  • get - 获取 key 对应的值
  • getDE - 获取 key 对应的值,与 setDE 配合使用
  • del - 删除 key
  • has - 判断 key 是否存在
  • hasDE - 判断延迟过期的 key 理论上是否存在
  • ttl - 获取 key 的生存时间(单位:s)
  • ttlDE - 获取延迟过期的 key 理论上的生存时间(单位:s)
  • expire - 设置一个 key 的生存时间(单位:s)
  • expireDE - 以延迟过期的方式设置一个 key 的生存时间(单位:s)
  • expireAt - 用UNIX时间戳设置一个 key 的过期时间
  • expireAtDE - 以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间
  • persist - 删除一个 key 的生存时间,使其永不过期
  • lock - 对 key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)
  • isLock - 判断 key 是否有锁标记
  • unLock - 移除 key 的锁标记
  • incr - 设置 key 的值按整数递增
  • incrByFloat - 设置 key 的值按浮点数递增
  • decr - 设置 key 的值按整数递减
  • mSet - 批量设置多个 key 对应的值
  • mSetNX - 当缓存中不存在 key 时,批量设置多个 key 对应的值
  • mGet - 获取所有给定 key 的值
  • mHas - 批量判断 key 是否存在
  • mDel - 批量删除 key

config

配置信息

<?php//设置缓存配置, 使用 array_merge 的方式合并到默认配置中
\linkcache\Cache::setConfig($config);
//获取配置信息$config = \linkcache\Cache::getConfig();
//获取指定缓存驱动的配置信息$config = \linkcache\Cache::getConfig('redis');
//默认配置信息
[
//默认使用的缓存驱动'default' => 'files',
//当前缓存驱动失效时,使用的备份驱动'fallback' => 'files',
'memcache' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1, 'persistent' => true, 'timeout' => 1, 'retry_interval' => 15, 'status' => true],
],
'compress' => ['threshold' => 2000, 'min_saving' => 0.2],
],
'memcached' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1],
],
//参考 Memcached::setOptions'options' => [],
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'database' => '',
'timeout' => ''
],
'ssdb' => [
'host' => '127.0.0.1',
'port' => 8888,
'password' => '',
'timeoutms' => ''
],
]

instance

实例化缓存对象

<?php//直接new一个$cache = new \linkcache\Cache();
//通过getInstance获取$cache = \linkcache\Cache::getInstance();
//根据驱动类型实例化,支持的驱动:redis,memcache,memcached,ssdb,files,apc,yac$cache = new \linkcache\Cache('files');
//或者$cache = \linkcache\Cache::getInstance('files');
//传入配置参数实例化$cache = new \linkcache\Cache('redis', ['host' => '127.0.0.1', 'port' => 6379]);

getDriver

获取缓存驱动实例

所有缓存驱动都必须实现 linkcache\interfaces\driver\Base 接口的方法,获取到缓存驱动实例后,可直接使用缓存驱动的方法,包括缓存驱动中没有定义但缓存驱动扩展对象中已定义的方法。

set

将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->set($key, $value);
//设置有过期时间的缓存$status = $cache->set($key, $value, $time);

setnx

当缓存中不存在 key 时,将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setnx($key, $value);
//设置有过期时间的缓存$status = $cache->setnx($key, $value, $time);

setDE

将参数中的 value设置为 key 的值,key 将自动延迟过期

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - key 的生存时间,单位是秒(s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setDE($key, $value);
//设置有过期时间的缓存$status = $cache->setDE($key, $value, $time);
//设置有过期时间的缓存并指定延迟过期时间$status = $cache->setDE($key, $value, $time, $delayTime);

get

获取 key 对应的值

参数

  • key - 字符串

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->get($key);

getDE

获取 key 对应的值,与 setDE 配合使用

参数

  • key - 字符串
  • isExpired - key 理论上是否已经过期,这是一个引用变量,不需要初始化

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->getDE($key, $isExpired);
//如果已经过期,处理其他逻辑if ($isExpired) {
//TODO
}

del

删除 key

参数

  • key - 字符串

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false注意:key 不存在时,也会返回 true

例子

<?php//删除key$status = $cache->del($key);

has

判断 key 是否存在

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->has($key);

hasDE

判断延迟过期的 key 理论上是否存在。设置自动延期的 key,过期时间实际会被修改,用方法 has 判断的话,可能 key 仍然存在,但是理论上已经过期了

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->hasDE($key);

ttl

获取 key 的生存时间(单位:s)

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取key的生存时间$ttl = $cache->ttl($key);

ttlDE

获取延迟过期的 key 理论上的生存时间(单位:s)。

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取延迟过期的 key 理论上的生存时间(单位:s)$ttl = $cache->ttlDE($key);

expire

设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//设置一个key的生存时间$status = $cache->expire($key, $time);

expireDE

以延迟过期的方式设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//以延迟过期的方式设置一个 key 的生存时间(单位:s)$status = $cache->expireDE($key, $time);
//自定义延迟过期时间$status = $cache->expireDE($key, $time, $delayTime);

expireAt

用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAt($key, $time);

expireAtDE

以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAtDE($key, $time);
//自定义延迟过期时间$status = $cache->expireAtDE($key, $time, $delayTime);

persist

删除一个 key 的生存时间,使其永不过期

参数

  • key - 字符串

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//删除一个key的生存时间,使其永不过期$status = $cache->persist($key);

lock

key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)

参数

  • key - 字符串
  • time - 整数,key锁标记 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果 key 已存在或设置失败,返回 false

例子

<?php//对key设置锁标记$status = $cache->lock($key, $time);

isLock

判断 key 是否有锁标记

参数

  • key - 字符串

返回值

Boolean - 如果有锁标记,返回 true; 如果没有锁标记或判断失败,返回 false

例子

<?php//判断key是否有锁标记$status = $cache->isLock($key);

unLock

移除 key 的锁标记

参数

  • key - 字符串

返回值

Boolean - 如果移除成功,返回 true; 如果失败,返回 false

例子

<?php//移除key的锁标记$status = $cache->unLock($key);

incr

设置 key 的值按整数递增

参数

  • key - 字符串
  • step - (可选参数) 整数,递增步长,默认值为 1,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 step 设置新值

例子

<?php//设置key的值按整数递增$value = $cache->incr($key, $step);

incrByFloat

设置 key 的值按浮点数递增

参数

  • key - 字符串
  • float - 浮点数,递增步长,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 float 设置新值

例子

<?php//设置key的值按浮点数递增$value = $cache->incrByFloat($key, $float);

decr

设置 key 的值按整数递减

参数

  • key - 字符串
  • step - (可选参数) 整数,递减步长,默认值为 1,可以为负值

返回值

Mixed - 递减后的值,失败返回 false,如果 key 不存在,则按 -step 设置新值

例子

<?php//设置key的值按整数递减$value = $cache->decr($key, $step);

mSet

批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//批量设置多个key对应的值$status = $cache->mSet($sets);

mSetNX

当缓存中不存在 key 时,批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//当缓存中不存在key时,批量设置多个key对应的值$status = $cache->mSetNX($sets);

mGet

获取所有给定 key 的值

参数

  • keys - 多个 key 组成的数组

返回值

array - 参数 keys 中的所有 key 与对应的 value 组成的数组,如果 key 不存在或是获取失败,对应的 value 值为 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//获取所有给定key的值$status = $cache->mGet($keys);

mHas

批量判断 key 是否存在

参数

  • keys - 多个 key 组成的数组

返回值

array - 返回存在的 key 的数组,如果判断失败返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量判断key是否存在$hasKeys = $cache->mHas($keys);

mDel

批量删除 key

参数

  • keys - 多个 key 组成的数组

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量删除key$status = $cache->mDel($keys);

如何防止惊群

什么是惊群

首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了缓存的高并发的系统下,如果缓存突然都消失了,会发生什么?首先数据库的压力必然骤增,接着负载迅速升高,响应时间变慢,进程/线程由于响应时间变慢导致集压并剧增,这就是惊群。惊群的后果就是系统不可用甚至宕机。当然这是一个非常极端的例子,但是能很好的解释惊群现象。真实环境中,可能会有个一些页面是访问非常频繁的,如果这些页面的缓存在同一时间或相近的时间突然过期了,在高并发的情况下也会产生惊群现象。

如何避免惊群

知道了什么是惊群,就不难防止惊群了。首先,避免缓存在同一时间过期;其次,如果缓存过期,不要让所有并发的进程/线程都去触发更新同一缓存数据。

例子

LinkCache 为此专门做了优化,使用 LinkCache 提供的一些特殊方法,可以有效的防止惊群现象。以下是简单的示例(伪代码):

<?php/* LinkCache专为防止惊群现象设计的方法包括: * setDE,getDE,hasDE,ttlDE,expireDE,expireAtDE,lock,isLock,unLock * 比较常用的是:setDE,getDE,lock *///设置缓存$cache->setDE($key,$value,$time);
//获取缓存$value = $cache->getDE($key,$isExpired);
//情况1,$value存在,$isExpired=false,不需要更新缓存if($value !== false){
//your code
}
//情况2,$value存在,$isExpired=true,需要更新缓存if($value !== false){
//your code
}
//更新缓存if($isExpired === true){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
}
//情况3,$value不存在,需要更新缓存if($value === false){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
//如果失败,说明已经有进程/线程对key加了锁标记,正在更新缓存else{
//如果并发大,建议此处做特殊处理,不要去更新缓存//特殊处理的代码
}
}

默认情况说明

  • Cache - \linkcache\Cache
  • drivers
    • files - \linkcache\drivers\Files
    • memcache - \linkcache\drivers\Memcache
    • memcached - \linkcache\drivers\Memcached
    • redis - \linkcache\drivers\Redis
    • ssdb - \linkcache\drivers\Ssdb
    • apc - \linkcache\drivers\Apc
    • yac - \linkcache\drivers\Yac

cache

  • 默认使用的缓存驱动: files
  • 默认使用的备用驱动: files
  • memcacheredisssdb 等缓存驱动的默认配置均参考官方默认配置
  • 自定义配置支持非驱动类型的key,但配置信息中需要有 driver_type 属性,否则会抛异常,示例如下:
<?phpuse \linkcache\Cache;
$config = [
'redis_m_db_1' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 1
],
'redis_m_db_2' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 2
],
'redis_s_db' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6381
]
];
Cache::setConfig($config);
//根据自定义配置实例化$redisM1 = newCache('redis_m_db_1');
$redisM2 = newCache('redis_m_db_2');
$redisS0 = newCache('redis_s_db');

drivers

  • 所有驱动默认使用备用缓存
  • 备用缓存优先使用实例化时构造函数传入的配置 config 中的 fallback 定义,如果没有定义,则使用 \linkcache\Cache::$config 中的 fallback 定义

files

  • 默认保存路径:优先使用上传文件临时目录,未定义则使用系统临时目录,并在目录下创建linkcache目录,作为 files 驱动的默认保存路径,代码如下: (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . '/linkcache'

memcache

  • memcache 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

memcached

  • memcached 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

redis

  • redis 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

ssdb

  • ssdb 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

apc

yac

  • 可自定义缓存 key 前缀,默认无 key 前缀

开发

如果你觉得LinkCache还不错,但又不支持你想用的缓存驱动,不妨尝试在LinkCache新增该缓存驱动的支持。

1.增加新的缓存驱动

目前有两种方式可以方便的开发新的缓存驱动支持

  • 继承 linkcache\abstracts\DriverSimple 抽象类

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\abstracts\DriverSimple;
      class Example extends DriverSimple {
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key * @param string $value * @return boolean */protectedfunctionsetOne($key, $value) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key * @return mixed */protectedfunctiongetOne($key) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key * @return boolean */protectedfunctiondelOne($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\abstracts\DriverSimple 抽象类中的抽象方法
  • 实现接口 linkcache\interfaces\driver\Base (Base接口是必须的,你也可以实现更多的接口:Incr,Lock,Multi)

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\interfaces\driver\Base;
      class Example implements Base {
      use \linkcache\traits\CacheDriver;
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionset($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 当键名不存在时设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionsetnx($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 设置键值,将自动延迟过期;<br> * 此方法用于缓存对过期要求宽松的数据;<br> * 使用此方法设置缓存配合getDE方法可以有效防止惊群现象发生 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,<=0则设置为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionsetDE($key, $value, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key 键名 * @return mixed|false 键值,失败返回false */publicfunctionget($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值,与setDE配合使用;<br> * 此方法用于获取setDE设置的缓存数据;<br> * 当isExpired为true时,说明key已经过期,需要更新;<br> * 更新数据时配合isLock和lock方法,防止惊群现象发生 * @param string $key 键名 * @param boolean $isExpired 是否已经过期 * @return mixed|false 键值,失败返回false */publicfunctiongetDE($key, &$isExpired = null) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key 键名 * @return boolean 是否成功 */publicfunctiondel($key) {
      //TODO 实现这个方法
      }
      /** * 是否存在键值 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhas($key) {
      //TODO 实现这个方法
      }
      /** * 判断延迟过期的键值理论上是否存在 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhasDE($key) {
      //TODO 实现这个方法
      }
      /** * 获取生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttl($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值理论生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttlDE($key) {
      //TODO 实现这个方法
      }
      /** * 设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @return boolean 是否成功 */publicfunctionexpire($key, $time) {
      //TODO 实现这个方法
      }
      /** * 以延迟过期的方式设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionexpireDE($key, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 移除指定键值的过期时间 * @param string $key 键名 * @return boolean 是否成功 */publicfunctionpersist($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\interfaces\driver\Base 接口中的方法

2.测试新增的驱动

为了保证代码的可靠性,不妨对新增的驱动做个测试吧。 我使用的是PHPUnit做的测试,版本:4.8.15

  • 测试步骤
    1. tests 目录新增测试类 TestDriverExample.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcacheTests;
      /** * TestDriverExample */class TestDriverExample extends TestDriverFiles
      {
      protected$cacheDriver = 'example';
      }
    2. 进入LinkCache项目目录,执行测试命令
      phpunit --bootstrap autoload_dev.php tests/TestDriverExample
      
      显示以下信息测试就通过啦~
      PHPUnit 4.8.15 by Sebastian Bergmann and contributors.
      ....................
      Time: 8.24 seconds, Memory: 8.75Mb
      OK (26 tests, 105 assertions)
      

LICENSE

使用非常灵活宽松的 New BSD License 协议

About

一个灵活高效的PHP缓存工具库

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

LinkCache - 一个灵活高效的PHP缓存工具库

LinkCache 是一个PHP编写的灵活高效的缓存工具库,提供多种缓存驱动支持,包括Memcache、Memcached、Redis、SSDB、文件缓存、APC、YAC等。通过LinkCache可以使不同缓存驱动实现操作统一,同时又可发挥不同缓存驱动各自的优势。LinkCache支持缓存 objectarray,同时为防止产生惊群现象做了优化。

环境要求

安装

composer 安装

LinkCache 可以通过 composer 安装,使用以下命令从 composer 下载安装 LinkCache

$ composer require dongnan/linkcache

手动下载安装

下载地址

安装方法

在你的入口文件中引入

<?php//引入 LinkCache 的自动加载文件include("path_to_linkcache/autoload.php");

如何使用

(提示:如果以下锚点定位的链接错误,导致页面找不到等问题,请访问 https://github.com/dongnan/LinkCache 查看文档)

  • config - 配置信息
  • instance - 缓存实例化
  • getDriver - 获取缓存驱动实例
  • set - 将参数中的 value设置为 key 的值
  • setnx - 当缓存中不存在 key 时,将参数中的 value 设置为 key 的值
  • setDE - 将参数中的 value设置为 key 的值,key 将自动延迟过期
  • get - 获取 key 对应的值
  • getDE - 获取 key 对应的值,与 setDE 配合使用
  • del - 删除 key
  • has - 判断 key 是否存在
  • hasDE - 判断延迟过期的 key 理论上是否存在
  • ttl - 获取 key 的生存时间(单位:s)
  • ttlDE - 获取延迟过期的 key 理论上的生存时间(单位:s)
  • expire - 设置一个 key 的生存时间(单位:s)
  • expireDE - 以延迟过期的方式设置一个 key 的生存时间(单位:s)
  • expireAt - 用UNIX时间戳设置一个 key 的过期时间
  • expireAtDE - 以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间
  • persist - 删除一个 key 的生存时间,使其永不过期
  • lock - 对 key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)
  • isLock - 判断 key 是否有锁标记
  • unLock - 移除 key 的锁标记
  • incr - 设置 key 的值按整数递增
  • incrByFloat - 设置 key 的值按浮点数递增
  • decr - 设置 key 的值按整数递减
  • mSet - 批量设置多个 key 对应的值
  • mSetNX - 当缓存中不存在 key 时,批量设置多个 key 对应的值
  • mGet - 获取所有给定 key 的值
  • mHas - 批量判断 key 是否存在
  • mDel - 批量删除 key

config

配置信息

<?php//设置缓存配置, 使用 array_merge 的方式合并到默认配置中
\linkcache\Cache::setConfig($config);
//获取配置信息$config = \linkcache\Cache::getConfig();
//获取指定缓存驱动的配置信息$config = \linkcache\Cache::getConfig('redis');
//默认配置信息
[
//默认使用的缓存驱动'default' => 'files',
//当前缓存驱动失效时,使用的备份驱动'fallback' => 'files',
'memcache' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1, 'persistent' => true, 'timeout' => 1, 'retry_interval' => 15, 'status' => true],
],
'compress' => ['threshold' => 2000, 'min_saving' => 0.2],
],
'memcached' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1],
],
//参考 Memcached::setOptions'options' => [],
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'database' => '',
'timeout' => ''
],
'ssdb' => [
'host' => '127.0.0.1',
'port' => 8888,
'password' => '',
'timeoutms' => ''
],
]

instance

实例化缓存对象

<?php//直接new一个$cache = new \linkcache\Cache();
//通过getInstance获取$cache = \linkcache\Cache::getInstance();
//根据驱动类型实例化,支持的驱动:redis,memcache,memcached,ssdb,files,apc,yac$cache = new \linkcache\Cache('files');
//或者$cache = \linkcache\Cache::getInstance('files');
//传入配置参数实例化$cache = new \linkcache\Cache('redis', ['host' => '127.0.0.1', 'port' => 6379]);

getDriver

获取缓存驱动实例

所有缓存驱动都必须实现 linkcache\interfaces\driver\Base 接口的方法,获取到缓存驱动实例后,可直接使用缓存驱动的方法,包括缓存驱动中没有定义但缓存驱动扩展对象中已定义的方法。

set

将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->set($key, $value);
//设置有过期时间的缓存$status = $cache->set($key, $value, $time);

setnx

当缓存中不存在 key 时,将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setnx($key, $value);
//设置有过期时间的缓存$status = $cache->setnx($key, $value, $time);

setDE

将参数中的 value设置为 key 的值,key 将自动延迟过期

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - key 的生存时间,单位是秒(s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setDE($key, $value);
//设置有过期时间的缓存$status = $cache->setDE($key, $value, $time);
//设置有过期时间的缓存并指定延迟过期时间$status = $cache->setDE($key, $value, $time, $delayTime);

get

获取 key 对应的值

参数

  • key - 字符串

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->get($key);

getDE

获取 key 对应的值,与 setDE 配合使用

参数

  • key - 字符串
  • isExpired - key 理论上是否已经过期,这是一个引用变量,不需要初始化

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->getDE($key, $isExpired);
//如果已经过期,处理其他逻辑if ($isExpired) {
//TODO
}

del

删除 key

参数

  • key - 字符串

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false注意:key 不存在时,也会返回 true

例子

<?php//删除key$status = $cache->del($key);

has

判断 key 是否存在

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->has($key);

hasDE

判断延迟过期的 key 理论上是否存在。设置自动延期的 key,过期时间实际会被修改,用方法 has 判断的话,可能 key 仍然存在,但是理论上已经过期了

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->hasDE($key);

ttl

获取 key 的生存时间(单位:s)

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取key的生存时间$ttl = $cache->ttl($key);

ttlDE

获取延迟过期的 key 理论上的生存时间(单位:s)。

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取延迟过期的 key 理论上的生存时间(单位:s)$ttl = $cache->ttlDE($key);

expire

设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//设置一个key的生存时间$status = $cache->expire($key, $time);

expireDE

以延迟过期的方式设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//以延迟过期的方式设置一个 key 的生存时间(单位:s)$status = $cache->expireDE($key, $time);
//自定义延迟过期时间$status = $cache->expireDE($key, $time, $delayTime);

expireAt

用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAt($key, $time);

expireAtDE

以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAtDE($key, $time);
//自定义延迟过期时间$status = $cache->expireAtDE($key, $time, $delayTime);

persist

删除一个 key 的生存时间,使其永不过期

参数

  • key - 字符串

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//删除一个key的生存时间,使其永不过期$status = $cache->persist($key);

lock

key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)

参数

  • key - 字符串
  • time - 整数,key锁标记 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果 key 已存在或设置失败,返回 false

例子

<?php//对key设置锁标记$status = $cache->lock($key, $time);

isLock

判断 key 是否有锁标记

参数

  • key - 字符串

返回值

Boolean - 如果有锁标记,返回 true; 如果没有锁标记或判断失败,返回 false

例子

<?php//判断key是否有锁标记$status = $cache->isLock($key);

unLock

移除 key 的锁标记

参数

  • key - 字符串

返回值

Boolean - 如果移除成功,返回 true; 如果失败,返回 false

例子

<?php//移除key的锁标记$status = $cache->unLock($key);

incr

设置 key 的值按整数递增

参数

  • key - 字符串
  • step - (可选参数) 整数,递增步长,默认值为 1,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 step 设置新值

例子

<?php//设置key的值按整数递增$value = $cache->incr($key, $step);

incrByFloat

设置 key 的值按浮点数递增

参数

  • key - 字符串
  • float - 浮点数,递增步长,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 float 设置新值

例子

<?php//设置key的值按浮点数递增$value = $cache->incrByFloat($key, $float);

decr

设置 key 的值按整数递减

参数

  • key - 字符串
  • step - (可选参数) 整数,递减步长,默认值为 1,可以为负值

返回值

Mixed - 递减后的值,失败返回 false,如果 key 不存在,则按 -step 设置新值

例子

<?php//设置key的值按整数递减$value = $cache->decr($key, $step);

mSet

批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//批量设置多个key对应的值$status = $cache->mSet($sets);

mSetNX

当缓存中不存在 key 时,批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//当缓存中不存在key时,批量设置多个key对应的值$status = $cache->mSetNX($sets);

mGet

获取所有给定 key 的值

参数

  • keys - 多个 key 组成的数组

返回值

array - 参数 keys 中的所有 key 与对应的 value 组成的数组,如果 key 不存在或是获取失败,对应的 value 值为 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//获取所有给定key的值$status = $cache->mGet($keys);

mHas

批量判断 key 是否存在

参数

  • keys - 多个 key 组成的数组

返回值

array - 返回存在的 key 的数组,如果判断失败返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量判断key是否存在$hasKeys = $cache->mHas($keys);

mDel

批量删除 key

参数

  • keys - 多个 key 组成的数组

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量删除key$status = $cache->mDel($keys);

如何防止惊群

什么是惊群

首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了缓存的高并发的系统下,如果缓存突然都消失了,会发生什么?首先数据库的压力必然骤增,接着负载迅速升高,响应时间变慢,进程/线程由于响应时间变慢导致集压并剧增,这就是惊群。惊群的后果就是系统不可用甚至宕机。当然这是一个非常极端的例子,但是能很好的解释惊群现象。真实环境中,可能会有个一些页面是访问非常频繁的,如果这些页面的缓存在同一时间或相近的时间突然过期了,在高并发的情况下也会产生惊群现象。

如何避免惊群

知道了什么是惊群,就不难防止惊群了。首先,避免缓存在同一时间过期;其次,如果缓存过期,不要让所有并发的进程/线程都去触发更新同一缓存数据。

例子

LinkCache 为此专门做了优化,使用 LinkCache 提供的一些特殊方法,可以有效的防止惊群现象。以下是简单的示例(伪代码):

<?php/* LinkCache专为防止惊群现象设计的方法包括: * setDE,getDE,hasDE,ttlDE,expireDE,expireAtDE,lock,isLock,unLock * 比较常用的是:setDE,getDE,lock *///设置缓存$cache->setDE($key,$value,$time);
//获取缓存$value = $cache->getDE($key,$isExpired);
//情况1,$value存在,$isExpired=false,不需要更新缓存if($value !== false){
//your code
}
//情况2,$value存在,$isExpired=true,需要更新缓存if($value !== false){
//your code
}
//更新缓存if($isExpired === true){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
}
//情况3,$value不存在,需要更新缓存if($value === false){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
//如果失败,说明已经有进程/线程对key加了锁标记,正在更新缓存else{
//如果并发大,建议此处做特殊处理,不要去更新缓存//特殊处理的代码
}
}

默认情况说明

  • Cache - \linkcache\Cache
  • drivers
    • files - \linkcache\drivers\Files
    • memcache - \linkcache\drivers\Memcache
    • memcached - \linkcache\drivers\Memcached
    • redis - \linkcache\drivers\Redis
    • ssdb - \linkcache\drivers\Ssdb
    • apc - \linkcache\drivers\Apc
    • yac - \linkcache\drivers\Yac

cache

  • 默认使用的缓存驱动: files
  • 默认使用的备用驱动: files
  • memcacheredisssdb 等缓存驱动的默认配置均参考官方默认配置
  • 自定义配置支持非驱动类型的key,但配置信息中需要有 driver_type 属性,否则会抛异常,示例如下:
<?phpuse \linkcache\Cache;
$config = [
'redis_m_db_1' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 1
],
'redis_m_db_2' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 2
],
'redis_s_db' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6381
]
];
Cache::setConfig($config);
//根据自定义配置实例化$redisM1 = newCache('redis_m_db_1');
$redisM2 = newCache('redis_m_db_2');
$redisS0 = newCache('redis_s_db');

drivers

  • 所有驱动默认使用备用缓存
  • 备用缓存优先使用实例化时构造函数传入的配置 config 中的 fallback 定义,如果没有定义,则使用 \linkcache\Cache::$config 中的 fallback 定义

files

  • 默认保存路径:优先使用上传文件临时目录,未定义则使用系统临时目录,并在目录下创建linkcache目录,作为 files 驱动的默认保存路径,代码如下: (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . '/linkcache'

memcache

  • memcache 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

memcached

  • memcached 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

redis

  • redis 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

ssdb

  • ssdb 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

apc

yac

  • 可自定义缓存 key 前缀,默认无 key 前缀

开发

如果你觉得LinkCache还不错,但又不支持你想用的缓存驱动,不妨尝试在LinkCache新增该缓存驱动的支持。

1.增加新的缓存驱动

目前有两种方式可以方便的开发新的缓存驱动支持

  • 继承 linkcache\abstracts\DriverSimple 抽象类

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\abstracts\DriverSimple;
      class Example extends DriverSimple {
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key * @param string $value * @return boolean */protectedfunctionsetOne($key, $value) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key * @return mixed */protectedfunctiongetOne($key) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key * @return boolean */protectedfunctiondelOne($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\abstracts\DriverSimple 抽象类中的抽象方法
  • 实现接口 linkcache\interfaces\driver\Base (Base接口是必须的,你也可以实现更多的接口:Incr,Lock,Multi)

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\interfaces\driver\Base;
      class Example implements Base {
      use \linkcache\traits\CacheDriver;
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionset($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 当键名不存在时设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionsetnx($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 设置键值,将自动延迟过期;<br> * 此方法用于缓存对过期要求宽松的数据;<br> * 使用此方法设置缓存配合getDE方法可以有效防止惊群现象发生 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,<=0则设置为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionsetDE($key, $value, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key 键名 * @return mixed|false 键值,失败返回false */publicfunctionget($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值,与setDE配合使用;<br> * 此方法用于获取setDE设置的缓存数据;<br> * 当isExpired为true时,说明key已经过期,需要更新;<br> * 更新数据时配合isLock和lock方法,防止惊群现象发生 * @param string $key 键名 * @param boolean $isExpired 是否已经过期 * @return mixed|false 键值,失败返回false */publicfunctiongetDE($key, &$isExpired = null) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key 键名 * @return boolean 是否成功 */publicfunctiondel($key) {
      //TODO 实现这个方法
      }
      /** * 是否存在键值 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhas($key) {
      //TODO 实现这个方法
      }
      /** * 判断延迟过期的键值理论上是否存在 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhasDE($key) {
      //TODO 实现这个方法
      }
      /** * 获取生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttl($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值理论生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttlDE($key) {
      //TODO 实现这个方法
      }
      /** * 设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @return boolean 是否成功 */publicfunctionexpire($key, $time) {
      //TODO 实现这个方法
      }
      /** * 以延迟过期的方式设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionexpireDE($key, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 移除指定键值的过期时间 * @param string $key 键名 * @return boolean 是否成功 */publicfunctionpersist($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\interfaces\driver\Base 接口中的方法

2.测试新增的驱动

为了保证代码的可靠性,不妨对新增的驱动做个测试吧。 我使用的是PHPUnit做的测试,版本:4.8.15

  • 测试步骤
    1. tests 目录新增测试类 TestDriverExample.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcacheTests;
      /** * TestDriverExample */class TestDriverExample extends TestDriverFiles
      {
      protected$cacheDriver = 'example';
      }
    2. 进入LinkCache项目目录,执行测试命令
      phpunit --bootstrap autoload_dev.php tests/TestDriverExample
      
      显示以下信息测试就通过啦~
      PHPUnit 4.8.15 by Sebastian Bergmann and contributors.
      ....................
      Time: 8.24 seconds, Memory: 8.75Mb
      OK (26 tests, 105 assertions)
      

LICENSE

使用非常灵活宽松的 New BSD License 协议

About

一个灵活高效的PHP缓存工具库

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

LinkCache - 一个灵活高效的PHP缓存工具库

LinkCache 是一个PHP编写的灵活高效的缓存工具库,提供多种缓存驱动支持,包括Memcache、Memcached、Redis、SSDB、文件缓存、APC、YAC等。通过LinkCache可以使不同缓存驱动实现操作统一,同时又可发挥不同缓存驱动各自的优势。LinkCache支持缓存 objectarray,同时为防止产生惊群现象做了优化。

环境要求

安装

composer 安装

LinkCache 可以通过 composer 安装,使用以下命令从 composer 下载安装 LinkCache

$ composer require dongnan/linkcache

手动下载安装

下载地址

安装方法

在你的入口文件中引入

<?php//引入 LinkCache 的自动加载文件include("path_to_linkcache/autoload.php");

如何使用

(提示:如果以下锚点定位的链接错误,导致页面找不到等问题,请访问 https://github.com/dongnan/LinkCache 查看文档)

  • config - 配置信息
  • instance - 缓存实例化
  • getDriver - 获取缓存驱动实例
  • set - 将参数中的 value设置为 key 的值
  • setnx - 当缓存中不存在 key 时,将参数中的 value 设置为 key 的值
  • setDE - 将参数中的 value设置为 key 的值,key 将自动延迟过期
  • get - 获取 key 对应的值
  • getDE - 获取 key 对应的值,与 setDE 配合使用
  • del - 删除 key
  • has - 判断 key 是否存在
  • hasDE - 判断延迟过期的 key 理论上是否存在
  • ttl - 获取 key 的生存时间(单位:s)
  • ttlDE - 获取延迟过期的 key 理论上的生存时间(单位:s)
  • expire - 设置一个 key 的生存时间(单位:s)
  • expireDE - 以延迟过期的方式设置一个 key 的生存时间(单位:s)
  • expireAt - 用UNIX时间戳设置一个 key 的过期时间
  • expireAtDE - 以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间
  • persist - 删除一个 key 的生存时间,使其永不过期
  • lock - 对 key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)
  • isLock - 判断 key 是否有锁标记
  • unLock - 移除 key 的锁标记
  • incr - 设置 key 的值按整数递增
  • incrByFloat - 设置 key 的值按浮点数递增
  • decr - 设置 key 的值按整数递减
  • mSet - 批量设置多个 key 对应的值
  • mSetNX - 当缓存中不存在 key 时,批量设置多个 key 对应的值
  • mGet - 获取所有给定 key 的值
  • mHas - 批量判断 key 是否存在
  • mDel - 批量删除 key

config

配置信息

<?php//设置缓存配置, 使用 array_merge 的方式合并到默认配置中
\linkcache\Cache::setConfig($config);
//获取配置信息$config = \linkcache\Cache::getConfig();
//获取指定缓存驱动的配置信息$config = \linkcache\Cache::getConfig('redis');
//默认配置信息
[
//默认使用的缓存驱动'default' => 'files',
//当前缓存驱动失效时,使用的备份驱动'fallback' => 'files',
'memcache' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1, 'persistent' => true, 'timeout' => 1, 'retry_interval' => 15, 'status' => true],
],
'compress' => ['threshold' => 2000, 'min_saving' => 0.2],
],
'memcached' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1],
],
//参考 Memcached::setOptions'options' => [],
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'database' => '',
'timeout' => ''
],
'ssdb' => [
'host' => '127.0.0.1',
'port' => 8888,
'password' => '',
'timeoutms' => ''
],
]

instance

实例化缓存对象

<?php//直接new一个$cache = new \linkcache\Cache();
//通过getInstance获取$cache = \linkcache\Cache::getInstance();
//根据驱动类型实例化,支持的驱动:redis,memcache,memcached,ssdb,files,apc,yac$cache = new \linkcache\Cache('files');
//或者$cache = \linkcache\Cache::getInstance('files');
//传入配置参数实例化$cache = new \linkcache\Cache('redis', ['host' => '127.0.0.1', 'port' => 6379]);

getDriver

获取缓存驱动实例

所有缓存驱动都必须实现 linkcache\interfaces\driver\Base 接口的方法,获取到缓存驱动实例后,可直接使用缓存驱动的方法,包括缓存驱动中没有定义但缓存驱动扩展对象中已定义的方法。

set

将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->set($key, $value);
//设置有过期时间的缓存$status = $cache->set($key, $value, $time);

setnx

当缓存中不存在 key 时,将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setnx($key, $value);
//设置有过期时间的缓存$status = $cache->setnx($key, $value, $time);

setDE

将参数中的 value设置为 key 的值,key 将自动延迟过期

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - key 的生存时间,单位是秒(s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setDE($key, $value);
//设置有过期时间的缓存$status = $cache->setDE($key, $value, $time);
//设置有过期时间的缓存并指定延迟过期时间$status = $cache->setDE($key, $value, $time, $delayTime);

get

获取 key 对应的值

参数

  • key - 字符串

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->get($key);

getDE

获取 key 对应的值,与 setDE 配合使用

参数

  • key - 字符串
  • isExpired - key 理论上是否已经过期,这是一个引用变量,不需要初始化

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->getDE($key, $isExpired);
//如果已经过期,处理其他逻辑if ($isExpired) {
//TODO
}

del

删除 key

参数

  • key - 字符串

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false注意:key 不存在时,也会返回 true

例子

<?php//删除key$status = $cache->del($key);

has

判断 key 是否存在

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->has($key);

hasDE

判断延迟过期的 key 理论上是否存在。设置自动延期的 key,过期时间实际会被修改,用方法 has 判断的话,可能 key 仍然存在,但是理论上已经过期了

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->hasDE($key);

ttl

获取 key 的生存时间(单位:s)

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取key的生存时间$ttl = $cache->ttl($key);

ttlDE

获取延迟过期的 key 理论上的生存时间(单位:s)。

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取延迟过期的 key 理论上的生存时间(单位:s)$ttl = $cache->ttlDE($key);

expire

设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//设置一个key的生存时间$status = $cache->expire($key, $time);

expireDE

以延迟过期的方式设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//以延迟过期的方式设置一个 key 的生存时间(单位:s)$status = $cache->expireDE($key, $time);
//自定义延迟过期时间$status = $cache->expireDE($key, $time, $delayTime);

expireAt

用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAt($key, $time);

expireAtDE

以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAtDE($key, $time);
//自定义延迟过期时间$status = $cache->expireAtDE($key, $time, $delayTime);

persist

删除一个 key 的生存时间,使其永不过期

参数

  • key - 字符串

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//删除一个key的生存时间,使其永不过期$status = $cache->persist($key);

lock

key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)

参数

  • key - 字符串
  • time - 整数,key锁标记 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果 key 已存在或设置失败,返回 false

例子

<?php//对key设置锁标记$status = $cache->lock($key, $time);

isLock

判断 key 是否有锁标记

参数

  • key - 字符串

返回值

Boolean - 如果有锁标记,返回 true; 如果没有锁标记或判断失败,返回 false

例子

<?php//判断key是否有锁标记$status = $cache->isLock($key);

unLock

移除 key 的锁标记

参数

  • key - 字符串

返回值

Boolean - 如果移除成功,返回 true; 如果失败,返回 false

例子

<?php//移除key的锁标记$status = $cache->unLock($key);

incr

设置 key 的值按整数递增

参数

  • key - 字符串
  • step - (可选参数) 整数,递增步长,默认值为 1,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 step 设置新值

例子

<?php//设置key的值按整数递增$value = $cache->incr($key, $step);

incrByFloat

设置 key 的值按浮点数递增

参数

  • key - 字符串
  • float - 浮点数,递增步长,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 float 设置新值

例子

<?php//设置key的值按浮点数递增$value = $cache->incrByFloat($key, $float);

decr

设置 key 的值按整数递减

参数

  • key - 字符串
  • step - (可选参数) 整数,递减步长,默认值为 1,可以为负值

返回值

Mixed - 递减后的值,失败返回 false,如果 key 不存在,则按 -step 设置新值

例子

<?php//设置key的值按整数递减$value = $cache->decr($key, $step);

mSet

批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//批量设置多个key对应的值$status = $cache->mSet($sets);

mSetNX

当缓存中不存在 key 时,批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//当缓存中不存在key时,批量设置多个key对应的值$status = $cache->mSetNX($sets);

mGet

获取所有给定 key 的值

参数

  • keys - 多个 key 组成的数组

返回值

array - 参数 keys 中的所有 key 与对应的 value 组成的数组,如果 key 不存在或是获取失败,对应的 value 值为 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//获取所有给定key的值$status = $cache->mGet($keys);

mHas

批量判断 key 是否存在

参数

  • keys - 多个 key 组成的数组

返回值

array - 返回存在的 key 的数组,如果判断失败返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量判断key是否存在$hasKeys = $cache->mHas($keys);

mDel

批量删除 key

参数

  • keys - 多个 key 组成的数组

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量删除key$status = $cache->mDel($keys);

如何防止惊群

什么是惊群

首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了缓存的高并发的系统下,如果缓存突然都消失了,会发生什么?首先数据库的压力必然骤增,接着负载迅速升高,响应时间变慢,进程/线程由于响应时间变慢导致集压并剧增,这就是惊群。惊群的后果就是系统不可用甚至宕机。当然这是一个非常极端的例子,但是能很好的解释惊群现象。真实环境中,可能会有个一些页面是访问非常频繁的,如果这些页面的缓存在同一时间或相近的时间突然过期了,在高并发的情况下也会产生惊群现象。

如何避免惊群

知道了什么是惊群,就不难防止惊群了。首先,避免缓存在同一时间过期;其次,如果缓存过期,不要让所有并发的进程/线程都去触发更新同一缓存数据。

例子

LinkCache 为此专门做了优化,使用 LinkCache 提供的一些特殊方法,可以有效的防止惊群现象。以下是简单的示例(伪代码):

<?php/* LinkCache专为防止惊群现象设计的方法包括: * setDE,getDE,hasDE,ttlDE,expireDE,expireAtDE,lock,isLock,unLock * 比较常用的是:setDE,getDE,lock *///设置缓存$cache->setDE($key,$value,$time);
//获取缓存$value = $cache->getDE($key,$isExpired);
//情况1,$value存在,$isExpired=false,不需要更新缓存if($value !== false){
//your code
}
//情况2,$value存在,$isExpired=true,需要更新缓存if($value !== false){
//your code
}
//更新缓存if($isExpired === true){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
}
//情况3,$value不存在,需要更新缓存if($value === false){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
//如果失败,说明已经有进程/线程对key加了锁标记,正在更新缓存else{
//如果并发大,建议此处做特殊处理,不要去更新缓存//特殊处理的代码
}
}

默认情况说明

  • Cache - \linkcache\Cache
  • drivers
    • files - \linkcache\drivers\Files
    • memcache - \linkcache\drivers\Memcache
    • memcached - \linkcache\drivers\Memcached
    • redis - \linkcache\drivers\Redis
    • ssdb - \linkcache\drivers\Ssdb
    • apc - \linkcache\drivers\Apc
    • yac - \linkcache\drivers\Yac

cache

  • 默认使用的缓存驱动: files
  • 默认使用的备用驱动: files
  • memcacheredisssdb 等缓存驱动的默认配置均参考官方默认配置
  • 自定义配置支持非驱动类型的key,但配置信息中需要有 driver_type 属性,否则会抛异常,示例如下:
<?phpuse \linkcache\Cache;
$config = [
'redis_m_db_1' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 1
],
'redis_m_db_2' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 2
],
'redis_s_db' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6381
]
];
Cache::setConfig($config);
//根据自定义配置实例化$redisM1 = newCache('redis_m_db_1');
$redisM2 = newCache('redis_m_db_2');
$redisS0 = newCache('redis_s_db');

drivers

  • 所有驱动默认使用备用缓存
  • 备用缓存优先使用实例化时构造函数传入的配置 config 中的 fallback 定义,如果没有定义,则使用 \linkcache\Cache::$config 中的 fallback 定义

files

  • 默认保存路径:优先使用上传文件临时目录,未定义则使用系统临时目录,并在目录下创建linkcache目录,作为 files 驱动的默认保存路径,代码如下: (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . '/linkcache'

memcache

  • memcache 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

memcached

  • memcached 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

redis

  • redis 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

ssdb

  • ssdb 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

apc

yac

  • 可自定义缓存 key 前缀,默认无 key 前缀

开发

如果你觉得LinkCache还不错,但又不支持你想用的缓存驱动,不妨尝试在LinkCache新增该缓存驱动的支持。

1.增加新的缓存驱动

目前有两种方式可以方便的开发新的缓存驱动支持

  • 继承 linkcache\abstracts\DriverSimple 抽象类

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\abstracts\DriverSimple;
      class Example extends DriverSimple {
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key * @param string $value * @return boolean */protectedfunctionsetOne($key, $value) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key * @return mixed */protectedfunctiongetOne($key) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key * @return boolean */protectedfunctiondelOne($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\abstracts\DriverSimple 抽象类中的抽象方法
  • 实现接口 linkcache\interfaces\driver\Base (Base接口是必须的,你也可以实现更多的接口:Incr,Lock,Multi)

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\interfaces\driver\Base;
      class Example implements Base {
      use \linkcache\traits\CacheDriver;
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionset($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 当键名不存在时设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionsetnx($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 设置键值,将自动延迟过期;<br> * 此方法用于缓存对过期要求宽松的数据;<br> * 使用此方法设置缓存配合getDE方法可以有效防止惊群现象发生 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,<=0则设置为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionsetDE($key, $value, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key 键名 * @return mixed|false 键值,失败返回false */publicfunctionget($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值,与setDE配合使用;<br> * 此方法用于获取setDE设置的缓存数据;<br> * 当isExpired为true时,说明key已经过期,需要更新;<br> * 更新数据时配合isLock和lock方法,防止惊群现象发生 * @param string $key 键名 * @param boolean $isExpired 是否已经过期 * @return mixed|false 键值,失败返回false */publicfunctiongetDE($key, &$isExpired = null) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key 键名 * @return boolean 是否成功 */publicfunctiondel($key) {
      //TODO 实现这个方法
      }
      /** * 是否存在键值 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhas($key) {
      //TODO 实现这个方法
      }
      /** * 判断延迟过期的键值理论上是否存在 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhasDE($key) {
      //TODO 实现这个方法
      }
      /** * 获取生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttl($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值理论生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttlDE($key) {
      //TODO 实现这个方法
      }
      /** * 设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @return boolean 是否成功 */publicfunctionexpire($key, $time) {
      //TODO 实现这个方法
      }
      /** * 以延迟过期的方式设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionexpireDE($key, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 移除指定键值的过期时间 * @param string $key 键名 * @return boolean 是否成功 */publicfunctionpersist($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\interfaces\driver\Base 接口中的方法

2.测试新增的驱动

为了保证代码的可靠性,不妨对新增的驱动做个测试吧。 我使用的是PHPUnit做的测试,版本:4.8.15

  • 测试步骤
    1. tests 目录新增测试类 TestDriverExample.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcacheTests;
      /** * TestDriverExample */class TestDriverExample extends TestDriverFiles
      {
      protected$cacheDriver = 'example';
      }
    2. 进入LinkCache项目目录,执行测试命令
      phpunit --bootstrap autoload_dev.php tests/TestDriverExample
      
      显示以下信息测试就通过啦~
      PHPUnit 4.8.15 by Sebastian Bergmann and contributors.
      ....................
      Time: 8.24 seconds, Memory: 8.75Mb
      OK (26 tests, 105 assertions)
      

LICENSE

使用非常灵活宽松的 New BSD License 协议

About

一个灵活高效的PHP缓存工具库

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

LinkCache - 一个灵活高效的PHP缓存工具库

LinkCache 是一个PHP编写的灵活高效的缓存工具库,提供多种缓存驱动支持,包括Memcache、Memcached、Redis、SSDB、文件缓存、APC、YAC等。通过LinkCache可以使不同缓存驱动实现操作统一,同时又可发挥不同缓存驱动各自的优势。LinkCache支持缓存 objectarray,同时为防止产生惊群现象做了优化。

环境要求

安装

composer 安装

LinkCache 可以通过 composer 安装,使用以下命令从 composer 下载安装 LinkCache

$ composer require dongnan/linkcache

手动下载安装

下载地址

安装方法

在你的入口文件中引入

<?php//引入 LinkCache 的自动加载文件include("path_to_linkcache/autoload.php");

如何使用

(提示:如果以下锚点定位的链接错误,导致页面找不到等问题,请访问 https://github.com/dongnan/LinkCache 查看文档)

  • config - 配置信息
  • instance - 缓存实例化
  • getDriver - 获取缓存驱动实例
  • set - 将参数中的 value设置为 key 的值
  • setnx - 当缓存中不存在 key 时,将参数中的 value 设置为 key 的值
  • setDE - 将参数中的 value设置为 key 的值,key 将自动延迟过期
  • get - 获取 key 对应的值
  • getDE - 获取 key 对应的值,与 setDE 配合使用
  • del - 删除 key
  • has - 判断 key 是否存在
  • hasDE - 判断延迟过期的 key 理论上是否存在
  • ttl - 获取 key 的生存时间(单位:s)
  • ttlDE - 获取延迟过期的 key 理论上的生存时间(单位:s)
  • expire - 设置一个 key 的生存时间(单位:s)
  • expireDE - 以延迟过期的方式设置一个 key 的生存时间(单位:s)
  • expireAt - 用UNIX时间戳设置一个 key 的过期时间
  • expireAtDE - 以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间
  • persist - 删除一个 key 的生存时间,使其永不过期
  • lock - 对 key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)
  • isLock - 判断 key 是否有锁标记
  • unLock - 移除 key 的锁标记
  • incr - 设置 key 的值按整数递增
  • incrByFloat - 设置 key 的值按浮点数递增
  • decr - 设置 key 的值按整数递减
  • mSet - 批量设置多个 key 对应的值
  • mSetNX - 当缓存中不存在 key 时,批量设置多个 key 对应的值
  • mGet - 获取所有给定 key 的值
  • mHas - 批量判断 key 是否存在
  • mDel - 批量删除 key

config

配置信息

<?php//设置缓存配置, 使用 array_merge 的方式合并到默认配置中
\linkcache\Cache::setConfig($config);
//获取配置信息$config = \linkcache\Cache::getConfig();
//获取指定缓存驱动的配置信息$config = \linkcache\Cache::getConfig('redis');
//默认配置信息
[
//默认使用的缓存驱动'default' => 'files',
//当前缓存驱动失效时,使用的备份驱动'fallback' => 'files',
'memcache' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1, 'persistent' => true, 'timeout' => 1, 'retry_interval' => 15, 'status' => true],
],
'compress' => ['threshold' => 2000, 'min_saving' => 0.2],
],
'memcached' => [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 1],
],
//参考 Memcached::setOptions'options' => [],
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'database' => '',
'timeout' => ''
],
'ssdb' => [
'host' => '127.0.0.1',
'port' => 8888,
'password' => '',
'timeoutms' => ''
],
]

instance

实例化缓存对象

<?php//直接new一个$cache = new \linkcache\Cache();
//通过getInstance获取$cache = \linkcache\Cache::getInstance();
//根据驱动类型实例化,支持的驱动:redis,memcache,memcached,ssdb,files,apc,yac$cache = new \linkcache\Cache('files');
//或者$cache = \linkcache\Cache::getInstance('files');
//传入配置参数实例化$cache = new \linkcache\Cache('redis', ['host' => '127.0.0.1', 'port' => 6379]);

getDriver

获取缓存驱动实例

所有缓存驱动都必须实现 linkcache\interfaces\driver\Base 接口的方法,获取到缓存驱动实例后,可直接使用缓存驱动的方法,包括缓存驱动中没有定义但缓存驱动扩展对象中已定义的方法。

set

将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->set($key, $value);
//设置有过期时间的缓存$status = $cache->set($key, $value, $time);

setnx

当缓存中不存在 key 时,将参数中的 value 设置为 key 的值

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - (可选参数) key 的生存时间,单位是秒(s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setnx($key, $value);
//设置有过期时间的缓存$status = $cache->setnx($key, $value, $time);

setDE

将参数中的 value设置为 key 的值,key 将自动延迟过期

参数

  • key - 字符串
  • value - 除了 resource 类型的所有的值
  • time - key 的生存时间,单位是秒(s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php//设置不过期的缓存$status = $cache->setDE($key, $value);
//设置有过期时间的缓存$status = $cache->setDE($key, $value, $time);
//设置有过期时间的缓存并指定延迟过期时间$status = $cache->setDE($key, $value, $time, $delayTime);

get

获取 key 对应的值

参数

  • key - 字符串

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->get($key);

getDE

获取 key 对应的值,与 setDE 配合使用

参数

  • key - 字符串
  • isExpired - key 理论上是否已经过期,这是一个引用变量,不需要初始化

返回值

Mixed - key 对应的值; 如果获取失败或 key 不存在,返回 false

例子

<?php//获取key对应的值$value = $cache->getDE($key, $isExpired);
//如果已经过期,处理其他逻辑if ($isExpired) {
//TODO
}

del

删除 key

参数

  • key - 字符串

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false注意:key 不存在时,也会返回 true

例子

<?php//删除key$status = $cache->del($key);

has

判断 key 是否存在

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->has($key);

hasDE

判断延迟过期的 key 理论上是否存在。设置自动延期的 key,过期时间实际会被修改,用方法 has 判断的话,可能 key 仍然存在,但是理论上已经过期了

参数

  • key - 字符串

返回值

Boolean - 如果 key 存在,返回 true;如果 key 不存在,返回 false

例子

<?php//判断key是否存在$status = $cache->hasDE($key);

ttl

获取 key 的生存时间(单位:s)

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取key的生存时间$ttl = $cache->ttl($key);

ttlDE

获取延迟过期的 key 理论上的生存时间(单位:s)。

参数

  • key - 字符串

返回值

Mixed - 生存剩余时间(单位:秒) -1 表示永不过期,-2 表示 key 不存在,失败返回 false

例子

<?php//获取延迟过期的 key 理论上的生存时间(单位:s)$ttl = $cache->ttlDE($key);

expire

设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//设置一个key的生存时间$status = $cache->expire($key, $time);

expireDE

以延迟过期的方式设置一个 key 的生存时间(单位:s)

参数

  • key - 字符串
  • time - 整数,key 的生存时间(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//以延迟过期的方式设置一个 key 的生存时间(单位:s)$status = $cache->expireDE($key, $time);
//自定义延迟过期时间$status = $cache->expireDE($key, $time, $delayTime);

expireAt

用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAt($key, $time);

expireAtDE

以延迟过期的方式用UNIX时间戳设置一个 key 的过期时间

参数

  • key - 字符串
  • time - UNIX时间戳(单位:s)
  • delayTime - (可选参数) 延迟过期时间,如果未设置,则使用配置中的设置

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//用UNIX时间戳设置一个key的过期时间$status = $cache->expireAtDE($key, $time);
//自定义延迟过期时间$status = $cache->expireAtDE($key, $time, $delayTime);

persist

删除一个 key 的生存时间,使其永不过期

参数

  • key - 字符串

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败或 key 不存在,返回 false

例子

<?php//删除一个key的生存时间,使其永不过期$status = $cache->persist($key);

lock

key 设置锁标记(此锁并不对 key 做修改限制,仅为 key 的锁标记)

参数

  • key - 字符串
  • time - 整数,key锁标记 的生存时间(单位:s)

返回值

Boolean - 如果设置成功,返回 true; 如果 key 已存在或设置失败,返回 false

例子

<?php//对key设置锁标记$status = $cache->lock($key, $time);

isLock

判断 key 是否有锁标记

参数

  • key - 字符串

返回值

Boolean - 如果有锁标记,返回 true; 如果没有锁标记或判断失败,返回 false

例子

<?php//判断key是否有锁标记$status = $cache->isLock($key);

unLock

移除 key 的锁标记

参数

  • key - 字符串

返回值

Boolean - 如果移除成功,返回 true; 如果失败,返回 false

例子

<?php//移除key的锁标记$status = $cache->unLock($key);

incr

设置 key 的值按整数递增

参数

  • key - 字符串
  • step - (可选参数) 整数,递增步长,默认值为 1,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 step 设置新值

例子

<?php//设置key的值按整数递增$value = $cache->incr($key, $step);

incrByFloat

设置 key 的值按浮点数递增

参数

  • key - 字符串
  • float - 浮点数,递增步长,可以为负值

返回值

Mixed - 递增后的值,失败返回 false,如果 key 不存在,则按 float 设置新值

例子

<?php//设置key的值按浮点数递增$value = $cache->incrByFloat($key, $float);

decr

设置 key 的值按整数递减

参数

  • key - 字符串
  • step - (可选参数) 整数,递减步长,默认值为 1,可以为负值

返回值

Mixed - 递减后的值,失败返回 false,如果 key 不存在,则按 -step 设置新值

例子

<?php//设置key的值按整数递减$value = $cache->decr($key, $step);

mSet

批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//批量设置多个key对应的值$status = $cache->mSet($sets);

mSetNX

当缓存中不存在 key 时,批量设置多个 key 对应的值

参数

  • sets - keyvalue 组成的键值对数组

返回值

Boolean - 如果设置成功,返回 true; 如果设置失败,返回 false

例子

<?php$sets = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
//当缓存中不存在key时,批量设置多个key对应的值$status = $cache->mSetNX($sets);

mGet

获取所有给定 key 的值

参数

  • keys - 多个 key 组成的数组

返回值

array - 参数 keys 中的所有 key 与对应的 value 组成的数组,如果 key 不存在或是获取失败,对应的 value 值为 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//获取所有给定key的值$status = $cache->mGet($keys);

mHas

批量判断 key 是否存在

参数

  • keys - 多个 key 组成的数组

返回值

array - 返回存在的 key 的数组,如果判断失败返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量判断key是否存在$hasKeys = $cache->mHas($keys);

mDel

批量删除 key

参数

  • keys - 多个 key 组成的数组

返回值

Boolean - 如果删除成功,返回 true; 如果删除失败,返回 false

例子

<?php$keys = ['key1', 'key2', 'key3'];
//批量删除key$status = $cache->mDel($keys);

如何防止惊群

什么是惊群

首先,我们使用缓存的主要目的就是为了高并发情况下的高可用,换句话说,在使用了缓存的高并发的系统下,如果缓存突然都消失了,会发生什么?首先数据库的压力必然骤增,接着负载迅速升高,响应时间变慢,进程/线程由于响应时间变慢导致集压并剧增,这就是惊群。惊群的后果就是系统不可用甚至宕机。当然这是一个非常极端的例子,但是能很好的解释惊群现象。真实环境中,可能会有个一些页面是访问非常频繁的,如果这些页面的缓存在同一时间或相近的时间突然过期了,在高并发的情况下也会产生惊群现象。

如何避免惊群

知道了什么是惊群,就不难防止惊群了。首先,避免缓存在同一时间过期;其次,如果缓存过期,不要让所有并发的进程/线程都去触发更新同一缓存数据。

例子

LinkCache 为此专门做了优化,使用 LinkCache 提供的一些特殊方法,可以有效的防止惊群现象。以下是简单的示例(伪代码):

<?php/* LinkCache专为防止惊群现象设计的方法包括: * setDE,getDE,hasDE,ttlDE,expireDE,expireAtDE,lock,isLock,unLock * 比较常用的是:setDE,getDE,lock *///设置缓存$cache->setDE($key,$value,$time);
//获取缓存$value = $cache->getDE($key,$isExpired);
//情况1,$value存在,$isExpired=false,不需要更新缓存if($value !== false){
//your code
}
//情况2,$value存在,$isExpired=true,需要更新缓存if($value !== false){
//your code
}
//更新缓存if($isExpired === true){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
}
//情况3,$value不存在,需要更新缓存if($value === false){
//对key加锁标记,如果成功,则处理更新缓存操作 (锁标记默认过期时间是60s,也可自定义过期时间)if($cache->lock($key)){
//更新缓存
}
//如果失败,说明已经有进程/线程对key加了锁标记,正在更新缓存else{
//如果并发大,建议此处做特殊处理,不要去更新缓存//特殊处理的代码
}
}

默认情况说明

  • Cache - \linkcache\Cache
  • drivers
    • files - \linkcache\drivers\Files
    • memcache - \linkcache\drivers\Memcache
    • memcached - \linkcache\drivers\Memcached
    • redis - \linkcache\drivers\Redis
    • ssdb - \linkcache\drivers\Ssdb
    • apc - \linkcache\drivers\Apc
    • yac - \linkcache\drivers\Yac

cache

  • 默认使用的缓存驱动: files
  • 默认使用的备用驱动: files
  • memcacheredisssdb 等缓存驱动的默认配置均参考官方默认配置
  • 自定义配置支持非驱动类型的key,但配置信息中需要有 driver_type 属性,否则会抛异常,示例如下:
<?phpuse \linkcache\Cache;
$config = [
'redis_m_db_1' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 1
],
'redis_m_db_2' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6380,
'database' => 2
],
'redis_s_db' => [
'driver_type' => 'redis',
'host' => '127.0.0.1',
'port' => 6381
]
];
Cache::setConfig($config);
//根据自定义配置实例化$redisM1 = newCache('redis_m_db_1');
$redisM2 = newCache('redis_m_db_2');
$redisS0 = newCache('redis_s_db');

drivers

  • 所有驱动默认使用备用缓存
  • 备用缓存优先使用实例化时构造函数传入的配置 config 中的 fallback 定义,如果没有定义,则使用 \linkcache\Cache::$config 中的 fallback 定义

files

  • 默认保存路径:优先使用上传文件临时目录,未定义则使用系统临时目录,并在目录下创建linkcache目录,作为 files 驱动的默认保存路径,代码如下: (ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir()) . '/linkcache'

memcache

  • memcache 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

memcached

  • memcached 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

redis

  • redis 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

ssdb

  • ssdb 连接断开后,最大重连次数为3次,重新建立连接后,连接重试次数清零

apc

yac

  • 可自定义缓存 key 前缀,默认无 key 前缀

开发

如果你觉得LinkCache还不错,但又不支持你想用的缓存驱动,不妨尝试在LinkCache新增该缓存驱动的支持。

1.增加新的缓存驱动

目前有两种方式可以方便的开发新的缓存驱动支持

  • 继承 linkcache\abstracts\DriverSimple 抽象类

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\abstracts\DriverSimple;
      class Example extends DriverSimple {
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key * @param string $value * @return boolean */protectedfunctionsetOne($key, $value) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key * @return mixed */protectedfunctiongetOne($key) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key * @return boolean */protectedfunctiondelOne($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\abstracts\DriverSimple 抽象类中的抽象方法
  • 实现接口 linkcache\interfaces\driver\Base (Base接口是必须的,你也可以实现更多的接口:Incr,Lock,Multi)

    1. src/linkcache/drivers 目录下创建新的缓存驱动类 Example.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcache\drivers;
      uselinkcache\interfaces\driver\Base;
      class Example implements Base {
      use \linkcache\traits\CacheDriver;
      /** * 构造函数 * @param array $config 配置 */publicfunction__construct($config = []) {
      $this->init($config);
      //TODO 完善这个方法
      }
      /** * 检查驱动是否可用 * @return boolean 是否可用 */publicfunctioncheckDriver() {
      //TODO 实现这个方法
      }
      /** * 设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionset($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 当键名不存在时设置键值 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,默认为-1,<=0则设置为永不过期 * @return boolean 是否成功 */publicfunctionsetnx($key, $value, $time = -1) {
      //TODO 实现这个方法
      }
      /** * 设置键值,将自动延迟过期;<br> * 此方法用于缓存对过期要求宽松的数据;<br> * 使用此方法设置缓存配合getDE方法可以有效防止惊群现象发生 * @param string $key 键名 * @param mixed $value 键值 * @param int $time 过期时间,<=0则设置为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionsetDE($key, $value, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 获取键值 * @param string $key 键名 * @return mixed|false 键值,失败返回false */publicfunctionget($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值,与setDE配合使用;<br> * 此方法用于获取setDE设置的缓存数据;<br> * 当isExpired为true时,说明key已经过期,需要更新;<br> * 更新数据时配合isLock和lock方法,防止惊群现象发生 * @param string $key 键名 * @param boolean $isExpired 是否已经过期 * @return mixed|false 键值,失败返回false */publicfunctiongetDE($key, &$isExpired = null) {
      //TODO 实现这个方法
      }
      /** * 删除键值 * @param string $key 键名 * @return boolean 是否成功 */publicfunctiondel($key) {
      //TODO 实现这个方法
      }
      /** * 是否存在键值 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhas($key) {
      //TODO 实现这个方法
      }
      /** * 判断延迟过期的键值理论上是否存在 * @param string $key 键名 * @return boolean 是否存在 */publicfunctionhasDE($key) {
      //TODO 实现这个方法
      }
      /** * 获取生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttl($key) {
      //TODO 实现这个方法
      }
      /** * 获取延迟过期的键值理论生存剩余时间 * @param string $key 键名 * @return int|false 生存剩余时间(单位:秒) -1表示永不过期,-2表示键值不存在,失败返回false */publicfunctionttlDE($key) {
      //TODO 实现这个方法
      }
      /** * 设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @return boolean 是否成功 */publicfunctionexpire($key, $time) {
      //TODO 实现这个方法
      }
      /** * 以延迟过期的方式设置过期时间 * @param string $key 键名 * @param int $time 过期时间(单位:秒)。不大于0,则设为永不过期 * @param int $delayTime 延迟过期时间,如果未设置,则使用配置中的设置 * @return boolean 是否成功 */publicfunctionexpireDE($key, $time, $delayTime = null) {
      //TODO 实现这个方法
      }
      /** * 移除指定键值的过期时间 * @param string $key 键名 * @return boolean 是否成功 */publicfunctionpersist($key) {
      //TODO 实现这个方法
      }
      }
    2. 实现 linkcache\interfaces\driver\Base 接口中的方法

2.测试新增的驱动

为了保证代码的可靠性,不妨对新增的驱动做个测试吧。 我使用的是PHPUnit做的测试,版本:4.8.15

  • 测试步骤
    1. tests 目录新增测试类 TestDriverExample.php
      <?php/** * linkcache - 一个灵活高效的PHP缓存工具库 * * @author Dong Nan <hidongnan@gmail.com> * @copyright (c) Dong Nan http://idongnan.cn All rights reserved. * @link http://git.oschina.net/dongnan/LinkCache * @license BSD (http://opensource.org/licenses/BSD-3-Clause) */namespacelinkcacheTests;
      /** * TestDriverExample */class TestDriverExample extends TestDriverFiles
      {
      protected$cacheDriver = 'example';
      }
    2. 进入LinkCache项目目录,执行测试命令
      phpunit --bootstrap autoload_dev.php tests/TestDriverExample
      
      显示以下信息测试就通过啦~
      PHPUnit 4.8.15 by Sebastian Bergmann and contributors.
      ....................
      Time: 8.24 seconds, Memory: 8.75Mb
      OK (26 tests, 105 assertions)
      

LICENSE

使用非常灵活宽松的 New BSD License 协议

About

一个灵活高效的PHP缓存工具库

Resources

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages