You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

972 lines
23 KiB

  1. <?php
  2. namespace App\Common;
  3. /**
  4. * redis操作类
  5. * 说明,任何为false的串,存在redis中都是空串。
  6. * 只有在key不存在时,才会返回false。
  7. * 这点可用于防止缓存穿透
  8. *
  9. */
  10. class Redis
  11. {
  12. private $redis;
  13. //当前数据库ID号
  14. protected $dbId=0;
  15. //当前权限认证码
  16. protected $auth;
  17. /**
  18. * 实例化的对象,单例模式.
  19. * @var \iphp\db\Redis
  20. */
  21. static private $_instance=array();
  22. private $k;
  23. //连接属性数组
  24. protected $attr=array(
  25. //连接超时时间,redis配置文件中默认为300秒
  26. 'timeout'=>30,
  27. //选择的数据库。
  28. 'db_id'=>0,
  29. );
  30. //什么时候重新建立连接
  31. protected $expireTime;
  32. protected $host;
  33. protected $port;
  34. private function __construct($config,$attr=array())
  35. {
  36. $this->attr = array_merge($this->attr,$attr);
  37. $this->redis = new \Redis();
  38. $this->port = $config['port'] ? $config['port'] : 6379;
  39. $this->host = $config['host'];
  40. $this->redis->connect($this->host, $this->port, $this->attr['timeout']);
  41. if($config['auth'])
  42. {
  43. $this->auth($config['auth']);
  44. $this->auth = $config['auth'];
  45. }
  46. $this->expireTime = time() + $this->attr['timeout'];
  47. }
  48. /**
  49. * 得到实例化的对象.
  50. * 为每个数据库建立一个连接
  51. * 如果连接超时,将会重新建立一个连接
  52. * @param array $config
  53. * @param int $dbId
  54. * @return \iphp\db\Redis
  55. */
  56. public static function getInstance($config, $attr = array())
  57. {
  58. //如果是一个字符串,将其认为是数据库的ID号。以简化写法。
  59. if(!is_array($attr))
  60. {
  61. $dbId = $attr;
  62. $attr = array();
  63. $attr['db_id'] = $dbId;
  64. }
  65. $attr['db_id'] = $attr['db_id'] ? $attr['db_id'] : 0;
  66. $k = md5(implode('', $config).$attr['db_id']);
  67. if(! (static::$_instance[$k] instanceof self))
  68. {
  69. static::$_instance[$k] = new self($config,$attr);
  70. static::$_instance[$k]->k = $k;
  71. static::$_instance[$k]->dbId = $attr['db_id'];
  72. //如果不是0号库,选择一下数据库。
  73. if($attr['db_id'] != 0){
  74. static::$_instance[$k]->select($attr['db_id']);
  75. }
  76. }
  77. elseif( time() > static::$_instance[$k]->expireTime)
  78. {
  79. static::$_instance[$k]->close();
  80. static::$_instance[$k] = new self($config,$attr);
  81. static::$_instance[$k]->k = $k;
  82. static::$_instance[$k]->dbId = $attr['db_id'];
  83. //如果不是0号库,选择一下数据库。
  84. if($attr['db_id']!=0)
  85. {
  86. static::$_instance[$k]->select($attr['db_id']);
  87. }
  88. }
  89. return static::$_instance[$k];
  90. }
  91. private function __clone(){}
  92. /**
  93. * 执行原生的redis操作
  94. * @return \Redis
  95. */
  96. public function getRedis()
  97. {
  98. return $this->redis;
  99. }
  100. /*****************hash表操作函数*******************/
  101. /**
  102. * 得到hash表中一个字段的值
  103. * @param string $key 缓存key
  104. * @param string $field 字段
  105. * @return string|false
  106. */
  107. public function hGet($key,$field)
  108. {
  109. return $this->redis->hGet($key,$field);
  110. }
  111. /**
  112. * 为hash表设定一个字段的值
  113. * @param string $key 缓存key
  114. * @param string $field 字段
  115. * @param string $value 值。
  116. * @return bool
  117. */
  118. public function hSet($key,$field,$value)
  119. {
  120. return $this->redis->hSet($key,$field,$value);
  121. }
  122. /**
  123. * 判断hash表中,指定field是不是存在
  124. * @param string $key 缓存key
  125. * @param string $field 字段
  126. * @return bool
  127. */
  128. public function hExists($key,$field)
  129. {
  130. return $this->redis->hExists($key,$field);
  131. }
  132. /**
  133. * 删除hash表中指定字段 ,支持批量删除
  134. * @param string $key 缓存key
  135. * @param string $field 字段
  136. * @return int
  137. */
  138. public function hdel($key,$field)
  139. {
  140. $fieldArr=explode(',',$field);
  141. $delNum=0;
  142. foreach($fieldArr as $row)
  143. {
  144. $row=trim($row);
  145. $delNum+=$this->redis->hDel($key,$row);
  146. }
  147. return $delNum;
  148. }
  149. /**
  150. * 返回hash表元素个数
  151. * @param string $key 缓存key
  152. * @return int|bool
  153. */
  154. public function hLen($key)
  155. {
  156. return $this->redis->hLen($key);
  157. }
  158. /**
  159. * 为hash表设定一个字段的值,如果字段存在,返回false
  160. * @param string $key 缓存key
  161. * @param string $field 字段
  162. * @param string $value 值。
  163. * @return bool
  164. */
  165. public function hSetNx($key,$field,$value)
  166. {
  167. return $this->redis->hSetNx($key,$field,$value);
  168. }
  169. /**
  170. * 为hash表多个字段设定值。
  171. * @param string $key
  172. * @param array $value
  173. * @return array|bool
  174. */
  175. public function hMset($key,$value)
  176. {
  177. if(!is_array($value))
  178. return false;
  179. return $this->redis->hMset($key,$value);
  180. }
  181. /**
  182. * 为hash表多个字段设定值。
  183. * @param string $key
  184. * @param array|string $value string以','号分隔字段
  185. * @return array|bool
  186. */
  187. public function hMget($key,$field)
  188. {
  189. if(!is_array($field))
  190. $field=explode(',', $field);
  191. return $this->redis->hMget($key,$field);
  192. }
  193. /**
  194. * 为hash表设这累加,可以负数
  195. * @param string $key
  196. * @param int $field
  197. * @param string $value
  198. * @return bool
  199. */
  200. public function hIncrBy($key,$field,$value)
  201. {
  202. $value=intval($value);
  203. return $this->redis->hIncrBy($key,$field,$value);
  204. }
  205. /**
  206. * 返回所有hash表的所有字段
  207. * @param string $key
  208. * @return array|bool
  209. */
  210. public function hKeys($key)
  211. {
  212. return $this->redis->hKeys($key);
  213. }
  214. /**
  215. * 返回所有hash表的字段值,为一个索引数组
  216. * @param string $key
  217. * @return array|bool
  218. */
  219. public function hVals($key)
  220. {
  221. return $this->redis->hVals($key);
  222. }
  223. /**
  224. * 返回所有hash表的字段值,为一个关联数组
  225. * @param string $key
  226. * @return array|bool
  227. */
  228. public function hGetAll($key)
  229. {
  230. return $this->redis->hGetAll($key);
  231. }
  232. /*********************有序集合操作*********************/
  233. /**
  234. * 给当前集合添加一个元素
  235. * 如果value已经存在,会更新order的值。
  236. * @param string $key
  237. * @param string $order 序号
  238. * @param string $value
  239. * @return bool
  240. */
  241. public function zAdd($key,$order,$value)
  242. {
  243. return $this->redis->zAdd($key,$order,$value);
  244. }
  245. /**
  246. * $value成员的order值,增加$num,可以为负数
  247. * @param string $key
  248. * @param string $num 序号
  249. * @param string $value
  250. * @return 返回新的order
  251. */
  252. public function zinCry($key,$num,$value)
  253. {
  254. return $this->redis->zinCry($key,$num,$value);
  255. }
  256. /**
  257. * 删除值为value的元素
  258. * @param string $key
  259. * @param stirng $value
  260. * @return bool
  261. */
  262. public function zRem($key,$value)
  263. {
  264. return $this->redis->zRem($key,$value);
  265. }
  266. /**
  267. * 集合以order递增排列后,0表示第一个元素,-1表示最后一个元素
  268. * @param string $key
  269. * @param int $start
  270. * @param int $end
  271. * @return array|bool
  272. */
  273. public function zRange($key,$start,$end)
  274. {
  275. return $this->redis->zRange($key,$start,$end);
  276. }
  277. /**
  278. * 集合以order递减排列后,0表示第一个元素,-1表示最后一个元素
  279. * @param string $key
  280. * @param int $start
  281. * @param int $end
  282. * @return array|bool
  283. */
  284. public function zRevRange($key,$start,$end)
  285. {
  286. return $this->redis->zRevRange($key,$start,$end);
  287. }
  288. /**
  289. * 集合以order递增排列后,返回指定order之间的元素。
  290. * min和max可以是-inf和+inf 表示最大值,最小值
  291. * @param string $key
  292. * @param int $start
  293. * @param int $end
  294. * @package array $option 参数
  295. * withscores=>true,表示数组下标为Order值,默认返回索引数组
  296. * limit=>array(0,1) 表示从0开始,取一条记录。
  297. * @return array|bool
  298. */
  299. public function zRangeByScore($key,$start='-inf',$end="+inf",$option=array())
  300. {
  301. return $this->redis->zRangeByScore($key,$start,$end,$option);
  302. }
  303. /**
  304. * 集合以order递减排列后,返回指定order之间的元素。
  305. * min和max可以是-inf和+inf 表示最大值,最小值
  306. * @param string $key
  307. * @param int $start
  308. * @param int $end
  309. * @package array $option 参数
  310. * withscores=>true,表示数组下标为Order值,默认返回索引数组
  311. * limit=>array(0,1) 表示从0开始,取一条记录。
  312. * @return array|bool
  313. */
  314. public function zRevRangeByScore($key,$start='-inf',$end="+inf",$option=array())
  315. {
  316. return $this->redis->zRevRangeByScore($key,$start,$end,$option);
  317. }
  318. /**
  319. * 返回order值在start end之间的数量
  320. * @param unknown $key
  321. * @param unknown $start
  322. * @param unknown $end
  323. */
  324. public function zCount($key,$start,$end)
  325. {
  326. return $this->redis->zCount($key,$start,$end);
  327. }
  328. /**
  329. * 返回值为value的order值
  330. * @param unknown $key
  331. * @param unknown $value
  332. */
  333. public function zScore($key,$value)
  334. {
  335. return $this->redis->zScore($key,$value);
  336. }
  337. /**
  338. * 返回集合以score递增加排序后,指定成员的排序号,从0开始。
  339. * @param unknown $key
  340. * @param unknown $value
  341. */
  342. public function zRank($key,$value)
  343. {
  344. return $this->redis->zRank($key,$value);
  345. }
  346. /**
  347. * 返回集合以score递增加排序后,指定成员的排序号,从0开始。
  348. * @param unknown $key
  349. * @param unknown $value
  350. */
  351. public function zRevRank($key,$value)
  352. {
  353. return $this->redis->zRevRank($key,$value);
  354. }
  355. /**
  356. * 删除集合中,score值在start end之间的元素 包括start end
  357. * min和max可以是-inf和+inf 表示最大值,最小值
  358. * @param unknown $key
  359. * @param unknown $start
  360. * @param unknown $end
  361. * @return 删除成员的数量。
  362. */
  363. public function zRemRangeByScore($key,$start,$end)
  364. {
  365. return $this->redis->zRemRangeByScore($key,$start,$end);
  366. }
  367. /**
  368. * 返回集合元素个数。
  369. * @param unknown $key
  370. */
  371. public function zCard($key)
  372. {
  373. return $this->redis->zCard($key);
  374. }
  375. /*********************队列操作命令************************/
  376. /**
  377. * 在队列尾部插入一个元素
  378. * @param unknown $key
  379. * @param unknown $value
  380. * 返回队列长度
  381. */
  382. public function rPush($key,$value)
  383. {
  384. return $this->redis->rPush($key,$value);
  385. }
  386. /**
  387. * 在队列尾部插入一个元素 如果key不存在,什么也不做
  388. * @param unknown $key
  389. * @param unknown $value
  390. * 返回队列长度
  391. */
  392. public function rPushx($key,$value)
  393. {
  394. return $this->redis->rPushx($key,$value);
  395. }
  396. /**
  397. * 在队列头部插入一个元素
  398. * @param unknown $key
  399. * @param unknown $value
  400. * 返回队列长度
  401. */
  402. public function lPush($key,$value)
  403. {
  404. return $this->redis->lPush($key,$value);
  405. }
  406. /**
  407. * 在队列头插入一个元素 如果key不存在,什么也不做
  408. * @param unknown $key
  409. * @param unknown $value
  410. * 返回队列长度
  411. */
  412. public function lPushx($key,$value)
  413. {
  414. return $this->redis->lPushx($key,$value);
  415. }
  416. /**
  417. * 返回队列长度
  418. * @param unknown $key
  419. */
  420. public function lLen($key)
  421. {
  422. return $this->redis->lLen($key);
  423. }
  424. /**
  425. * 返回队列指定区间的元素
  426. * @param unknown $key
  427. * @param unknown $start
  428. * @param unknown $end
  429. */
  430. public function lRange($key,$start,$end)
  431. {
  432. return $this->redis->lrange($key,$start,$end);
  433. }
  434. /**
  435. * 返回队列中指定索引的元素
  436. * @param unknown $key
  437. * @param unknown $index
  438. */
  439. public function lIndex($key,$index)
  440. {
  441. return $this->redis->lIndex($key,$index);
  442. }
  443. /**
  444. * 设定队列中指定index的值。
  445. * @param unknown $key
  446. * @param unknown $index
  447. * @param unknown $value
  448. */
  449. public function lSet($key,$index,$value)
  450. {
  451. return $this->redis->lSet($key,$index,$value);
  452. }
  453. /**
  454. * 删除值为vaule的count个元素
  455. * PHP-REDIS扩展的数据顺序与命令的顺序不太一样,不知道是不是bug
  456. * count>0 从尾部开始
  457. * >0 从头部开始
  458. * =0 删除全部
  459. * @param unknown $key
  460. * @param unknown $count
  461. * @param unknown $value
  462. */
  463. public function lRem($key,$count,$value)
  464. {
  465. return $this->redis->lRem($key,$value,$count);
  466. }
  467. /**
  468. * 删除并返回队列中的头元素。
  469. * @param unknown $key
  470. */
  471. public function lPop($key)
  472. {
  473. return $this->redis->lPop($key);
  474. }
  475. /**
  476. * 删除并返回队列中的尾元素
  477. * @param unknown $key
  478. */
  479. public function rPop($key)
  480. {
  481. return $this->redis->rPop($key);
  482. }
  483. /*************redis字符串操作命令*****************/
  484. /**
  485. * 设置一个key
  486. * @param unknown $key
  487. * @param unknown $value
  488. */
  489. public function set($key,$value)
  490. {
  491. return $this->redis->set($key,$value);
  492. }
  493. /**
  494. * 得到一个key
  495. * @param unknown $key
  496. */
  497. public function get($key)
  498. {
  499. return $this->redis->get($key);
  500. }
  501. /**
  502. * 设置一个有过期时间的key
  503. * @param unknown $key
  504. * @param unknown $expire
  505. * @param unknown $value
  506. */
  507. public function setex($key,$expire,$value)
  508. {
  509. return $this->redis->setex($key,$expire,$value);
  510. }
  511. /**
  512. * 设置一个key,如果key存在,不做任何操作.
  513. * @param unknown $key
  514. * @param unknown $value
  515. */
  516. public function setnx($key,$value)
  517. {
  518. return $this->redis->setnx($key,$value);
  519. }
  520. /**
  521. * 批量设置key
  522. * @param unknown $arr
  523. */
  524. public function mset($arr)
  525. {
  526. return $this->redis->mset($arr);
  527. }
  528. /*************redis 无序集合操作命令*****************/
  529. /**
  530. * 返回集合中所有元素
  531. * @param unknown $key
  532. */
  533. public function sMembers($key)
  534. {
  535. return $this->redis->sMembers($key);
  536. }
  537. /**
  538. * 求2个集合的差集
  539. * @param unknown $key1
  540. * @param unknown $key2
  541. */
  542. public function sDiff($key1,$key2)
  543. {
  544. return $this->redis->sDiff($key1,$key2);
  545. }
  546. /**
  547. * 添加集合。由于版本问题,扩展不支持批量添加。这里做了封装
  548. * @param unknown $key
  549. * @param string|array $value
  550. */
  551. public function sAdd($key,$value)
  552. {
  553. if(!is_array($value))
  554. $arr=array($value);
  555. else
  556. $arr=$value;
  557. foreach($arr as $row)
  558. $this->redis->sAdd($key,$row);
  559. }
  560. /**
  561. * 返回无序集合的元素个数
  562. * @param unknown $key
  563. */
  564. public function scard($key)
  565. {
  566. return $this->redis->scard($key);
  567. }
  568. /**
  569. * 从集合中删除一个元素
  570. * @param unknown $key
  571. * @param unknown $value
  572. */
  573. public function srem($key,$value)
  574. {
  575. return $this->redis->srem($key,$value);
  576. }
  577. /*************redis管理操作命令*****************/
  578. /**
  579. * 选择数据库
  580. * @param int $dbId 数据库ID号
  581. * @return bool
  582. */
  583. public function select($dbId)
  584. {
  585. $this->dbId=$dbId;
  586. return $this->redis->select($dbId);
  587. }
  588. /**
  589. * 清空当前数据库
  590. * @return bool
  591. */
  592. public function flushDB()
  593. {
  594. return $this->redis->flushDB();
  595. }
  596. /**
  597. * 返回当前库状态
  598. * @return array
  599. */
  600. public function info()
  601. {
  602. return $this->redis->info();
  603. }
  604. /**
  605. * 同步保存数据到磁盘
  606. */
  607. public function save()
  608. {
  609. return $this->redis->save();
  610. }
  611. /**
  612. * 异步保存数据到磁盘
  613. */
  614. public function bgSave()
  615. {
  616. return $this->redis->bgSave();
  617. }
  618. /**
  619. * 返回最后保存到磁盘的时间
  620. */
  621. public function lastSave()
  622. {
  623. return $this->redis->lastSave();
  624. }
  625. /**
  626. * 返回key,支持*多个字符,?一个字符
  627. * 只有* 表示全部
  628. * @param string $key
  629. * @return array
  630. */
  631. public function keys($key)
  632. {
  633. return $this->redis->keys($key);
  634. }
  635. /**
  636. * 删除指定key
  637. * @param unknown $key
  638. */
  639. public function del($key)
  640. {
  641. return $this->redis->del($key);
  642. }
  643. /**
  644. * 判断一个key值是不是存在
  645. * @param unknown $key
  646. */
  647. public function exists($key)
  648. {
  649. return $this->redis->exists($key);
  650. }
  651. /**
  652. * 为一个key设定过期时间 单位为秒
  653. * @param unknown $key
  654. * @param unknown $expire
  655. */
  656. public function expire($key,$expire)
  657. {
  658. return $this->redis->expire($key,$expire);
  659. }
  660. /**
  661. * 返回一个key还有多久过期,单位秒
  662. * @param unknown $key
  663. */
  664. public function ttl($key)
  665. {
  666. return $this->redis->ttl($key);
  667. }
  668. /**
  669. * 设定一个key什么时候过期,time为一个时间戳
  670. * @param unknown $key
  671. * @param unknown $time
  672. */
  673. public function exprieAt($key,$time)
  674. {
  675. return $this->redis->expireAt($key,$time);
  676. }
  677. /**
  678. * 关闭服务器链接
  679. */
  680. public function close()
  681. {
  682. return $this->redis->close();
  683. }
  684. /**
  685. * 关闭所有连接
  686. */
  687. public static function closeAll()
  688. {
  689. foreach(static::$_instance as $o)
  690. {
  691. if($o instanceof self)
  692. $o->close();
  693. }
  694. }
  695. /** 这里不关闭连接,因为session写入会在所有对象销毁之后。
  696. public function __destruct()
  697. {
  698. return $this->redis->close();
  699. }
  700. **/
  701. /**
  702. * 返回当前数据库key数量
  703. */
  704. public function dbSize()
  705. {
  706. return $this->redis->dbSize();
  707. }
  708. /**
  709. * 返回一个随机key
  710. */
  711. public function randomKey()
  712. {
  713. return $this->redis->randomKey();
  714. }
  715. /**
  716. * 得到当前数据库ID
  717. * @return int
  718. */
  719. public function getDbId()
  720. {
  721. return $this->dbId;
  722. }
  723. /**
  724. * 返回当前密码
  725. */
  726. public function getAuth()
  727. {
  728. return $this->auth;
  729. }
  730. public function getHost()
  731. {
  732. return $this->host;
  733. }
  734. public function getPort()
  735. {
  736. return $this->port;
  737. }
  738. public function getConnInfo()
  739. {
  740. return array(
  741. 'host'=>$this->host,
  742. 'port'=>$this->port,
  743. 'auth'=>$this->auth
  744. );
  745. }
  746. /*********************事务的相关方法************************/
  747. /**
  748. * 监控key,就是一个或多个key添加一个乐观锁
  749. * 在此期间如果key的值如果发生的改变,刚不能为key设定值
  750. * 可以重新取得Key的值。
  751. * @param unknown $key
  752. */
  753. public function watch($key)
  754. {
  755. return $this->redis->watch($key);
  756. }
  757. /**
  758. * 取消当前链接对所有key的watch
  759. * EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH
  760. */
  761. public function unwatch()
  762. {
  763. return $this->redis->unwatch();
  764. }
  765. /**
  766. * 开启一个事务
  767. * 事务的调用有两种模式Redis::MULTI和Redis::PIPELINE,
  768. * 默认是Redis::MULTI模式,
  769. * Redis::PIPELINE管道模式速度更快,但没有任何保证原子性有可能造成数据的丢失
  770. */
  771. public function multi($type=\Redis::MULTI)
  772. {
  773. return $this->redis->multi($type);
  774. }
  775. /**
  776. * 执行一个事务
  777. * 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行
  778. */
  779. public function exec()
  780. {
  781. return $this->redis->exec();
  782. }
  783. /**
  784. * 回滚一个事务
  785. */
  786. public function discard()
  787. {
  788. return $this->redis->discard();
  789. }
  790. /**
  791. * 测试当前链接是不是已经失效
  792. * 没有失效返回+PONG
  793. * 失效返回false
  794. */
  795. public function ping()
  796. {
  797. return $this->redis->ping();
  798. }
  799. public function auth($auth)
  800. {
  801. return $this->redis->auth($auth);
  802. }
  803. /*********************自定义的方法,用于简化操作************************/
  804. /**
  805. * 得到一组的ID号
  806. * @param unknown $prefix
  807. * @param unknown $ids
  808. */
  809. public function hashAll($prefix,$ids)
  810. {
  811. if($ids==false)
  812. return false;
  813. if(is_string($ids))
  814. $ids=explode(',', $ids);
  815. $arr=array();
  816. foreach($ids as $id)
  817. {
  818. $key=$prefix.'.'.$id;
  819. $res=$this->hGetAll($key);
  820. if($res!=false)
  821. $arr[]=$res;
  822. }
  823. return $arr;
  824. }
  825. /**
  826. * 生成一条消息,放在redis数据库中。使用0号库。
  827. * @param string|array $msg
  828. */
  829. public function pushMessage($lkey,$msg)
  830. {
  831. if(is_array($msg)){
  832. $msg = json_encode($msg);
  833. }
  834. $key = md5($msg);
  835. //如果消息已经存在,删除旧消息,已当前消息为准
  836. //echo $n=$this->lRem($lkey, 0, $key)."\n";
  837. //重新设置新消息
  838. $this->lPush($lkey, $key);
  839. $this->setex($key, 3600, $msg);
  840. return $key;
  841. }
  842. /**
  843. * 得到条批量删除key的命令
  844. * @param unknown $keys
  845. * @param unknown $dbId
  846. */
  847. public function delKeys($keys,$dbId)
  848. {
  849. $redisInfo=$this->getConnInfo();
  850. $cmdArr=array(
  851. 'redis-cli',
  852. '-a',
  853. $redisInfo['auth'],
  854. '-h',
  855. $redisInfo['host'],
  856. '-p',
  857. $redisInfo['port'],
  858. '-n',
  859. $dbId,
  860. );
  861. $redisStr=implode(' ', $cmdArr);
  862. $cmd="{$redisStr} KEYS \"{$keys}\" | xargs {$redisStr} del";
  863. return $cmd;
  864. }
  865. }