Jedis使用總結 前段時(shí)間細節的了解了Jedis的使用,Jedis是redis的java版本的客戶(hù)端實(shí)現。
本文做個(gè)總結,主要分享如下內容:【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適用于批處理。當有大量的操作需要一次性執行的時(shí)候,可以用管道。
示例:Jedis jedis = new Jedis(String, int); Pipeline p = jedis.pipelined(); p.set(key,value);//每個(gè)操作都發(fā)送請求給redis-server p.get(key,value); p.sync();//這段代碼獲取所有的response 這里我進(jìn)行了20w次連續操作(10w讀,10w寫(xiě)),不用pipeline耗時(shí):187242ms,用pipeline耗時(shí):1188ms,可見(jiàn)使用管道后的性能上了一個(gè)臺階。看了代碼了解到,管道通過(guò)一次性寫(xiě)入請求,然后一次性讀取響應。
也就是說(shuō)jedis是:request response,request response,。;pipeline則是:request request。
response response的方式。這樣無(wú)需每次請求都等待server端的響應。
二、跨jvm的id生成器 談到這個(gè)話(huà)題,首先要知道redis-server端是單線(xiàn)程來(lái)處理client端的請求的。這樣來(lái)實(shí)現一個(gè)id生成器就非常簡(jiǎn)單了,只要簡(jiǎn)單的調用jdeis.incr(key);就搞定了。
你或許會(huì )問(wèn),incr是原子操作嗎,能保證不會(huì )出現并發(fā)問(wèn)題嗎,不是說(shuō)了嗎,server端是單線(xiàn)程處理請求的。三、【跨jvm的鎖實(shí)現【watch】【multi】】 首先說(shuō)下這個(gè)問(wèn)題的使用場(chǎng)景,有些時(shí)候我們業(yè)務(wù)邏輯是在不同的jvm進(jìn)程甚至是不同的物理機上的jvm處理的。
這樣如何來(lái)實(shí)現不同jvm上的同步問(wèn)題呢,其實(shí)我們可以基于redis來(lái)實(shí)現一個(gè)鎖。具體事務(wù)和監聽(tīng)請參考文章:redis學(xué)習筆記之事務(wù) 暫時(shí)找到三種實(shí)現方式:1. 通過(guò)jedis.setnx(key,value)實(shí)現 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; //默認超時(shí)時(shí)間(毫秒) public static final long DEFAULT_TIME_OUT = 3000; public static JedisPool pool; public static final Random r = new Random(); //鎖的超時(shí)時(shí)間(秒),過(guò)期刪除 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) if (jedis.setnx(key, LOCKED) == 1) { jedis.expire(key, EXPIRE); locked = true; return locked; } // 短暫休眠,nano避免出現活鎖 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; } public boolean lock() { return lock(DEFAULT_TIME_OUT); } // 無(wú)論是否加鎖成功,必須調用 public void unlock() { try { if (locked) jedis.del(key); } finally { pool.returnResource(jedis); } } }2. 通過(guò)事務(wù)(multi)實(shí)現 由于采納第一張方法,第二種跟第三種實(shí)現只貼了關(guān)鍵代碼,望諒解。
^_^ public boolean lock_2(long timeout) { long nano = System.nanoTime(); timeout *= ONE_MILLI_NANOS; try { while ((System.nanoTime() - nano) Transaction t = jedis.multi(); // 開(kāi)啟事務(wù),當server端收到multi指令 // 會(huì )將該client的命令放入一個(gè)隊列,然后依次執行,知道收到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避免出現活鎖 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; }3. 通過(guò)事務(wù)+監聽(tīng)實(shí)現 public boolean lock_3(long timeout) { long nano = System.nanoTime(); timeout *= ONE_MILLI_NANOS; try { while ((System.nanoTime() - nano) jedis.watch(key); // 開(kāi)啟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避免出現活鎖 Thread.sleep(3, r.nextInt(500)); } } catch (Exception e) { } return false; } 最終采用第一種實(shí)現,因為加鎖只需發(fā)送一個(gè)請求,效率最高。四、【redis分布式】 最后一個(gè)話(huà)題,jedis的分布式。
在jedis的源碼里發(fā)現了兩種hash算法(MD5,MURMUR Hash(默認)),也可以自己實(shí)現redis.clients.util.Hashing接口擴展。 List hosts = new ArrayList(); //server1 JedisShardInfo host1 = new JedisShardInfo("", 6380, 2000); //server2 JedisShardInfo host2 = new 。
Object是所有類(lèi)的父類(lèi),任何類(lèi)都默認繼承Object。Object類(lèi)到底實(shí)現了哪些方法?
1.clone方法
保護方法,實(shí)現對象的淺復制,只有實(shí)現了Cloneable接口才可以調用該方法,否則拋出異常。
2.getClass方法
final方法,獲得運行時(shí)類(lèi)型。
3.toString方法
該方法用得比較多,一般子類(lèi)都有覆蓋。
4.finalize方法
該方法用于釋放資源。因為無(wú)法確定該方法什么時(shí)候被調用,很少使用。
5.equals方法
該方法是非常重要的一個(gè)方法。一般equals和==是不一樣的,但是在Object中兩者是一樣的。子類(lèi)一般都要重寫(xiě)這個(gè)方法。
6.hashCode方法
該方法用于哈希查找,重寫(xiě)了equals方法一般都要重寫(xiě)hashCode方法。這個(gè)方法在一些具有哈希功能的Collection中用到。
一般必須滿(mǎn)足obj1.equals(obj2)==true。可以推出obj1.hash-
Code()==obj2.hashCode(),但是hashCode相等不一定就滿(mǎn)足equals。不過(guò)為了提高效率,應該盡量使上面兩個(gè)條件接近等價(jià)。
7.wait方法
wait方法就是使當前線(xiàn)程等待該對象的鎖,當前線(xiàn)程必須是該對象的擁有者,也就是具有該對象的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long
timeout)設定一個(gè)超時(shí)間隔,如果在規定時(shí)間內沒(méi)有獲得鎖就返回。
調用該方法后當前線(xiàn)程進(jìn)入睡眠狀態(tài),直到以下事件發(fā)生。
(1)其他線(xiàn)程調用了該對象的notify方法。
(2)其他線(xiàn)程調用了該對象的notifyAll方法。
(3)其他線(xiàn)程調用了interrupt中斷該線(xiàn)程。
(4)時(shí)間間隔到了。
此時(shí)該線(xiàn)程就可以被調度了,如果是被中斷的話(huà)就拋出一個(gè)InterruptedException異常。
8.notify方法
該方法喚醒在該對象上等待的某個(gè)線(xiàn)程。
9.notifyAll方法
該方法喚醒在該對象上等待的所有線(xiàn)程。
redis是key-value存儲系統。
key-value分布式存儲系統查詢(xún)速度快、存放數據量大、支持高并發(fā),非常適合通過(guò)主鍵進(jìn)行查詢(xún),但不能進(jìn)行復雜的條件查詢(xún)。
如果輔以Real-Time Search Engine(實(shí)時(shí)搜索引擎)進(jìn)行復雜條件檢索、全文檢索,就可以替代并發(fā)性能較低的MySQL等關(guān)系型數據庫,達到高并發(fā)、高性能,節省幾十倍服務(wù)器數 量的目的。
以MemcacheDB、Tokyo Tyrant為代表的key-value分布式存儲,在上萬(wàn)并發(fā)連接下,輕松地完成高速查詢(xún)。而MySQL,在幾百個(gè)并發(fā)連接下,就基本上崩潰了。
在此基礎上,redis支持各種不同方式的排序。與memcached一樣,為了保證效率,數據都是緩存在內存中。區別的是redis會(huì )周期性的把更新的數據寫(xiě)入磁盤(pán)或者把修改操作寫(xiě)入追加的記錄文件,并且在此基礎上實(shí)現了master-slave(主從)同步。
擴展資料:
Jedis主存保護是存儲保護的重要環(huán)節。主存保護一般有存儲區域保護和訪(fǎng)問(wèn)方式保護。存儲區域保護可采用界限寄存器方式,由系統軟件經(jīng)特權指令給定上、下界寄存器內容,從而劃定每個(gè)用戶(hù)程序的區域,禁止越界訪(fǎng)問(wèn)。
Jedis當兩鍵符合時(shí)才允許執行存取操作,從而保護別的程序區域不被侵犯,環(huán)狀保護是把系統程序和用戶(hù)程序按重要性分層,稱(chēng)為環(huán),對每個(gè)環(huán)都規定訪(fǎng)問(wèn)它的級別,違反規定的存取操作是非法的,以此實(shí)現對正在執行的程序的保護。
參考資料來(lái)源:百度百科-Key-Value
主要有兩種方式:
① 快照持久化
在Redis配置文件中已經(jīng)自動(dòng)開(kāi)啟了,
格式是:save N M
表示在N秒之內,redis至少發(fā)生M次修改則redis抓快照到磁盤(pán)。
當然我們也可以手動(dòng)執行save或者bgsave(異步)命令來(lái)做快照
②append only file AOF持久化
總共有三種模式,如
appendfsync everysec默認的是每秒強制寫(xiě)入磁盤(pán)一次
appendfsync always 每次執行寫(xiě)操作的時(shí)候就強制寫(xiě)入磁盤(pán)
appendfsync no 完全取決于os,性能最好但是持久化沒(méi)法保證
其中第三種模式最好。redis默認的也是采取第三種模式。
簡(jiǎn)單說(shuō)一下,除了一些公司自主開(kāi)發(fā)的集群外。常用的一般有三種:
1. 使用redis-trib.rb,這個(gè)是安裝redis時(shí)就自帶的一種集群,采用了服務(wù)端分片的方式。Jedis使用JedisCluster類(lèi)來(lái)訪(fǎng)問(wèn)。
2. 使用Jedis帶的客戶(hù)端分片ShardedJedisPool類(lèi)。
3. 使用代理進(jìn)行分片twemproxy,連接代理可以使用Jedis類(lèi)(單鏈接)和JedisPool類(lèi)(多鏈接)。
下面提供一個(gè)JedisCluster的例子:
JedisCluster cluster;
public void init() {
// 加載redis配置文件
ResourceBundle bundle = ResourceBundle.getBundle("redis");
if (bundle == null) {
throw new ("[redis.properties] is not found!");
}
// 創(chuàng )建jedis池配置實(shí)例
JedisPoolConfig config = new JedisPoolConfig();
// 設置池配置項值
config.setMaxTotal(Integer.valueOf(bundle.getString("redis.pool.maxActive").trim()));
config.setMaxIdle(Integer.valueOf(bundle.getString("redis.pool.maxIdle").trim()));
config.setMaxWaitMillis(Long.valueOf(bundle.getString("redis.pool.maxWait").trim()));
config.setTestOnBorrow(Boolean.valueOf(bundle.getString("redis.pool.testOnBorrow").trim()));
config.setTestOnReturn(Boolean.valueOf(bundle.getString("redis.pool.testOnReturn").trim()));
Set hps = new HashSet();
hps.add(new HostAndPort("192.168.242.133", 4001));
hps.add(new HostAndPort("192.168.242.133", 4002));
hps.add(new HostAndPort("192.168.242.133", 4003));
hps.add(new HostAndPort("192.168.242.133", 4004));
cluster = new JedisCluster(hps, 2000, 5);
}
public void test() {
// 這里就可以使用cluster進(jìn)行各種redis的操作了(與Jedis類(lèi)的接口類(lèi)似)
cluster.set("key", "value");
}
如果要了解其它的,請留言給我。
這些都是父類(lèi)的方法
java.awt.Frame extends java.awt.Window
java.awt.Window extends java.awt.Container
java.awt.Container extends java.awt.Component
java.awt.Component extends ava.lang.Object
聲明:本網(wǎng)站尊重并保護知識產(chǎn)權,根據《信息網(wǎng)絡(luò )傳播權保護條例》,如果我們轉載的作品侵犯了您的權利,請在一個(gè)月內通知我們,我們會(huì )及時(shí)刪除。
蜀ICP備2020033479號-4 Copyright ? 2016 學(xué)習?shū)B(niǎo). 頁(yè)面生成時(shí)間:2.695秒