blog
blog copied to clipboard
MEM快速缓存实现(原来,php不受这一套,只能换成文件缓存)
最近有个php项目,单机部署,在做性能优化的时候发现有些内部处理函数需要做下数据缓存。因为项目没有部署 redis 或者 memcached,所以就想偷个懒,直接把缓存放到内存来实现,反正是单机部署方便快捷,只需要控制好缓存量别被挤爆了就好。
嗯,不用想了,单例模式上……信心满满,很快就写好了!
然而……然而……童话里都是骗人的!!!
一顿操作萌如虎……上机运行之后,结果并不是想象中的那样,数据怎么也缓存不起来,调试了好久,最后才发现,原来是php运行机制的问题。
最近刚接触 php,对它的运行机制还不熟悉,惯性的就用了其他语言(JAVA/NODE/..)的实现方法,但是由于 PHP 是解释运行的,PHP 页面被解释执行后,所有相关的资源都会被回收,对象也被销毁了,所以PHP 程序无法做到常驻内存运行。
当然,目前好像也出现了一些常驻内存的解决方法,但是基于目前的项目环境(PHP 5.3.29),感觉会得不偿失,还是老老实实用 redis 或者 memcached 吧。
不过,代码不写也写了,虽然用不了但删掉也可惜,就放出来当个纪念吧!
基于RAM内存的数据缓存方案(由于机制问题,缓存效果并不生效)
<?php
/**
* 缓存管理(内存缓存)
* 单例模式
*
* 用法举例:mem_cache::ins()->get('key');
*
* $Author: Eric Chen 2020-11-4
* $Email: [email protected]
*/
if (!defined('IN_ECTOUCH'))
{
die('Hacking attempt');
}
class mem_cache
{
private static $_instance = null;
private $_pool; // 缓存池
private $_maxlen; // 缓存key数量限制,防止爆内存
private $_lucky_percent; // 缓存失效概率 0-100(读缓存的时候,设定一个概率让它清空,也就是除了时间还提供了一个随机机制让缓存更新,如果想关闭该机制,则设定为0即可
private function __construct($maxlen = 100, $percent = 5){
$this->_pool = array();
$this->_maxlen = $maxlen;
$this->_lucky_percent = $percent;
}
/*
* 公有化获取实例方法
*
* 用法:mem_cache::ins()->xxx
*/
public static function ins(){
if (!(self::$_instance instanceof mem_cache)){
self::$_instance = new mem_cache();
}
return self::$_instance;
}
// 设置缓存,默认有效时间为 600s
public function set($key, $value, $seconds = 600, $percent = -1) {
if (!$key) {
return;
}
// 检测超池
if (count($this->_pool, COUNT_NORMAL ) >= $this->_maxlen) {
// 先清一次过期缓存,如果还是饱和,则直接返回
if(!$this->gc()) {
return;
}
}
// 更新/添加缓存
$obj_item = new mem_cache_item();
$obj_item->value = $value;
$obj_item->time = time();
$obj_item->maxage = $seconds;
$obj_item->lucky_percent = $percent < 0 ? $this->_lucky_percent : $percent;
$this->_pool[$key] = $obj_item;
}
public function get($key) {
if (!$key || !$this->_pool[$key]) {
return false;
}
// 过期/缓存异常/命中清除概率,则判断为缓存不可取,清除它
if (!$this->_pool[$key]->value ||
(time() - $this->_pool[$key]->time) > $this->_pool[$key]->maxage ||
mt_rand(0, 100) < $this->_pool[$key]->lucky_percent
) {
$this->delete($key);
return false;
}
return $this->_pool[$key]->value;
}
public function delete($key) {
if (!$key) {
return;
}
unset($this->_pool[$key]);
}
// 清除过期缓存,清完后还饱和则返回false,否则返回true
public function gc() {
foreach($this->_pool as $key => $item) {
if (!$this->_pool[$key]->value ||
(time() - $this->_pool[$key]->time) > $this->_pool[$key]->maxage
) {
$this->delete($key);
}
}
if (count($this->_pool, COUNT_NORMAL) >= $this->_maxlen) {
return false;
}
return true;
}
// 清除所有缓存
public function clear() {
$this->_pool = array();
}
}
// 缓存元素类
class mem_cache_item
{
public $value;
public $time;
public $maxage;
public $lucky_percent;
}
?>
基于文件系统的数据缓存方案
更新@2020-11-5 今天把它改成了基于文件系统的缓存,自测没问题,记录一下:
<?php
/**
* 缓存管理(内存缓存)
* 单例模式
*
* 用法举例:mem_cache::ins()->get('key');
*
* $Author: Eric Chen 2020-11-5
* $Email: [email protected]
*/
if (!defined('IN_ECTOUCH'))
{
die('Hacking attempt');
}
class mem_cache
{
private static $_instance = null;
private $_cache_path;
private $_lucky_percent; // 缓存命中率 0-100(读缓存的时候,设定一个命中概率,不命中的时候不返回数据,让调用方以为没缓存然后去取数据更新缓存,也就是除了时间还提供了一个随机机制让缓存更新,如果想关闭该机制,则设定为100即可
private function __construct($percent = 98)
{
$this->_lucky_percent = $percent;
$this->_cache_path = ROOT_PATH . 'data/mem_cache/';
if (!file_exists($this->_cache_path)) {
if (!make_dir($this->_cache_path)) {
return false;
}
}
}
/*
* 公有化获取实例方法
*
* 用法:mem_cache::ins()->xxx
*/
public static function ins()
{
if (!(self::$_instance instanceof mem_cache)) {
self::$_instance = new mem_cache();
}
return self::$_instance;
}
// 设置缓存,默认有效时间为 600s
public function set($key, $value, $seconds = 600, $percent = -1)
{
if (!$key) {
return;
}
// 更新/添加缓存
$obj_item = new mem_cache_item();
$obj_item->value = $value;
$obj_item->time = time();
$obj_item->maxage = $seconds;
$obj_item->lucky_percent = ($percent < 0 || $percent > 100) ? $this->_lucky_percent : $percent;
$cache_file = $this->_cache_path . crc32($key) . '.cache';
file_put_contents($cache_file, serialize($obj_item)); // 序列化写入
}
public function get($key)
{
if (!$key) {
return false;
}
// 从文件读取缓存
$cache_file = $this->_cache_path . crc32($key) . '.cache';
if (!is_file($cache_file)) {
// echo 'cache not found. key: ' . $key . '<br>';
return false;
}
$fp = fopen($cache_file, 'r');
$item = unserialize(fread($fp, filesize($cache_file))); //反序列化,并赋值
// 缓存异常或者过期,清理它
if (!$item || !$item->value || (time() - $item->time) > $item->maxage) {
// echo 'cache exist but not good, to delete it. key: ' .$key .'<br>';
$this->delete($key);
return false;
}
// 命中率
if (mt_rand(0, 100) > $item->lucky_percent) {
// echo 'cache exist, but I do not want to show you. key: ' . $key . '<br>';
return false;
}
return $item->value;
}
public function delete($key)
{
if (!$key) {
return;
}
// 删除缓存文件
$cache_file = $this->_cache_path . crc32($key) . '.cache';
unlink($cache_file);
// echo 'delete cache. key: ' . $key . '<br>';
}
// 清除所有缓存
public function clear()
{
$this->delete_directory($this->_cache_path);
}
public function delete_directory($dirname)
{
if (is_dir($dirname)) {
$dir_handle = opendir($dirname);
}
if (!$dir_handle) {
return false;
}
while ($file = readdir($dir_handle)) {
if ($file != "." && $file != "..") {
if (!is_dir($dirname . "/" . $file)) {
unlink($dirname . "/" . $file);
} else {
$this->delete_directory($dirname . '/' . $file);
}
}
}
closedir($dir_handle);
rmdir($dirname);
return true;
}
}
// 缓存元素类
class mem_cache_item
{
public $value;
public $time;
public $maxage;
public $lucky_percent;
}
?>