使用mysql的性能没有redis快。
利用redis的list结构做存储。
支持将之前的mysql结构转成redis
请勿转载个人代码,个人原创实现的逻辑。
上代码:
下载
<?php
namespace copyCommonCodes;
use think\Db;
class PubRecentRds
{
protected static $table = 'im_user_recent';
/**
CREATE TABLE `im_user_recent` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL DEFAULT '0' COMMENT '主uid',
`targetId` int(11) NOT NULL DEFAULT '0' COMMENT '目标id',
`isGroup` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0是人; 1是群',
`lastTime` int(11) NOT NULL DEFAULT '0' COMMENT '最后聊天时间',
`lastMsgId` char(20) NOT NULL DEFAULT '' COMMENT '最后一条消息id ',
`isTop` int(11) NOT NULL DEFAULT '0' COMMENT '是否置顶 >1则降序排序',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uid` (`uid`,`isGroup`,`targetId`) USING BTREE,
KEY `lastTime` (`lastTime`),
KEY `uid_2` (`uid`,`isTop`)
) ENGINE=InnoDB AUTO_INCREMENT=1543 DEFAULT CHARSET=utf8mb4 COMMENT='用户的最近联系人表';
*/
//群成员列表缓存名字
protected static function _getRecentListCacheName($uid) {
return 'chat.myRecentList2.'.$uid;
}
//单个聊天对象的缓存
protected static $_recentValCacheName = 'chat.recentVal';
//field 结构:myUid_targetId_isGroup
protected static function _getUserTargetKey($myUid, $targetId, $isGroup) {
return $myUid .'_'. $targetId .'_'. intval($isGroup);
}
//最近联系人 val
protected static function _opt_val($operate = 'set/remove/set/get', $myUid = 0, $hisSid = 0, $isGroup = 0, $data=[])
{
$uKey = self::_getUserTargetKey($myUid, $hisSid, $isGroup);
switch ($operate) {
case 'set':// 设置val
if(is_array($data)) $data = json_encode($data);
RDS::hset(self::$_recentValCacheName, $uKey, $data);
return $uKey;
case 'remove': //移除单个缓存
RDS::hdel(self::$_recentValCacheName, $uKey);
break;
case 'get': //读取
$json = RDS::hget(self::$_recentValCacheName, $uKey);
return $json ? json_decode($json, 1) : [];
}
}
//检测我的群列表是否写入过缓存
protected static function _checkMyRecentListCache($uid) {
$zCacheName = self::_getRecentListCacheName($uid);
//批量写入缓存
$pushListCache = function ($zCacheName, $recList) use($uid) {
foreach ($recList as $v_) {
$uKey = self::_opt_val('set', $uid, $v_['targetId'], $v_['isGroup'], $v_);
RDS::rpush($zCacheName, $uKey);
}
};
if(!RDS::existsKey($zCacheName)) {
$recList = self::_getMyRecenterBySql($uid);
$pushListCache($zCacheName, $recList);
} else {
// print_r('已有缓存');
}
}
//更新最近联系人的 时间 消息id 置顶
//使用场景(2):
//单聊的更新(群聊不需要频繁操作 有单独的缓存)
//最近联系人的置顶操作
// $data = ['lastMsgId'=> '', 'lastTime'=> '', 'isTop' => ''];
protected static function _updateRecord($uid, $hisSid, $isGroup, $data) {
$lastVal = self::_opt_val('get', $uid, $hisSid, $isGroup);
if($lastVal) {
$lastVal = array_merge($lastVal, $data);
} else {
if(!isset($data['targetId'])) $data['targetId'] = $hisSid;
if(!isset($data['isGroup'])) $data['isGroup'] = $isGroup;
if(!isset($data['lastTime'])) $data['lastTime'] = time();
if(!isset($data['lastMsgId'])) $data['lastMsgId'] = '';
if(!isset($data['isTop'])) $data['isTop'] = 0;
$lastVal = $data;
}
self::_opt_val('set',$uid, $hisSid, $isGroup, json_encode($lastVal));
}
//过度版 最近联系人
protected static function _getMyRecenterBySql($myUid)
{
//获取最近联系人
$where_ = [
'uid' => $myUid,
];
return Db::table(self::$table)->field('isGroup,targetId,lastTime,isTop,lastMsgId')->where($where_)->order('lastTime', 'desc')->select();
}
//检测是否需要插入最近联系人 好友消息:每次都要检测; 群消息: 如果存在则不需要更新,直接更新群的最近消息即可
protected static function _ifExistRecent($myUid = 0, $hisSid = 0, $isGroup = 0){
$zCacheName = self::_getRecentListCacheName($myUid);
$uKey = self::_getUserTargetKey($myUid, $hisSid, $isGroup);
$removeNum = RDS::lrem($zCacheName, $uKey); //如果删除数量>0 则存在此key
if($removeNum) {
//删除成功 需要 恢复原key
RDS::rpush($zCacheName, $uKey);
}
return $removeNum;
}
//===================== 公开接口 开始 =======================================
//插入最近联系人
//使用场景
//好友的每一条新消息
// 群聊的每一条新消息
public static function addRecent($myUid, $hisSid, $isGroup, $msgId, $time){
$exist = self::_ifExistRecent($myUid, $hisSid, $isGroup);
if(!$exist) {
//list写入
$zCacheName = self::_getRecentListCacheName($myUid);
$uKey = self::_getUserTargetKey($myUid, $hisSid, $isGroup);
RDS::rpush($zCacheName, $uKey);
//value更新
self::_updateRecord($myUid, $hisSid, $isGroup, [
'lastMsgId'=> $msgId, 'lastTime'=> $time, 'isTop' => 0,
]);
} else {
if(!$isGroup) {
//好友单聊,每条消息都需要更新value
self::_updateRecord($myUid, $hisSid, $isGroup, [
'lastMsgId'=> $msgId, 'lastTime'=> $time, 'isTop' => 0,
]);
//群聊有单独的更新方法 optGroupLastInfo
}
}
}
//移除最近联系人
public static function removeRecenter($myUid = 0, $hisSid = 0, $isGroup = 0)
{
Db::table(self::$table)->where([
'uid' => $myUid,
'isGroup' => intval($isGroup),
'targetId' => $hisSid
])->delete();
$recentValCacheName = 'chat.recentVal';
$zCacheName = self::_getRecentListCacheName($myUid);
$uKey = self::_getUserTargetKey($myUid, $hisSid,$isGroup);
RDS::lrem($zCacheName, $uKey);
RDS::hdel($recentValCacheName, $uKey);
//删除多余缓存
$isGroup = intval($isGroup);
$key_ = "{$myUid}_{$hisSid}_{$isGroup}";
RDS::hdel('chat.targetInMysql', $key_);
}
//我的最近联系人缓存查询
public static function getMyRecentListInCache($myUid)
{
self::_checkMyRecentListCache($myUid);
$zCacheName = self::_getRecentListCacheName($myUid);
$keyList = RDS::lrange($zCacheName, 0, 2000);
//最新的消息和时间在缓存里拿
$res = RDS::hMGet(self::$_recentValCacheName, $keyList);
// print_r('mGets'.PHP_EOL);
// print_r($res);
// exit;
$backList = [];
foreach ($keyList as $uKey_) {
$cacheInfo = isset($res[$uKey_]) ? json_decode($res[$uKey_], 1) : [];
$backList[] = $cacheInfo;
}
return $backList;
}
public static function setTop($myUid, $hisSid, $isGroup)
{
// top +1
$lastVal = self::_opt_val('get', $myUid, $hisSid, $isGroup);
$top_ = isset($lastVal['isTop']) ? $lastVal['isTop'] : 0;
$top_ ++;
self::_updateRecord($myUid, $hisSid, $isGroup, [
'isTop' => $top_
]);
}
//取消置顶
public static function unTop($myUid, $hisSid, $isGroup)
{
// top +1
$lastVal = self::_opt_val('get', $myUid, $hisSid, $isGroup);
$top_ = isset($lastVal['isTop']) ? $lastVal['isTop'] : 0;
$top_ --;
if($top_<0) $top_ = 0;
self::_updateRecord($myUid, $hisSid, $isGroup, [
'isTop' => $top_
]);
}
//操作群的最后一条消息
public static function optGroupLastInfo($operate='set/get/del', $gid, $uniqueId='', $time='') {
$cacheName = 'chat.groupLastMsg';
switch ($operate) {
case 'set'://插入最近联系人/群
RDS::hset($cacheName, $gid, $uniqueId .'|'. $time);
break;
case 'exist':
RDS::hexists($cacheName, $gid);
break;
case 'del':
RDS::hdel($cacheName, $gid);
break;
case 'get':
$val_ = RDS::hget($cacheName, $gid);
if(!$val_) {
return ['', ''];
}
list($uniqueId, $time) = explode('|', $val_);
return [$uniqueId, $time];
}
}
}