隨筆-4  評(píng)論-10  文章-0  trackbacks-0
          創(chuàng)建日期:2012年7月3日
          修改日期:2012年7月19日
          前段時(shí)間細(xì)節(jié)的了解了Jedis的使用,Jedis是redis的java版本的客戶端實(shí)現(xiàn)。
          本文做個(gè)總結(jié),主要分享如下內(nèi)容:

          【pipeline】【分布式的id生成器】【分布式鎖【watch】【multi】】【redis分布式】
          好了,一個(gè)一個(gè)來(lái)。
          一、 Pipeline
          官方的說(shuō)明是:starts a pipeline,which is a very efficient way to send lots of command and read all the responses when you finish sending them。簡(jiǎn)單點(diǎn)說(shuō)pipeline適用于批處理。當(dāng)有大量的操作需要一次性執(zhí)行的時(shí)候,可以用管道。
          示例:
          Jedis jedis = new Jedis(String, int);
          Pipeline p = jedis.pipelined();
          p.set(key,value);//每個(gè)操作都發(fā)送請(qǐng)求給redis-server
          p.get(key,value);

          p.sync();//這段代碼獲取所有的response
          這里我進(jìn)行了20w次連續(xù)操作(10w讀,10w寫),不用pipeline耗時(shí):187242ms,用pipeline耗時(shí):1188ms,可見使用管道后的性能上了一個(gè)臺(tái)階。看了代碼了解到,管道通過一次性寫入請(qǐng)求,然后一次性讀取響應(yīng)。也就是說(shuō)jedis是:request response,request response,...;pipeline則是:request request... response response的方式。這樣無(wú)需每次請(qǐng)求都等待server端的響應(yīng)。

          二、 跨jvm的id生成器 
          談到這個(gè)話題,首先要知道redis-server端是單線程來(lái)處理client端的請(qǐng)求的。
          這樣來(lái)實(shí)現(xiàn)一個(gè)id生成器就非常簡(jiǎn)單了,只要簡(jiǎn)單的調(diào)用jdeis.incr(key);就搞定了。
          你或許會(huì)問,incr是原子操作嗎,能保證不會(huì)出現(xiàn)并發(fā)問題嗎,前面說(shuō)過,server端是單線程處理請(qǐng)求的。

          三、 【跨jvm的鎖實(shí)現(xiàn)【watch】【multi】】
          首先說(shuō)下這個(gè)問題的使用場(chǎng)景,有些時(shí)候我們業(yè)務(wù)邏輯是在不同的jvm進(jìn)程甚至是不同的物理機(jī)上的jvm處理的。這樣如何來(lái)實(shí)現(xiàn)不同jvm上的同步問題呢,其實(shí)我們可以基于redis來(lái)實(shí)現(xiàn)一個(gè)鎖。
          具體事務(wù)和監(jiān)聽請(qǐng)參考文章:redis學(xué)習(xí)筆記之事務(wù)
           暫時(shí)找到三種實(shí)現(xiàn)方式:
          1. 通過jedis.setnx(key,value)實(shí)現(xiàn)
               import java.util.Random;

          import org.apache.commons.pool.impl.GenericObjectPool.Config;

          import redis.clients.jedis.Jedis;
          import redis.clients.jedis.JedisPool;
          import redis.clients.jedis.Transaction;

          /**
           * 
          @author Teaey
           
          */
          public class RedisLock {
              //加鎖標(biāo)志
              public static final String LOCKED = "TRUE";
              public static final long ONE_MILLI_NANOS = 1000000L;
              //默認(rèn)超時(shí)時(shí)間(毫秒)
              public static final long DEFAULT_TIME_OUT = 3000;
              public static JedisPool pool;
              public static final Random r = new Random();
              //鎖的超時(shí)時(shí)間(秒),過期刪除
              public static final int EXPIRE = 5 * 60;
              static {
                  pool = new JedisPool(new Config(), "host", 6379);
              }
              private Jedis jedis;
              private String key;
              //鎖狀態(tài)標(biāo)志
              private boolean locked = false;

              public RedisLock(String key) {
                  this.key = key;
                  this.jedis = pool.getResource();
              }

              public boolean lock(long timeout) {
                  long nano = System.nanoTime();
                  timeout *= ONE_MILLI_NANOS;
                  try {
                      while ((System.nanoTime() - nano) < timeout) {
                          if (jedis.setnx(key, LOCKED) == 1) {
                              jedis.expire(key, EXPIRE);
                              locked = true;
                              return locked;
                          }
                          // 短暫休眠,nano避免出現(xiàn)活鎖
                          Thread.sleep(3, r.nextInt(500));
                      }
                  } catch (Exception e) {
                  }
                  return false;
              }
              public boolean lock() {
                  return lock(DEFAULT_TIME_OUT);
              }

              // 無(wú)論是否加鎖成功,必須調(diào)用
              public void unlock() {
                  try {
                      if (locked)
                          jedis.del(key);
                  } finally {
                      pool.returnResource(jedis);
                  }
              }
          }
          2. 通過事務(wù)(multi)實(shí)現(xiàn)
          由于采納第一張方法,第二種跟第三種實(shí)現(xiàn)只貼了關(guān)鍵代碼,望諒解。^_^
               public boolean lock_2(long timeout) {
                  long nano = System.nanoTime();
                  timeout *= ONE_MILLI_NANOS;
                  try {
                      while ((System.nanoTime() - nano) < timeout) {
                          Transaction t = jedis.multi();
                          // 開啟事務(wù),當(dāng)server端收到multi指令
                          
          // 會(huì)將該client的命令放入一個(gè)隊(duì)列,然后依次執(zhí)行,知道收到exec指令
                          t.getSet(key, LOCKED);
                          t.expire(key, EXPIRE);
                          String ret = (String) t.exec().get(0);
                          if (ret == null || ret.equals("UNLOCK")) {
                              return true;
                          }
                          // 短暫休眠,nano避免出現(xiàn)活鎖
                          Thread.sleep(3, r.nextInt(500));
                      }
                  } catch (Exception e) {
                  }
                  return false;
              }
          3. 通過事務(wù)+監(jiān)聽實(shí)現(xiàn)
              public boolean lock_3(long timeout) {
                  long nano = System.nanoTime();
                  timeout *= ONE_MILLI_NANOS;
                  try {
                      while ((System.nanoTime() - nano) < timeout) {
                          jedis.watch(key);
                          // 開啟watch之后,如果key的值被修改,則事務(wù)失敗,exec方法返回null
                          String value = jedis.get(key);
                          if (value == null || value.equals("UNLOCK")) {
                              Transaction t = jedis.multi();
                              t.setex(key, EXPIRE, LOCKED);
                              if (t.exec() != null) {
                                  return true;
                              }
                          }
                          jedis.unwatch();
                          // 短暫休眠,nano避免出現(xiàn)活鎖
                          Thread.sleep(3, r.nextInt(500));
                      }
                  } catch (Exception e) {
                  }
                  return false;
              }
          最終采用第一種實(shí)現(xiàn),因?yàn)榧渔i只需發(fā)送一個(gè)請(qǐng)求,效率最高。
          四、 【redis分布式】
              最后一個(gè)話題,jedis的分布式。在jedis的源碼里發(fā)現(xiàn)了兩種hash算法(MD5,MURMUR Hash(默認(rèn))),也可以自己實(shí)現(xiàn)redis.clients.util.Hashing接口擴(kuò)展。
              List<JedisShardInfo> hosts = new ArrayList<JedisShardInfo>();
                  //server1
                  JedisShardInfo host1 = new JedisShardInfo("", 6380, 2000);
                  //server2
                  JedisShardInfo host2 = new JedisShardInfo("", 6381, 2000);
                  hosts.add(host1);
                  hosts.add(host2);
                  ShardedJedis jedis = new ShardedJedis(hosts);
                  jedis.set("key", "");


          另外寫博客真費(fèi)力。。。
          posted on 2012-07-03 13:32 沖杯茶喝 閱讀(24083) 評(píng)論(6)  編輯  收藏

          評(píng)論:
          # re: Jedis使用總結(jié)【pipeline】【分布式的id生成器】【分布式鎖【watch】【multi】】【redis分布式】 2013-01-25 10:08 | finallygo
          2.6之后,支持了script,又有新的辦法了  回復(fù)  更多評(píng)論
            
          # re: Jedis使用總結(jié)【pipeline】【分布式的id生成器】【分布式鎖【watch】【multi】】【redis分布式】 2013-05-03 09:54 | 沖杯茶喝
          沒時(shí)間研究了,如果有新的,可以完善本文@finallygo
            回復(fù)  更多評(píng)論
            
          # re: Jedis使用總結(jié)【pipeline】【分布式的id生成器】【分布式鎖【watch】【multi】】【redis分布式】 2013-05-23 00:53 | leealways887
          請(qǐng)問博主,為什么我用了pipeline之后,設(shè)置10萬(wàn)條數(shù)據(jù),程序反應(yīng)不過來(lái),倒是不用pipeline,程序還能正常運(yùn)行呢?  回復(fù)  更多評(píng)論
            
          # re: Jedis使用總結(jié)【pipeline】【分布式的id生成器】【分布式鎖【watch】【multi】】【redis分布式】[未登錄] 2014-02-11 15:44 | young
          @leealways887
          數(shù)據(jù)量太大了吧 內(nèi)存用完了  回復(fù)  更多評(píng)論
            
          # re: Jedis使用總結(jié)【pipeline】【分布式的id生成器】【分布式鎖【watch】【multi】】【redis分布式】 2014-11-03 17:36 | louzhu
          什么叫反應(yīng)不過來(lái)!據(jù)我說(shuō)之,redis服務(wù)器應(yīng)該不會(huì)反應(yīng)不過來(lái)。

          @leealways887
            回復(fù)  更多評(píng)論
            
          # re: Jedis使用總結(jié)【pipeline】【分布式的id生成器】【分布式鎖【watch】【multi】】【redis分布式】 2016-05-20 10:13 | fruwei
          第二種的t.expire(key, EXPIRE);有問題吧 ,如果一直有鎖請(qǐng)求,豈不是每次都把這個(gè)過期時(shí)間重新設(shè)置了?應(yīng)該先判斷下有沒有超時(shí),沒有就設(shè)置,有的話就不設(shè)置。  回復(fù)  更多評(píng)論
            

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 寿宁县| 内乡县| 兴文县| 上高县| 潮州市| 民勤县| 婺源县| 合肥市| 平武县| 武城县| 三门峡市| 铜鼓县| 莆田市| 张家港市| 绥滨县| 巩留县| 南丹县| 固安县| 周至县| 靖宇县| 雅江县| 康保县| 洪雅县| 临城县| 阳新县| 马山县| 咸丰县| 台北县| 新乐市| 永济市| 鄱阳县| 唐河县| 若尔盖县| 拜城县| 乌海市| 广平县| 札达县| 会理县| 常熟市| 东宁县| 白城市|