1. 程式人生 > 實用技巧 >redis-快取設計-字首匹配

redis-快取設計-字首匹配

說明

錄入:

是將錄入字元的String 的各個char 的ASCII碼轉為16進位制 在通過-拼接起來,通過zadd新增 score設定為0 則會通過value 16進位制進行排序

查詢

將查詢的字元轉換為16進位制通過-拼接

start計算:通過匹配字元16進位制最後以為進1算出起始16進位制 再+g 包括所有起始範圍

end計算:通過匹配字元16進位制+g 包括所有範圍

然後zadd臨時加入到redis 預設通過value排序則將匹配字元大概包裹起來

然後通過2個臨時資料獲得rank 再根據起始和結zrank獲得資料 過濾掉多餘的 再講16進位制轉換為字元 返回

錄入資料

 //unicode編碼
private String coding(String s) { char[] chars = s.toCharArray(); StringBuffer buffer = new StringBuffer(); for (char aChar : chars) { //通過字元對應的ASCII碼轉換為16進位制再根據-拼接起來 -4f60-7231-5e7f-5dde-30 String s1 = Integer.toString(aChar, 16); buffer.append(
"-" + s1); } String encoding = buffer.toString(); return encoding; } //新增資料 public long add(Jedis con, String member, String key) { //進行編碼 儲存的是 每個字元進行ASCII碼轉換的16進位制 String coding = coding(member); //如果score為0 則會根據value來進行排序 Long zadd = con.zadd(key, 0, coding);
return zadd; }

匹配資料

    /**
     * unicode解碼 字元通過16進位制轉換為ASCII碼 即可得到對應字元
     *
     * @param s
     * @return
     */
    private String decoding(String s) {
        String[] split = s.split("-");
        StringBuffer buffer = new StringBuffer();

        for (String s1 : split) {
            if (!s1.trim().equals("")) {
                //將16進位制 轉換成ASCII碼 ASCII碼對應的字元
                char i = (char) Integer.parseInt(s1, 16);
                buffer.append(i);
            }
        }
        return buffer.toString();
    }


    /**
     * 16進位制
     * 二進位制與16進位制對應 0-0 1-1 2-2 3-3 4-4 5-5- 6-6 7-7 8-8 9-9 10-a  11-b 12-c 13-d 14-e 15-f 16-g
     */
    private static final String VALID_CHARACTERS = "0123456789abcdefg";
    
    private String[] findPrefixRange(String prefix) {
        int posn = VALID_CHARACTERS.indexOf(prefix.charAt(prefix.length() - 1));    //查找出字首字串最後一個字元在列表中的位置
        char suffix = VALID_CHARACTERS.charAt(posn > 0 ? posn - 1 : 0);                //找出前驅字元
        String start = null;
        String end = null;
        //針對於單純查0的 沒有前置了 所以不用拼g
        if (posn == 0) {
            start = prefix;
            end = prefix + "g";//end拼g 把範圍內的全查出來
        } else {
            start = prefix.substring(0, prefix.length() - 1) + suffix + 'g';        //生成字首字串的前驅字串
            end = prefix + 'g';
            //    防止多個群成員可以同時操作有序集合,將相同的前驅字串和後繼字串插入有序集合//生成字首字串的後繼字串
            String identifier = UUID.randomUUID().toString();
            start += identifier;
            end += identifier;
        }
        return new String[]{start, end};
    }


    //查詢資料
    public List<String> find(Jedis con, String member, String key) {
        List<String> list = new ArrayList<>();
        member = coding(member);//把輸入的字元轉換成16進位制字串,因為redis裡面存的是每個字元對應的16進位制字串
        String[] range = findPrefixRange(member);
        String start = range[0];
        String end = range[1];
        //往zset插入起始和結束的字元,將匹配的結果的資料包起來
        con.zadd(key, 0, start);
        con.zadd(key, 0, end);
        while (true) {
            con.watch(key);
            //根據插入包起來的資料rank拿出啟始和結束的字元
            int sindex = con.zrank(key, start).intValue();
            int eindex = con.zrank(key, end).intValue();            //找出兩個插入元素的位置
            int erange = Math.min(sindex + 9, eindex - 2);                //因為最多展示10個,所以計算出結束為止
            Transaction transaction = con.multi();
            transaction.zrem(key, start);
            transaction.zrem(key, end);
            transaction.zrange(key, sindex, erange);
            List<Object> results = transaction.exec();
            if (results != null) {
                Set<String> set = (Set<String>) results.get(results.size() - 1);
                list.addAll(set);
                break;
            }
        }
        ListIterator<String> iterator = list.listIterator();
        // 這裡過濾多個成員新增前驅字串和後繼字串引起的不符合的資料
        while (iterator.hasNext()) {
            String string = iterator.next();
            if (string.indexOf("g") != -1) {
                iterator.remove();
            } else {
                iterator.set(decoding(string));//把16進位制字串轉換回來
            }
        }
        return list;
    }

測試

    public static void main(String[] args)
            throws Exception {
        Jedis conn = new Jedis("127.0.0.1", 6379);
        conn.flushDB();
        AutoComplete auto_complete = new AutoComplete();
        for (int i = 0; i < 1; i++) {
            auto_complete.add(conn, "你愛廣州" + i, "test1");
        }
        auto_complete.add(conn, "我愛廣州", "test1");
        auto_complete.add(conn, "我愛成都", "test1");
        auto_complete.add(conn, "你好啊", "test1");
        auto_complete.add(conn, "aaabbb", "test1");
        auto_complete.add(conn, "ac", "test1");
        auto_complete.add(conn, "01", "test1");
        auto_complete.add(conn, "02", "test1");
        auto_complete.add(conn, "11", "test1");
        List<String> numbers = auto_complete.find(conn, "我愛成", "test1");
        System.out.println(JSON.toJSONString(numbers));
    }

列印

["我愛成都"]