Redis基礎

 

原文:https://blog.csdn.net/cmqwan/article/details/97128715 

Redis

和memcache的區別
數據結構
內存使用率,key-value的話memcache更好
效率,單個value的大小100k以上redis更好
集羣部署,redis有原生支持
爲什麼單線程能有很高的效率
具體原因
單線程模型,避免了上下文切換
IO多路複用機制
純內存操作
連接過程
文件事件處理器(網絡事件處理器、file event handler),這個是單線程的採用IO多路複用機制監聽多個socket。

socket進來之後,如果有事件(比如說連接),IO多路複用程序就會將這個socket(這時候連接已經和connect事件綁定) 推到消息隊列中。

文件事件分派器從隊列中取出socket,檢查事件,根據不同的事件分給不同的處理器。

包括多個socket,io多路複用程序,消息隊列,文件事件分派器,事件處理器(命令請求處理器、命令回覆處理器、連接應答處理器,等等)


一次連接流程
服務端打開socket監聽
客戶端和服務端連接socket,這時候產生一個connect事件,後臺表示爲AE_READABLE
io多路複用程序將這個socket推到消息隊列裏面
分派器判斷是 連接應答處理器,進行處理,連接成功
這時候將socket和ae_writeable綁定,
io多路複用程序看到這個又有事件了,就又推到消息隊列
分派器判斷是 命令回覆處理器,就返回數據,然後和這個事件取消關聯
多個socket,io多路複用程序,消息隊列,文件事件分派器,事件處理器(命令請求處理器、命令回覆處理器、連接應答處理器,等等)
哪些類型
string
字符串
set key val,get key
簡單的key-value存儲,部門組織樹,用戶數據
list
數組
lpush key val1 val2,lpop key,lrange key 0 10
存粉絲、評論、lrange可以分頁,消息隊列
hash
鍵值對
hmset key key1 val1 key2 val2,hget key key1
用戶信息-鑑權碼-私鑰
set
不重複無序列表
sadd key val1 val2,smembers key
部門關係緩存
sort set
有序數組/帶權重值列表
zadd key score1 val1 score2 val2,zrangebyscore key
排行榜
從海量數據中查找某個key前綴
keys
keys pattern,

一次性返回全部滿足條件數據,會阻塞redis

scan
scan cursor pattern count,

按pattern條件從下標cursor開始找count個數據,不一定會是count,大致相等。返回結果包括下一個遊標位置和列表

持久化
持久化的意義
故障恢復
雲備份到一個存儲上
rdb
內存快照的形式
RDB方式,sava 600 10,600秒內有10次寫操作,則觸發。
將數據快照保存,有可能丟失數據。
優點:適合做冷備份、性能(不需要每時每刻),恢復快
缺點:丟數據
aof
把所有操作指令保存下來,存到一個文件中
內存和文件中有一層os-cache,每隔1s會調用f-sync
一次只會寫一個aof文件
aof文件不可能無限增大,BG-REWRITE-AOF。會根據當前快照,進行重寫aof文件
優點:數據丟少(1s),append-only模式寫磁盤-速度快,記錄是人可讀的
缺點:佔用磁盤大,qps寫會降低,脆弱點,數據恢復比較慢
序列化方式
JdkSerializationRedisSerializer
使用JDK提供的序列化功能。 優點是反序列化時不需要提供類型信息(class),但缺點是需要實現Serializable接口,還有序列化後的結果非常龐大,是JSON格式的5倍左右,這樣就會消耗redis服務器的大量內存。

GenericJackson2JsonRedisSerializer
StringRedisSerializer
不能序列化Bean,只能序列化字符串類型的數據,
如果value都是字符串類型,可以用該方式序列化

GenericFastJsonRedisSerializer
數據過期/淘汰
這個是緩存,有容量限制
過期之後,還是佔用內存
過期策略
設置了過期時間的key什麼時候刪除?定期刪除和惰性刪除,

這2個結合起來還是有可能漏掉一些key,這時候就需要內存淘汰機制登場

定期刪除
每隔100ms隨機抽去一些設置了超時時間的key,檢查是否過期

過期則刪除

這個會導致有可能一些key已經過期,但是沒有刪掉

惰性刪除
查詢某個key的時候,惰性檢查,是否過期

如果過期則返回空

內存淘汰機制
redis內存佔用過多的時候,會進行內存淘汰

具體策略
noeviction,報錯
allkeys-lru,所有key走lru算法
allkeys-random,所有key走隨機刪除
volatile-lru,設置過期時間走lru算法
volatile-random,設置過期時間的key走隨機刪除
volatile-ttl,設置過期時間的key走"按過期時間最短"的算法
LRU代碼實現
鏈表+hashmap

add、remove、refresh用來操作鏈表

get、put用來提供api

package com.lizhaoblog.code.io.redis;

import java.util.HashMap;

class Node {
    public Node(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public Node pre;
    public Node next;
    public String key;
    public String value;
}

public class LRUCache {
    private Node head;
    private Node end;
    //緩存上限
    private int limit;
    private HashMap<String,Node> map;

    public LRUCache(int limit) {
        this.limit = limit;
        map = new HashMap();
    }

    public String get(String key) {
        Node node = map.get(key);
        if (node == null) {
            return null;
        }
        //調整node到尾部
        refreshNode(node);
        return node.value;
    }

    public void put(String key, String value) {
        Node node = map.get(key);
        if (node == null) {
            //key不存在直接插入
            while (map.size() >= limit) {
                //去除鏈表內的節點
                String oldKey = removeNode(head);
                //去除map中的緩存
                map.remove(oldKey);
            }
            node = new Node(key, value);
            //鏈表中加入節點
            addNode(node);
            //map中加入節點
            map.put(key, node);
        } else {
            //更新節點並調整到尾部
            node.value = value;
            refreshNode(node);
        }
    }

    private void refreshNode(Node node) {
        //如果訪問的是尾節點,無須移動節點
        if (node == end) {
            return;
        }
        //把節點移動到尾部,相當於做一次刪除插入操作
        removeNode(node);
        addNode(node);
    }

    private String removeNode(Node node) {
        //尾節點
        if (node == end) {
            end = end.pre;
        } else if (node == head) {
            //頭結點
            head = head.next;
        } else {
            //中間節點
            node.pre.next = node.next;
            node.next.pre = node.pre;
        }
        return node.key;
    }

    private void addNode(Node node) {
        if (end != null) {
            end.next = node;
            node.pre = end;
            node.next = null;
        }
        end = node;
        if (head == null) {
            head = node;
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章