Go 语言自 1.18 版本开始支持泛型(Generics)。泛型允许我们为函数和数据结构定义类型参数,增强代码的复用性和类型安全性。然而,泛型并不是在所有情况下都适用,正确地使用泛型能带来代码清晰性和性能提升,错误使用则可能使代码复杂或降低性能。

本文旨在帮助你了解在什么情况下应该考虑使用泛型。

泛型的优点

  1. 代码复用性:泛型允许用一种实现支持多种类型,减少代码重复。
  2. 类型安全:在编译时检查类型,防止运行时类型错误。
  3. 抽象能力:抽象地处理数据结构和算法,提升代码表达力。
  4. 性能:避免因为使用interface{}类型带来的类型断言,从而减少运行时开销。

何时使用泛型?

泛型适用于以下场景:

  • 你有一组算法或数据结构逻辑,它们在不同的类型上运作,但逻辑完全相同。
  • 你想写高度通用的代码来简化代码库,同时保持类型安全。
  • 你的代码需要同时处理多种类型,但不想牺牲性能。
  • 你希望避免冗余代码,减少维护工作量。

何时不适合使用泛型?

泛型不是万金油,某些情况下反而带来负担:

  • 只有单一具体类型需求时,泛型会增加代码复杂度。
  • 泛型代码过于复杂,导致可读性变差,增加维护难度。
  • 性能占优且简单逻辑可以接受轻微重复代码时,不必强制用泛型。
  • 当泛型参数变得过于复杂(过多约束、嵌套)时,可能适得其反。

不要把interface类型替换为类型参数

Go语言有interface类型,interface支持某种意义上的泛型编程。

举个例子,被广泛使用的io.Reader接口提供了一种泛型机制用于读取数据,比如支持从文件和随机数生成器里读取数据。

如果你对某些类型的变量的操作只是调用该类型的方法,那就直接使用interface类型,不要使用类型参数。io.Reader从代码角度易于阅读且高效,没必要使用类型参数。

举个例子,有人可能会把下面第1个基于interface类型的ReadSome版本修改为第2个基于类型参数的版本。

1
2
3
func ReadSome(r io.Reader) ([]byte, error)

func ReadSome[T io.Reader](r T) ([]byte, error)

不要做这种修改,使用第1个基于interface的版本会让函数更容易编写和阅读,并且函数执行效率也几乎一样。

注意:尽管可以使用不同的方式来实现泛型,并且泛型的实现可能会随着时间的推移而发生变化,但是Go 1.18中泛型的实现在很多情况下对于类型为interface的变量和类型为类型参数的变量处理非常相似。这意味着使用类型参数通常并不会比使用interface快,所以不要单纯为了程序运行速度而把interface类型修改为类型参数,因为它可能并不会运行更快。

用例示例

例如处理一组不同类型的切片排序、查找操作,可以使用泛型函数避免重复编写针对不同类型的代码。

非泛型版本

1
2
func IntContains(slice []int, val int) bool { ... }
func StringContains(slice []string, val string) bool { ... }

上述重复很多类似逻辑。

泛型版本

1
func Contains[T comparable](slice []T, val T) bool { ... }

案例2: Go内置的容器类型

入参是一个map,要返回该map的所有key组成的slice,key的类型可以是map支持的任意key类型。

1
2
3
4
5
6
7
8
9
// MapKeys returns a slice of all the keys in m.
// The keys are not returned in any particular order.
func MapKeys[Key comparable, Val any](m map[Key]Val) []Key {
s := make([]Key, 0, len(m))
for k := range m {
s = append(s, k)
}
return s
}

这段代码没有对map里key的类型做任何限定,并且没有用map里的value,因此这段代码适用于所有的map类型。这就是使用类型参数的一个很好的示例。

这种场景下,也可以使用反射(reflection),但是反射是一种比较别扭的编程模型,在编译期没法做静态类型检查,并且会导致运行期的速度变慢。

结语

泛型是强大的工具,但需用时权衡复杂度与收益。建议代码在泛型和具体类型之间平衡。合适时用泛型,实现代码复用和类型安全;不合适则回归简单可理解方案。

如果你正在考虑是否使用泛型,问自己:

  • 这段代码是否适用于多种类型?
  • 是否存在类型重复代码?

参考

redis

“两大维度”就是指系统维度和应用维度,“三大主线”也就是指高性能、高可靠和高可扩展(可以简称为“三高”)

系统维度

需要了解 Redis 的各项关键技术的设计原理

三大主线

  • 高性能: 线程模型、数据结构、持久化、网络框架;
  • 高可靠: 主从复制、哨兵机制;
  • 高可扩展: 数据分片、负载均衡。

常见问题

redis

文章指出特斯拉利用激光雷达车队在奥斯汀等地收集数据,并非放弃纯视觉路线,而是为Robotaxi建立高精度的地面实况(Ground Truth)。通过时间戳同步技术,将激光雷达的精确测量值与摄像头图像自动配对,高效生成海量训练数据,从而“教导”纯视觉系统独立完成距离和速度估算,加速自动驾驶的实现。

阅读全文 »

《日经亚洲》报道,来自八个国家14个学术机构的研究人员被发现在论文中隐藏提示,以操纵AI审稿人给出正面评价,引发了关于学术诚信和AI在同行评审中作用的激烈辩论。

阅读全文 »

下面的sql查询同一张表的数据,差异仅在where条件不同,但是同样是active =true的记录在返回的结果中,排序顺序不同,这是为什么

1
2
3
SELECT id,name FROM `tag` WHERE corp_id =4 ORDER BY active DESC LIMIT 10;

SELECT id,name FROM `tag` WHERE (corp_id =4) AND active =true ORDER BY active DESC LIMIT 10;

这两条SQL语句返回的数据顺序不一致的原因在于第一条SQL语句中 active 列可能存在除了 true (或 1) 以外的其他值,比如 false (或 0) 或者 NULL

让我们更详细地分析一下:

SQL 1:

1
SELECT id, name FROM `tag` WHERE corp_id = 4 ORDER BY active DESC LIMIT 10;
  • WHERE corp_id = 4: 首先过滤出 corp_id 为 4 的所有行。
  • ORDER BY active DESC: 然后按照 active 列降序排序。 这意味着:
    • true (或 1) 的行会排在前面。
    • false (或 0) 的行会排在 true 之后。
    • NULL 的行会排在 false 之前,但具体位置取决于数据库实现。 MySQL会默认把 NULL 值放在最前面
  • LIMIT 10: 最后只返回排序后的前10行。

SQL 2:

1
SELECT id, name FROM `tag` WHERE (corp_id = 4) AND active = true ORDER BY active DESC LIMIT 10;
  • WHERE corp_id = 4 AND active = true: 首先过滤出 corp_id 为 4 并且 activetrue 的所有行。
  • ORDER BY active DESC: 然后按照 active 列降序排序。 由于 WHERE 子句已经确保了所有行的 active 都是 true,所以这个排序实际上是多余的,因为所有行的 active 值都相同。
  • LIMIT 10: 最后只返回排序后的前10行。

关键区别在于WHERE子句的过滤条件:

  • 第一条SQL语句包含了所有 corp_id = 4 的行,然后根据 active 进行排序,NULL值可能在最前面。
  • 第二条SQL语句只包含了 corp_id = 4 并且 active = true 的行。 排序没有实际作用。

举例说明:

假设你的 tag 表有以下数据(简化):

id name corp_id active
1 A 4 true
2 B 4 false
3 C 4 NULL
4 D 4 true
5 E 4 false
6 F 4 NULL
7 G 4 true
8 H 4 false
9 I 4 NULL
10 J 4 true
11 K 4 false

SQL 1 的结果 (可能):

假设 MySQL 把 NULL 放在最前面排序

  1. id = 3, name = C (active = NULL)
  2. id = 6, name = F (active = NULL)
  3. id = 9, name = I (active = NULL)
  4. id = 1, name = A (active = true)
  5. id = 4, name = D (active = true)
  6. id = 7, name = G (active = true)
  7. id = 10, name = J (active = true)
  8. id = 2, name = B (active = false)
  9. id = 5, name = E (active = false)
  10. id = 8, name = H (active = false)

SQL 2 的结果:

  1. id = 1, name = A (active = true)
  2. id = 4, name = D (active = true)
  3. id = 7, name = G (active = true)
  4. id = 10, name = J (active = true)
    … 以及其他 active = true 的行,直到达到10行。 因为排序没意义,结果具有不确定性,取决于数据库内部的行存储顺序。

总结:

  • 如果 active 列只包含 truefalse,并且没有 NULL 值,那么这两条SQL语句的结果在 idname 上可能相同,但在结果排序上可能不同(第一条语句明确排序了,第二条排序实质上无效)。
  • 如果 active 列包含 NULL 值,这是最可能导致结果不同的原因,因为第一条语句会受到NULL值排序的影响。

如何解决:

如果你的意图是只获取 activetrue 的记录,那么第二条SQL语句是正确的选择。

如果你想要获取所有 corp_id = 4 的记录,并让 activetrue 的记录排在最前面,可以使用以下SQL语句(假设你想把 NULL 值放在最后):

1
2
3
4
5
6
7
8
9
10
SELECT id, name
FROM `tag`
WHERE corp_id = 4
ORDER BY
CASE
WHEN active IS NULL THEN 1 -- 把 NULL 放在最后
ELSE 0 -- 把 true 和 false 放在前面
END,
active DESC -- 然后按照 true > false 排序
LIMIT 10;

或者,更简单的使用 IFNULL 函数(MySQL 特性):

1
2
3
4
5
SELECT id, name
FROM `tag`
WHERE corp_id = 4
ORDER BY IFNULL(active, 0) DESC
LIMIT 10;

这条语句将 NULL 视为 0 (false),从而将 true 的行放在最前面。 如果你想将 NULL 视为 True, 将 0 改为 1 即可.

或者,增加排序字段,用id和active同时排序:

1
2
3
4
5
6
7
8
SELECT id,name 
FROM `tag`
WHERE corp_id =4
ORDER BY id,active DESC LIMIT 10;

SELECT id,name
FROM `tag` WHERE (corp_id =4) AND active =true
ORDER BY id,active DESC LIMIT 10;

选择哪种方法取决于你想要如何处理 active 列中的 NULL 值。 最重要的是理解你的数据和你想达到的结果,然后选择正确的SQL语句。

在 Elasticsearch 中,向已有索引的 mapping 里新增字段时,如果你尝试添加一个已经存在的字段(即字段名重复),会出现以下情况:

  • 不能修改已存在字段的类型:Elasticsearch 不允许修改已存在字段的类型或映射配置。如果你试图用不同的类型或属性重新定义已存在字段,操作会失败并报错,因为字段映射一旦确定,不能更改[3][5]。

  • 如果新增字段名和已有字段完全一致且映射相同,则相当于“重复添加”,这通常不会有实际影响,但也不会做任何修改,mapping 保持不变。

  • 如果新增字段名重复但映射不同,Elasticsearch 会拒绝更新 mapping,返回错误提示,防止数据索引混乱[3][5]。

  • 新增字段时,必须保证字段名唯一且映射合理,否则需要新建索引并通过 Reindex API 迁移数据来实现字段类型变更[3][5]。

总结:

操作场景 结果说明
新增字段名不存在 成功添加字段到 mapping
新增字段名已存在且映射相同 无变化,mapping 不会重复添加
新增字段名已存在但映射不同 报错,更新失败,不能修改字段类型

因此,新增字段时如果字段名重复且映射不同,ES 会拒绝更新 mapping 并报错,你需要通过新建索引和重新索引数据来变更字段类型。

这是 Elasticsearch 设计的限制,保证倒排索引结构的稳定性和数据一致性[3][5]。

[1] https://codeshellme.github.io/2021/02/es-mappings/
[2] https://blog.csdn.net/weixin_48990070/article/details/120342866
[3] http://masikkk.com/article/Elasticsearch-Mapping/
[4] http://www.zbpblog.com/blog-458.html
[5] https://www.cnblogs.com/wupeixuan/p/12514843.html
[6] https://www.cnblogs.com/shoufeng/p/10648835.html
[7] https://blog.csdn.net/yxd179/article/details/82907796
[8] https://scsundefined.gitbooks.io/elasticsearch-reference-cn/content/s12/00_mapping.html

Elasticsearch 中 textkeyword 是两种常用的字符串字段类型,它们的主要区别在于是否进行分词,进而影响索引和查询行为。

1. textkeyword 的区别

特性 text keyword
是否分词 会分词,进行全文分析 不分词,整体作为一个词项索引
适用场景 需要全文检索、模糊查询、相关度排序 需要精确匹配、过滤、排序、聚合
支持的查询类型 matchmatch_phrase 等全文查询 termterms 精确查询
支持聚合/排序 不支持(性能差且不合理) 支持
存储限制 无字符长度限制 默认最大长度256字符,超过不索引(可配置)
典型用途 文章内容、评论、描述等长文本 用户名、邮箱、标签、状态、ID等

2. 使用案例

2.1 Mapping 示例(含 multi-fields)

通常为了兼顾全文检索和精确匹配,字段会定义成 text 类型,同时添加一个 keyword 子字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PUT my_index
{
"mappings": {
"properties": {
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}

这样,title 字段既可以全文检索,也可以做精确匹配和聚合。

2.2 查询示例

  • 全文检索(text 字段)
1
2
3
4
5
6
7
8
GET my_index/_search
{
"query": {
"match": {
"title": "Elasticsearch tutorial"
}
}
}
  • 精确匹配(keyword 字段)
1
2
3
4
5
6
7
8
GET my_index/_search
{
"query": {
"term": {
"title.keyword": "Elasticsearch tutorial"
}
}
}
  • 聚合示例(keyword 字段)
1
2
3
4
5
6
7
8
9
10
11
GET my_index/_search
{
"size": 0,
"aggs": {
"titles": {
"terms": {
"field": "title.keyword"
}
}
}
}

2.3 修改 Mapping

Elasticsearch 不支持直接修改已有字段的类型。如果想给已有索引新增 keyword 子字段,需要使用 动态模板或在创建索引时定义好,或者新建索引并重建数据。

示例:新增字段时定义 multi-fields

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PUT my_index/_mapping
{
"properties": {
"new_field": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}

如果字段已存在且类型不同,修改会失败,需要新建索引。

3. 总结

  • text 适合全文检索,支持分词和相关度评分,不能用于聚合和排序。
  • keyword 适合精确匹配、过滤、排序和聚合,不分词。
  • 多数字符串字段建议用 text + keyword 多字段映射,兼顾两种需求。
  • 查询时全文检索用 match 查询 text 字段,精确匹配用 term 查询 keyword 字段。

以上内容基于 Elasticsearch 官方设计理念及社区实践总结[1][2][3][4][6]。如果需要,我可以帮你写具体的 mapping 和查询模板。

[1] https://www.cnblogs.com/hahaha111122222/p/12177377.html
[2] https://blog.csdn.net/UbuntuTouch/article/details/128904528
[3] https://cloud.tencent.com/developer/article/2357713
[4] https://bbs.huaweicloud.com/blogs/410730
[5] https://www.cnblogs.com/Rawls/p/10069670.html
[6] https://blog.csdn.net/weixin_41860630/article/details/126471632
[7] https://blog.51cto.com/u_15730090/5510216
[8] https://blog.51cto.com/u_15278282/2933670

Elasticsearch 中字段类型繁多,合理选择字段类型对索引效率、查询性能和存储空间都有重要影响。以下是常用字段类型的全面介绍及区别解析,结合核心、复合、地理和特殊类型,帮助你理解它们的作用和应用场景。

1. 核心数据类型

类型 说明 适用场景与特点
text 用于全文检索的字符串字段,会经过分词器拆分成词项并建立倒排索引。 适合长文本、描述、文章内容等需要模糊匹配、分词查询的场景。不支持排序和精确聚合
keyword 不分词的字符串字段,整体作为一个词项索引。 适合存储结构化短文本,如用户名、邮箱、标签、状态码等。支持精确匹配、过滤、排序和聚合。
long 64位整数 存储时间戳、ID、计数等大范围整数。支持范围查询、排序、聚合。
integer 32位整数 存储较小范围的整数,如数量、等级等。支持范围查询、排序、聚合。
float/double 单精度/双精度浮点数 存储金额、权重、评分等带小数的数值。支持范围查询、排序、聚合。
boolean 布尔值,true 或 false 存储二元状态,如开关、是否激活等。支持过滤和聚合。
date 日期时间类型 存储日期时间,支持多种格式。方便时间范围查询、排序和时间聚合。
binary 二进制数据,不支持检索和聚合 存储图片、文件等二进制内容,主要用于存储,不用于搜索。

2. 复合数据类型

类型 说明 适用场景与特点
object JSON 对象,包含多个字段 存储结构化数据,字段之间无独立索引,数组中对象匹配可能出现跨对象匹配问题。
nested 嵌套对象数组,每个对象独立索引 解决数组中对象字段交叉匹配问题,适合复杂数组结构,支持嵌套查询。
array Elasticsearch 不单独定义数组类型,字段可直接存储数组值 支持存储同类型多个值,数组中元素类型由字段类型决定。

3. 地理空间类型

类型 说明 适用场景与特点
geo_point 经纬度坐标点 存储地理位置点,支持基于距离的查询和排序。
geo_shape 复杂地理形状,如多边形、线等 适合存储区域边界、路径等复杂地理信息,支持空间关系查询。

4. 特殊类型

类型 说明 适用场景与特点
ip IPv4 或 IPv6 地址 存储IP地址,支持范围查询。
completion 自动补全建议字段 用于实现搜索自动补全功能。
token_count 统计字符串中词条数量 用于分析文本长度或复杂度。
murmur3 哈希值字段 用于快速哈希计算和索引。
percolator 存储查询以便反向匹配文档 实现基于查询的索引反向匹配。

5. 字符串类型的区别详解

类型 分词情况 支持排序/聚合 适用查询类型 典型用途
text 分词 不支持排序 match、全文检索、模糊查询 文章内容、评论、描述等长文本
keyword 不分词 支持排序/聚合 term、精确匹配、过滤 用户名、标签、状态、ID等

Elasticsearch 5.x 以后,string 类型被拆分为 textkeyword,分别满足全文检索和精确匹配需求[2][3][5]。

6. 数值类型的选择

根据数值大小和精度选择合适类型:

类型 说明 典型范围/用途
byte 8位整数 -128 到 127
short 16位整数 -32,768 到 32,767
integer 32位整数 -2^31 到 2^31-1
long 64位整数 大整数,如时间戳、ID
float 单精度浮点数 金额、评分等带小数数据
double 双精度浮点数 高精度小数
scaled_float 通过缩放因子存储浮点数 节省存储空间,适合定点数存储

总结

  • 全文检索用 text,结构化精确匹配用 keyword
  • 数值类型根据范围和精度选择,保证存储和查询效率
  • 日期类型专门处理时间,支持多种格式和时间操作
  • 复杂结构用 objectnested,后者支持嵌套查询,避免字段跨对象匹配错误
  • 地理数据和特殊需求有专门类型支持,满足多样化业务场景

合理选择字段类型,是 Elasticsearch 索引设计的关键,直接影响查询性能和存储效率[1][2][3][4][5]。

[1] https://cloud.tencent.com/developer/article/2357713
[2] https://xiaoxiami.gitbook.io/elasticsearch/ji-chu/mapping/zi-duan-de-shu-ju-lei-xing
[3] https://www.cnblogs.com/tanghaorong/p/16323253.html
[4] https://cloud.tencent.com/developer/article/2260312
[5] https://developer.aliyun.com/article/969878
[6] https://blog.csdn.net/aben_sky/article/details/121515175
[7] https://www.cnblogs.com/shoufeng/p/10692113.html
[8] https://blog.csdn.net/ZYC88888/article/details/83059040
[9] https://developer.aliyun.com/article/707773