ithinkphp/smart-cache

smartCache 是个基于sql词法分析后,根据sql里涉及到的表来操作缓存的类,通俗说就是表里的数据没做增删改操作,那么缓存永远有效,只要做了增删改操作,缓存立马失效,重新读取数据后,再次写入缓存

v1.0.0 2019-04-01 08:39 UTC

README

- iThinkphp官方出品

项目地址 : https://gitee.com/iThinkphp/smart-cache

使用手册 : https://www.kancloud.cn/ithinkphp/smart-cache

产品名称 iThinkphp
交流QQ群 419395011
联系邮箱 wf585858@yeah.net
官方网站 www.ithinkphp.org
后台演示 demo.ithinkphp.org/admin (输入验证码即可登录)
前台演示 demo.ithinkphp.org
交流社区 forum.ithinkphp.org
码云仓库 https://gitee.com/iThinkphp/iThinkphp
开发手册(持续更新中) https://www.kancloud.cn/ithinkphp/main

介绍

  1. smartCache 是个基于 sql 词法分析后,根据sql里涉及到的表来操作缓存的类,通俗说就是表里的数据没做增删改操作,那么缓存永远有效,只要做了增删改操作,缓存立马失效,重新读取数据后,再次写入缓存
  2. 比起通常大家所使用的缓存机制,它最大的特点是没有所谓的缓存失效时间,而是根通过监控表里数据的变化来控制缓存数据的失效期,可以确保取出的数据永远都和数据库里的数据同步,监控的依据就是执行的sql。下面一节会详细的介绍其工作机制。
  3. 其缓存方式默认使用thinkphp5中的cache类,这意味着可以使用 redis,memcache,文件等各种方式缓存数据,如果无法满足你的需求,也可以自定义缓存方法

安装

使用composer安装

如果你没有安装composer : https://www.kancloud.cn/ithinkphp/main/1007695

composer require ithinkphp/smart-cache

使用

  • 获取数据方法

getData($sql , callable $callback)

$sql - String

查数据的 sql,如果是使用thinkphp5,可以在通过设置 fetchSql 为 true 来得到 参考 https://www.kancloud.cn/manual/thinkphp5/118089

$callback - callable

一个闭包,闭包里调用执行这个sql的逻辑,并反回数据

典型使用

	$query = <<<SQL
SELECT `b`.`role_id` FROM `ithink_role` `curr_tab` INNER JOIN `ithink_user_role` `b` ON `curr_tab`.`id` = `b`.`role_id` WHERE ( `curr_tab`.`status` <> '2' AND `curr_tab`.`status` = '1' ) AND `b`.`user_id` = 1 
SQL;

	$data = smartCache::getData($query , function() {
		//返回数据库查询数据的逻辑写在这里,直接返回这个sql对应的数据,smartCache会在需要的时候调用这个闭包,有缓存就会走缓存
		return [
			'aa' => 'aaaa' ,
			'bb' => 'vvbb' ,
		];
	});

	print_r($data);
  • 清除缓存数据

clearData($sql)

$sql - String

对应执行增删改的 sql ,只要有要执行增删改的地方,都要调用这个方法,它会在自动将涉及到的表的所有查询sql的缓存数据清除

典型使用

	$query = <<<SQL
UPDATE `dbsytem`.`ithink_role` SET `uid` = 2 WHERE `id` = 2 ;
SQL;

	//所有的增删改语句执行之前都调用这个而方法,传入对应增删改的sql即可
	smartCache::clearData($query);
  • 是否开启缓存

isEnableCache($enable)

$enable - bool

是否开启缓存,如果关闭,将会清空当前所有缓存,并不会缓存数据

典型使用

	smartCache::isEnableCache(0);

  • 填写路径,开启调试

logPath($path)

$path - String

所有操作过的sql写入文件,方便调试,不设置路径则不写入

典型使用

	smartCache::logPath('./smartCache.log');
  • 注册写缓存方法

setCacheFunction(callable $callback)

$callback - callable

默认情况我们使用thinkphp5里的缓存类,已经在 composer 里声明依赖,如果你需要用自己的缓存方式,在这里注册,调用这两个方法

典型使用

	smartCache::setCacheFunction(function($key , $value) {
		//自定义的缓存数据逻辑
		//setCache($key , $value);
	});
  • 注册读缓存方法

getCacheFunction(callable $callback)

$callback - callable

默认情况我们使用thinkphp5里的缓存类,已经在 composer 里声明依赖,如果你需要用自己的缓存方式,在这里注册,调用这两个方法

典型使用

	smartCache::getCacheFunction(function($key) {
		//自定义的取缓存逻辑
		//return getCache($key);
	});

  • 设置默认缓存机制参数

setCacheConfig($config = [] )

$config - array

如果使用默认的缓存机制,调用此方法进行设置 如果自己定义缓存机制,无需调用 手册参考 https://www.kancloud.cn/manual/thinkphp5/118131

典型使用

	smartCache::setCacheConfig([
		// 缓存类型为File
		'type'   => 'File' ,
		// 缓存有效期为永久有效
		'expire' => 0 ,
		//缓存前缀
		'prefix' => 'think' ,
		// 指定缓存目录
		'path'   => 'runtime/cache/' ,
	]);

工作机制详解

  1. 在执行查询操作时调用getData方法,会用sql词法解析器解析出查询sql里所有涉及到的表名,即便是子查询的表名也可以解析出来,用查询sql的hash值为键把表名和对应的数据分别缓存起来
  2. 执行增删改操作时调用clearData方法,同样会解析sql里的表名,之后会遍历所有的查询sql,取出每个查询sql对应的表名和增删改sql对应的表名做对比,只要查询sql里的表名和增删改sql有重合,那么对应的查询sql缓存的数据就会失效,下次查询时重新读取数据库,实现实时更新。

iThinkphp中调用代码参考

代码位于 ithinkphp/common/model/ModelBase.php

		protected static function init()
		{
			//设置是否开启缓存
			smartCache::isEnableCache(0);

			Db::listen(function($sql , $time , $explain) {
				//打开调试,把所有执行的sql写到文件里 与index.php同目录
				config('app_debug') && file_put_contents('./runtime.sql' , $sql . "\r\n" , 8);

				//所有操作过的sql写入文件,方便调试 与index.php同目录
				config('app_debug') && smartCache::logPath('./smartCache.log');

				//监听sql,所有执行insert,update和delete的sql都要执行 smartCache 的清空缓存操作
				if(preg_match('/^\s*(?:insert|update|delete)/i' , $sql))
				{
					//按sql解析,让对应表的缓存失效
					smartCache::clearData($sql);
				}
			});
		}


		/**
		 * 抽象查询方法的基础方法
		 * 所有模型的查询数据都走这个方法,数据会被缓存
		 *
		 * @param array         $condition   查询条件,根据logic层定义的字段传入
		 * @param string        $flag        查询类型 self::getAvailableOnly|self::getActivedOnly|self::getAvailableOnly
		 * @param string        $selectType  查询方法 tp 官方介绍的查询方法 select|find|column|value|count。。。   https://www.kancloud.cn/manual/thinkphp5/138666
		 * @param array         $params      查询方法里传入的参数
		 * @param callable|null $sqlCallback 如果传的话,sql都会作用的回调,是thinkphp使用paginate方法时候 $this->fetchSql(1) 没办法返回查询的sql,返回的是个对象,这是thinkphp的bug!!!!!
		 *
		 * @return array
		 */
		public function execSelect($condition = [] , $flag = self::getAvailableOnly , $selectType = 'select' , $params = [] , callable $sqlCallback = null)
		{
			//根据flag设置查询类型
			(method_exists($this , $flag)) && $this->$flag();
			//设置查询条件
			$this->setCondition($condition);

			//获取执行sql
			$sql = call_user_func_array([
				$this->fetchSql(1) ,
				$selectType ,
			] , $params);

			is_callable($sqlCallback) && ($sql = $sqlCallback($sql));

			//按sql解析,自动判断读缓存还是数据库
			return smartCache::getData($sql , function() use ($params , $selectType , $flag , $condition) {
				//上面的生成sql后所有查询条件会清空,所以这里要再执行一次
				(method_exists($this , $flag)) && $this->$flag();
				$this->setCondition($condition);

				return call_user_func_array([
					$this ,
					$selectType ,
				] , $params);
			});
		}