1. 程式人生 > >redis的資料安全與效能保障

redis的資料安全與效能保障

快照持久化方式:可以將存在於某一時刻的所有資料都寫入硬盤裡面。
只追加檔案:(append-only file,AOF),它會在執行寫命令時,將被執行的寫命令複製到硬盤裡面。


redis.conf引數配置:
save 900 1, 900秒內有一次寫入,存入快照
save 300 10
,300秒內有10次寫入,存入快照
save 60 10000,60秒內有10000此寫入,存入快照
同步機制(快照持久化):
appendfsync always:每個Redis寫命令都要同步寫入硬碟
,比較影響效能
appendfsync everysec:每秒執行一次同步,顯式地將多個寫命令同步到硬碟
(推薦)
appendfsync no:讓作業系統來決定應該何時進行同步,有不確定性
AOF持久化:
auto-aof-rewrite-percentage 100


auto-aof-rewrite-min-size 64mb
就是當AOF檔案的體積大雨64MB,並且AOF檔案的體積比上一次重寫之後的體積大了一倍(100%)的時候,Redis會對AOF檔案執行重寫操作,整理AOF檔案。


主從伺服器:
如果使用者在啟動Redis伺服器的時候,指定了一個包含slaveof host port選項的配置檔案,那麼Redis伺服器將根據該選項給定的IP地址和埠來連線主伺服器。
jedis.info()可以查詢到大量與伺服器相關的訊息。


關於redis的文章,網路上大多介紹的不是特別全面,簡明易懂,下面是一個遊戲平臺商品交易市場,用redis實現的例子,裡面充分用到了redis的事務特性,並對每行程式碼做出了詳細解釋:
/**
     * 使用者將商品放入市場的過程
     * @param jedis
     * @param itemid
     * @param sellerid
     * @param price
     * @return
     */
    private boolean listItem(Jedis jedis, String itemid, String sellerid, double price) {
        String inventory = "inventory:" + sellerid;
        String item = itemid + "." + sellerid;
        long end = System.currentTimeMillis() + 5000;

        while (System.currentTimeMillis() < end) {
            //監護使用者包裹發生的變化,此處用了watch的樂觀鎖,watch之後其他執行緒都不可修改包裹資訊
            jedis.watch(inventory);
            //如果指定的商品不在使用者包裹裡面,那麼停止監控並返回false
            if (!jedis.sismember(inventory, itemid)) {
                jedis.unwatch();
                return false;
            }
            //從jedis.multi()到trans.exec(),這是一個redis事務,redis事務可以一次性提交其中的所有命令,避免了分步提交
            //資料的不一致性,同時減少了與redis連線的次數,縮短時間。
            Transaction trans = jedis.multi();
            //把商品新增到商品市場,並移除包裹中商品。
            trans.zadd("market:", price, item);
            trans.srem(inventory, itemid);
            //若因為watch的樂觀鎖返回失敗,則繼續重試。
            List<Object> results = trans.exec();
            if (results == null){
                continue;
            }
            return true;
        }
        return false;
    }

    /**
     * 使用者從市場購買商品
     * @param jedis
     * @param buyerid
     * @param itemid
     * @param sellerid
     * @param lprice
     * @return
     */
    private boolean purchaseItem(Jedis jedis, String buyerid, String itemid, String sellerid, double lprice) {

        String buyer = "users:" + buyerid;
        String seller = "users:" + sellerid;
        String item = itemid + "." + sellerid;
        String inventory = "inventory:" + buyerid;
        long end = System.currentTimeMillis() + 10000;
        while (System.currentTimeMillis() < end) {
            //此處從市場拿走了商品,從買家拿走了錢,所以兩者狀態要同時watch
            try {
                jedis.watch("market:", buyer);
                Set<Tuple> tuples = jedis.zrangeWithScores("market:", 0, -1);
                System.out.println("The market PurchaseItem liuzhe contains:");
                for (Tuple tuple : tuples) {
                    System.out.println("  " + tuple.getElement() + ", " + tuple.getScore());
                }

                System.out.println("jedis.name():" + jedis);

                double price = jedis.zscore("market:", item);
                int funds = Integer.parseInt(jedis.hget(buyer, "funds"));
                //檢查商品價格是否變化,以及買家是否有錢購買
                if (price != lprice || price > funds) {
                    jedis.unwatch();
                    return false;
                }
                Transaction trans = jedis.multi();
                //先將買家錢給賣家,在將市場商品給買家
                trans.hincrBy(seller, "funds", (long) price);
                trans.hincrBy(buyer, "funds", (long) (-price));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                trans.sadd(inventory, itemid);
                trans.zrem("market:", item);
                List<Object> results = trans.exec();
                //如果有人正在修改,watch的樂觀鎖會返回空資料,重試。
                if (results == null) {
                    System.out.println("purchaseItem results null continue!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                    continue;
                }
                return true;
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
        return false;
    }
    /**
     * redis中提供了pipelline引數,可以讓使用者在不用封裝mutil()與exec()的時候,將命令一次請求,即非事務型
     * 一次性連線redis進行處理。
     * @param jedis
     * @param token
     * @param user
     * @param item
     */
    private void updateTokenPipeline(Jedis jedis, String token, String user, String item) {
        long timestamp = System.currentTimeMillis();
        Pipeline pipe = jedis.pipelined();
        pipe.hset("login:", token, user);
        pipe.zadd("recent:", timestamp, token);
        if (item != null) {
            pipe.zadd("viewed:", timestamp, token);
            pipe.zremrangeByRank("viewed:" + token, 0, -26);
            pipe.zincrby("viewed:", -1, item);
        }
        pipe.exec();
    }
關於redis的安全以及事務性,在這裡就基本介紹完畢,redis其效能非常高,但也不該亂用,應該用在合適的地方。