国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Spring Data Redis 讓 NoSQL 快如閃電(2)

lewif / 3327人閱讀

摘要:布隆過濾器布隆過濾器是一種空間利用率較高的概率數據結構,用來測試某元素是否某個集的一員。則利用布隆過濾器過濾掉不包含特殊行或列的塊磁盤讀取,使讀取速度得到明顯提升。搜索,就能發現很多布隆過濾器項目,其中一些還支持可調諧精度。

【編者按】本文作者為 Xinyu Liu,文章的第一部分重點概述了 Redis 方方面面的特性。在第二部分,將介紹詳細的用例。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現。

把 Redis 當作數據庫的用例

現在我們來看看在服務器端 Java 企業版系統中把 Redis 當作數據庫的各種用法吧。無論用例的簡繁,Redis 都能幫助用戶優化性能、處理能力和延遲,讓常規 Java 企業版技術棧望而卻步。

1. 全局唯一增量計數器

我們先從一個相對簡單的用例開始吧:一個增量計數器,可顯示某網站受到多少次點擊。Spring Data Redis 有兩個適用于這一實用程序的類:RedisAtomicIntegerRedisAtomicLong。和 Java 并發包中的 AtomicIntegerAtomicLong 不同的是,這些 Spring 類能在多個 JVM 中發揮作用。

列表 3:全局唯一增量計數器

RedisAtomicLong counter = 
    new RedisAtomicLong("UNIQUE_COUNTER_NAME", redisTemplate.getConnectionFactory()); 
Long myCounter = counter.incrementAndGet();// return the incremented value

請注意整型溢出并謹記,在這兩個類上進行操作需要付出相對較高的代價。

2. 全局悲觀鎖

時不時的,用戶就得應對服務器集群的爭用。假設你從一個服務器集群運行一個預定作業。在沒有全局鎖的情況下,集群中的節點會發起冗余作業實例。假設某個聊天室分區可容納 50 人。如果聊天室已滿,就需要創建新的聊天室實例來容納另外 50 人。

如果檢測到聊天室已滿但沒有全局鎖,集群中的各個節點就會創建自有的聊天室實例,為整個系統帶來不可預知的因素。列表 4 介紹了應當如何充分利用 SETNX(SET if Not eXists:如果不存在,則設置)這一 Redis 命令來執行全局悲觀鎖。

列表4:全局悲觀鎖

public String aquirePessimisticLockWithTimeout(String lockName,            int acquireTimeout, int lockTimeout) {        
  
  if (StringUtils.isBlank(lockName) || lockTimeout <= 0)            
      return null;        
      final String lockKey = lockName;
        String identifier = UUID.randomUUID().toString(); 
        Calendar atoCal = Calendar.getInstance();
        atoCal.add(Calendar.SECOND, acquireTimeout);
        Date atoTime = atoCal.getTime();        
        
        while (true) {            
           // try to acquire the lock            
           if (redisTemplate.execute(new RedisCallback() {                @Override                
           public Boolean doInRedis(RedisConnection connection)                        throws DataAccessException {                    
           return connection.setNX(
redisTemplate.getStringSerializer().serialize(lockKey), redisTemplate.getStringSerializer().serialize(identifier));
                }
            })) {   // successfully acquired the lock, set expiration of the lock
             redisTemplate.execute(new RedisCallback() {                      @Override                    
             public Boolean doInRedis(RedisConnection connection)                            throws DataAccessException {                        
              return connection.expire(redisTemplate
                                .getStringSerializer().serialize(lockKey),
                                lockTimeout);
                    }
                });                
                return identifier;
            } else { // fail to acquire the lock                
            // set expiration of the lock in case ttl is not set yet.                if (null == redisTemplate.execute(new RedisCallback() {                    @Override                    
            public Long 
      doInRedis(RedisConnection connection)                            
         throws DataAccessException 
         {                        
              return connection.ttl(redisTemplate
                                .getStringSerializer().serialize(lockKey));
                    }
                })) {                    // set expiration of the lock
                    redisTemplate.execute(new RedisCallback() 
                    {                        
                    @Override                        
                    public Boolean 
                    
           doInRedis(RedisConnection connection)                                        throws DataAccessException {                            
           return connection.expire(redisTemplate
                                .getStringSerializer().serialize(lockKey),
                                    lockTimeout);
                        }
                    }); 
}                if (acquireTimeout < 0) // no wait                    
                 return null;                
                 else {                    
                     try {
                        Thread.sleep(100l); // wait 100 milliseconds before retry
                    } catch (InterruptedException ex) {
                    }
                }                if (new Date().after(atoTime))                    break;
            }
        }        return null;
    }    
    
    
    public void 
releasePessimisticLockWithTimeout(String lockName, String identifier) {        if (StringUtils.isBlank(lockName) || StringUtils.isBlank(identifier))            return;        

     final String lockKey = lockName;

        redisTemplate.execute(new RedisCallback() {                          @Override                    
        public Void doInRedis(RedisConnection connection)                            throws DataAccessException {                        
        byte[] ctn = connection.get(redisTemplate
                                .getStringSerializer().serialize(lockKey));                        if(ctn!=null && identifier.equals(redisTemplate.getStringSerializer().deserialize(ctn)))
                            connection.del(redisTemplate.getStringSerializer().serialize(lockKey));                        return null;
                    }
                });
    }

如果使用關系數據庫,一旦最先生成鎖的程序意外退出,鎖就可能永遠得不到釋放。Redis 的 EXPIRE 設置可確保在任何情況下釋放鎖。

3. 位屏蔽(Bit Mask)

假設 web 客戶端需要輪詢一臺 web 服務器,針對某個數據庫中的多個表查詢客戶指定更新內容。如果盲目地查詢所有相應的表以尋找潛在更新,成本較高。為了避免這一做法,可以嘗試在 Redis 中給每個客戶端保存一個整型作為臟指標,整型的每個數位表示一個表。該表中存在客戶所需更新時,設置數位。輪詢期間,不會觸發對表的查詢,除非設置了相應數位。就獲取并將這樣的位屏蔽設置為 STRING 而言,Redis 非常高效。

4. 排行榜(Leaderboard)

Redis 的 ZSET 數據結構為游戲玩家排行榜提供了簡潔的解決方案。ZSET 的工作方式有些類似于 Java 中的 PriorityQueue,各個對象均為經過排序的數據結構,井井有條??梢园凑辗謹蹬懦鲇螒蛲婕以谂判邪裆系奈恢?。Redis 的 ZSET 定義了一份內容豐富的命令列表,支持靈活有效的查詢。例如,ZRANGE(包括 ZREVRANGE)可返回有序集內的指定范圍要素。

你可以使用這一命令列出排行榜前 100 名玩家。ZRANGEBYSCORE 返回指定分數范圍內的要素(例如列出得分為 1000 至 2000 之間的玩家),ZRNK 則返回有序集內的要素的排名,諸如此類。

5. 布隆(Bloom)過濾器

布隆過濾器 (Bloom filter) 是一種空間利用率較高的概率數據結構,用來測試某元素是否某個集的一員。可能會出現誤報匹配,但不會漏報。查詢可返回“可能在集內”或“肯定不在集內”。

就在線服務和離線服務包括大數據分析等方面,布隆過濾器數據結構都能派上很多用場。Facebook 利用布隆過濾器進行輸入提示搜索,為用戶輸入的查詢提取朋友和朋友的朋友。Apache HBase 則利用布隆過濾器過濾掉不包含特殊行或列的 HFile 塊磁盤讀取,使讀取速度得到明顯提升。Bitly 用布隆過濾器來避免將用戶重定向到惡意網站,而 Quara 則在訂閱后端執行了一個切分的布隆過濾器,用來過濾掉之前查看過的內容。在我自己的項目里,我用布隆過濾器追蹤用戶對各個主題的投票情況。

借助出色的速度和處理能力,Redis 極好地融合了布隆過濾器。搜索 GitHub,就能發現很多 Redis 布隆過濾器項目,其中一些還支持可調諧精度。

6. 高效的全局通知:發布/訂閱渠道

Redis 發布/訂閱渠道的工作方式類似于一個扇出消息傳遞系統,或 JMS 語義中的一個主題。JMS 主題和 Redis 發布/訂閱渠道的一個區別是,通過 Redis 發布的消息并不持久。消息被推送給所有相連的客戶端后,Redis 上就會刪除這一消息。換句話說,訂閱者必須一直在線才能接收新消息。Redis 發布/訂閱渠道的典型用例包括實時配置分布、簡單的聊天服務器等。

在 web 服務器集群中,每個節點都可以是 Redis 發布/訂閱渠道的一個訂閱者。發布到渠道上的消息也會被即時推送到所有相連節點。這一消息可以是某種配置更改,也可以是針對所有在線用戶的全局通知。和恒定輪詢相比,這種推送溝通模式顯然極為高效。

Redis 性能優化

Redis 非常強大,但也可以從整體上和根據特定編程場景做出進一步優化。可以考慮以下技巧。

存活時間

所有 Redis 數據結構都具備存活時間 (TTL) 屬性。當你設置這一屬性時,數據結構會在過期后自動刪除。充分利用這一功能,可以讓 Redis 保持較低的內存損耗。

管道技術

在一條請求中向 Redis 發送多個命令,這種方法叫做管道技術。這一技術節省了網絡往返的成本,這一點非常重要,因為網絡延遲可能比 Redis 延遲要高上好幾個量級。但這里存在一個陷阱:管道中的 Redis 命令列表必須預先確定,并且應當彼此獨立。如果一個命令的參數是由先前命令的結果計算得出,管道技術就不起作用。列表 5 給出了 Redis 管道技術的一個示例。

列表 5:管道技術

@Override
public List fetchLeaderboard(String key, String... playerIds) {    
   final List entries = new ArrayList<>();
    redisTemplate.executePipelined(new RedisCallback() {    // enable Redis Pipeline        
    @Override 
        public Object doInRedis(RedisConnection connection) throws DataAccessException { 
            for(String playerId : playerIds) {
                Long rank = connection.zRevRank(key.getBytes(), playerId.getBytes());
                Double score = connection.zScore(key.getBytes(), playerId.getBytes());
                LeaderboardEntry entry = new LeaderboardEntry(playerId, 
                score!=null?score.intValue():-1, rank!=null?rank.intValue():-1);
                entries.add(entry);
            }        
            return null; 
        }
    }); 
    return entries; 
}

副本集和切分

Redis 支持主從副本配置。和 MongoDB 一樣,副本集也是不對稱的,因為從節點是只讀的,以便共享讀取工作量。我在文章開頭提到過,也可以執行切分來橫向擴展 Redis 的處理能力和存儲容量。事實上,Redis 非常強大,據亞馬遜公司的內部基準顯示,類型 r3.4xlarge 的一個 EC2 實例每秒可輕松處理 100000 次請求。傳說還有把每秒 700000 次請求作為基準的。對于中小型應用程序,通常無需考慮 Redis 切分。(請參見這篇非常出色的文章《運行中的 Redis》,進一步了解 Redis 的性能優化和切分。)

Redis 中的事務

Redis 并不像關系數據庫管理系統那樣能支持全面的 ACID 事務,但其自有的事務也非常有效。從本質上來說,Redis 事務是管道、樂觀鎖、確定提交和回滾的結合。其思想是執行一個管道中的一個命令列表,然后觀察某一關鍵記錄的潛在更新(樂觀鎖)。根據所觀察的記錄是否會被另一個進程更新,該命令列表或整體確定提交,或完全回滾。

下面以某個拍賣網站上的賣方庫存為例。買方試圖從賣方處購買某件商品時,你負責觀察 Redis 事務內的賣方庫存變化。同時,你要從同一個庫存中刪除此商品。事務關閉前,如果庫存被一個以上進程觸及(例如,如果兩個買方同時購買了同一件商品),事務將回滾,否則事務會確定提交?;貪L后可開始重試。

Spring Data Redis 中的事務陷阱

我在 Spring 的 RedisTemplateredisTemplate.setEnableTransactionSupport(true); 中啟用 Redis 事務時得到一個慘痛的教訓:Redis 會在運行幾天后開始返回垃圾數據,導致數據嚴重損壞。StackOverflow 上也報道了類似情況。

在運行一個 monitor 命令后,我的團隊發現,在進行 Redis 操作或 RedisCallback 后,Spring 并沒有自動關閉 Redis 連接,而事實上它是應該關閉的。如果再次使用未關閉的連接,可能會從意想不到的 Redis 密鑰返回垃圾數據。有意思的是,如果在 RedisTemplate 中把事務支持設為 false,這一問題就不會出現了。

我們發現,我們可以先在 Spring 語境里配置一個 PlatformTransactionManager(例如 DataSourceTransactionManager),然后再用 @Transactional 注釋來聲明 Redis 事務的范圍,讓 Spring 自動關閉 Redis 連接。

根據這一經驗,我們相信,在 Spring 語境里配置兩個多帶帶的 RedisTemplate 是很好的做法:其中一個 RedisTemplates 的事務設為 false,用于大多數 Redis 操作,另一個 RedisTemplates 的事務已激活,僅用于 Redis 事務。當然必須要聲明 PlatformTransactionManager@Transactional,以防返回垃圾數值。

另外,我們還發現了 Redis 事務和關系數據庫事務(在本例中,即 JDBC)相結合的不利之處?;旌闲褪聞盏谋憩F和預想的不太一樣。

結論

我希望通過這篇文章向其他 Java 企業開發師介紹 Redis 的強大之處,尤其是將 Redis 用作遠程數據緩存和用于易揮發數據時。在這里我介紹了 Redis 的六個有效用例,分享了一些性能優化技巧,還說明了我的 Glu Mobile 團隊怎樣解決了 Spring Data Redis 事務配置不當造成的垃圾數據問題。我希望這篇文章能夠激發你對 Redis NoSQL 的好奇心,讓你能夠受到啟發,在自己的 Java 企業版系統里創造出一番天地。

本文系 OneAPM 工程師編譯整理。OneAPM 能為您提供端到端的 Java 應用性能解決方案,我們支持所有常見的 Java 框架及應用服務器,助您快速發現系統瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,Java 監控從來沒有如此簡單。想閱讀更多技術文章,請訪問 OneAPM 官方技術博客。

本文轉自 OneAPM 官方博客

原文地址:http://www.javaworld.com/article/3062899/big-data/lightning-fast-nosql-with-spring-data-redis.html?page=2

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64852.html

相關文章

  • Spring Data Redis NoSQL 快如閃電 (1)

    摘要:以遠程緩存服務器見長,對易揮發數據來說是極快型數據庫。即使成功寫入數據庫,最后也可能會因為網絡故障而使得緩存服務器以失敗告終。 【編者按】本文作者為 Xinyu Liu,詳細介紹了 Redis 的特性,并輔之以豐富的用例。在本文的第一部分,將重點概述 Redis 的方方面面。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現。 建立在 Java 企業版之上的多層體系結構是強大的服務...

    JerryC 評論0 收藏0
  • 快如閃電,觸控優先。新一代的純前端控件集 WijmoJS發布新版本了

    摘要:全球最大的控件提供商葡萄城宣布,新一代純前端控件發布版本,進一步增強產品功能,并支持在上的安裝和發布,極大的提升了產品的易用性。葡萄城的控件和軟件產品在國內外屢獲殊榮,在全球被數十萬家企業學校和政府機構廣泛應用。 全球最大的控件提供商葡萄城宣布,新一代純前端控件 WijmoJS 發布2018 v1 版本,進一步增強產品功能,并支持在 Npm 上的安裝和發布,極大的提升了產品的易用性。 ...

    aikin 評論0 收藏0
  • 快如閃電,觸控優先。新一代的純前端控件集 WijmoJS發布新版本了

    摘要:全球最大的控件提供商葡萄城宣布,新一代純前端控件發布版本,進一步增強產品功能,并支持在上的安裝和發布,極大的提升了產品的易用性。葡萄城的控件和軟件產品在國內外屢獲殊榮,在全球被數十萬家企業學校和政府機構廣泛應用。 全球最大的控件提供商葡萄城宣布,新一代純前端控件 WijmoJS 發布2018 v1 版本,進一步增強產品功能,并支持在 Npm 上的安裝和發布,極大的提升了產品的易用性。 ...

    陳江龍 評論0 收藏0

發表評論

0條評論

lewif

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<