前言
Redis是一個(gè)著名的key-value存儲(chǔ)系統(tǒng),也是nosql中的最常見(jiàn)的一種。其實(shí),個(gè)人認(rèn)為,redis最強(qiáng)大的地方不在于其存儲(chǔ),而在于其強(qiáng)大的緩存作用。
我們可以把它想象成一個(gè)巨大的(多借點(diǎn)集群,聚合多借點(diǎn)的內(nèi)存)的Map,也就是Key-Value。
所以,我們可以把它做成緩存組件。
官方推薦的Java版客戶(hù)端是jedis,非常強(qiáng)大和穩(wěn)定,支持事務(wù)、管道及有jedis自身實(shí)現(xiàn)。我們對(duì)redis數(shù)據(jù)的操作,都可以通過(guò)jedis來(lái)完成。
那我們就來(lái)看一看,jedis不同的調(diào)用方式:
(1)普通同步方式
這是一種最簡(jiǎn)單和最基礎(chǔ)的調(diào)用方式,對(duì)于簡(jiǎn)單的數(shù)據(jù)存取需求,我們可以通過(guò)這種方式調(diào)用。
1
2
3
4
5
6
7
8
9
10
11
|
public void jedisNormal() { 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é)果,標(biāo)記是否成功。 |
(2)事務(wù)方式(Transactions)
所謂事務(wù),即一個(gè)連續(xù)操作,是否執(zhí)行是一個(gè)事務(wù),要么完成,要么失敗,沒(méi)有中間狀態(tài)。
而redis的事務(wù)很簡(jiǎn)單,他主要目的是保障,一個(gè)client發(fā)起的事務(wù)中的命令可以連續(xù)的執(zhí)行,而中間不會(huì)插入其他client的命令,也就是事務(wù)的連貫性。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public void jedisTrans() { 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<Object> results = tx.exec(); long end = System.currentTimeMillis(); System.out.println( "Transaction SET: " + ((end - start)/ 1000.0 ) + " seconds" ); jedis.disconnect(); } //我們調(diào)用jedis.watch(…)方法來(lái)監(jiān)控key,如果調(diào)用后key值發(fā)生變化,則整個(gè)事務(wù)會(huì)執(zhí)行失敗。另外,事務(wù)中某個(gè)操作失敗,并不會(huì)回滾其他操作。這一點(diǎn)需要注意。還有,我們可以使用discard()方法來(lái)取消事務(wù)。 |
(3)管道(Pipelining)
管道是一種兩個(gè)進(jìn)程之間單向通信的機(jī)制。
那再redis中,為何要使用管道呢?有時(shí)候,我們需要采用異步的方式,一次發(fā)送多個(gè)指令,并且,不同步等待其返回結(jié)果。這樣可以取得非常好的執(zhí)行效率。
1
2
3
4
5
6
7
8
9
10
11
12
|
public void jedisPipelined() { 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<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println( "Pipelined SET: " + ((end - start)/ 1000.0 ) + " seconds" ); jedis.disconnect(); } |
(4)管道中調(diào)用事務(wù)
對(duì)于,事務(wù)以及管道,這兩個(gè)概念我們都清楚了。
在某種需求下,我們需要異步執(zhí)行命令,但是,又希望多個(gè)命令是有連續(xù)的,所以,我們就采用管道加事務(wù)的調(diào)用方式。jedis是支持在管道中調(diào)用事務(wù)的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public void jedisCombPipelineTrans() { 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<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println( "Pipelined transaction: " + ((end - start)/ 1000.0 ) + " seconds" ); jedis.disconnect(); } //效率上可能會(huì)有所欠缺 |
(5)分布式直連同步調(diào)用
這個(gè)是分布式直接連接,并且是同步調(diào)用,每步執(zhí)行都返回執(zhí)行結(jié)果。類(lèi)似地,還有異步管道調(diào)用。
其實(shí)就是分片。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public void jedisShardNormal() { List<JedisShardInfo> 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(); } |
(6)分布式直連異步調(diào)用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public void jedisShardpipelined() { List<JedisShardInfo> 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<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println( "Pipelined@Sharing SET: " + ((end - start)/ 1000.0 ) + " seconds" ); sharding.disconnect(); } |
(7)分布式連接池同步調(diào)用
如果,你的分布式調(diào)用代碼是運(yùn)行在線(xiàn)程中,那么上面兩個(gè)直連調(diào)用方式就不合適了,因?yàn)橹边B方式是非線(xiàn)程安全的,這個(gè)時(shí)候,你就必須選擇連接池調(diào)用。
連接池的調(diào)用方式,適合大規(guī)模的redis集群,并且多客戶(hù)端的操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public void jedisShardSimplePool() { List<JedisShardInfo> 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(); } |
(8)分布式連接池異步調(diào)用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public void jedisShardPipelinedPool() { List<JedisShardInfo> 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<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); pool.returnResource(one); System.out.println( "Pipelined@Pool SET: " + ((end - start)/ 1000.0 ) + " seconds" ); pool.destroy(); } |
(9)需要注意的地方
事務(wù)和管道都是異步模式。在事務(wù)和管道中不能同步查詢(xún)結(jié)果。比如下面兩個(gè)調(diào)用,都是不允許的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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<Object> 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<Object> results = pipeline.syncAndReturnAll(); |
事務(wù)和管道都是異步的,個(gè)人感覺(jué),在管道中再進(jìn)行事務(wù)調(diào)用,沒(méi)有必要,不如直接進(jìn)行事務(wù)模式。
分布式中,連接池的性能比直連的性能略好(見(jiàn)后續(xù)測(cè)試部分)。
分布式調(diào)用中不支持事務(wù)。
因?yàn)槭聞?wù)是在服務(wù)器端實(shí)現(xiàn),而在分布式中,每批次的調(diào)用對(duì)象都可能訪(fǎng)問(wèn)不同的機(jī)器,所以,沒(méi)法進(jìn)行事務(wù)。
總結(jié)
分布式中,連接池方式調(diào)用不但線(xiàn)程安全外,根據(jù)上面的測(cè)試數(shù)據(jù),也可以看出連接池比直連的效率更好。
經(jīng)測(cè)試分布式中用到的機(jī)器越多,調(diào)用會(huì)越慢。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:http://blog.csdn.net/u011130752/article/details/53483388