本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/95

redis的hash结构中存储了如下的数据:

$input = array(
"key" => $key, //唯一的key值
"qid" => $qid, //问题id
"value" => $startTime_$endTime, //开始时间_结束时间
)

需求:每天凌晨跑定时脚本,跑出一个key下的所有qid,判断当前时间与value,当$endTime<time()时,即为过期qid,需要删除它。

所以我们怎么找出所有的(field,values)呢,我想到了如下的方法:

  • 通过redis的HGETALL命令。
  • 在DB中存储一份key=>qid的集合。
  • 通过redis的HSCAN命令。

通过redis的HGETALL命令

$input = array(
"key" => $key,
); $ret = $objRedis->HGETALL($input);
var_dump($ret);

result:

array(3) {
["ret"]=>
array(1) {
["business_AdvancedPackageOne_2"]=>
array(16) {
[0]=>
array(2) {
["field"]=>
string(2) "58"
["value"]=>
string(3) "1_1"
}
[1]=>
array(2) {
["field"]=>
string(2) "56"
["value"]=>
string(5) "56_57"
}
[2]=>
array(2) {
["field"]=>
string(2) "57"
["value"]=>
string(5) "57_58"
}
...
}
}
["err_no"]=>
int(0)
["err_msg"]=>
string(2) "OK"
}

但是此方法有2个缺点:

  • 公司的redis中间层对返回数据做了64KB大小的限制,如果返回包的大小超过64KB,就会返回错误。
  • HGETALL会在大数据集下表现的很低效,复杂度为O(n),n为hash表的大小。

弃用。

在DB中存储一份key=>qid的集合

简答来说,就是在DB中也存储一份key=>qid的映射。首先先查找key对应的qid有哪些,在去redis中查找,所需要的操作只是HGET操作:

$input = array(
"key" => $key, //唯一的key值
"qid" => $qid, //问题id
)
$ret = $objRedis->HGET($input);

此方法的缺点在于:从DB中取出qid,需要循环遍历qid集合,一次次去读redis,不像HGETALL那样一次可以全部读出来。但是循环遍历操作我认为可以通过如下两个方法去解决:

  • 将循环遍历操作变为批量读取:
$input = array(
'reqs' => array(
$input1,
$input2,
...,
),
);
$rpc->$cmd($input);
  • 在数据库中增加startTime和endTime两个字段,直接在数据库层判断哪些qid过期,将过期的qid取出来,直接组装成数据,调用hdel一次性全部删除(以下是我脚本中的一段代码):
/**
* @desc 获取当前机构行家过期的增值包1的qid集合
* @param $objBusinessDB
* @param $businessId
* @param $currentTime
* @return array
*/
function getMergeQid($objBusinessDB,$businessId,$currentTime){
//过期的qid集合
$outDateQid = array();
$outDateId = array(); //查询过期的包信息
$sql = "select * from busines_package where businessId={$businessId} and endTime<{$currentTime} and deleted=0";
$dbRet = $objBusinessDB->query($sql);
if($dbRet === false){
Bd_Log::warning("select table fail.sql[".$sql."]");
return $outDateQid;
} foreach($dbRet as $value){
$extpack = mc_pack_pack2array($value['extpack']);
//合并qid信息
if(isset($extpack['qid']) && !empty($extpack['qid'])){
$outDateQid = array_merge($outDateQid,$extpack['qid']);
}
$outDateId[] = intval($value['id']);
} $sql = sprintf("update business_package set deleted=2 where id in (%s)",implode($outDateId));
$dbRet = $objBusinessDB->query($sql);
if($dbRet === false){
Bd_Log::warning("update table fail.sql[".$sql."]");
} return $outDateQid;
}
    //获取过期的qid集合
$outDateQid = getMergeQid($objBusinessDB,$businessId,$currentTime);
//删除redis中的qid记录
$redisRet = $objDaoRedis->HDEL($redisKey,$outDateQid);
if($redisRet['err_no'] != 0){
Bd_Log::warning("delete advancedPackage fail.businessId[$businessId] currentTime[$currentTime]");
}

为了防止过期的qid在第二天重复被读取,在发现有过期的qid集合时,将对应的deleted字段设置为2。这样的话就能防止被重复读取(where deleted=0),提高效率。

采用。

通过redis的HSCAN命令

关于HSCAN命令的介绍:SCAN

基本思想是:通过redis自身的HSCAN命令,循环读取一个key下的所有qid。优点很明显:

  • 相比于HGETALL,都能读取出一个key下所有的(field,value)。但是由于是循环读取,占用redis服务器的资源和耗时都较少。
  • 不用在DB中冗余存储一份key=>qid的映射,操作也相对于简单了许多。

但是悲催的是,公司的redis服务层不支持hscan命令:

array(2) {
["err_no"]=>
int(405)
["err_msg"]=>
string(37) "Unknown Method: unknown method(HSCAN)"
}

放弃。

综上,采用第二种方法:在DB中存储一份key=>qid的集合。

redis hash结构 遍历某一个key下所有的(field,values)的方法的更多相关文章

  1. Redis hash结构 和常用命令

    Redis 数据结构 -- 哈希 hash 是 一个 String 类型的field 和 value 的映射表 hash 的键值 对在内存中的一种无序的状态 命令 说明 备注 hdel key fie ...

  2. redis hash结构如何设置过期时间

    Redis中有个设置时间过期的功能,即通过setex或者expire实现,目前redis没有提供hsetex()这样的方法,redis中过期时间只针对顶级key类型,对于hash类型是不支持的,这个时 ...

  3. Redis hash结构

    1. select 更换命名空间 select 1 2. 设置hash,key为mp,键为name 值为zhangsan  hexists判断hash的key是否存在 3. 获得map中键为name的 ...

  4. Hashtable数据存储结构-遍历规则,Hash类型的复杂度为啥都是O(1)-源码分析

    Hashtable 是一个很常见的数据结构类型,前段时间阿里的面试官说只要搞懂了HashTable,hashMap,HashSet,treeMap,treeSet这几个数据结构,阿里的数据结构面试没问 ...

  5. spring-session之四:Spring Session下的Redis存储结构

    spring-session项目启动后 127.0.0.1:6379> keys * 1) "spring:session:index:org.springframework.sess ...

  6. PHP5实现foreach语言结构遍历一个类的实例

    PHP5实现foreach语言结构遍历一个类 创建一个类集成Iterator接口,并实现Iterator里面的方法即可,下面见实例代码实现 <?php class Test implements ...

  7. Go redis hash存储结构体

    需求 需要存储用户数据到redis,结构是hash. 然后取出来,自动转成结构体. 结构体 type UserCache struct { Id int64 `json:"id"` ...

  8. ***Redis hash是一个string类型的field和value的映射表.它的添加、删除操作都是O(1)(平均)。hash特别适合用于存储对象

    http://redis.readthedocs.org/en/latest/hash/hset.html HSET HSET key field value   (存一个对象的时候key存) 将哈希 ...

  9. Python:遍历一个目录下所有的文件及文件夹,然后计算每个文件的字符和line的小程序

    编写了一个遍历一个目录下所有的文件及文件夹,然后计算每个文件的字符和line的小程序,先把程序贴出来. #coding=utf-8 ''' Created on 2014年7月14日 @author: ...

随机推荐

  1. [模拟]P1202 [USACO1.1]黑色星期五Friday the Thirteenth

    原题 解析: 坑 其实.样例的部分是从周六~周五输出的,习惯不同吧..这里考虑到从这个月的13号到下一个月的13号所花天数为这个月的天数,然后愉快的判断一下闰年即可.这里的周一~周日编号为0~6,一月 ...

  2. Android 在通知栏实现计时功能

    Notification是APP 向系统发出通知时,它将先以图标的形式显示在通知栏中.用户可以下拉通知栏查看通知的详细信息.我们可以在通知栏实现自定义的效果,也可以结合service和BroadCas ...

  3. ajax处理级联访问数据库显示

    首先创建简单的html页面,写出如下代码: 静态页相关代码: js代码:

  4. 浏览器输入URL加载的全过程都发生了什么事情,你知道?

    什么是URL: 统一资源定位符(URL,英文 Uniform / Universal Reaource Locator 的缩写) 标准的URL由服务类型(协议).存放资源的主机域名(可以是域名或者ip ...

  5. SSL/TLS 握手过程详解

    在现代社会,互联网已经渗透到人们日常生活的方方面面,娱乐.经济.社会关系等都离不开互联网的帮助.在这个背景下,互联网安全就显得十分重要,没有提供足够的安全保障,人们是不会如此依赖它的.幸运的是,在大牛 ...

  6. ##6.2 Neutron计算节点-- openstack pike

    ##6.2 Neutron计算节点 openstack pike 安装 目录汇总 http://www.cnblogs.com/elvi/p/7613861.html ##6.2 Neutron计算节 ...

  7. poj 2769 Reduced ID Numbers 同余定理

    链接:http://poj.org/problem?id=2769 题意:寻找数m,是的对于n个数的余数不同 思路:暴力,优化:同余部分不用测试 代码: #include <iostream&g ...

  8. hdu 1150 Machine Schedule 最小覆盖点集

    题意:x,y两台机器各在一边,分别有模式x0 x1 x2 ... xn, y0 y1 y2 ... ym, 现在对给定K个任务,每个任务可以用xi模式或者yj模式完成,同时变换一次模式需要重新启动一次 ...

  9. Ubuntu TensorFlow 源码 Android Demo的编译运行

    Ubuntu TensorFlow 源码 Android Demo的编译运行 一. 安装 Android 的SDK和NDK SDK 配置 A:下载 国内下载地址选最新的: SDK: https://d ...

  10. C++雾中风景4:多态引出的困惑,对象的拷贝?

    C++作为一门面向对象的语言,自然具备了面向对象的三大特征:封装,继承,多态.在学习多态性质的过程中,发现了C++与其他语言很大的区别(坑?).在C++中的=操作符的使用与C++呈现的内存模型似乎并不 ...