ithinkphp/smart-cache

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

dev-master 2019-01-23 09:03 UTC

README

iThinkphp官方出品

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

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

2. 比起通常大家所使用的缓存机制,它最大的特点是没有所谓的缓存失效时间,而是根通过监控表里数据的变化来控制缓存数据的失效期,可以确保取出的数据永远都和数据库里的数据同步,监控的依据就是执行的sql。下面一节会详细的介绍其工作机制。

3. 其缓存方式默认使用thinkphp5中的cache类,这意味着可以使用 redis,memcache,文件等各种方式缓存数据,如果无法满足你的需求,也可以自定义缓存方法

安装使用

推荐使用composer安装(https://www.phpcomposer.com/):

composer require ithinkphp/smart-cache

使用方法

查询数据并缓存数据

 $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() {
      //执行 $query 语句的逻辑,返回这个sql对应的数据,smartCache 会在没取到缓存数据时候调用这个闭包,有缓存就会走缓存而不执行这里的逻辑
      return [
         'aa' = 'aaaa' ,
         'bb' = 'vvbb' ,
      ];
   });
 
 print_r($data);
 

getData 方法接受两个参数

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

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

清除缓存数据

        $query = <<<SQL
 UPDATE 
     `dbsytem`.`ithink_role` 
 SET
     `uid` = 2
 WHERE `id` = 2 ;
 SQL;
 
  //所有的增删改语句执行之前都调用这个而方法,传入对应增删改的sql即可
  smartCache::clearData($query);

clearData 方法接受一个参数

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

其它api

      //如果使用默认的缓存,调用此方法进行设置
      //如果自己定义缓存方法,无需调用
      //手册参考 https://www.kancloud.cn/manual/thinkphp5/118131
      smartCache::setCacheConfig([
         // 缓存类型为File
         'type'   => 'File' ,
         // 缓存有效期为永久有效
         'expire' => 0 ,
         //缓存前缀
         'prefix' => 'think' ,
         // 指定缓存目录
         'path'   => 'runtime/cache/' ,
      ]);

      //默认情况我们使用thinkphp5里的缓存类,已经在 composer 里声明依赖,如果你需要用自己的缓存方式,在这里注册,调用这两个方法
      smartCache::setCacheFunction(function($key , $value) {
         //自定义的缓存数据逻辑
         //setCache($key , $value);
      });

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

工作机制详解

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

iThinkphp中调用代码参考


		/**
		 * 抽象查询方法的基础方法
		 * 所有模型的查询数据都走这个方法,数据会被缓存
		 *
		 * @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);
			});
		}
		
		
		protected static function init()
		{
			//设置是否开启缓存
			smartCache::isEnableCache(0);

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