背景:对于一些Redis里所提供的数据结构,用Hash可能是用得最多的,为何呢?因为日常中很多东西都可以用它来表示。
把一个复杂的需要序列化的东西变为一个HashTable进行存储,利用Hash存储节约内存,这样还可能更快更少的消耗实现高并发:

<?php
/** 每天下达指令的也就是x权限(每种鸡蛋的上下限),
而r就是只有看温度的权限,而w就是有设置温度权限 **/

$arr = array(
    "tcp" =>array(
  "ip"=>"192.168.1.1",
  "fd"=>"2",
  "mac"=>"00-50-56-C0-00-08",
  "chineseName"=>"乐窝108109",
  "EnglishName"=>"LevooAllCanBeHatch"
    ),
    "frame" =>array(
  array(
       "ip"=>"192.168.1.1",
       "fd"=>"2",
       "token"=>"jfdjfldjflkdjdjfkldf",
       "isconnected"=>0,
       "chmod"=>"rwx"

  ),
  array(
       "ip"=>"192.168.1.1",
       "fd"=>"2",  
       "token"=>"jfdjfldjflkdjdj1fkl2df",
       "isconnected"=>1,
       "chmod"=>"rw"
  
  ),
  array(
       "ip"=>"192.168.1.1",
       "fd"=>"2",
       "token"=>"jfdjfldjflkdjdjfk33ld22f",
       "isconnected"=>1,
       "chmod"=>"x"
  
  ),
      array(
       "ip"=>"192.168.1.1",
       "fd"=>"2",  
       "token"=>"jfdjfl323djdjfkld22f",
       "isconnected"=>1,
       "chmod"=>"rwx"
  
  ),
  array(
       "ip"=>"192.168.1.1",
       "fd"=>"2",
       "token"=>"jf1121fldjflkdjdjfkld22f",
       "isconnected"=>1,
       "chmod"=>"rwx"
  )
    )
);
print_r($arr);
echo json_encode($arr);
?>




命令示例:
    1. HSET/HGET/HDEL/HEXISTS/HLEN/HSETNX:
    #在Shell命令行启动Redis客户端程序
    /> redis-cli
    #给键值为myhash的键设置字段为field1,值为stephen。
    redis 127.0.0.1:6379> hset myhash field1 "stephen"
    (integer) 1
    #获取键值为myhash,字段为field1的值。
    redis 127.0.0.1:6379> hget myhash field1
    "stephen"
    #myhash键中不存在field2字段,因此返回nil。
    redis 127.0.0.1:6379> hget myhash field2
    (nil)
    #给myhash关联的Hashes值添加一个新的字段field2,其值为liu。
    redis 127.0.0.1:6379> hset myhash field2 "liu"
    (integer) 1
    #获取myhash键的字段数量。
    redis 127.0.0.1:6379> hlen myhash
    (integer) 2
    #判断myhash键中是否存在字段名为field1的字段,由于存在,返回值为1。
    redis 127.0.0.1:6379> hexists myhash field1
    (integer) 1
    #删除myhash键中字段名为field1的字段,删除成功返回1。
    redis 127.0.0.1:6379> hdel myhash field1
    (integer) 1
    #再次删除myhash键中字段名为field1的字段,由于上一条命令已经将其删除,因为没有删除,返回0。
    redis 127.0.0.1:6379> hdel myhash field1
    (integer) 0
    #判断myhash键中是否存在field1字段,由于上一条命令已经将其删除,因为返回0。
    redis 127.0.0.1:6379> hexists myhash field1
    (integer) 0
    #通过hsetnx命令给myhash添加新字段field1,其值为stephen,因为该字段已经被删除,所以该命令添加成功并返回1。
    redis 127.0.0.1:6379> hsetnx myhash field1 stephen
    (integer) 1
    #由于myhash的field1字段已经通过上一条命令添加成功,因为本条命令不做任何操作后返回0。
    redis 127.0.0.1:6379> hsetnx myhash field1 stephen
    (integer) 0

   2. HINCRBY:
    #删除该键,便于后面示例的测试。
    redis 127.0.0.1:6379> del myhash
    (integer) 1
    #准备测试数据,该myhash的field字段设定值1。
    redis 127.0.0.1:6379> hset myhash field 5
    (integer) 1
    #给myhash的field字段的值加1,返回加后的结果。
    redis 127.0.0.1:6379> hincrby myhash field 1
    (integer) 6
    #给myhash的field字段的值加-1,返回加后的结果。
    redis 127.0.0.1:6379> hincrby myhash field -1
    (integer) 5
    #给myhash的field字段的值加-10,返回加后的结果。
    redis 127.0.0.1:6379> hincrby myhash field -10
    (integer) -5  

    3. HGETALL/HKEYS/HVALS/HMGET/HMSET:
    #删除该键,便于后面示例测试。
    redis 127.0.0.1:6379> del myhash
    (integer) 1
    #为该键myhash,一次性设置多个字段,分别是field1 = "hello", field2 = "world"。
    redis 127.0.0.1:6379> hmset myhash field1 "hello" field2 "world"
    OK
    #获取myhash键的多个字段,其中field3并不存在,因为在返回结果中与该字段对应的值为nil。
    redis 127.0.0.1:6379> hmget myhash field1 field2 field3
    1) "hello"
    2) "world"
    3) (nil)
    #返回myhash键的所有字段及其值,从结果中可以看出,他们是逐对列出的。
    redis 127.0.0.1:6379> hgetall myhash
    1) "field1"
    2) "hello"
    3) "field2"
    4) "world"
    #仅获取myhash键中所有字段的名字。
    redis 127.0.0.1:6379> hkeys myhash
    1) "field1"
    2) "field2"
    #仅获取myhash键中所有字段的值。
    redis 127.0.0.1:6379> hvals myhash
    1) "hello"
    2) "world"

来自:http://www.cnblogs.com/stephen-liu74/archive/2012/03/19/2352932.html
实践如下:
Redis学习手册(Hashes数据类型):

/usr/local/redis/bin/redis-cli -h 10.44.202.177 -p 6379
10.44.202.177:6379> hset myhash field1 "stephen"
(integer) 1
10.44.202.177:6379>  hget myhash field1
"stephen"
10.44.202.177:6379> hget myhash field2
(nil)
10.44.202.177:6379>  hset myhash field2 "liu"
(integer) 1
10.44.202.177:6379>  hlen myhash
(integer) 2
10.44.202.177:6379> hexists myhash field1
(integer) 1
10.44.202.177:6379>
10.44.202.177:6379>  hdel myhash field1
(integer) 1
10.44.202.177:6379>  hdel myhash field1
(integer) 0
10.44.202.177:6379> hexists myhash field1
(integer) 0
10.44.202.177:6379>  hsetnx myhash field1 stephen
(integer) 1
10.44.202.177:6379>  hsetnx myhash field1 stephen
(integer) 0
10.44.202.177:6379>  del myhash
(integer) 1
10.44.202.177:6379> hset myhash field 5
(integer) 1
10.44.202.177:6379>  hincrby myhash field 1
(integer) 6
10.44.202.177:6379>  hincrby myhash field -1
(integer) 5
10.44.202.177:6379>  hincrby myhash field -10
(integer) -5
10.44.202.177:6379>  del myhash
(integer) 1
10.44.202.177:6379>  hmset myhash field1 "hello" field2 "world"
OK
10.44.202.177:6379>  hmget myhash field1 field2 field3
1) "hello"
2) "world"
3) (nil)
10.44.202.177:6379> hgetall myhash
1) "field1"
2) "hello"
3) "field2"
4) "world"
10.44.202.177:6379>  hkeys myhash
1) "field1"
2) "field2"
10.44.202.177:6379> hvals myhash
1) "hello"
2) "world"



PHP如何设置Hash:

<?php
$redis = new redis();
$redis->connect('10.51.77.34', 6379);
$redis->delete('test');
$redis->hset('test', 'key1', 'hello');
echo $redis->hget('test', 'key1');     //结果:hello

echo "<br>";
$redis->hSetNx('test', 'key1', 'world');
echo $redis->hget('test', 'key1');   //结果:hello

$redis->delete('test');
$redis->hSetNx('test', 'key1', 'world');
echo "<br>";
echo $redis->hget('test', 'key1');   //结果:world

echo $redis->hlen('test');   //结果:1
var_dump($redis->hdel('test','key1'));  //结果:bool(true)

$redis->delete('test');
$redis->hSet('test', 'a', 'x');
$redis->hSet('test', 'b', 'y');
$redis->hSet('test', 'c', 'z');
print_r($redis->hkeys('test'));  //结果:Array ( [0] => a [1] => b [2] => c )

print_r($redis->hvals('test'));  //结果:Array ( [0] => x [1] => y [2] => z )

print_r($redis->hgetall('test'));  //结果:Array ( [a] => x [b] => y [c] => z )

var_dump($redis->hExists('test', 'a'));  //结果:bool(true)

$redis->delete('test');
echo $redis->hIncrBy('test', 'a', 3);    //结果:3
echo $redis->hIncrBy('test', 'a', 1);    //结果:4

$redis->delete('test');
var_dump($redis->hmset('test', array('name' =>'tank', 'sex'=>"man"))); //结果:bool(true)
print_r($redis->hmget('test', array('name', 'sex')));  //结果:Array ( [name] => tank [sex] => man )

$redis->hSet("hashA", "name", "iname");
$redis->hSet("hashA", "age", "age");

// 同时设置多个值
$redis->hMset("hashA", [
    "gender" => "male",
    "salary" => 12000
]);
$redis->hGet("hashA", "salary");

// 获得多个值
var_dump($redis->hMGet("hashA", ["name", "gender"]));
?>




php redishash.php
hello<br>hello<br>world1int(1)
Array
(
    [0] => a
    [1] => b
    [2] => c
)
Array
(
    [0] => x
    [1] => y
    [2] => z
)
Array
(
    [a] => x
    [b] => y
    [c] => z
)
bool(true)
34bool(true)
Array
(
    [name] => tank
    [sex] => man
)
array(2) {
  ["name"]=>
  string(5) "iname"
  ["gender"]=>
  string(4) "male"
}
来自:http://blog.csdn.net/qjwcn/article/details/45293035
背景:在工作中经常会遇到一些关于定时任务的实际场景,比如每天凌晨1点自动备份数据库,或者,每隔1小时执行一次爬虫脚本,这种固定时间执行固定动作的需求我们称之为定时任务,利用crontab即可轻松实现。如果我们对自动备份数据库这个定时任务改变一下需求(这种情况就像你邀请一个人,一天内如果没有人来或有人来你通知下你,你邀请的人来了,这种任务。二、再就是公司没啥好的设备,钱少,网太烂了搞一个任务比如Mysql备份数据库的脚本,比如备份Redis的数据Bgsave的Scp拷贝经常出现网络不好,第一次备份会失败,于是得第二次这种垃圾需求。有垃圾需求就有解决办法,于于优雅或不优雅是一回事,但得技术人员觉得有一个流程总比没有流程好,本来没有方案的,于是就有技术方案。),如图:
点击在新窗口中浏览此图片  
如果仍然利用crontab来实现,就有点勉强了。类似这种需求最常见的是服务器之间的消息通知,假如服务器B由于网络不稳定或者服务器压力较大导致不能即时对服务器A的消息作出正确响应,那么服务器A就会延迟一段时间再次发送消息,直到收到服务器B的正确响应或者超出最大通知次数为止。过去的做法是定时扫表,把通知失败的消息再次发送一遍,虽然可以多次发送通知,但是发送间隔太短会增加服务器B的压力,发送间隔太长消息的时效性就不能保证,显然处理这种延时任务用crontab根本不能解决问题。
Node之Error: Cannot find module 'redis:
#npm install -g redis
/usr/lib
└─┬ redis@2.7.1
  ├── double-ended-queue@2.1.0-0
  ├── redis-commands@1.3.1
  └── redis-parser@2.6.0
环境变量:
#rpm -ql nodejs-6.10.3-1.el7.x86_64
/usr/bin/node
/usr/lib/node_modules
export NODE_PATH=/usr/lib/node_modules
#echo $NODE_PATH  
/usr/lib/node_modules
#node notice.js
订阅成功

select 3
OK
setex msg_2 2 chokingwin
OK
client.on("pmessage", function(pattern, channel, expiredKey) {
    console.log(pattern + "|" + channel + "|" + expiredKey);
_             _keyevent@3__:expired|__keyevent@3__:expired|msg_2




从Redis 2.8.0版本起,加入了"Keyspace notifications"(即"键空间通知")的功能。按照官方的说法:键空间通知,允许Redis客户端从“发布/订阅”通道中建立订阅关系,以便客户端能够在Redis中的数据因某种方式受到影响时收到相应事件。比如:所有改变给定key的命令;所有经过lpush操作的key;所有在0号数据库中过期的key等等。我们在处理延时任务的时候,先把通知失败的消息ID作为key的一部分存到redis缓存中,并设定过期时间(相当于延时),当这条缓存数据失效的时候,通过订阅关系(用NodeJS实现)就可以收到消息,通过分析消息就可以知道过期KEY,这样就可以再次发送消息通知,从而实现延时任务。
不过,需要注意一点:Redis的发布/订阅目前是即发即弃(fire and forget)模式的,因此无法实现事件的可靠通知。也就是说,如果发布/订阅的客户端断链之后又重连,则在客户端断链期间的所有事件都丢失了。
核心部分是两个Redis的终端,分别连接上Redis,并打开这个特性,另一个终端是监控的,这块里面用代码进行编写订阅,如下:
订阅,作者用的是Node,我在这儿不得不打下广告了,Swoole是不是应该也能支持这个功能?https://wiki.swoole.com/wiki/page/523.html ,http://blog.csdn.net/koastal/article/details/52869140,subscribe。
psubscribe来自:https://wiki.swoole.com/wiki/page/590.html

<?php
$serv = new Swoole\Server("127.0.0.1", 9501);
$serv->set(array(
    'worker_num' => 8,   //工作进程数量
    'daemonize' => false, //是否作为守护进程
));
$serv->on('connect', function ($serv, $fd){
    echo "Client:Connect.\n";
});
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
    $val = "";
    $redis = new Swoole\Coroutine\Redis();
    $redis->connect('10.51.77.34', 6379);
    while (true) {
        $val = $redis->psubscribe(['psubscribe __keyevent@3__:expired']);
        //订阅的channel,以第一次调用subscribe时的channel为准,后续的subscribe调用是为了收取Redis Server>的回包
        //如果需要改变订阅的channel,请close掉连接,再调用subscribe
        var_dump($val);
    }                                                                                                    
});
$serv->on('close', function ($serv, $fd) {
    echo "Client: Close.\n";
});
$serv->start();


Swoole的这个Redis的Coroutine必须要有一个端口暴露,这是和Node最大的不同吧?上面这个图我试着使用了一下,感觉有点问题。
=============================================================================

#redis-cli -h 10.51.77.34
10.51.77.34:6379>  psubscribe __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyevent@0__:expired"
3) (integer) 1



1) "pmessage"
2) "__keyevent@0__:expired"
3) "__keyevent@0__:expired"
4) "name"



塞一个数据进去:

#redis-cli -h 10.51.77.34
10.51.77.34:6379> config set notify-keyspace-events Ex
OK
10.51.77.34:6379> setex name 10 chokingwin
OK


===================================================================================
关于expired事件通知的发送时间
Redis使用以下两种方式删除过期的键:a:当一个键被访问时,程序会对这个键进行检查,如果键已过期,则删除该键;b:系统会在后台定期扫描并删除那些过期的键。
当过期键被以上两种方式中的任意一种发现并且删除时,才会产生expired事件通知。
Redis不保证生存时间(TTL)变为 0 的键会立即被删除:如果没有命令访问这个键,或者设置生存时间的键非常多的话,那么在键的生存时间变为0,到该键真正被删除,这中间可能会有一段比较显著的时间间隔。
因此,Redis产生expired事件通知的时间,是过期键被删除的时候,而不是键的生存时间变为 0 的时候。
接下来我们开始代码实现(假定阅读本文的同学已正确安装Nginx/PHP/Redis/NodeJS的环境)。

一、与本文相关的环境信息
Redis配置文件路径:/etc/redis/6379.conf
测试用的Redis库编号为:3
监听消息的NodeJS文件:/NodeApp/notice.js
发送消息的PHP代码为:/send.php
接收redis数据的PHP代码:/test.php
业务流程:首先运行notice.js开启监听,然后运行send.php发送消息,如果没有收到成功响应,将消息ID存入redis缓存,之后按照10秒、30秒、60秒、120秒、300秒的时间间隔,再次发送消息通知,直到收到对消息的成功响应,或者超出最大通知次数为止。

二、修改Redis配置文件
因为键空间通知功能需要耗费一定的CPU时间,因此默认情况下,该功能是关闭的。可以通过修改配置文件,或者通过CONFIG SET命令,设置notify-keyspace-events选项,来启用或关闭该功能。
该选项的值为空字符串时,该功能禁用,选项值为非空字符串时,启用该功能,非空字符串由特定的多个字符组成,每个字符表示不同的意义:
K keyspace事件,事件以__keyspace@<db>__为前缀进行发布
E keyevent事件,事件以__keyevent@<db>__为前缀进行发布
g 一般性的,非特定类型的命令,比如del,expire,rename等
$ 字符串特定命令
l 列表特定命令
s 集合特定命令
h 哈希特定命令
z 有序集合特定命令
x 过期事件,当某个键过期并删除时会产生该事件
e 驱逐事件,当某个键因maxmemore策略而被删除时,产生该事件
A g$lshzxe的别名,因此”AKE”意味着所有事件
注意:该选项的值中至少需要包含K或者E,否则不会发布任何事件。比如,如果需要开启针对列表的keyspace事件通知,则该选项需要配置为"Kl"。

我们在服务器上运行vim /etc/redis/6379.conf,找到notify-keyspace-events开头的一行,将其配置为:notify-keyspace-events Ex,含义为:发布keyevent事件,使用过期事件(当每一个key失效时,都会生成该事件)。保存退出,并重启redis服务。如图:
点击在新窗口中浏览此图片

三、安装Node扩展
在网站根目录下,依次运行:
npm init #初始化创建package.json
npm install redis #安装redis扩展
npm install mysql #安装mysql扩展

四、实现send.php

为了便于实现延时的计算,我们将存入redis的key格式设计为:固定前缀+消息ID+时间戳+次数,如:noticeId_12345678_1482991887_2点击在新窗口中浏览此图片
关键代码:
$delayArr=[0,10,30,60,120,300];//延时间隔,相对于首次通知时间,单位为 s
$res=doSomething();//发送消息
$content=date('Y-m-d H:i:s').' 第 '.$nums.' 次发送通知,消息ID为:'.$noticeId."\n";
if($res==true){
$content.='消息发送成功'."\n";
}else{//未收到对方回应
$content.='消息发送失败,等待下次重发'."\n";
$expTime=$delayArr[$nums];
$nums++;
saveNoticeToRedis($noticeId,$stamp,$nums,$expTime);//存入缓存
}
//记录日志
file_put_contents($root.'/tmp.log',$content,FILE_APPEND);

五、实现 notice.js
服务器端运行notice.js后,会一直监听redis的Expired事件,取到ExpiredKey后,把消息ID、时间、通知次数,POST给test.php,从而实现再次发送消息。

关键代码:
var client = redis.createClient('6379', '127.0.0.1');
client.psubscribe("__keyevent@"+redisDB+"__:expired",function(){
//console.log('订阅成功');
});
client.on("pmessage", function(pattern, channel, expiredKey) {
var tmpArr=expiredKey.split('_');
if(tmpArr[0]==keyPrefix){
console.log('-----expired Key-----',expiredKey);
var noticeId=tmpArr[1];
var stamp=parseInt(tmpArr[2]);
var nums=parseInt(tmpArr[3]);
sendPost(noticeId,stamp,nums,logFile);//向test.php发送数据
}else{
console.log('-----error Key-----',expiredKey);
writeLog(logFile,'The key "'+expiredKey+'" is a error key.');
}
});

六、实现 test.php
点击在新窗口中浏览此图片

关键代码:

$delayArr=[0,10,30,60,120,300];//延时间隔,相对于首次通知时间,单位为 s
$res=doSomething();//发送消息
$content=date('Y-m-d H:i:s').' 第 '.$nums.' 次发送通知,消息ID为:'.$noticeId."\n";
if($res==true){
$content.='消息发送成功'."\n";
}else{//未收到对方回应
if($nums && $nums>=6){
$content.='消息ID:'.$noticeId."已达到最大通知次数,任务停止\n";
}else{
$content.='消息发送失败,等待下次重发'."\n";
$expTime=$stamp+$delayArr[$nums]-time();
$nums++;
saveNoticeToRedis($noticeId,$stamp,$nums,$expTime);//存入缓存
}
}
//记录日志
file_put_contents($root.'/tmp.log',$content,FILE_APPEND);

七、测试结果
点击在新窗口中浏览此图片

八、其他说明
本文内容为个人原创,首发今日头条,同时提供代码下载地址,供大家学习交流。本人以后还会发布更多原创干货,如果觉得有用,希望及时关注本头条号。

代码下载地址:http://www.i1981.com/zb_users/upload/2016/12/20161223.zip
DownLoad:
下载文件
点击这里下载文件


From: http://www.toutiao.com/a6369425996433408257/?tt_from=weixin&utm_campaign=client_share&app=news_article&utm_source=weixin&iid=11032449540&utm_medium=toutiao_ios&wxshare_count=1
ls  -lart /data/redis6413 |less
-rw-r--r--  1 redis redis 211841024 Jan  9 14:00 temp-33345.rdb
-rw-r--r--  1 redis redis 212078592 Jan  9 15:00 temp-53462.rdb
-rw-r--r--  1 redis redis 220446720 Jan  9 16:00 temp-8399.rdb
-rw-r--r--  1 redis redis 212865024 Jan  9 17:00 temp-28516.rdb

http://blog.csdn.net/opens_tym/article/details/10097805
背景: 有时间对Redis的一个测试来看,特别是经过了网络,此时,会傻傻分不清楚是Redis本来性能就差,还是网络不好(PHP调用Redis的机器和Redis分离或不在同一网段),这个工具可以直接在Redis上测试Redis,或是在PHP上测试Redis,这样有一个粗粒度的判断和把握。



阅读全文
背景:如果redis缓慢多是hgetall导致的当然其他的情况也有...

在没关注这个函数之前,一直用的Memcache的数据存储方式,但是自从更换了redis之后,对于一个hash的数据存与取 对于Memcache方便甚多,但是问题来了,一个hash的列表如果量不大的情况,用hGetAll函数几乎看不出问题,一旦这个列表超过50或者更多时,此时用hGetAll函数便能很直观的看到性能问题,这里就不作数据分析了。

Redis是单线程的!当它处理一个请求时其他的请求只能等着。通常请求都会很快处理完,但是当我们使用HGETALL的时候,必须遍历每个字段来获取数据,这期间消耗的CPU资源和字段数成正比,如果还用了PIPELINING,无疑更是雪上加霜。
PERFORMANCE = CPUs / OPERATIONs
也就是说,此场景下为了提升性能,要么增加运算过程中的CPU数量;要么降低运算过程中的操作数量。在为了继续使用hash结构的数据,又要解决此问题,比较方便的方法就是将hash以序列化字符串存储,取的时候先取出反序列化的数据,再用hGet(key,array(hash..))。

例如:
....
$arrKey = array('dbfba184bef630526a75f2cd073a6098','dbfba184bef630526a75f2cd0dswet98')
$strKey = 'test';
$obj->hmGet($strKey,$arrKey);
把原本的hGetAll操作简化为hGet,也就是说,不再需要遍历hash中的每一个字段,因此即便不能让多个CPU参与运算,但是却大幅降低了操作数量,所以性能的提升仍然是显著的;当然劣势也很明显,和所有的冗余方式一样,此方案浪费了大量的内存。

有人会问,这样虽然没有了遍历字段的过程,但是却增加了反序列化的过程,而反序列化的成本往往也是很高的,难道这样也能提升性能?问题的关键在于开始我们遍历字段的操作是在一个cpu上完成的,后来反序列化的操作,不管是什么语言,都可以通过多进程或多线程来保证是在多个cpu上完成的,所以性能总体上是提升的。

另外,很多人直觉是通过运行redis多实例来解决问题。确实,这样可以增加运算过程中的CPU数量,有助于提升性能,但是需要注意的是,hGetAll和PIPELINING往往会让运算过程中的操作数量呈几何级爆炸式增长,相比之下,我们能增加的redis多实例数量简直就是杯水车薪,所以本例中这种方法不能彻底解决问题。

来自:http://www.mudbest.com/redis%E7%9A%84hgetall%E5%87%BD%E6%95%B0%E7%9A%84%E6%80%A7%E8%83%BD%E9%97%AE%E9%A2%98/?utm_source=tuicool&utm_medium=referral
Redis的慢日志是一个系统记录了超出规定的执行时间查询。执行时间不包括I/O操作,比如与客户会话,发送回复等等,只是实际执行的命令(这就是线程被阻塞而无法执行命令的唯一阶段所需的时间为在此期间其他请求)。可以用两个参数来配置的慢日志:slowlog-log-slower-than告诉Redis是什么的执行时间,以微秒为单位,以超过为获得记录的命令。需要注意的是负数禁用慢日志,而零值强制每个命令的记录。slowlog-max-len是慢日志的长度。最小值是零。当一个新的命令被记录和慢日志已处于其最大长度时,最早的一个是从记录的命令队列中移出以腾出空间。该配置可以通过编辑redis.conf完成或当服务器使用CONFIG GET和Config中设置的命令运行。

一、Redis默认记录超过10000us的命令:
10.64.*.54:6379> config get slowlog-log-slower-than
1) "slowlog-log-slower-than"
2) "10000"


二、默认保留128条慢查询日志:
10.64.*.54:6379> config get slowlog-max-len
1) "slowlog-max-len"
2) "128"


三、慢查询日志查询,2条:
[root@rh08 ~]# redis-cli -h 10.64.*.54
10.64.6.54:6379>  slowlog get 2
1) 1) (integer) 2
   2) (integer) 1470219973
   3) (integer) 36910
   4) 1) "info"
2) 1) (integer) 1
   2) (integer) 1469739564
   3) (integer) 14997
   4) 1) "save"
背景:如果要用c写成一个服务部署4台机器,均得和memcached交互,还得用这个库,如PHP的扩展啥的没怎么和c的lib打交到,这下实践一下挺好的,过程比较曲折但是也发现了这memcached用新的gcc还是对配置上有一些影响的,就单说配置这块的门槛就变高了,更别说c下的调试啥的,实践起来仅仅通过网上的一篇文章未必能搞定。
libmemcached是C客户端到memcached服 务器的接口库。具有低内存占用率、线程安全、并提供对memcached功能的全面支持。它还采用多种命令行工具,包括: memcat、memflush、memrm、memstat、memslap。
在Ubuntu上安装memcached和libmemcached  http://www.linuxidc.com/Linux/2010-04/25543.htm
libmemcached C/C++ API使用实例 http://www.linuxidc.com/Linux/2012-01/52516.htm
memcached简单的使用教程(转载):          http://blog.csdn.net/huangqiwa/article/details/21174425
使用连接池访问memcached(libmemcached)的完整例子:http://blog.csdn.net/hzhxxx/article/details/41961355

(1)封装 libmemcached 构建 memcached 客户端:http://blog.csdn.net/yanghw0510/article/details/7292236
(2)libmemcached使用(c 客户端连接 memcached) :
(3)libmemcached 下载地址。https://launchpad.net/libmemcached/

1、下载安装libmemcached
$ wget http://launchpad.net/libmemcached/1.0/0.44/+download/libmemcached-0.44.tar.gz
$ tar xvzf libmemcached-0.44tar.gz
$ cd libmemcached-0.44
$ ./configure
$ make
$ sudo make install
libmemcached 默认安装在/usr/local/,头文件安装在/usr/local/include/libmemcachde/,动态库默认安装在/usr/local/lib/下。
我在这儿下的:https://launchpadlibrarian.net/165454254/libmemcached-1.0.18.tar.gz
注:
如果报”./libmemcached-1.0/memcached.h:46:27: error: tr1/cinttypes: No such file or directory”错误,则需要升级gcc版本.
处理如下:
yum install gcc44 gcc44-c++ libstdc++44-devel
export CC=/usr/bin/gcc44
export CXX=/usr/bin/g++44
重新编译安装libmemcached-1.0.9
关键信息:error: tr1/cinttypes: No such file or directory
报错原因:libmemcached需要 gcc 4.2 以上版本才可编译,而centos 5.4 的gcc版本只有4.1 ,详见:https://bugs.launchpad.net/libmemcached/+bug/1076181
解决方法:安装gcc44的扩展包,详见:http://gearman.info/build/centos5-8.html
export就是在于此,把这个gcc和g++的版本号给提上去,以方便这个memcached的编译:
/usr/bin/gcc44 -v
gcc version 4.4.7 20120313 (Red Hat 4.4.7-1) (GCC)

gcc -v
gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)

/usr/bin/g++44 -v
gcc version 4.4.7 20120313 (Red Hat 4.4.7-1) (GCC)
g++ -v
gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)
——————————————————————————————————————————————————————————
这个问题在安gearman时也遇到过,升级gcc,参考连接: http://jackxiang.com/post/7693/ ,软链接如下所示:
export CC=/usr/bin/gcc44 or export CC=/usr/bin/gcc
export CXX=/usr/bin/g++44
./configure && make && make install      

./configure && make && make install
/usr/bin/install -c -m 644 support/libmemcached.pc '/usr/local/lib/pkgconfig'
make[2]: Leaving directory `/tmp/testmemcachedcpp/libmemcached-1.0.18'
make[1]: Leaving directory `/tmp/testmemcachedcpp/libmemcached-1.0.18'

g++ -o testmemcached testmemcached.cpp -lmemcached  
In file included from /usr/local/include/libmemcached/memcached.h:39,
                 from testmemcached.cpp:6:
/usr/local/include/libmemcached-1.0/memcached.h:46:23: error: cinttypes: No such file or directory
明天再整下,估计是so没有给ldd进来...
2、libmemcached简单测试使用


编译:g++ -o testmemcached testmemcached.cpp -lmemcached
运行:./testmemcached
结果:Save data:value sucessful!
      Get value:value sucessful!
      Delete key:key sucessful!

实践编译一下:
g++ -o testmemcached testmemcached.cpp -lmemcached
In file included from /usr/local/include/libmemcached/memcached.h:39,
                 from testmemcached.cpp:6:
/usr/local/include/libmemcached-1.0/memcached.h:46:23: error: cinttypes: No such file or directory
看来还得重新export一次新的编译器,因为昨天的关了终端,今天的又没了:
export CC=/usr/bin/gcc44 or export CC=/usr/bin/gcc  //这行有问题~
export CXX=/usr/bin/g++44
g++ -o testmemcached testmemcached.cpp -lmemcached
为何还是不行?
echo $CC
/usr/bin/gcc  //没变,这儿有问题,再重新设置一次,查到上面这个or有问题:export CC=/usr/bin/gcc44。
# echo $CXX
/usr/bin/g++44 //变了

重新设置,好了:
export CC=/usr/bin/gcc44
/usr/bin/gcc44

继续折腾,/usr/bin/gcc44 -o testmemcached testmemcached.cpp -lmemcached
In file included from /usr/lib/gcc/x86_64-redhat-linux6E/4.4.7/../../../../include/c++/4.4.7/cinttypes:35,
                 from /usr/local/include/libmemcached-1.0/memcached.h:46,
                 from /usr/local/include/libmemcached/memcached.h:39,
                 from testmemcached.cpp:6:
/usr/lib/gcc/x86_64-redhat-linux6E/4.4.7/../../../../include/c++/4.4.7/c++0x_warning.h:31:2: error: #error This file requires compiler and library support for the upcoming ISO C++ standard, C++0x. This support is currently experimental, and must be enabled with the -std=c++0x or -std=gnu++0x compiler options.


在网上找了一下,发现是这样的:http://blog.163.com/guixl_001/blog/static/4176410420121021111117987/ 里说得加一个新的参数,是和宏相关,那就加了,
指定一个新的参数:-std=c++0x :
/usr/bin/gcc44  -o testmemcached testmemcached.cpp -lmemcached   -std=c++0x            
/usr/local/lib/libmemcached.so: undefined reference to `pthread_once'
collect2: ld returned 1 exit status


/usr/bin/g++44  -o testmemcached testmemcached.cpp -lmemcached   -std=c++0x              
/usr/local/lib/libmemcached.so: undefined reference to `pthread_once'
怎样彻底解决"undefined reference to `pthread_create'"问题 ,说是加个: -lrt ,这一看就是那个memcached的库包含了这个文件:#include<pthread.h>
来源参考:
http://bbs.chinaunix.net/thread-1586752-1-1.html

于是Ok啦,最终O了,如下:
[root@test testmemcachedcpp]# /usr/bin/g++44  -o testmemcached testmemcached.cpp -lmemcached -lrt  -std=c++0x
[root@test testmemcachedcpp]#  
运行一下试试:
[root@test testmemcachedcpp]#  ./testmemcached
./testmemcached: error while loading shared libraries: libmemcached.so.11: cannot open shared object file: No such file or directory
[root@test /]# find . -name "libmemcached.so.11"
./usr/local/lib/libmemcached.so.11
打开配置文件 vi /etc/ld.so.conf
加上一行:/usr/local/lib
执行/sbin/ldconfig /etc/ld.so.conf
[root@test testmemcachedcpp]# /sbin/ldconfig /etc/ld.so.conf
[root@test testmemcachedcpp]# ./testmemcached              
[root@test testmemcachedcpp]#
没报错了~,用gdb看一下 加个-g参数:
/usr/bin/g++44 -g  -o testmemcached testmemcached.cpp -lmemcached -lrt  -std=c++0x

发现localhost没有开11211,于是找了一台机器把ip和端口写上,重新编译一次后运行Ok,如下:
[root@test testmemcachedcpp]# ./testmemcached
Save data:value sucessful!
Get value:value sucessful!

少了一个删除成功的,简单调试一下:
(gdb) n
Get value:value sucessful!
50         rc=memcached_delete(memc,key.c_str(),key_length,expiration);
(gdb) p key.c_str()
$5 = 0x184372e8 "key"
(gdb) p key_length
$6 = 3
(gdb) n
51         if(rc==MEMCACHED_SUCCESS)
(gdb) p rc
$3 = MEMCACHED_INVALID_ARGUMENTS

也就是调这个函数的参数不对,返回不是MEMCACHED_SUCCESS,所以没有打印出:cout<<"Delete key:"<<key<<" sucessful!"<<endl;

####################新的代码里grep了一下该方法###########################
./tests/mem_udp.cc:                 memcached_delete(memc, test_literal_param("foo"), 0));
./libmemcached-1.0/memcached.hpp:    return memcached_success(memcached_delete(memc_, key.c_str(), key.length(), 0));
./libmemcached-1.0/memcached.hpp:    return memcached_success(memcached_delete(memc_,
./tests/libmemcached-1.0/replication.cc:    memcached_return_t del_rc= memcached_delete(memc_replicated,
./tests/libmemcached-1.0/mem_functions.cc:    rc= memcached_delete(memc, "foo", 3, 1);

./tests/libmemcached-1.0/mem_functions.cc:    test_compare(MEMCACHED_BUFFERED, (rc= memcached_delete(memc, "foo", 3, 0)));
./tests/libmemcached-1.0/mem_functions.cc:    test_compare(MEMCACHED_SUCCESS, memcached_delete(memc, "foo", 3, 0));
./tests/libmemcached-1.0/mem_functions.cc:    test_compare(MEMCACHED_NOTFOUND, memcached_delete(memc, "foo", 3, 0));

####################################################################
于是修改为下面这样:
//rc=memcached_delete(memc,key.c_str(),key_length,expiration);
rc=memcached_delete(memc,key.c_str(),key_length,0);

./testmemcached
Save data:value2 sucessful!
Get value:value2 sucessful!
Delete key:key sucessful!

刚才失败时有值,现在运行后没值了,给成功删除了:
[root@localhost ~]# telnet 192.168.108.7 11211
Escape character is '^]'.
get key
VALUE key 0 5
value
END
get key
END



后面怎么结合c的socket 及epoll进行进程/线程调试就不再细说了,我也在摸索中,应该也有相关的调试办法,但这儿说明这个memcached搞个新的gcc版本库才能编译确实带了相当多编译及配置上的的麻烦~

最后,用C函数里的add函数实现类php的memcache的扩展的原子操作函数,如下:
原子操作方法,假如有add后就不能再add,必须删除后才能add,这个可以用作锁:
./libmemcached-1.0.18/libmemcached-1.0/memcached.hpp:    


和php的add函数一样第一次运行时没有问题此时值设置成功,再运行add时一次就add不进去,add设置进去后,用set是能重新设置进去的,只是用add原子操作是不行的,用它来做锁很好使:
1)代码片段:

2)运行情况:
[root@test testmemcachedcpp]# gdb testmemcached
b 41
r
Add data:value unsucessful,Perhaps add key before set !
(gdb) p rc
$2 = MEMCACHED_NOTSTORED
但是能够set进去:
42         rc=memcached_set(memc,key.c_str(),key.length(),value.c_str(),value.length(),expiration,flags);
(gdb) n
43         if(rc==MEMCACHED_SUCCESS)
(gdb) p rc
$3 = MEMCACHED_SUCCESS
EOF


对add后的键对应的值,再用set方法是可以设置的,如下:
string key = "key2";
string value = "jackxiang";
rc=memcached_add(memc,key.c_str(),key.length(),value.c_str(),value.length(),expiration,flags);
后再修改这个值:
value = "xiangdong";
rc=memcached_set(memc,key.c_str(),key.length(),value.c_str(),value.length(),expiration,flags);

用gdb的断点跟踪一下并用telnet在设置后查看,如下:
[root@localhost ~]# telnet 192.168.108.7 11211
Escape character is '^]'.
get key2
END
get key2
VALUE key2 0 9
jackxiang
END
get key2
VALUE key2 0 9
xiangdong
END

听说,在下载客户端libmemcached-1.0.18/tests 下有测试列子,也可参考。比如cpp_example.cc
ls ~+/cpp_example.cc
/tmp/testmemcachedcpp/libmemcached-1.0.18/tests/cpp_example.cc

摘自:http://www.cppblog.com/kefeng/archive/2010/10/11/129422.html
         http://blog.chinaunix.net/uid-52437-id-2108905.html
后发现这个哥们写的文章很详细,http://blog.chinaunix.net/uid-20548989-id-1667248.html

加入add后,rc=MEMCACHED_NOTSTORED情况,代码贴下面:
背景:关于队列,Linux底层已有数据结构支持,也有Erlang写的rabbitQ,这个MemcacheQ和张宴兄弟的httpsqs一样的基于libevent的http接口加上BerkeleyDB(现在好你没更新了吧?),应该还是相当稳定的,有时间安个试试。

安装实践的过程如下,Libevent已经在我的虚拟机里安过了,不再描述,安装来源:http://blog.sina.com.cn/s/blog_a0db295e01015eqt.html
开始安装:
(1)berkeley-db:
http://download.oracle.com/otn/berkeley-db/db-5.3.15.tar.gz
~/software/db-5.3.15/build_unix# ../dist/configure --prefix=/usr/local/webserver/berkeleyDB && make && make install
(2)memcacheq:
http://code.google.com/p/memcacheq/downloads/list
~/software/memcacheq-0.2.0# ./configure --prefix=/usr/local/webserver/memcacheq --enable-threads --with-libevent --with-bdb=/usr/local/webserver/berkelyDB
#-j 8
make -j 8
make install
注意:在没有安装BerkeleyDB的情况下,会提示( configure: error: cannot find libdb.so in /usr/local/BerkeleyDB.5.3/lib);
It is normal verbose message. If you dont want to keep it, you can start without '-v'.

make && make install
测试是否安装成功:
/usr/local/webserver/memcacheq/bin/memcacheq -h
如果报错:
memcacheq: error while loading shared libraries: libdb-5.3.so: cannot open shared object file: No such file or directory
解决方法:
ln -s /usr/local/webserver/BerkeleyDB.5.3/lib/libdb-5.3.so /usr/lib/libdb-5.3.so
ldconfig
之前可先根据实际安装路径配置链接库:
可以修改/etc/ld.so.conf,增加BDB和libevent的库路径(建议)
/usr/local/BerkeleyDB.5.0/lib
/usr/local/lib
也可以临时export LD_LIBRARY_PATH=/usr/local/BerkeleyDB.5.0/lib/:/usr/local/lib:$LD_LIBRARY_PATH

(3)启动服务:
/usr/local/webserver/memcacheq/bin/memcacheq -d -r -uroot -p11212 -H /data0/memcacheq/data -N -R -v -L 1024 -B 1024  /data/logs/mq_error.log 2>&1
(/data0/memcacheq/data 必须自己创建)
mkdir -p  /data0/memcacheq/data
chmod -R 777 /data0/memcacheq/data

(4)检查是否启动成功:
ps -aux |grep memcacheq

(5)增加到开机启动:
编辑 /etc/rc.local
增加以下内容:
/usr/local/webserver/memcacheq/bin/memcacheq -d -r -uroot -p11212 -H /var/memcacheq/data -N -R -v -L 1024 -B 1024  /data/logs/mq_error.log 2>&1

(6)参数不对容易出现"qstats dump thread: a qstats is dump.",请加上参数v:
(It is normal verbose message. If you dont want to keep it, you can start without '-v'.)
nohup /usr/local/memcacheq/bin/memcacheq -d -u www -p61611 -r -H /usr/local/memcacheq/data -N -v -L 1024 -B 1023 > /usr/local/memcacheq/logs/mq_error.log 2>&1

MemcacheQ使用以及与PHP连接 实验之shell下telnet操作该queque:
实践来源,MemcacheQ使用以及与PHP连接:(里面有shell操作队列的方法,很适用!)
http://ifxoxo.com/use-memcacheq.html
开始实践:
1)set 增加一个信息到队列的尾部
telnet 127.0.0.1 11212
set <queue name> <flags> 0 <message_len>
<put your message body here>
STORED
#示例
#插入第一条数据
set test_queue 0 0 13
first_message
STORED
#插入第二条数据
set test_queue 0 0 8
message2
STORED
实践如下:

注:信息不能大于message_len。大于message_len会报错.message_len以字节为单位。

#超出长度时示例
set test_queue 0 0 2
tes
CLIENT_ERROR bad data chunk
ERROR
#你超出message_len范围后会报错,错误的数据集。
2)查看队列:stats queue
3)get 从队列的头部查询一条信息并销毁:
get <queue name>
VALUE <queue name> <flags> <message_len>
<your message body will come here>
END
#示例:
get test_queue
VALUE test_queue 0 13
first_message
END
#可以看出是查询的第一个插入的信息。
4)delete 删除队列:
delete <queue name>
#示例:
delete test_queue
DELETED
#此时再查看队列状态:
stats queue
END
#已经删除了。
———————与php 连接———————
注意:和php连接,需要事先安装memcache的php扩展,如未安装,可以点击: linux下安装PHP扩展:Memcache
linux下安装PHP扩展:Memcache的文章来源:http://ifxoxo.com/memcache-install.html  其实就是安装memcache的扩展,如果网站以前有连接memcache,现在就不用安了。
PHP示例:

root@192.168.0.6:/data0/htdocs/blog# php testmemcacheq.php
注释:memcache_get($memcache_obj, 'demoqueue1');并执行后:
root@192.168.0.6:/data0/htdocs/blog# telnet 127.0.0.1 11212
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get demoqueue1
VALUE demoqueue1 0 17
反过来,设置shell后,用php扩展去获取(注意用shell操作时最后的数字长度最好大于写的字符长度,否则报错:CLIENT_ERROR bad data chunk。):


PHP代码获取如下:
root@192.168.0.6:/data0/htdocs/blog# php testmemcacheq.php
te
________________摘自其他项目团队的php代码_______________

_____________________________________________________
罗列安装使用的Url如下:
http://blog.sina.com.cn/s/blog_a0db295e01015eqt.html
http://ifxoxo.com/use-memcacheq.html
来自:http://www.com133.com/56
HttpSQS:
http://blog.s135.com/httpsqs/

PHP下的使用方法:http://memcachedb.org/memcacheq/
sina微博队列memcacheq 服务安装与原理:
http://blog.163.com/song_0803/blog/static/46097597201131510533947/
背景:
     Memcache这个存起来的数据,有时要看全面点,有时php代码里可以看到key,然后,通过telnet去查,
      http://www.jackxiang.com/post/2484/
    但有一个可以遍历的程序也算是方便不少。



来源:http://www.cnblogs.com/sunli/archive/2008/11/01/1324153.html
find命令:

查找当前目录下递归本层所有的文件夹,按评论说的作了下改动:

就是列出当前所有的文件夹,这样可以对一个满是Linux安装文件包并解压后的包含:tar.gz 目录等,做统计和删除,很有用。
注意:
-maxdepth   1       (其中‘1’为你本层目录里面查找,要是为‘2’的话就是两层目录下面的文件查找)

其实我是想删除这个目录下所有给解压的目录,只留下安装包,命令如下:
如何设置自己的共享库目录?/etc/ld.so.conf下增加了路径,还执行了ldconfig后,还提示没找到*.so?
真是烦啊,以前也没在其他版本的linux下搞过这东西,不会是magic的问题吧?
搜索了一天,都是怎么教人设定共享库的啊
/etc/ld.so.conf
是添加一行:
/my/lib
此目录已经建立了,而且把相关的so文件也放进去了
接着
#ldconfig
在安装东西,还是提示没有找到那几个so文件
重起也不行,但要是把这些so拷到/usr/lib下就不会出现说找不到它们  
大老们具体说你们是怎么解决这个自设共享库目录的????
谢了先~!~@!


缘起:
同样叫回忆未来,难免看看他的文章,实验实验,在实验
基于HTTP协议的轻量级开源简单队列服务:HTTPSQS[原创],发现:
[root@localhost httpsqs-1.1]# /usr/bin/httpsqs -h
/usr/bin/httpsqs: error while loading shared libraries: libtokyocabinet.so.9: cannot open shared object file: No such file or directory

于是看留言也有这个问题:

# httpsqs
httpsqs: error while loading shared libraries: libtokyocabinet.so.9: cannot open shared object file: No such file or directory
安装完后出现这个哦。centos5.3 32位系统
张宴 回复于 2009-12-31 17:39
这个是tokyocabinet动态链接库找不到的问题。你可以按照以下步骤解决:
1、确认tokyocabinet是否安装成功(查看/usr/local/lib/libtokyocabinet.so.9文件是否存在)
2、如果存在,还是报这个错误,将/usr/local/lib添加到/etc/ld.so.conf文件中,然后在命令行执行/sbin/ldconfig,最后启动httpsqs

这么回事:



[root@localhost httpsqs-1.1]# vi /etc/ld.so.conf

include ld.so.conf.d/*.conf
/usr/local/lib



[root@localhost httpsqs-1.1]# ldconfig


[root@localhost httpsqs-1.1]# /usr/bin/httpsqs -h
--------------------------------------------------------------------------------------------------
HTTP Simple Queue Service - httpsqs v1.1

Author: Zhang Yan (http://blog.s135.com), E-mail: net@s135.com
This is free software, and you are welcome to modify and redistribute it under the New BSD License

-l <ip_addr>  interface to listen on, default is 0.0.0.0
-p <num>      TCP port number to listen on (default: 1218)
-x <path>     database directory (example: /opt/httpsqs/data)
-t <second>   timeout for an http request (default: 1)
-d            run as a daemon
-h            print this help and exit

Use command "killall httpsqs", "pkill httpsqs" and "kill PID of httpsqs" to stop httpsqs.
Please note that don't use the command "pkill -9 httpsqs" and "kill -9 PID of httpsqs"!

Please visit "http://code.google.com/p/httpsqs" for more help information.

--------------------------------------------------------------------------------------------------


我的原则就是:从来在模仿,从未被超越,呵呵。


HTTPSQS 如队列和出队列:

curl "http://localhost:1218/?name=your_queue_name&opt=put&data=经过URL编码的文本消息"

curl "http://localhost:1218/?charset=utf-8&name=your_queue_name&opt=get"


http://blog.s135.com/httpsqs/#entrymore
分页: 1/1 第一页 1 最后页 [ 显示模式: 摘要 | 列表 ]