1. 程式人生 > >Java快取機制

Java快取機制

1 Java快取

1.1 jvm內建快取

Java中實現快取的方式有很多,比如用static hashMap基於記憶體快取的jvm內建快取,簡單不實用,保物件的有效性和週期無法控制,容易造成記憶體急劇上升。常用的有Oscache(主要針對jsp頁面),Ehcache(主要針對資料庫訪問層),Jcache,Jbosscache等等很多

缺點:容易記憶體溢位、沒有持久化(服務重啟後丟失)、執行緒安全、多個伺服器(多個jvm)之間的資料不能共享。

1.2 java操作eache

利用spring Boot搭建應用(可參考這裡的快取配置)。

{
id: 35,
name: "springboot2.2",
age: 99
}

原理?

1、當客戶端請求資料時,如果伺服器端配置了快取,第一步去快取裡面查詢,如果有跳4,沒有則往2

2、傳送jdbc請求操作資料庫查詢資料

3、將查詢到的資料返回給快取,並儲存在快取中

4、將從(快取|資料庫)查詢到的資料返回給客戶端。

好處?效率高(因為不用建立jdbc連線等)、降低了資料庫的壓力(不用每次請求都要去資料庫查,資料庫也會累啊)。

缺點?從上述也看到了,有可能產生資料不一致的情況,清除快取可解決。

和oscache區別? ehcache 主要是對資料庫訪問的快取,相同的查詢語句只需查詢一次資料庫,從而提高了查詢的速度,使用spring的AOP可以很容易實現這一功能。 oscache

主要是對頁面的快取,可以整頁或者指定網頁某一部分快取,同時指定他的過期時間,這樣在此時間段裡面訪問的資料都是一樣的。

2 Redis

關係型資料庫:持久、主外來鍵、編寫SQL語句、存放在硬碟。

非關係型資料庫:一般用於快取、值存放在記憶體(所以效率是真的高)、key-vakye形式、容易資料丟失(不過很好解決)、有點小類似jvm內建快取(不過這個更牛嗨,因為可以多個伺服器間共享資料)。

redis?可以持久化mongdb?儲存json格式

2.1 Redis概述

完全開源免費、最受BSD協議、高效能的key-value費關係型資料庫支援持久化、支援key-value(String)\list\set\zert\hash

等資料結構的儲存、支援備份

好處?減輕資料庫訪問的壓力。效率高?(訪問記憶體肯定比訪問硬碟快,這是常識)

應用場景?(token生成、session共享、分散式鎖、驗證碼、自增id(訂單id))

2.2 安裝redis

2.2.1 windows安裝redis

1、下載redis

2、解壓redis-latest-windws.zip檔案,將start.bat檔案拷貝紙redis目錄

3、編輯redis.windows.conf檔案,取消requirepass的註釋(前面不能有空格),空格然後新增密碼比如(123456)儲存。以requirepass 123456為例

4、雙擊start.bat執行

5、通過這個客戶端工具測試一下

2.2.2 linux安裝redis

1、下載redis

2、mkdir -p /usr/local/redis/bin,mkdir -p /usr/local/redis/etc

3、copy redis-3.0.0.tar.gz到使用者目錄比如/root

4、解壓tar -zxvf redis-3.0.0.tar.gz

5、cd redis-3.0.0/後,make一下

6、進入src目錄make install後就安裝成功了。

7、cd cd /root/redis-3.0.0/(redis安裝目錄)

8、cp redis.conf /usr/local/redis/etc

9、cd src

10、cp mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server redis-sentinel /usr/local/redis/bin
11、修改 redis.conf檔案

daemonize yes --- 修改為yes  後臺啟動

requirepass 123456  ----註釋取消掉設定賬號密碼

ps aux | grep '6379'  --- 查詢埠

kill -15 9886 --- 殺死重置

kill -9 9886 --- 強制殺死

service iptables stop 停止防火牆

12、cd /usr/local/redis/bin

./redis-server /usr/local/redis/etc/redis.conf啟動服務

13、./redis-cli -h 127.0.0.1 -p 6379 -a "123456"  --- redis 使用賬號密碼連線或者windows下的客戶端工具進行連線測試

PING 結果表示成功

14、停止redis

redis-cli shutdown  或者 kill redis程序的pid

15、放開一下埠,好像外部即使註釋掉bind 127.0.0.1也不能訪問/sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT

2.3 redis基本資料型別

2.3.1 字串

127.0.0.1:6379> set name raolei
OK
127.0.0.1:6379> set itboy www.itboy.com
OK
127.0.0.1:6379> get name
"raolei"
127.0.0.1:6379> get itboy
"www.itboy.com"

常用命令:

2.3.2 list

127.0.0.1:6379> lpush listkey redis
(integer) 1
127.0.0.1:6379> lpush listkey mysql
(integer) 2
127.0.0.1:6379> lpush listkey mongdb
(integer) 3
127.0.0.1:6379> lpush listkey hbase
(integer) 4
127.0.0.1:6379> lrange listkey 0 10
1) "hbase"
2) "mongdb"
3) "mysql"
4) "redis"

list是簡單的字串列表,按照插入順序排序,可在頭和尾插入,最多包含2^32-1個元素(40多億)。

常用命令:

2.3.3 set

Setstring型別的無序集合。集合成員是唯一的,這就意味著集合中不能出現重複的資料。

雜湊表實現。新增,刪除,查詢的複雜度都是O(1)。每個集合可儲存40多億個成員

127.0.0.1:6379> sadd setkey redis
(integer) 1
127.0.0.1:6379> sadd setkey redis
(integer) 0
127.0.0.1:6379> sadd setkey redis
(integer) 0
127.0.0.1:6379> sadd setkey redis
(integer) 0
127.0.0.1:6379> sadd setkey redis
(integer) 0
127.0.0.1:6379> sadd setkey mysql
(integer) 1
127.0.0.1:6379> sadd setkey mongdb
(integer) 1
127.0.0.1:6379> SMEMBERS setkey
1) "mysql"
2) "mongdb"
3) "redis"

常用命令:

2.3.4 sorted set

與set一樣也是string型別元素的集合,且不允許重複的成員。不同的是每個元素都會關聯一個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。有序集合的成員是唯一的,但分數(score)卻可以重複。

集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1) 集合中最大的成員數為 232 - 1 (4294967295, 每個集合可儲存40多億個成員)

127.0.0.1:6379> zadd zsetkey 1 redis
(integer) 1
127.0.0.1:6379> zadd zsetkey 2 mysql
(integer) 1
127.0.0.1:6379> zadd zsetkey 2 mongdb
(integer) 1
127.0.0.1:6379> smembers setkey
1) "mysql"
2) "mongdb"
3) "redis"

常用命令:

2.3.5 hash

是一個string型別的fieldvalue的對映表(和map差不多,只是兼職都是字串),hash特別適合用於儲存物件。Redis 中每個 hash 可以儲存 232 - 1 鍵值對(40多億)。

127.0.0.1:6379> hmset hmset name "redis tutorial"
OK
127.0.0.1:6379> hmset hmset age 24
OK
127.0.0.1:6379> hgetall hmset
1) "name"
2) "redis tutorial"
3) "age"
4) "24"

redis怎麼存放物件?通過將物件序列化為json字串,存放為字串形式,之後讀取在反序列化為物件,這樣很快很快

2.4 Spring Boot整合redis

新增依賴:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring2.0整合redis所需common-pool2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>

配置檔案:

########################################################
###Redis (RedisConfiguration)
########################################################
spring:
  redis:
    database: 0
    host: 192.168.245.134
    port: 6379
    password: 123456
    jedis:
      pool:
        max-idle: 8
        min-idle: 0
        max-active: 8
        max-wait: -1ms
    timeout: 5000ms

service:

@Service
public class RedisService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //字串
    public void setStringKey(String key,String value,Long time){
        setObject(key,value,time);
    }
    public void setStringKey(String key,String value){
        setObject(key,value,null);
    }

    //set
    public void setSetKey(String key,Set value){
        setObject(key,value,null);
    }
    //list
    public void setListKey(String key,List value){
        setObject(key,value,null);
    }

    public String getStringKey(String key){
        return (String) getObject(key,new String());
    }
    public Set getSetKey(String key){
        return (Set) getObject(key,new HashSet<String>());
    }
    public List getListKey(String key){
        return (List) getObject(key,new ArrayList<String>());
    }




    public void setObject(String key,Object value,Long time){
        if(StringUtils.isEmpty(key)||value==null){
            return;
        }
        //字串型別
        if(value instanceof String){
            String value1= (String) value;
            if(time!=null){
                stringRedisTemplate.opsForValue().set(key,value1,time,TimeUnit.SECONDS);
            } else{
                stringRedisTemplate.opsForValue().set(key,value1);
            }
            return;
        }
        //list型別
        else if(value instanceof List){
            List<String> list= (List<String>) value;
            for (String s:list) {
                stringRedisTemplate.opsForList().leftPush(key,s);
            }
            return;
         }
         //set
         else if(value instanceof Set){
            Set<String> strings= (Set<String>) value;
            for (String s : strings) {
                stringRedisTemplate.opsForSet().add(key,s);
            }
            return;
        }
        /**
         * .....
         */
    }

    public Object getObject(String key,Object object){
        if(StringUtils.isEmpty(key)||object==null){
            return null;
        }
        else if (object instanceof String){
            return stringRedisTemplate.opsForValue().get(key);
        }
        else if(object instanceof List){
            return stringRedisTemplate.opsForList().range(key,0,stringRedisTemplate.opsForList().size(key));
        }
        else if(object instanceof Set){
            return stringRedisTemplate.opsForSet().members(key);
        }
        return null;
    }
}

controller:

@RestController
public class IndexController {
    @Autowired
    private RedisService redisService;

    @RequestMapping("/setString")
    public String setString(@PathParam("key") String key,
                            @PathParam("value") String value){
        redisService.setStringKey(key,value);
        return redisService.getStringKey(key);
    }
    @RequestMapping("/setSet")
    public Set<String> setSet(@PathParam("key") String key,
                      @PathParam("value") String value){
        HashSet<String> strings = new HashSet<>();
        strings.add(value);
        strings.add(value+"1");
        strings.add(value+"2");
        strings.add(value+"3");
        redisService.setSetKey(key,strings);
        return redisService.getSetKey(key);
    }
    @RequestMapping("/setList")
    public List<String> setList(@PathParam("key") String key,
                        @PathParam("value") String value){
        ArrayList<String> strings = new ArrayList<>();
        strings.add(value);
        strings.add(value+"1");
        strings.add(value+"2");
        strings.add(value+"3");
        redisService.setListKey(key,strings);
        return redisService.getListKey(key);
    }
}

2.5 主從複製和哨兵機制理解

為什麼?資料備份、讀寫分離、叢集、高可用(宕機容錯機制)。

一般情況下,Redis高可用都是一主多從,而不像其他比如Nginx多主多從。

所謂主從複製,主要是為了減輕單臺伺服器的壓力(比如圖中的master),通過多臺伺服器的冗餘來保證高可用(單臺宕機容錯),實現讀寫分離、資料備份、叢集等。

如圖,其中master可讀可寫,但是當有客戶端連線達到叢集時,如果是讀操作就從slave從節點中隨機選擇一臺伺服器進行響應,如果是寫操作,那麼操作主伺服器。這就是讀寫分離了不是嗎。。。

問題?主伺服器寫之後,怎麼同步到從伺服器?(主從複製搞定,往下看)

問題?主伺服器宕機了,怎麼寫?通過哨兵機制,哨兵其實就是一個監聽器,一直監聽這主伺服器,如果主伺服器掛了,他就會使用投票(隨機)從主伺服器中選擇一臺伺服器作為主伺服器,此乃高可用。

問題?那如果整個叢集掛了呢?有一個東西叫做keepalived監聽器(其實就是一個shell寫的重啟服務的命令指令碼),如果監聽到某一臺伺服器掛了,他就會自動重啟的(一般30秒內,聽說可能不準確)。如果一直啟動失敗?那就沒辦法了,他只能傳送一封郵件給運維人員了。

2.5.1 主從複製實現

原理:通過快照檔案(類似mysql的二進位制可執行檔案),當master有更新時,從伺服器slave會實時請求得到該快照檔案,進行執行,如果網路問題?別擔心過,會重試的。

過程:

1:當一個從資料庫啟動時,會向主資料庫傳送sync命令,

2:主資料庫接收到sync命令後會開始在後臺儲存快照(執行rdb操作),並將儲存期間接收到的命令快取起來

3:當快照完成後,redis會將快照檔案和所有快取的命令傳送給從資料庫。

4:從資料庫收到後,會載入快照檔案並執行收到的快取的命令。

對於redis伺服器來說,和mysql有很大不同,只要設定好主從伺服器之後,主伺服器master可讀可寫,從伺服器slave僅可讀,不像mysql那樣需要分配使用者和mycat外掛來控制讀寫分離。

配置過程:

1、準備伺服器,如上圖中三臺伺服器(192.168.245.134,192.168.245.135,192.168.245.136),選擇一臺為主伺服器master(這臺伺服器什麼也不用做)

2、所以redis主從複製只需要配置從伺服器slave就OK,修改redis.conf配置檔案,放開以下兩行的註釋,新增如下內容。兩臺從伺服器都要修改。

slaveof 192.168.245.134 6379
#主伺服器的ip和埠號

# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the slave to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the slave request.
#
masterauth 123456
#主伺服器的認證密碼

3、測試

#進入master主伺服器
192.168.245.134:6379> info
#回車後看到如下內容即代表成功
# Replication
role:master
connected_slaves:2 #兩臺從伺服器
slave0:ip=192.168.245.135,port=6379,state=online,offset=127,lag=1 #一些描述資訊
slave1:ip=192.168.245.136,port=6379,state=online,offset=127,lag=1 #一些描述資訊


#進入任何一臺伺服器比如135,同樣info以下,看到如下內容即代表成功。
# Replication
role:slave  #角色從伺服器
master_host:192.168.245.134  #主伺服器ip
master_port:6379 #主服務埠
master_link_status:up #狀態
master_last_io_seconds_ago:10
master_sync_in_progress:0


#主伺服器
192.168.245.134:6379> set master "192.168.245.134"
OK
192.168.245.134:6379> get master
"192.168.245.134"
192.168.245.134:6379> 
##可讀可寫是吧???????
#########剛剛設定的這條資料從伺服器有嗎??????############
192.168.245.135:6379> get master
"192.168.245.134"
192.168.245.135:6379> set slave "192.168.245.135"
(error) READONLY You can't write against a read only slave.
192.168.245.135:6379> 
############喲呵有資料的,主從複製成功,主從間資料同步問題解決,而且不能寫,讀寫分離也搞定了########
[[email protected] bin]# ./redis-cli -h 192.168.245.136 -p 6379 -a "123456"
192.168.245.136:6379> ping
PONG
192.168.245.136:6379> get master
"192.168.245.134"
192.168.245.136:6379> set slave "192.168.245.136"
(error) READONLY You can't write against a read only slave.
192.168.245.136:6379> 

################另一臺從伺服器也一樣######################

2.5.2 哨兵機制實現

原理:哨兵用於管理多個redis伺服器,執行以下三個任務:

1、監控(monitoring):哨兵(sentinel)會不斷檢查你的master和slave是否運作正常

2、提醒(notification):當被監控的某個redis出現問題時,哨兵(sentinel)可以通過API向管理員或者其他應用程式傳送通知

3、自動故障遷移(automatic failover):當一個master1不能正常工作時,哨兵會開始一次自動故障遷移操作,他將會失效master1的其中一個slave升級為新的master2,並讓失效master1的其他slave的master1改為新的master2。當客戶端試圖連線失效的master1時,叢集也會向客戶端返回新的master2地址,使得叢集可以使用新的master2代替失效的master1

哨兵是一個分散式系統,可以在一個架構中執行多個哨兵程序,這些程序使用流言協議(gossipprotocols)來接收關於master是否下線的訊息,並使用投票協議(agreement protocols)來決定是否執行自動故障遷移以及選擇哪個slave作為新的master。

每個哨兵會向其他哨兵(sentinel)、master、slave定時傳送訊息,來確認對方是否還活著,如果對方在指定的時間(可配置)內未響應,則暫時認為對方已掛(主觀認為宕機,Subjective Down,sdown)

若哨兵群中的多數sentinel都報告某一個master沒響應,系統認為該master徹底死亡(客觀真正的宕機,Objective Down,oDwon),通過一定vote演算法,從生下的slave節點中選擇提升一臺為master,然後自動修改相關配置

雖然哨兵(sentinel) 釋出為一個單獨的可執行檔案 redis-sentinel ,但實際上它只是一個執行在特殊模式下的 Redis 伺服器,你可以在啟動一個普通 Redis 伺服器時通過給定 --sentinel 選項來啟動哨兵(sentinel).

實現:

這裡以上面三臺伺服器為基礎,選擇192.168.245.136這臺伺服器為哨兵(可以選擇多臺,此處僅一臺為例)。

注意:如果主從複製時,主伺服器沒有配置masterauth 123456請加上,這個坑了我很久很久,導致哨兵時一直連不上,最後檢視日誌才搞定,記得先將這個加到主伺服器的redis.conf中,然後重啟一下

1、修改redis安裝目錄下的sentinel.conf

sentinel monitor mymaster 192.168.245.134 6379 1
# 主節點名稱 主機ip 埠號 選舉次數(就是說當有幾臺sentinel認為master掛了才是真的掛了,因為這裡只有一個哨兵,所以為1)
sentinel down-after-milliseconds mymaster 30
#就是說多少ms後沒有給老子響應,老子就覺得你掛了。,預設30s,這裡設定為30ms,本地測試追求實時
sentinel config-epoch mymaster 1
#這個數字表示在發生主從複製的時候,比如master1向master2切換時,可以同時有多少個slave能對master2執行同步(也就是複製其實),越多越好?>如果太多了那麼大家都去複製了,誰來響應客戶端的請求?太少?太少的話,每個都要來一遍,怕是要到天黑哦,根據實際情況吧,這裡只有三臺所以
為設為1.

sentinel auth-pass mymaster 123456
#主伺服器密碼

2、啟動哨兵:nohup ./redis-server ../etc/sentinel.conf --sentinel  2>1 1>nohup.log &

3、如何停止ps -aux | grep 埠號,kill -9 pid即可

4、測試?

#我們首先檢視master(134)的info                  master
role:master
connected_slaves:2
slave0:ip=192.168.245.135,port=6379,state=online,offset=5349,lag=1
slave1:ip=192.168.245.136,port=6379,state=online,offset=5349,lag=0


#135的               slave 
role:slave
master_host:192.168.245.134
master_port:6379
master_link_status:up


#136 的             slave 
role:slave
master_host:192.168.245.134
master_port:6379
master_link_status:up


#我現在講master停掉?       master
#134
192.168.245.134:6379> shutdown
not connected> 


#135 的info replication
192.168.245.135:6379> info replication
# Replication
role:slave
master_host:192.168.245.136
master_port:6379
master_link_status:up
#看到主伺服器變為136,

#136?
192.168.245.136:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.245.135,port=6379,state=online,offset=739,lag=0
slave1:ip=192.168.245.134,port=6379,state=online,offset=739,lag=0

#基本上成功了,最後試試讀寫分離以及在測試一些其他的,這裡不再展示



2.6 資料持久化

資料持久化:就是將記憶體中的資料儲存到硬碟,redis支援AOF和RDB兩種儲存方式

2.6.1 RDB儲存

RDB是指在一個時間點,如果達到所配置的資料修改量,就寫入一個臨時檔案,持久化結束後,用這個臨時檔案替換上次持久化的檔案,達到資料恢復。(二進位制檔案方式

有兩種儲存方式:1、(阻塞)主程序直接拍快照(snapshot),然後阻塞客戶端請求寫入IO磁碟。2、(非阻塞)當要寫入磁碟時,新建(fork)一個子程序,子程序將當前資料庫快照寫入磁碟,而主程序繼續處理客戶端請求。

每次快照持久化都是將記憶體資料完整寫入到磁碟一次,並不 是增量的只同步髒資料。如果資料量大的話,而且寫操作比較多,必然會引起大量的磁碟io操作,可能會嚴重影響效能。

優點:使用單獨子程序進行持久化,主程序不會進行任何IO操作,保證了redis的高效能

缺點:RDB需要間隔一段時間進行持久化,而且必須達到相應修改數量,所以如果持久化之間發生故障,會造成資料丟失,常用於資料要求不嚴謹的時候。

配置:

#dbfilename:持久化資料儲存在本地的檔案
dbfilename dump.rdb
#dir:持久化資料儲存在本地的路徑,如果是在/redis/redis-3.0.6/src下啟動的redis-cli,則資料會儲存在當前src目錄下
dir ./

##snapshot觸發的時機,save   
 
##如下距離上一次持久化已經900s了,如果有大於等於1個變更,才會snapshot 
save 900 1
##對於此值的設定,需要謹慎,評估系統的變更操作密集程度  
##可以通過“save “””來關閉snapshot功能  

save 300 10  #表示間隔達到300s時如果更改了10次以上,就snapshot,如果你在10s時已經10次了,立馬持久化
save 60 10000 #同理間隔達到60s時如果更改了10000次以上,就snapshot,如果你在10s時已經10000次了,立馬持久化

##當snapshot時出現錯誤無法繼續時,是否阻塞客戶端“變更操作”,“錯誤”可能因為磁碟已滿/磁碟故障/OS級別異常等  
stop-writes-on-bgsave-error yes  
##是否啟用rdb檔案壓縮,預設為“yes”,壓縮往往意味著“額外的cpu消耗”,同時也意味這較小的檔案尺寸以及較短的網路傳輸時間  
rdbcompression yes  

這裡我將save 300 10改為save 30 10進行測試一下(首先先停掉哨兵機制,不然宕機後他就重新選一臺主的了):

改好後重啟一下服務:

進行5次更改操作
192.168.245.136:6379> get name
"1"
192.168.245.136:6379> set name 1
OK
192.168.245.136:6379> set name 2
OK
192.168.245.136:6379> set name 3
OK
192.168.245.136:6379> set name 4
OK
192.168.245.136:6379> set name 5
OK
192.168.245.136:6379> get name
"5"

#立馬kill掉redis程序,一定要kill如果主動關閉服務,他是會進行snapshot的
[[email protected] bin]# ps -aux | grep 6379
root       1572  0.1  0.9 140840  9640 ?        Ssl  02:02   0:00 ./redis-server *:6379
root       1577  0.0  0.5  20160  5184 pts/0    S+   02:02   0:00 ./redis-cli -h 192.168.245.136 -p 6379 -a 123456
root       1580  0.0  0.0 112676   984 pts/1    R+   02:03   0:00 grep --color=auto 6379
[[email protected] bin]# kill -9 1572

#重啟服務
[[email protected] bin]# ./redis-server ../etc/redis.conf 
[[email protected] bin]# ./redis-cli -h 192.168.245.136 -p 6379 -a "123456"
192.168.245.136:6379> get name
"1"
192.168.245.136:6379> 

#可以看到我最後的是“5”,而這裡是“1”,資料丟失了##########################
#更改10次呢?,是成功進行持久化了的,這裡不展示了,篇幅過大。
#而且如果你將dump.rdb檔案刪除後,達到snapshot條件時,會自動建立一個新的檔案,持久化其實就是將該檔案備份,下次將那些持久化後的檔案再放過來不就達到資料恢復了嗎????????????

2.6.2 AOF儲存

以日誌檔案方式儲存,其實就是將你“操作+資料”指令格式化後追加到操作日誌檔案的尾部,必須append(已經寫入到檔案或者即將寫入),才會進行資料的實際變更。日誌檔案儲存了歷史所有的操作過程;當 server 需要資料恢復時,可以直接 replay 此日誌檔案,即可還原所有的操作過程。內容是字串,容易閱讀和解析。

缺點:AOF 檔案比 RDB 檔案大,且恢復速度慢。

只會記錄變更操作”(例如:set/del ),如果 server 中持續的大量變更操作,將會導致 AOF 檔案非常的龐大,意味著 server 失效後,資料恢復的過程將會很長;事實上,一條資料經過多次變更,將會產生多條 AOF 記錄,其實只要儲存當前的狀態,歷史的操作記錄是可以拋棄的;因為 AOF 持久化模式還伴生了“AOF rewrite”

因為最多丟失最後一次寫入檔案的資料,所以很好修復,直接手工更改檔案或者重新來一次即可。

修改redis.conf檔案:

##此選項為aof功能的開關,預設為“no”,可以通過“yes”來開啟aof功能  
##只有在“yes”下,aof重寫/檔案同步等特性才會生效  
appendonly yes  

##指定aof檔名稱  
appendfilename appendonly.aof  

##指定aof操作中檔案同步策略,有三個合法值:always everysec no,預設為everysec每秒
appendfsync everysec  
##在aof-rewrite期間,appendfsync是否暫緩檔案同步,"no"表示“不暫緩”,“yes”表示“暫緩”,預設為“no”  
no-appendfsync-on-rewrite no  

##aof檔案rewrite觸發的最小檔案尺寸(mb,gb),只有大於此aof檔案大於此尺寸是才會觸發rewrite,預設“64mb”,建議“512mb”  
auto-aof-rewrite-min-size 64mb  

##相對於“上一次”rewrite,本次rewrite觸發時aof檔案應該增長的百分比。  
##每一次rewrite之後,redis都會記錄下此時“新aof”檔案的大小(例如A),那麼當aof檔案增長到A*(1 + p)之後  
##觸發下一次rewrite,每一次aof記錄的新增,都會檢測當前aof檔案的尺寸。  
auto-aof-rewrite-percentage 100  

重啟服務後,根據你啟動redis所在目錄下出現appendonly.aof檔案。

執行以下操作:

192.168.245.136:6379> set name 124
OK
192.168.245.136:6379> set name 145
OK
192.168.245.136:6379> set age 56
OK
192.168.245.136:6379> del age
(integer) 1
192.168.245.136:6379> get name
"145"

檢視aof檔案內容:

[[email protected] bin]# cat appendonly.aof 
*2
$6
SELECT
$1
0
*3
$3
set
$4
name
$3
124
*3
$3
set
$4
。。。

查詢的是不會出現在裡面,而且這個檔案是動態的。。看起來挺好的,就是頻繁的更改造成檔案過大,到時候恢復起來有點太慢了,有人說那麼RDB中將時間改快點要求改小點?大哥,人家是全量複製,那豈不是時間都花去複製了,還處理什麼請求?這個雖然慢,可是是追加方式啊,將就了。

2.6.3 redis宕機後,值會失效嗎?

不會,redis預設開啟RDB儲存,如果是直接關閉服務,那麼會自動備份,如果是kill或者斷電如果沒達到RDB配置的要求則不會持久化,而且這個雖然是非阻塞的,但是畢竟全量複製啊,保證了redis的效能,但是CPU可受不了啊。因此實際情況中,最好採用AOP方式,實時而且快,但是就是容易造成檔案過大,恢復困難。

2.7 redis事務

Redis 事務可以一次執行多個命令, 並且帶有以下兩個重要的保證:

事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。

事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。

multi 開啟事務 exec提交事務。

序號

命令及描述

1

 
取消事務,放棄執行事務塊內的所有命令。

2

EXEC 
執行所有事務塊內的命令。

3

MULTI 
標記一個事務塊的開始。

4

 
取消 WATCH 命令對所有 key 的監視。

5

 
監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那麼事務將被打斷。

2.8 釋出訂閱

Redis 釋出訂閱(pub/sub)是一種訊息通訊模式:傳送者(pub)傳送訊息,訂閱者(sub)接收訊息。Redis 客戶端可以訂閱任意數量的頻道

下圖展示了頻道 channel1 以及訂閱這個頻道的三個客戶端 —— client2 client5 client1 之間的關係:

當有新訊息通過 PUBLISH 命令傳送給頻道 channel1 時, 這個訊息就會被髮送給訂閱它的三個客戶端:

例子:

1、建立訂閱頻道名為 redisChatSimple

192.168.245.136:6379> subscribe redsiChatSample
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redsiChatSample"

2、重新開個客戶端,同一頻道釋出訊息

192.168.245.136:6379> publish redsiChatSample "gogog"
(integer) 1
192.168.245.136:6379> publish redsiChatSample "gogog22"
(integer) 1
192.168.245.136:6379> 

3、客戶端顯示如下資訊:

192.168.245.136:6379> subscribe redsiChatSample
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redsiChatSample"
3) (integer) 1
1) "message"
2) "redsiChatSample"
3) "gogog"
1) "message"
2) "redsiChatSample"
3) "gogog22"

厲害了!!!

相關推薦

Java快取機制

1 Java快取 1.1 jvm內建快取 Java中實現快取的方式有很多,比如用static hashMap基於記憶體快取的jvm內建快取,簡單不實用,保物件的有效性和週期無法控制,容易造成記憶體急劇上升。常用的有Oscache(主要針對jsp頁面),Ehcache(主要

Map實現java快取機制的簡單例項

快取是java中主要的內容,主要目的是緩解專案訪問資料庫的壓力以及提升訪問資料的效率,以下是通過Map實現java快取的功能,並沒有用cache相關框架。 一、快取管理類 CacheMgr.java package cache.com; import java.util

java快取機制 Redis / ehcache

首先了解一下這兩種快取機制的區別 ehcache直接在jvm虛擬機器中快取,速度快,效率高;但是快取共享麻煩,叢集分散式應用不方便。 redis是通過socket訪問到快取服務,效率比ecache低,比資料庫要快很多,處理叢集和分散式快取方便。適合應用於各個系統間快取共享,

LeetCode 146. LRU快取機制java實現)

參考解答 總結:這道題主要要知道選取何種資料結構並且在 O(1) 時間複雜度內完成這兩種操作? O(1) 的get方法肯定要用到HashMap() LinkedList(雙向連結串列)可以以O(1)時間複雜度,很方便地實現資料的插入和刪除 所以,將兩個資料結構聯合使用,Ha

JAVA中Integer的快取機制

Integer的快取機制: Integer是對小資料(-128~127)是有快取的,再jvm初始化的時候,資料-128~127之間的數字便被快取到了本地記憶體中,如果初始化-128~127之間的數字,會直接從記憶體中取出,不需要新建一個物件. public static v

java物件中的三種狀態和髒檢查及重新整理快取機制

瞬時狀態   瞬時狀態又稱臨時狀態.如果java物件與資料庫中的資料沒有任何的關聯,即此java物件在資料庫中沒有相關聯的記錄,此時java物件的狀態為瞬時狀態,session對於 瞬時狀態的ava物件是一無所知的,當物件不再被其他物件引用時,它的所有資料也就丟失了,物件將會被java虛擬機器按照垃圾回收

JAVA語法細節-中間變數快取機制

         在程式設計中,我們經常遇到 i++ 或 ++i 等變數自加操作,一般來說,如 i=0; x=i++ 很容易就會思考到 i 是先使用,後自加,然後x=0; i=1;  對於 x=++i ; 則是先自加,再引用, i=i+1,

java 中間變數快取機制(i++,++i)

public class Test { public static void main(String[] args) { int i = 0; i = i ++ ; System.out.println(i); }

快取機制:java快取的原理

外存:   也就是我們經常說的(CDEF盤的大小)外儲存器是指除計算機記憶體及CPU快取以外的儲存器,此類儲存器一般斷電後仍然能儲存資料。常見的外儲存器有硬碟、軟盤、光碟、U盤等,一般的軟體都是安裝在外存中 記憶體:   記憶體是計算機中重要的部件之一,它是與CPU進行

【Redis快取機制】14.Java連線Redis_Jedis_主從模式

redis的主從模式之前提到過,這裡我們使用redis來實現主從模式。 首先在VMware虛擬機器中的Linux中開啟兩個終端,一個是使用者jack,一個是newuser: 然後我們jack作為主機,redis服務執行在6379埠,我們設定newuser為從機,設定其red

【Redis快取機制】12.Java連線Redis_Jedis_常用API

上一篇總結我們使用我們本地的Eclipse中建立的jedis工程,連結 到了我們處於VMware虛擬機器上的Linux系統上的Redis服務,我們接下來 講一下jedis的一些常用的API。 (1)jedis儲存字串package cn.com.redis; import

java物件本地快取機制的實現

本地快取機制,利用java.util.concurrent,很好的管理本地記憶體儲存的物件內容。 建立屬性:  /**      * 具體內容存放的地方      */     private ConcurrentHashMap<String, Object>

【Redis快取機制】13.Java連線Redis_Jedis_事務

Jedis事務我們使用JDBC連線Mysql的時候,每次執行sql語句之前,都需要開啟事務;在MyBatis中,也需要使用openSession()來獲取session事務物件,來進行sql執行、查詢等操作。當我們對資料庫的操作結束的時候,是事務物件負責關閉資料庫連線。事務

Java三大框架之——Hibernate中的三種資料持久狀態和快取機制

Hibernate中的三種狀態     瞬時狀態:剛建立的物件還沒有被Session持久化、快取中不存在這個物件的資料並且資料庫中沒有這個物件對應的資料為瞬時狀態這個時候是沒有OID。   持久狀態:物件經過Session持久化操作,快取中存在這個物件的資料為持久狀

如何利用快取機制實現JAVA類反射效能提升30倍

一次效能提高30倍的JAVA類反射效能優化實踐 文章來源:宜信技術學院 & 宜信支付結算團隊技術分享第4期-支付結算部支付研發團隊高階工程師陶紅《JAVA類反射技術&優化》 分享者:宜信支付結算部支付研發團隊高階工程師陶紅 原文首發於宜信支付結算技術團隊公號:野指標 在實際工作中的一些特定

Java框架之MyBatis 07-動態SQL-快取機制-逆向工程-分頁外掛

MyBatis   今天大年初一,你在學習!不學習做什麼,鬥地主...人都湊不齊。學習吧,學習使我快樂!除了詩和遠方還有責任,我也想擔當,我也想負責,可臣妾做不到啊,怎麼辦?你說怎麼辦,為啥人家能做到你做不到,因為人家比你多做了那麼一點點。哪一點點?就那麼一點點,只要你也多做那麼一點點,不就做到了!...就那

java反射機制

else ++ 類型 應該 動態 error param 字母 什麽 最近在做一個項目。 需求是這樣的,前端傳一個參數param表示要從服務器獲取的參數,後端需要把對應的參數從服務器中取出來。 本來覺得沒什麽,應該蠻簡單。結果一看表,嗯,40多個參數,如果用if...els

Java反射機制深入詳解

const 運行時 設計 應用程序 類加載器 分配 import 程序 為什麽 一.概念   反射就是把Java的各種成分映射成相應的Java類。   Class類的構造方法是private,由JVM創建。   反射是java語言的一個特性,它允程序在運行時(註意不是編譯的

利用JAVA反射機制實現調用私有方法

parse try ble cat 權限 利用 enabled tde mod 1.fragment是AccessibilityFragment的對象。須要被調用的方法的類。 setAccessible(true)並非將方法的訪問權限改成了public。而是取

Java反射機制詳解一

java 反射 反射機制 工廠模式 1反射機制是什麽反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。在面向對象的世界裏,萬事萬物皆對象.在ja