簡(jiǎn)介
Redis模塊負(fù)責(zé)與Redis數(shù)據(jù)庫(kù)交互,并提供Redis的相關(guān)API支持;
Redis模塊提供redis與redis.connection這兩個(gè)服務(wù);
redis.connection服務(wù)提供redis連接對(duì)象;
redis服務(wù)提供Illuminate\Redis\RedisManager對(duì)象,負(fù)責(zé)與Redis打交道的這部分管理工作;
配置項(xiàng)
以下是筆者的示例;
default是默認(rèn)的Redis連接對(duì)象名,值是連接對(duì)象的參數(shù);
app('redis.connection')返回的就是該默認(rèn)連接對(duì)象;
mydefine是筆者定義的Redis連接對(duì)象名;
通過(guò)執(zhí)行app('redis')->connection('mydefine')可以獲取該連接對(duì)象;
mycluster1是筆者定義的Redis集群對(duì)象名;
通過(guò)執(zhí)行app('redis')->connection('mycluster1')可以獲取該集群對(duì)象;
'redis'=> [
????'client'=>'predis',
????'default'=> [
????????????'host'=> env('REDIS_HOST','127.0.0.1'),
????????????'password'=> env('REDIS_PASSWORD',null),
????????????'port'=> env('REDIS_PORT',6379),
????????????'database'=>0,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
????],
????'mydefine'=> [
????????????'host'=> env('REDIS_HOST','127.0.0.1'),
????????????'password'=> env('REDIS_PASSWORD',null),
????????????'port'=> env('REDIS_PORT',6379),
????????????'database'=>4,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
????],
????'clusters'=> [
????????????'mycluster1'=> [? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
????????????????????[
????????????????????????????'host'=> env('REDIS_HOST','127.0.0.1'),
????????????????????????????'password'=> env('REDIS_PASSWORD',null),
????????????????????????????'port'=> env('REDIS_PORT',6379),
????????????????????????????'database'=>1,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????
????????????????????],
????????????????????[
????????????????????????????'host'=> env('REDIS_HOST','127.0.0.1'),
????????????????????????????'password'=> env('REDIS_PASSWORD',null),
????????????????????????????'port'=> env('REDIS_PORT',6379),
????????????????????????????'database'=>2,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
????????????????????],? ? ? ? ? ??
????????????????????[
????????????????????????????'host'=> env('REDIS_HOST','127.0.0.1'),
????????????????????????????'password'=> env('REDIS_PASSWORD',null),
????????????????????????????'port'=> env('REDIS_PORT',6379),
????????????????????????????'database'=>3,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
????????????????????],? ? ? ??
????????????],? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
????],? ? ? ? ??
],
使用
簡(jiǎn)單
入門(mén)級(jí)操作
普通 set / get 操作;?set操作,如果鍵名存在,則會(huì)覆蓋原有的值;
$redis= app('redis.connection');
$redis->set('library','predis');????//存儲(chǔ) key 為 library, 值為 predis 的記錄;
$redis->get('library');????//獲取 key 為 library 的記錄值
set?/?get多個(gè) key-value
$mkv =array('usr:0001'=>'First user','usr:0002'=>'Second user','usr:0003'=>'Third user');
$redis->mset($mkv);????// 存儲(chǔ)多個(gè) key 對(duì)應(yīng)的 value
$retval = $redis ->mget (array_keys( $mkv));????//獲取多個(gè)key對(duì)應(yīng)的value
存放帶存儲(chǔ)時(shí)效的記錄
$redis->setex('library',?10,?'predis');?????//?存儲(chǔ) key 為 library, 值為 predis 的記錄, 有效時(shí)長(zhǎng)為?10?秒
add操作,不會(huì)覆蓋已有值
$redis->setnx('foo',12) ;????//返回true, 添加成功
$redis->setnx('foo',34) ;????//返回false, 添加失敗,因?yàn)橐呀?jīng)存在鍵名為 foo 的記錄
set的變種,結(jié)果返回替換前的值
$redis->getset('foo',?56) ;?????//?返回?34; 如果之前不存在記錄,則返回 null
incrby/incr/decrby/decr?對(duì)值的遞增和遞減
$redis->incr('foo') ;????//返回57,同時(shí) foo 的值為57
$redis->incrby('foo',2) ;????//返回59,同時(shí) foo 的值為59
檢測(cè)是否存在值
$redis->exists('foo');
刪除
$redis->del('foo');?????//?成功刪除返回?true, 失敗則返回?false
type類(lèi)型檢測(cè),字符串返回?string,列表返回?list,set?表返回?set/zset,hash?表返回?hash;
$redis->type('foo');
append?連接到已存在字符串
$redis->get('str');????//返回 test????
$redis->append('str','_123');????//返回累加后的字符串長(zhǎng)度8,此時(shí) str 為'test_123'
setrange?部分替換操作, 并返回字符串長(zhǎng)度
$redis->setrange('str',0,'abc');????//返回3, 第2個(gè)參數(shù)為0時(shí)等同于 set 操作
$redis->setrange('str',2,'cd');????//返回4, 表示從第2個(gè)字符后替換,這時(shí)'str'為'abcd'
substr?部分獲取操作
$redis->substr('str',?0,?2);?????//?返回'abc'; 表示從第?0?個(gè)起,取到第?2?個(gè)字符
strlen?獲取字符串長(zhǎng)度
$redis->strlen ('str');?????//?返回?4; 此時(shí)?'str'?為?'abcd'
setbit位存儲(chǔ)
$redis->setbit('binary',?31,?1);?????//表示在第31位存入1,這邊可能會(huì)有大小端問(wèn)題?不過(guò)沒(méi)關(guān)系, getbit 應(yīng)該不會(huì)有問(wèn)題
getbit位獲取
$redis->getbit('binary',?31);?????//返回1
keys?模糊查找功能,支持 * 號(hào)以及 ? 號(hào) (匹配一個(gè)字符)
$redis->set('foo1',123);
$redis->set('foo2',456);
$redis->keys('foo*');????//返回 foo1 和 foo2 的 array$redis->keys('f?o?');//同上
randomkey隨機(jī)返回一個(gè)key
$redis->randomkey() ;?????//?可能是返回?'foo1'?或者是?'foo2'?及其它任何已存在的 key
rename/renamenx方法對(duì)key進(jìn)行改名,所不同的是renamenx不允許改成已存在的key
$redis->rename('str',?'str2');?????//?把原先命名為'str'的 key 改成了?'str2'
expire?設(shè)置 key-value 的時(shí)效性
ttl?獲取剩余有效期
persist?重新設(shè)置為永久存儲(chǔ)
$redis->expire('foo',10);????//設(shè)置有效期為10秒
$redis->ttl('foo');????//返回剩余有效期值10秒
$redis->persisit('foo');????//取消 expire 行為
dbsize?返回redis當(dāng)前數(shù)據(jù)庫(kù)的記錄總數(shù)
$redis->dbsize() ;
隊(duì)列操作
rpush/rpushx?有序列表操作,從隊(duì)列后插入元素;lpush/lpushx?和?rpush/rpushx?的區(qū)別是插入到隊(duì)列的頭部,同上,'x'含義是只對(duì)已存在的 key 進(jìn)行操作
$redis->rpush('fooList','bar1');????//返回列表長(zhǎng)度1
$redis->lpush('fooList','bar0');????//返回列表長(zhǎng)度2
$redis->rpushx('fooList','bar2');????//返回3, rpushx只對(duì)已存在的隊(duì)列做添加,否則返回0
llen返回當(dāng)前列表長(zhǎng)度
$redis->llen('fooList');?????//?返回?3
lrange?返回隊(duì)列中一個(gè)區(qū)間的元素
$redis->lrange('fooList',0,1);? ????// 返回?cái)?shù)組包含第0個(gè)至第1個(gè), 共2個(gè)元素
$redis->lrange('fooList',0,-1);???? //返回第0個(gè)至倒數(shù)第一個(gè), 相當(dāng)于返回所有元素
lindex?返回指定順序位置的 list 元素
$redis->lindex('fooList',?1) ; ????// 返回'bar1'
lset?修改隊(duì)列中指定位置的value
$redis->lset('fooList',?1,?'123'); ????// 修改位置?1?的元素, 返回 true
lrem?刪除隊(duì)列中左起指定數(shù)量的字符
$redis->lrem('fooList',?1,?'_') ;?????//?刪除隊(duì)列中左起(右起使用-1)?1個(gè) 字符'_'(若有)
lpop/rpop?類(lèi)似棧結(jié)構(gòu)地彈出(并刪除)最左或最右的一個(gè)元素
$redis->lpop('fooList') ;????//返回'bar0'
$redis->rpop('fooList') ;????//返回'bar2'
ltrim隊(duì)列修改,保留左邊起若干元素,其余刪除
$redis->ltrim('fooList',?0,?1) ;?????//?保留左邊起第?0?個(gè)至第?1?個(gè)元素
rpoplpush?從一個(gè)隊(duì)列中?pop?出元素并?push?到另一個(gè)隊(duì)列
$redis->rpush('list1','ab0');
$redis->rpush('list1','ab1');
$redis->rpush('list2','ab2');
$redis->rpush('list2','ab3');
$redis->rpoplpush('list1','list2');????//結(jié)果list1 =>array('ab0'), list2 =>array('ab1','ab2','ab3')
$redis->rpoplpush('list2','list2');????//也適用于同一個(gè)隊(duì)列, 把最后一個(gè)元素移到頭部 list2 =>array('ab3','ab1','ab2')
linsert在隊(duì)列的中間指定元素前或后插入元素
$redis->linsert('list2','before','ab1','123');? ????//表示在元素'ab1'之前插入'123'
$redis->linsert('list2','after','ab1','456');? ???? //表示在元素'ab1'之后插入'456'
blpop/brpop?阻塞并等待一個(gè)列隊(duì)不為空時(shí),再pop出最左或最右的一個(gè)元素(這個(gè)功能在php以外可以說(shuō)非常好用)
$redis->blpop('list3',?10) ;?????//?如果 list3 為空則一直等待,直到不為空時(shí)將第一元素彈出,?10?秒后超時(shí)
set 集合操作
sadd增加set集合元素, 返回true, 重復(fù)返回false
$redis->sadd('set1','ab');
$redis->sadd('set1','cd');
$redis->sadd('set1','ef');
srem?移除指定元素
$redis->srem('set1',?'cd');?????//?刪除'cd'元素
spop?彈出首元素
$redis->spop('set1');?????//?返回?'ab'
smove?移動(dòng)當(dāng)前set集合的指定元素到另一個(gè)set集合
$redis->sadd('set2','123');
$redis->smove('set1','set2','ab');????//移動(dòng)'set1'中的'ab'到'set2', 返回trueorfalse;此時(shí)'set1'集合不存在'ab'這個(gè)值
scard?返回當(dāng)前set表元素個(gè)數(shù)
$redis->scard('set2');?????//?返回?2
sismember?判斷元素是否屬于當(dāng)前set集合
$redis->sismember('set2',?'123');?????//?返回?true?or?false
smembers?返回當(dāng)前set集合的所有元素
$redis->smembers('set2');?????//?返回 array('123','ab')
sinter/sunion/sdiff?返回兩個(gè)表中元素的交集/并集/補(bǔ)集
$redis->sadd('set1','ab') ;
$redis->sinter('set2','set1') ;????//返回array('ab')
sinterstore/sunionstore/sdiffstore?將兩個(gè)表交集/并集/補(bǔ)集元素 copy 到第三個(gè)表中
$redis->set('foo',0);
$redis->sinterstore('foo','set1');????//等同于將'set1'的內(nèi)容copy到'foo'中,并將'foo'轉(zhuǎn)為set表
$redis->sinterstore('foo', array('set1','set2'));????//將'set1'和'set2'中相同的元素 copy 到'foo'表中, 覆蓋'foo'原有內(nèi)容
srandmember?返回表中一個(gè)隨機(jī)元素
$redis->srandmember('set1') ;
有序set表操作
sadd?增加元素,并設(shè)置序號(hào),成功返回true,重復(fù)返回false
$redis->zadd('zset1',1,'ab');
$redis->zadd('zset1',2,'cd');
$redis->zadd('zset1',3,'ef');
zincrby?對(duì)指定元素索引值的增減,改變?cè)嘏帕写涡?/p>
$redis?-> zincrby (?'zset1'?,?10?,?'ab'?) ;?????//返回11
zrem?移除指定元素
$redis->zrem('zset1',?'ef');?????//?返回?true?or?false
zrange?按位置次序返回表中指定區(qū)間的元素
$redis->zrange('zset1',0,1);????//返回位置0和1之間(兩個(gè))的元素
$redis->zrange('zset1',0, -1);????//返回位置0和倒數(shù)第一個(gè)元素之間的元素(相當(dāng)于所有元素)
zrevrange?同上,返回表中指定區(qū)間的元素,按次序倒排
$redis->zrevrange('zset1',?0, -1);?????//?元素順序和zrange相反
zrangebyscore/zrevrangebyscore?按順序/降序返回表中指定索引區(qū)間的元素
$redis->zadd('zset1',3,'ef');
$redis->zadd('zset1',5,'gh');
$redis->zrangebyscore('zset1',2,9);? ????//返回索引值2-9之間的元素array('ef','gh')
$redis->zrangebyscore('zset1',2,9,'withscores'); ????// 返回索引值2-9之間的元素并包含索引值array(array('ef',3),array('gh',5))
$redis->zrangebyscore('zset1',2,9,array('withscores'=>true,'limit'=>array(1,2)));????? //返回索引值2-9之間的元素,'withscores'=>true表示包含索引值;'limit'=>array(1,2),表示偏移1條,返回2條,結(jié)果為array(array('ef',3),array('gh',5))
zunionstore/zinterstore?將多個(gè)表的并集/交集存入另一個(gè)表中
$redis->zunionstore('zset3',array('zset1','zset2','zset0'));????//將'zset1','zset2','zset0'的并集存入'zset3'
$redis->zunionstore('zset3',array('zset1','zset2'),array('weights'=>array(2,1)));????//weights參數(shù)表示權(quán)重,其中表示并集后 zset1集合的分 * 2 后存儲(chǔ)到 zset3 集合, zset2集合的分 * 1 后存儲(chǔ)到 zset3 集合
$redis->zunionstore('zset3',array('zset1','zset2'),array('aggregate'=>'max'));????//'aggregate' => 'max'或'min'表示并集后相同的元素是取大值或是取小值
zcount?統(tǒng)計(jì)一個(gè)索引區(qū)間的元素個(gè)數(shù)
$redis->zcount('zset1',3,5);????//返回2
$redis->zcount('zset1','(3',5));????//'(3'表示索引值在3-5之間但不含3,同理也可以使用'(5'表示上限為5但不含5
zcard?統(tǒng)計(jì)元素個(gè)數(shù)
$redis->zcard('zset1');?????//?返回?4
zscore?查詢(xún)?cè)氐乃饕?/p>
$redis->zscore('zset1',?'ef');?????//?返回?3
zremrangebyscore?刪除一個(gè)索引區(qū)間的元素
$redis->zremrangebyscore('zset1',?0,?2);?????//?刪除索引在0-2之間的元素('ab','cd'), 返回刪除元素個(gè)數(shù)2
zrank/zrevrank?返回元素所在表順序/降序的位置(不是索引)
$redis->zrank('zset1',?'ef');?????//?返回0,因?yàn)樗堑谝粋€(gè)元素;zrevrank則返回1(最后一個(gè))
zremrangebyrank?刪除表中指定位置區(qū)間的元素
$redis->zremrangebyrank('zset1',?0,?10);?????//刪除位置為0-10的元素,返回刪除的元素個(gè)數(shù)2
Hash表操作
hset/hget?存取hash表的數(shù)據(jù)
$redis->hset('hash1','key1','v1');????//將key為'key1'value為'v1'的元素存入hash1表
$redis->hset('hash1','key2','v2');
$redis->hget('hash1','key1');????//取出表'hash1'中的key'key1'的值,返回'v1'
hexists?返回hash表中的指定key是否存在
$redis->hexists('hash1',?'key1') ;?????//true?or?false
hdel?刪除hash表中指定key的元素
$redis->hdel('hash1',?'key2') ;?????//true?or?false
hlen?返回hash表元素個(gè)數(shù)
$redis->hlen('hash1');?????//?返回?1
hsetnx?增加一個(gè)元素,但不能重復(fù)
$redis->hsetnx('hash1','key1','v2') ;????//false
$redis->hsetnx('hash1','key2','v2') ;????//true
hmset/hmget?存取多個(gè)元素到hash表
$redis->hmset('hash1',array('key3'=>'v3','key4'=>'v4'));
$redis->hmget('hash1',array('key3','key4'));????// 返回相應(yīng)的值 array('v3','v4')
hincrby?對(duì)指定key進(jìn)行累加
$redis->hincrby('hash1','key5',3);????//不存在,則存儲(chǔ)并返回3;存在,即返回 原有值 +3;
$redis->hincrby('hash1','key5',10);????//返回13
hkeys?返回hash表中的所有key
$redis->hkeys('hash1');?????//?返回array('key1',?'key2',?'key3',?'key4',?'key5')
hvals?返回hash表中的所有value
$redis->hvals('hash1');?????//?返回 array('v1','v2','v3','v4',13)
hgetall?返回整個(gè)hash表元素
$redis->hgetall('hash1');?????//?返回 array('key1'=>'v1','key2'=>'v2','key3'=>'v3','key4'=>'v4','key5'=>13)
排序操作
sort?排序
$redis->rpush('tab',3);
$redis->rpush('tab',2);
$redis->rpush('tab',17);
$redis->sort('tab');????// 返回 array(2,3,17)// 使用參數(shù),可組合使用 array('sort' => 'desc','limit' => array(1, 2))
$redis->sort('tab',array('sort'=>'desc'));????// 降序排列,返回 array(17,3,2)
$redis->sort('tab',array('limit'=>array(1,2)));????//返回順序位置中1的元素2個(gè)(這里的2是指?jìng)€(gè)數(shù),而不是位置),返回array(3,17)
$redis->sort('tab',array('limit'=>array('alpha'=>true)));????//按首字符排序返回array(17,2,3),因?yàn)?7的首字符是'1'所以排首位置
$redis->sort('tab',array('limit'=>array('store'=>'ordered')));????//表示永久性排序,返回元素個(gè)數(shù)
$redis->sort('tab',array('limit'=>array('get'=>'pre_*')));????//使用了通配符'*'過(guò)濾元素,表示只返回以'pre_'開(kāi)頭的元素
Redis管理操作
info?顯示服務(wù)當(dāng)狀態(tài)信息
$redis->info();
select?指定要操作的數(shù)據(jù)庫(kù)
$redis->select(4);?//?指定數(shù)據(jù)庫(kù)的下標(biāo)
flushdb?清空當(dāng)前庫(kù)
$redis->flushdb();
move?移動(dòng)當(dāng)庫(kù)的元素到其它數(shù)據(jù)庫(kù)
$redis->set('tomove','bar');
$redis->move('tomove',4);
slaveof?配置從服務(wù)器
$redis->slaveof('127.0.0.1',80);????//配置127.0.0.1端口80的服務(wù)器為從服務(wù)器
$redis->slaveof();????//清除從服務(wù)器
同步保存服務(wù)器數(shù)據(jù)到磁盤(pán)
$redis->save();
異步保存服務(wù)器數(shù)據(jù)到磁盤(pán)
$redis->bgsave ();
返回最后更新磁盤(pán)的時(shí)間
$redis->lastsave();
批量操作
$replies? =? $redis -> pipeline (function( $pipe ){? ?
????????$pipe -> ping ( ) ;? ?
????????$pipe -> flushdb ( ) ;? ?
????????$pipe -> incrby ('counter',10) ;????//增量操作
????????$pipe -> incrby ('counter',30) ;? ?
????????$pipe -> exists ('counter') ;? ?
????????$pipe -> get ('counter') ;? ?
????????$pipe -> mget ('does_not_exist','counter') ;?
} ) ;
print_r ( $replies ) ;
CAS,事務(wù)性操作
functionzpop( $client ,? $zsetKey ){? ?
????????$element? =null;? ?
????????$options? =array(
????????????????????'cas'=>true,????// Initialize with support for CAS operations
????????????????????'watch'=>? $zsetKey ,????// Key that needs to be WATCHed to detect changes
????????????????????'retry'=>3,????// Number of retries on aborted transactions, after// which the client bails out with an exception.
????????????) ;? ?
????????????$txReply? =? $client -> multiExec ( $options ,function( $tx )use( $zsetKey ,? & $element ){? ? ? ??
????????????????????@list( $element )? =? $tx -> zrange ( $zsetKey ,0,0) ;
????????????????????if(isset( $element ) )? {? ? ? ? ? ?
????????????????????????????????$tx -> multi ( ) ;????// With CAS, MULTI *must* be explicitly invoked.
????????????????????????????????$tx -> zrem ( $zsetKey ,? $element ) ;? ? ? ?
????????????????????}? ?
????????????} ) ;
????????????return$element ;
}
$zpopped? = zpop ( $redis ,'zset') ;
echo????isset( $zpopped ) ? "ZPOPed $zpopped" : "Nothing to ZPOP!","\n";
對(duì)存取的key加前綴,如: 'nrk:'
$redis -> getProfile ( ) -> setPreprocessor (newKeyPrefixPreprocessor ('nrk:') ) ;
分布式存儲(chǔ)的一些方法
$multiple_servers? =array(
????????????array(
????????????????????????'host'=>'127.0.0.1',
????????????????????????'port'=>6379,
????????????????????????'database'=>15,
????????????????????????'alias'=>'first',? ?
????????????) ,
????????????array(
????????????????????????'host'=>'127.0.0.1',
????????????????????????'port'=>6380,
????????????????????????'database'=>15,
????????????????????????'alias'=>'second',? ?
????????????) ,
);
usePredis\Distribution\IDistributionStrategy;
class????NaiveDistributionStrategy????implements????IDistributionStrategy{
????????private????$_nodes ,? $_nodesCount ;
????????public????function????__constructor( ){
????????????????????$this->_nodes? =array( ) ;
????????????????????$this->_nodesCount? =0;? ?
????????}
????????public????function????add( $node ,? $weight? =? null ){
????????????????????$this->_nodes [ ]? =? $node ;
????????????????????$this->_nodesCount ++;? ?
????????}
????????public????function????remove( $node ){
????????????????????$this->_nodes? =? array_filter ($this->_nodes ,function( $n )use( $node ){
????????????????????????????????return$n? !==? $node ;? ? ? ?
????????????????????} ) ;
????????????????????$this->_nodesCount? =? count ($this->_nodes ) ;? ?
????????}
????????public????function????get( $key ){? ? ? ?
????????????????$count? =$this->_nodesCount ;
????????????????if( $count? ===0)? {
????????????????????????????throw????new????RuntimeException ('No connections') ;? ? ? ?
????????????????}
????????????????return????$this->_nodes [ $count? >1?? abs ( crc32 ( $key )? %? $count )? :0] ;? ? ???????
?????????}
????????public????function????generateKey( $value ){
????????????????????returncrc32 ( $value ) ;? ?
????????}
}
配置鍵分布策略
$options=? array (
????????????'key_distribution'=>? new NaiveDistributionStrategy ( ) ,
) ;
$redis=? new Predis\Client ($multiple_servers,$options) ;
for($i=? 0 ;$iset("key:$i",? str_pad ($i,? 4 ,'0',? 0 ) ) ;
????????????$redis-> get ("key:$i") ;
}
$server1=$redis-> getClientFor ('first') -> info ( ) ;
$server2=$redis-> getClientFor ('second') -> info ( ) ;
printf( "Server '%s' has %d keys while server '%s' has %d keys.\n",'first',$server1['db15'] ['keys'] ,'second',$server2['db15'] ['keys']) ;