关于查询方式的总结与讨论

简介

查询的本质,是在一堆数据中,找出自己想要的数据,几乎是所有信息化系统都具备的功能。

本文介绍在程序设计中,或者在系统开发中,几种常见的查询方式及特点。

(1)顺序查询

顺序查询,即遍历所有元素,判断当前元素是否为自己想要的。

在程序设计语言中,通过循环语句加判断语句,如 Java

java 复制代码
List<Integer> data = List.of(1,2,3,4);
for (Integer datum : data) {
    if (datum == 2) {
        System.out.println("这是我想要的元素 = " + data);
    }
}

顺序查询的优点是能随机访问,就是能通过下标或者说序号访问元素。

这也是它底层实现的特点使其具备的优点,顺序存储可以使其在内存中占有一段连续的空间,如上面的"1,2,3,4",一个数占 4 个字节,那么想要访问第 3 个元素,只需访问起始位置的内存地址 + 3个元素的偏移量,就能直接访问到第 3 个元素。

在 Java 中,实现 RandomAccess 接口的容器具备了这个特点。

当然,随机访问需要容器占有一段连续的内存空间,这肯定会造成内存使用率不高,会有碎片的内存空间,相对的,就有了链表的数据结构,通过给单个元素增加引用节点,可以将多个碎片化的内存空间使用起来,可以提高内存使用率,但同时单个元素除了要存数据,还要存引用,单个元素就会占用更高的内存。

集合和链表,也是一种平衡和取舍。

(2)哈希查询

哈希查询,在存储元素时,使用元素的值给每一个元素生成一个唯一 ID,查询时,通过 ID,直接查询。

比如元素"张三的信息,李四的信息,王五的信息,赵六的信息",我们写一个"哈希算法",这个算法的作用是,**传入的值相同,会得到一个一模一样的返回值。**存入时,用"张三"为哈希算法入参,得到一个返回值,将张三的信息存入到这个返回值的地址上,查询时,同理,用"张三"为入参,得到返回值,获取这个返回值地址上的值。

这个查询的效率也是杠杠的,在 Java 中,HashMap 时哈希查询的一种实现,在开发中,我们为了避免循环查询数据库,降低接口耗时,常常将所需要的数据库数据一次性查出来,再使用 HashMap 组装起来,通过 .get(key) 的方式直接获取指定元素。

(循环查询)

java 复制代码
List<String> data = List.of("张三", "李四", "王五", "赵六");
for (String datum : data) {
    // 查询数据库
    UserInfo userInfo = dataMapper.selectByUsername(data);
    // 继续其他的业务
    .......
}

(哈希查询)

java 复制代码
List<String> data = List.of("张三", "李四", "王五", "赵六");
// 一次性查出所有
List<UserInfo> userInfoList = dataMapper.selectAll(data);
// 组装 HashMap
Map<String, UserInfo> userInfoMap = new HashMap<>();
for (UserInfo userInfo : userInfoList) {
    userInfoMap.put(userInfo.getUsername(), userInfo);
}
for (String datum : data) {
    // 取元素时,直接 get(datum) 即可
    UserInfo userInfo = userInfoMap.get(datum);
    // 继续其他的业务
    .......
}

哈希查询的缺点时,它无法做到范围查询,比如查询 id > 2 的元素,就只能循环查询,挨个判断。所以数据库就不能采用这种设计(当然还有众多因素的考虑),数据库需要支持多种数据类型的存储和查询,范围查询很常见。

(3)二叉树查询

二叉树查询,通过树状结构,将元素按照一定的算法逻辑存储,查询时按照树的存储逻辑查询。

以平衡二叉树为例(AVL),将一堆元素按照"左小右大"的特点存储,如下图,80 > 40,存在右边,8 < 40,存在左边,每个元素都是如此,此时再存入一个元素,如 4,

  • 4 < 40,存左边

  • 4 < 8,存左边

  • 4 > 3,存右边

  • 4 < 5,存左边,最后元素 4,就存在了

如下:

查询逻辑也是一样的,因为整个结构都是有序的,就是二分查询。

(4)分词查询

分词查询,最典型的技术是 Elasticsearch,存入时将数据拆分成多个小的片段,查询时,将查询的内容也进行拆分,用拆分后的片段进行匹配,找到所需要的记录。

如,这是一段关于查询的文章,被拆分成"这是","一段","关于","查询","的","文章"片段,当用关键词"查询的文章"检索时,"查询的文章",也被被拆分成,"查询","的","文章",最终查询的结果,也就是片段的匹配结果,某条记录片段匹配的数量越多,说明查询结果的匹配度越高,也就最符合查询结果。

这就是分词查询的实现思想,当然还有细节,如分词算法,某些名牌名称不应该被分词,如 "毛源昌眼镜","毛源昌"不应该被分为"毛","源","昌";还有分词后的匹配算法,也有相关的权重计算,最终查询结果是片段匹配 + 权重的结果。

(5)写入查询

写入查询,是查询思想的一个转变,当要查询某条记录是否存在时,不去直接查询这条记录,而是通过写入的方式来查询。

举个例子,某数据库表中,要查符合某些条件的记录是否存在,可以直接拼接条件查询,判断查询结果。也可以将这些条件设置成一个组合唯一约束,如果能成功写入,说明记录不存在,不能说明记录已存在。实际开发中,MySQL 有快照读和本地读的机制,高并发读写可能会出现读到的不是最新的记录,而通过写入查询就能避免这问题。(在避免 MQ 重复消费消息时会用得上,记录消费记录,每次消费前写入一条记录,能成功写入表示未被消费)

除此之外,分布式锁也是一种体现,能不能获取锁,通过写入来判断。这个写入操作可以是 Redis、MySQL、Zookeeper,甚至是文件操作------能不能生成某个文件,能生成就获取锁。

总结

顺序查询:最常用,最符合人脑思考。

哈希查询:非常高效,要善于使用。

二叉树查询:可以范围查询,设计得好,用起来就好,如 MySQL 底层实现。

分词查询:海量数据的模糊查询场景,如社交、电商平台。

写入查询:高并发读场景,考虑使用改读为写。

相关推荐
一点程序2 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
打工的小王2 小时前
redis(四)搭建哨兵模式:一主二从三哨兵
数据库·redis·缓存
怪兽源码4 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
csdn_aspnet4 小时前
ASP.NET Core 中的依赖注入
后端·asp.net·di·.net core
昊坤说不出的梦5 小时前
【实战】监控上下文切换及其优化方案
java·后端
疯狂踩坑人5 小时前
【Python版 2026 从零学Langchain 1.x】(二)结构化输出和工具调用
后端·python·langchain
春生野草5 小时前
Redis
数据库·redis·缓存
橘子师兄7 小时前
C++AI大模型接入SDK—ChatSDK封装
开发语言·c++·人工智能·后端
@ chen7 小时前
Spring事务 核心知识
java·后端·spring
一点技术8 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统