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

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

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

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

          三、 【跨jvm的鎖實現(xiàn)【watch】【multi】】
          首先說下這個問題的使用場景,有些時候我們業(yè)務(wù)邏輯是在不同的jvm進程甚至是不同的物理機上的jvm處理的。這樣如何來實現(xiàn)不同jvm上的同步問題呢,其實我們可以基于redis來實現(xiàn)一個鎖。
          具體事務(wù)和監(jiān)聽請參考文章:redis學(xué)習(xí)筆記之事務(wù)
           暫時找到三種實現(xiàn)方式:
          1. 通過jedis.setnx(key,value)實現(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 {
              //加鎖標志
              public static final String LOCKED = "TRUE";
              public static final long ONE_MILLI_NANOS = 1000000L;
              //默認超時時間(毫秒)
              public static final long DEFAULT_TIME_OUT = 3000;
              public static JedisPool pool;
              public static final Random r = new Random();
              //鎖的超時時間(秒),過期刪除
              public static final int EXPIRE = 5 * 60;
              static {
                  pool = new JedisPool(new Config(), "host", 6379);
              }
              private Jedis jedis;
              private String key;
              //鎖狀態(tài)標志
              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);
              }

              // 無論是否加鎖成功,必須調(diào)用
              public void unlock() {
                  try {
                      if (locked)
                          jedis.del(key);
                  } finally {
                      pool.returnResource(jedis);
                  }
              }
          }
          2. 通過事務(wù)(multi)實現(xiàn)
          由于采納第一張方法,第二種跟第三種實現(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ù),當server端收到multi指令
                          
          // 會將該client的命令放入一個隊列,然后依次執(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)聽實現(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;
              }
          最終采用第一種實現(xiàn),因為加鎖只需發(fā)送一個請求,效率最高。
          四、 【redis分布式】
              最后一個話題,jedis的分布式。在jedis的源碼里發(fā)現(xiàn)了兩種hash算法(MD5,MURMUR Hash(默認)),也可以自己實現(xiàn)redis.clients.util.Hashing接口擴展。
              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", "");


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

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

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

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 大石桥市| 措勤县| 安顺市| 社旗县| 谷城县| 河北区| 海丰县| 凌源市| 育儿| 古交市| 云梦县| 丹江口市| 乌拉特中旗| 永仁县| 瑞安市| 乐昌市| 那曲县| 望谟县| 峡江县| 泰来县| 古交市| 神农架林区| 陆良县| 驻马店市| 阿荣旗| 华亭县| 婺源县| 台北市| 建瓯市| 板桥市| 汾阳市| 嘉鱼县| 张家界市| 阿克苏市| 临澧县| 东明县| 汾西县| 黑河市| 日照市| 嘉祥县| 灌云县|