IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 使用StreamAPI 断言组合,结合本地缓存做模糊查询(比mysql效率提升近1000倍) -> 正文阅读

[大数据]使用StreamAPI 断言组合,结合本地缓存做模糊查询(比mysql效率提升近1000倍)

使用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;
    }
}
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-06-21 21:28:50  更:2022-06-21 21:29:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 22:41:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码