jedis之調(diào)用方式

jedis是一個著名的key-value存儲系統(tǒng)盛垦,而作為其官方推薦的java版客戶端jedis也非常強大和穩(wěn)定,支持事務冗懦、管道及有jedis自身實現(xiàn)的分布式景殷。

在這里對jedis關(guān)于事務、管道和分布式的調(diào)用方式做一個簡單的介紹和對比:

一杖狼、普通同步方式

最簡單和基礎(chǔ)的調(diào)用方式炼蛤,

@Test

public?void?test1Normal()?{

Jedis?jedis?=?new?Jedis("localhost");

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

String?result?=?jedis.set("n"?+?i,?"n"?+?i);

}

long?end?=?System.currentTimeMillis();

System.out.println("Simple?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

jedis.disconnect();

}

很簡單吧,每次set之后都可以返回結(jié)果蝶涩,標記是否成功理朋。

二、事務方式(Transactions)

redis的事務很簡單绿聘,他主要目的是保障嗽上,一個client發(fā)起的事務中的命令可以連續(xù)的執(zhí)行,而中間不會插入其他client的命令熄攘。

看下面例子:

@Test

public?void?test2Trans()?{

Jedis?jedis?=?new?Jedis("localhost");

long?start?=?System.currentTimeMillis();

Transaction?tx?=?jedis.multi();

for?(int?i?=?0;?i?<?100000;?i++)?{

tx.set("t"?+?i,?"t"?+?i);

}

List?results?=?tx.exec();

long?end?=?System.currentTimeMillis();

System.out.println("Transaction?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

jedis.disconnect();

}

我們調(diào)用jedis.watch(…)方法來監(jiān)控key兽愤,如果調(diào)用后key值發(fā)生變化,則整個事務會執(zhí)行失敗挪圾。另外浅萧,事務中某個操作失敗,并不會回滾其他操作哲思。這一點需要注意惯殊。還有,我們可以使用discard()方法來取消事務也殖。

三土思、管道(Pipelining)

有時务热,我們需要采用異步方式,一次發(fā)送多個指令己儒,不同步等待其返回結(jié)果崎岂。這樣可以取得非常好的執(zhí)行效率。這就是管道闪湾,調(diào)用方法如下:

@Test

public?void?test3Pipelined()?{

Jedis?jedis?=?new?Jedis("localhost");

Pipeline?pipeline?=?jedis.pipelined();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set("p"?+?i,?"p"?+?i);

}

List?results?=?pipeline.syncAndReturnAll();

long?end?=?System.currentTimeMillis();

System.out.println("Pipelined?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

jedis.disconnect();

}

四冲甘、管道中調(diào)用事務

就Jedis提供的方法而言,是可以做到在管道中使用事務途样,其代碼如下:

@Test

public?void?test4combPipelineTrans()?{

jedis?=?new?Jedis("localhost");

long?start?=?System.currentTimeMillis();

Pipeline?pipeline?=?jedis.pipelined();

pipeline.multi();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set(""?+?i,?""?+?i);

}

pipeline.exec();

List?results?=?pipeline.syncAndReturnAll();

long?end?=?System.currentTimeMillis();

System.out.println("Pipelined?transaction:?"?+?((end?-?start)/1000.0)?+?"?seconds");

jedis.disconnect();

}

但是經(jīng)測試(見本文后續(xù)部分)江醇,發(fā)現(xiàn)其效率和單獨使用事務差不多,甚至還略微差點何暇。

五陶夜、分布式直連同步調(diào)用

@Test

public?void?test5shardNormal()?{

List?shards?=?Arrays.asList(

new?JedisShardInfo("localhost",6379),

new?JedisShardInfo("localhost",6380));

ShardedJedis?sharding?=?new?ShardedJedis(shards);

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

String?result?=?sharding.set("sn"?+?i,?"n"?+?i);

}

long?end?=?System.currentTimeMillis();

System.out.println("Simple@Sharing?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

sharding.disconnect();

}

這個是分布式直接連接,并且是同步調(diào)用裆站,每步執(zhí)行都返回執(zhí)行結(jié)果条辟。類似地,還有異步管道調(diào)用宏胯。

六羽嫡、分布式直連異步調(diào)用

@Test

public?void?test6shardpipelined()?{

List?shards?=?Arrays.asList(

new?JedisShardInfo("localhost",6379),

new?JedisShardInfo("localhost",6380));

ShardedJedis?sharding?=?new?ShardedJedis(shards);

ShardedJedisPipeline?pipeline?=?sharding.pipelined();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set("sp"?+?i,?"p"?+?i);

}

List?results?=?pipeline.syncAndReturnAll();

long?end?=?System.currentTimeMillis();

System.out.println("Pipelined@Sharing?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

sharding.disconnect();

}

七、分布式連接池同步調(diào)用

如果肩袍,你的分布式調(diào)用代碼是運行在線程中杭棵,那么上面兩個直連調(diào)用方式就不合適了,因為直連方式是非線程安全的氛赐,這個時候颜屠,你就必須選擇連接池調(diào)用。ShardedJedis是通過一致性哈希來實現(xiàn)分布式緩存的鹰祸,通過一定的策略把不同的key分配到不同的redis server上甫窟,達到橫向擴展的目的。

ShardedJedis的使用方法除了配置時有點區(qū)別蛙婴,其他和Jedis基本類似粗井,有一點要注意的是ShardedJedis不支持多命令操作,像mget街图、mset浇衬、brpop等可以在redis命令后一次性操作多個key的命令,具體包括哪些餐济,大家可以看Jedis下的MultiKeyCommands這個類耘擂,這里面就包含了所有的多命令操作。

在初始化ShardedJedisPool時絮姆,我們還可以傳入ShardedJedis采用的hash算法醉冤,支持MURMUR_HASHmd5兩種算法秩霍,默認是使用MURMUR_HASH(可以查看redis.clients.util.Hashing類查看相關(guān)的信息)


@Test

public?void?test7shardSimplePool()?{

List?shards?=?Arrays.asList(

new?JedisShardInfo("localhost",6379),

new?JedisShardInfo("localhost",6380));

ShardedJedisPool?pool?=?new?ShardedJedisPool(new?JedisPoolConfig(),?shards);

ShardedJedis?one?=?pool.getResource();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

String?result?=?one.set("spn"?+?i,?"n"?+?i);

}

long?end?=?System.currentTimeMillis();

pool.returnResource(one);

System.out.println("Simple@Pool?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

pool.destroy();

}

jedis獲取后一定要關(guān)閉,這和我們使用數(shù)據(jù)庫連接池是一樣的蚁阳,放在finally塊中保證jedis的關(guān)閉.

ps:如果大家使用的jdk是1.7版本或者以上的話铃绒,可以使用1.7加入的try-with-resources語句。

另外還可以傳入keyTagPattern來指定我們key的分布策略螺捐,所有能夠匹配keyTagPattern的key(通過正則匹配)將放在同一個redis里颠悬,默認的是直接使用key來進行判定。Redis自帶了一個Sharded.keyTagPattern定血,如下

Pattern DEFAULT_KEY_TAG_PATTERN = Pattern.compile("\\{(.+?)\\}");

我們可以用下面的代碼來實際測試下

ShardedJedis jedis =jedisPool.getResource();

jedis.set("cnblog", "cnblog");

jedis.set("redis", "redis");

jedis.set("test", "test");

jedis.set("123456", "1234567");

Client client1= jedis.getShard("cnblog").getClient();

Client client2= jedis.getShard("redis").getClient();

Client client3= jedis.getShard("test").getClient();

Client client4= jedis.getShard("123456").getClient();////打印key在哪個server中System.out.println("cnblog in server:" + client1.getHost() + " and port is:" +client1.getPort());

System.out.println("redis? in server:" + client2.getHost() + " and port is:" +client2.getPort());

System.out.println("test? in server:" + client3.getHost() + " and port is:" +client3.getPort());

System.out.println("123456 in server:" + client4.getHost() + " and port is:" + client4.getPort());


八赔癌、分布式連接池異步調(diào)用

@Test

public?void?test8shardPipelinedPool()?{

List?shards?=?Arrays.asList(

new?JedisShardInfo("localhost",6379),

new?JedisShardInfo("localhost",6380));

ShardedJedisPool?pool?=?new?ShardedJedisPool(new?JedisPoolConfig(),?shards);

ShardedJedis?one?=?pool.getResource();

ShardedJedisPipeline?pipeline?=?one.pipelined();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set("sppn"?+?i,?"n"?+?i);

}

List?results?=?pipeline.syncAndReturnAll();

long?end?=?System.currentTimeMillis();

pool.returnResource(one);

System.out.println("Pipelined@Pool?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

pool.destroy();

}

九、需要注意的地方

事務和管道都是異步模式澜沟。在事務和管道中不能同步查詢結(jié)果灾票。比如下面兩個調(diào)用,都是不允許的:

Transaction?tx?=?jedis.multi();

for?(int?i?=?0;?i?<?100000;?i++)?{

tx.set("t"?+?i,?"t"?+?i);

}

System.out.println(tx.get("t1000").get());??//不允許

List?results?=?tx.exec();

Pipeline?pipeline?=?jedis.pipelined();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set("p"?+?i,?"p"?+?i);

}

System.out.println(pipeline.get("p1000").get());?//不允許

List?results?=?pipeline.syncAndReturnAll();

事務和管道都是異步的倔喂,個人感覺铝条,在管道中再進行事務調(diào)用靖苇,沒有必要席噩,不如直接進行事務模式。

分布式中贤壁,連接池的性能比直連的性能略好(見后續(xù)測試部分)悼枢。

分布式調(diào)用中不支持事務。

因為事務是在服務器端實現(xiàn)脾拆,而在分布式中馒索,每批次的調(diào)用對象都可能訪問不同的機器,所以名船,沒法進行事務绰上。

十、測試

運行上面的代碼渠驼,進行測試蜈块,其結(jié)果如下:

Simple?SET:?5.227?seconds

Transaction?SET:?0.5?seconds

Pipelined?SET:?0.353?seconds

Pipelined?transaction:?0.509?seconds

Simple@Sharing?SET:?5.289?seconds

Pipelined@Sharing?SET:?0.348?seconds

Simple@Pool?SET:?5.039?seconds

Pipelined@Pool?SET:?0.401?seconds

另外,經(jīng)測試分布式中用到的機器越多迷扇,調(diào)用會越慢百揭。上面是2片,下面是5片:

Simple@Sharing?SET:?5.494?seconds

Pipelined@Sharing?SET:?0.51?seconds

Simple@Pool?SET:?5.223?seconds

Pipelined@Pool?SET:?0.518?seconds

下面是10片:

Simple@Sharing?SET:?5.9?seconds

Pipelined@Sharing?SET:?0.794?seconds

Simple@Pool?SET:?5.624?seconds

Pipelined@Pool?SET:?0.762?seconds

下面是100片:

Simple@Sharing?SET:?14.055?seconds

Pipelined@Sharing?SET:?8.185?seconds

Simple@Pool?SET:?13.29?seconds

Pipelined@Pool?SET:?7.767?seconds

分布式中蜓席,連接池方式調(diào)用不但線程安全外器一,根據(jù)上面的測試數(shù)據(jù),也可以看出連接池比直連的效率更好厨内。

十一祈秕、完整的測試代碼

package?com.example.nosqlclient;

import?java.util.Arrays;

import?java.util.List;

import?org.junit.AfterClass;

import?org.junit.BeforeClass;

import?org.junit.Test;

import?redis.clients.jedis.Jedis;

import?redis.clients.jedis.JedisPoolConfig;

import?redis.clients.jedis.JedisShardInfo;

import?redis.clients.jedis.Pipeline;

import?redis.clients.jedis.ShardedJedis;

import?redis.clients.jedis.ShardedJedisPipeline;

import?redis.clients.jedis.ShardedJedisPool;

import?redis.clients.jedis.Transaction;

import?org.junit.FixMethodOrder;

import?org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)

public?class?TestJedis?{

private?static?Jedis?jedis;

private?static?ShardedJedis?sharding;

private?static?ShardedJedisPool?pool;

@BeforeClass

public?static?void?setUpBeforeClass()?throws?Exception?{

List?shards?=?Arrays.asList(

new?JedisShardInfo("localhost",6379),

new?JedisShardInfo("localhost",6379));?//使用相同的ip:port,僅作測試

jedis?=?new?Jedis("localhost");

sharding?=?new?ShardedJedis(shards);

pool?=?new?ShardedJedisPool(new?JedisPoolConfig(),?shards);

}

@AfterClass

public?static?void?tearDownAfterClass()?throws?Exception?{

jedis.disconnect();

sharding.disconnect();

pool.destroy();

}

@Test

public?void?test1Normal()?{

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

String?result?=?jedis.set("n"?+?i,?"n"?+?i);

}

long?end?=?System.currentTimeMillis();

System.out.println("Simple?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

}

@Test

public?void?test2Trans()?{

long?start?=?System.currentTimeMillis();

Transaction?tx?=?jedis.multi();

for?(int?i?=?0;?i?<?100000;?i++)?{

tx.set("t"?+?i,?"t"?+?i);

}

//System.out.println(tx.get("t1000").get());

List?results?=?tx.exec();

long?end?=?System.currentTimeMillis();

System.out.println("Transaction?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

}

@Test

public?void?test3Pipelined()?{

Pipeline?pipeline?=?jedis.pipelined();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set("p"?+?i,?"p"?+?i);

}

//System.out.println(pipeline.get("p1000").get());

List?results?=?pipeline.syncAndReturnAll();

long?end?=?System.currentTimeMillis();

System.out.println("Pipelined?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

}

@Test

public?void?test4combPipelineTrans()?{

long?start?=?System.currentTimeMillis();

Pipeline?pipeline?=?jedis.pipelined();

pipeline.multi();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set(""?+?i,?""?+?i);

}

pipeline.exec();

List?results?=?pipeline.syncAndReturnAll();

long?end?=?System.currentTimeMillis();

System.out.println("Pipelined?transaction:?"?+?((end?-?start)/1000.0)?+?"?seconds");

}

@Test

public?void?test5shardNormal()?{

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

String?result?=?sharding.set("sn"?+?i,?"n"?+?i);

}

long?end?=?System.currentTimeMillis();

System.out.println("Simple@Sharing?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

}

@Test

public?void?test6shardpipelined()?{

ShardedJedisPipeline?pipeline?=?sharding.pipelined();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set("sp"?+?i,?"p"?+?i);

}

List?results?=?pipeline.syncAndReturnAll();

long?end?=?System.currentTimeMillis();

System.out.println("Pipelined@Sharing?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

}

@Test

public?void?test7shardSimplePool()?{

ShardedJedis?one?=?pool.getResource();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

String?result?=?one.set("spn"?+?i,?"n"?+?i);

}

long?end?=?System.currentTimeMillis();

pool.returnResource(one);

System.out.println("Simple@Pool?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

}

@Test

public?void?test8shardPipelinedPool()?{

ShardedJedis?one?=?pool.getResource();

ShardedJedisPipeline?pipeline?=?one.pipelined();

long?start?=?System.currentTimeMillis();

for?(int?i?=?0;?i?<?100000;?i++)?{

pipeline.set("sppn"?+?i,?"n"?+?i);

}

List?results?=?pipeline.syncAndReturnAll();

long?end?=?System.currentTimeMillis();

pool.returnResource(one);

System.out.println("Pipelined@Pool?SET:?"?+?((end?-?start)/1000.0)?+?"?seconds");

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渺贤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子踢步,更是在濱河造成了極大的恐慌癣亚,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件获印,死亡現(xiàn)場離奇詭異述雾,居然都是意外死亡,警方通過查閱死者的電腦和手機兼丰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門玻孟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鳍征,你說我怎么就攤上這事黍翎。” “怎么了艳丛?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵匣掸,是天一觀的道長。 經(jīng)常有香客問我氮双,道長碰酝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任戴差,我火速辦了婚禮送爸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘暖释。我一直安慰自己袭厂,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布球匕。 她就那樣靜靜地躺著纹磺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亮曹。 梳的紋絲不亂的頭發(fā)上橄杨,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音乾忱,去河邊找鬼讥珍。 笑死,一個胖子當著我的面吹牛窄瘟,可吹牛的內(nèi)容都是我干的衷佃。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼蹄葱,長吁一口氣:“原來是場噩夢啊……” “哼氏义!你這毒婦竟也來了锄列?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤惯悠,失蹤者是張志新(化名)和其女友劉穎邻邮,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體克婶,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡筒严,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了情萤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸭蛙。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖筋岛,靈堂內(nèi)的尸體忽然破棺而出娶视,到底是詐尸還是另有隱情,我是刑警寧澤睁宰,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布肪获,位于F島的核電站,受9級特大地震影響柒傻,放射性物質(zhì)發(fā)生泄漏孝赫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一诅愚、第九天 我趴在偏房一處隱蔽的房頂上張望寒锚。 院中可真熱鬧劫映,春花似錦违孝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至祖今,卻和暖如春校坑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背千诬。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工耍目, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人徐绑。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓邪驮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親傲茄。 傳聞我的和親對象是個殘疾皇子毅访,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容