当前位置:网站首页>使用StreamAPI 斷言組合,結合本地緩存做模糊查詢(比mysql效率提昇近1000倍)

使用StreamAPI 斷言組合,結合本地緩存做模糊查詢(比mysql效率提昇近1000倍)

2022-06-21 22:06:00 每天都要加油呀!

使用StreamAPI 斷言結合本地緩存做模糊查詢(比mysql效率提昇近1000倍)

最近手上有個需求,需要做模糊查詢,第一時間想到的是mysql分頁模糊查詢,但是甲方的需求是模糊查詢所有的數據,且這個模糊查詢也不好通過添加索引進行優化

拿到這個需求後,我往大概2w條數據的數據庫中全錶模糊查詢了一下,耗時大概在10s左右:

image-20220620201529997

然後我想其實我的數據量並不大,錶中的數據很難也幾乎不可能突破十萬條數據,如果我直接存在JVM緩存中是不是快一些

說幹就幹,這裏我使用Stream中斷言組合的方式進行模糊查詢,不熟悉的小夥伴可以看附錄中的小例子

這裏我根據模糊查詢的字段進行斷言組合:

@Override
public List<T> likeQuery(Map<String, Object> queryMap) {
    
    // 這裏需要先組合斷言,因為是與運算,所以初始化一個成功的斷言
    Predicate<Book> p = Objects::nonNull;
    for (Entry<String, Object> entry : queryMap.entrySet()) {
    
        String k = entry.getKey();
        String v = String.valueOf(entry.getValue());
        Predicate<Book> p2 = null;
        if (k.equalsIgnoreCase("bookName")) {
    
            p2 = book -> book.matchName(v);
        } else if (k.equalsIgnoreCase("author")) {
    
            p2 = book -> book.matchAuthor(v);
        } else if (k.equalsIgnoreCase("publishArea")) {
    
            p2 = book -> book.matchPublishArea(v);
        }
        // 斷言組合
        if (p2 != null){
    
            p = p.and(p2);
        }
    }
    // 我是自己用List做的緩存,所有這裏通過this獲取
    return this.stream()
            .filter(p)
            .collect(Collectors.toList());
}

然後在實體類中寫上比較的邏輯:

@Override
public  boolean matchName(String str) {
    
    if(this.bookName == null) return false;
    return this.bookName.contains(str);
}

接下來就是看效果的時候了,實測兩萬條數據做模糊查詢不會超過100ms,一般會更低,且這裏還包括http請求的時間

image-20220620202412884

通過AB測對該接口做一下壓測,共設置1000個線程,做十萬次請求並記錄時間

十萬個請求僅耗時31秒

image-20220620202612170

查看JVM堆空間占用情况

發現緩存2萬條數據,堆空間占用也在合理範圍,且GC後占用內存會大大降低

image-20220620203046719

線程數也在合理範圍

在前端模糊查詢,非常絲滑,毫無卡頓

1

小結:

這裏建議不要輕易使用並行流對性能進行提昇,因為並且流在處理過程中有許多不可控的風險,例如如果使用線程不安全的集合類進行並行流操作時,極有可能產生線程安全問題

筆者建議使用fork/join框架手動拆分任務,保證線程安全

附錄,Stream斷言組合模糊查詢例子

Predicate:<T>斷言型接口; 輸入一個對象,返回一個Boolean值,需要實現的抽象函數為boolean test(T t)

例如:

/** * 4.Predicate<T>:斷言型接口 */
@Test
public void test04(){
    
    List<String> list= Arrays.asList("Hello","我乃梁奉先是也","Lambda","www","ok");
    //過濾長度大於三的字符串
    System.out.println(filterStr(list,(str)->str.length()>3));
}
//需求,將滿足條件的字符串添加到集合中
private List<String> filterStr(List<String> list, Predicate<String> predicate) {
    
    List<String> strList = new ArrayList<>();
    for (String str : list) {
    
        if (predicate.test(str)) {
    
            strList.add(str);
        }
    }
    return strList;
}

斷言組合(謂詞組合),與或非

public class PredicateFunction {
    
    //抽取公共代碼
    static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
    
        List<Apple> result = new ArrayList<>();
        inventory.forEach(apple -> {
    
            if(p.test(apple)){
    
                result.add(apple);
            }
        });
        return result;
    }

    public static void main(String[] args) {
    
        String[] colors = {
    "green","yellow","blue"};
        Random r = new Random();
        List<Apple> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
            list.add(new Apple(colors[r.nextInt(colors.length)],r.nextInt(200)));
        }
        System.out.println(filterApples(list,Apple::isGreenApple));
        System.out.println(filterApples(list,Apple::isHealthyApple));
        //組合兩個條件
        Predicate<Apple> p1 = Apple::isGreenApple;
        Predicate<Apple> p2 = Apple::isHealthyApple;
        //或運算
        System.out.println(filterApples(list,p1.or(p2)));
        //and運算
        System.out.println(filterApples(list,p1.and(p2)));
    }
}

@Data
@AllArgsConstructor
class Apple{
    
    private String color;
    private int weight;
    public static boolean isGreenApple(Apple apple){
    
        return "green".equalsIgnoreCase(apple.getColor());
    }
    public static boolean isHealthyApple(Apple apple){
    
        return apple.getWeight() > 150;
    }
}
原网站

版权声明
本文为[每天都要加油呀!]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/172/202206212016375110.html