腾讯旗下CDN国际版(EdgeOne)限时活动,测速并分享至Twitter(x.com)和Facebook可获取2个免费套餐
活动链接
https://edgeone.ai/get-free-plan
腾讯旗下CDN国际版(EdgeOne)限时活动,测速并分享至Twitter(x.com)和Facebook可获取2个免费套餐
https://edgeone.ai/get-free-plan
文章指出特斯拉利用激光雷达车队在奥斯汀等地收集数据,并非放弃纯视觉路线,而是为Robotaxi建立高精度的地面实况(Ground Truth)。通过时间戳同步技术,将激光雷达的精确测量值与摄像头图像自动配对,高效生成海量训练数据,从而“教导”纯视觉系统独立完成距离和速度估算,加速自动驾驶的实现。
《日经亚洲》报道,来自八个国家14个学术机构的研究人员被发现在论文中隐藏提示,以操纵AI审稿人给出正面评价,引发了关于学术诚信和AI在同行评审中作用的激烈辩论。
介绍如何判断两个IP地址是否属于同一个网段,解释其底层原理,并提供Go语言的实现代码。
下面的sql查询同一张表的数据,差异仅在where条件不同,但是同样是active =true的记录在返回的结果中,排序顺序不同,这是为什么
1 | SELECT id,name FROM `tag` WHERE corp_id =4 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; |
corp_id
= 4: 首先过滤出 corp_id
为 4 的所有行。active
DESC: 然后按照 active
列降序排序。 这意味着:true
(或 1) 的行会排在前面。false
(或 0) 的行会排在 true
之后。NULL
的行会排在 false
之前,但具体位置取决于数据库实现。 MySQL会默认把 NULL 值放在最前面SQL 2:
1 | SELECT id, name FROM `tag` WHERE (corp_id = 4) AND active = true ORDER BY active DESC LIMIT 10; |
corp_id
= 4 AND active
= true: 首先过滤出 corp_id
为 4 并且 active
为 true
的所有行。active
DESC: 然后按照 active
列降序排序。 由于 WHERE
子句已经确保了所有行的 active
都是 true
,所以这个排序实际上是多余的,因为所有行的 active
值都相同。关键区别在于WHERE子句的过滤条件:
corp_id = 4
的行,然后根据 active
进行排序,NULL值可能在最前面。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 放在最前面排序
id = 3, name = C
(active = NULL)id = 6, name = F
(active = NULL)id = 9, name = I
(active = NULL)id = 1, name = A
(active = true)id = 4, name = D
(active = true)id = 7, name = G
(active = true)id = 10, name = J
(active = true)id = 2, name = B
(active = false)id = 5, name = E
(active = false)id = 8, name = H
(active = false)SQL 2 的结果:
id = 1, name = A
(active = true)id = 4, name = D
(active = true)id = 7, name = G
(active = true)id = 10, name = J
(active = true)active = true
的行,直到达到10行。 因为排序没意义,结果具有不确定性,取决于数据库内部的行存储顺序。active
列只包含 true
和 false
,并且没有 NULL 值,那么这两条SQL语句的结果在 id
和 name
上可能相同,但在结果排序上可能不同(第一条语句明确排序了,第二条排序实质上无效)。active
列包含 NULL
值,这是最可能导致结果不同的原因,因为第一条语句会受到NULL值排序的影响。如何解决:
如果你的意图是只获取 active
为 true
的记录,那么第二条SQL语句是正确的选择。
如果你想要获取所有 corp_id = 4
的记录,并让 active
为 true
的记录排在最前面,可以使用以下SQL语句(假设你想把 NULL 值放在最后):
1 | SELECT id, name |
或者,更简单的使用 IFNULL
函数(MySQL 特性):
1 | SELECT id, name |
这条语句将 NULL
视为 0
(false),从而将 true
的行放在最前面。 如果你想将 NULL 视为 True, 将 0 改为 1 即可.
或者,增加排序字段,用id和active同时排序:
1 | SELECT id,name |
选择哪种方法取决于你想要如何处理 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 中 text
和 keyword
是两种常用的字符串字段类型,它们的主要区别在于是否进行分词,进而影响索引和查询行为。
text
与 keyword
的区别特性 | text |
keyword |
---|---|---|
是否分词 | 会分词,进行全文分析 | 不分词,整体作为一个词项索引 |
适用场景 | 需要全文检索、模糊查询、相关度排序 | 需要精确匹配、过滤、排序、聚合 |
支持的查询类型 | match 、match_phrase 等全文查询 |
term 、terms 精确查询 |
支持聚合/排序 | 不支持(性能差且不合理) | 支持 |
存储限制 | 无字符长度限制 | 默认最大长度256字符,超过不索引(可配置) |
典型用途 | 文章内容、评论、描述等长文本 | 用户名、邮箱、标签、状态、ID等 |
通常为了兼顾全文检索和精确匹配,字段会定义成 text
类型,同时添加一个 keyword
子字段:
1 | PUT my_index |
这样,title
字段既可以全文检索,也可以做精确匹配和聚合。
1 | GET my_index/_search |
1 | GET my_index/_search |
1 | GET my_index/_search |
Elasticsearch 不支持直接修改已有字段的类型。如果想给已有索引新增 keyword
子字段,需要使用 动态模板或在创建索引时定义好,或者新建索引并重建数据。
示例:新增字段时定义 multi-fields
1 | PUT my_index/_mapping |
如果字段已存在且类型不同,修改会失败,需要新建索引。
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 中字段类型繁多,合理选择字段类型对索引效率、查询性能和存储空间都有重要影响。以下是常用字段类型的全面介绍及区别解析,结合核心、复合、地理和特殊类型,帮助你理解它们的作用和应用场景。
类型 | 说明 | 适用场景与特点 |
---|---|---|
text | 用于全文检索的字符串字段,会经过分词器拆分成词项并建立倒排索引。 | 适合长文本、描述、文章内容等需要模糊匹配、分词查询的场景。不支持排序和精确聚合。 |
keyword | 不分词的字符串字段,整体作为一个词项索引。 | 适合存储结构化短文本,如用户名、邮箱、标签、状态码等。支持精确匹配、过滤、排序和聚合。 |
long | 64位整数 | 存储时间戳、ID、计数等大范围整数。支持范围查询、排序、聚合。 |
integer | 32位整数 | 存储较小范围的整数,如数量、等级等。支持范围查询、排序、聚合。 |
float/double | 单精度/双精度浮点数 | 存储金额、权重、评分等带小数的数值。支持范围查询、排序、聚合。 |
boolean | 布尔值,true 或 false | 存储二元状态,如开关、是否激活等。支持过滤和聚合。 |
date | 日期时间类型 | 存储日期时间,支持多种格式。方便时间范围查询、排序和时间聚合。 |
binary | 二进制数据,不支持检索和聚合 | 存储图片、文件等二进制内容,主要用于存储,不用于搜索。 |
类型 | 说明 | 适用场景与特点 |
---|---|---|
object | JSON 对象,包含多个字段 | 存储结构化数据,字段之间无独立索引,数组中对象匹配可能出现跨对象匹配问题。 |
nested | 嵌套对象数组,每个对象独立索引 | 解决数组中对象字段交叉匹配问题,适合复杂数组结构,支持嵌套查询。 |
array | Elasticsearch 不单独定义数组类型,字段可直接存储数组值 | 支持存储同类型多个值,数组中元素类型由字段类型决定。 |
类型 | 说明 | 适用场景与特点 |
---|---|---|
geo_point | 经纬度坐标点 | 存储地理位置点,支持基于距离的查询和排序。 |
geo_shape | 复杂地理形状,如多边形、线等 | 适合存储区域边界、路径等复杂地理信息,支持空间关系查询。 |
类型 | 说明 | 适用场景与特点 |
---|---|---|
ip | IPv4 或 IPv6 地址 | 存储IP地址,支持范围查询。 |
completion | 自动补全建议字段 | 用于实现搜索自动补全功能。 |
token_count | 统计字符串中词条数量 | 用于分析文本长度或复杂度。 |
murmur3 | 哈希值字段 | 用于快速哈希计算和索引。 |
percolator | 存储查询以便反向匹配文档 | 实现基于查询的索引反向匹配。 |
类型 | 分词情况 | 支持排序/聚合 | 适用查询类型 | 典型用途 |
---|---|---|---|---|
text | 分词 | 不支持排序 | match、全文检索、模糊查询 | 文章内容、评论、描述等长文本 |
keyword | 不分词 | 支持排序/聚合 | term、精确匹配、过滤 | 用户名、标签、状态、ID等 |
Elasticsearch 5.x 以后,
string
类型被拆分为text
和keyword
,分别满足全文检索和精确匹配需求[2][3][5]。
根据数值大小和精度选择合适类型:
类型 | 说明 | 典型范围/用途 |
---|---|---|
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
。object
或 nested
,后者支持嵌套查询,避免字段跨对象匹配错误。合理选择字段类型,是 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
这是一个非常经典并且常常被误解的数据库优化问题。
简短的回答是:这个说法不完全正确。 更准确地说,它是一个需要根据具体情况分析的优化建议,而不是一个绝对的规则。
下面我将详细解释“为什么会有这个说法”以及“什么时候应该或不应该加索引”。
这个建议的核心在于一个概念:索引的基数(Cardinality)。
user_id
, email
。status
(‘pending’, ‘approved’, ‘rejected’),gender
(‘male’, ‘female’, ‘other’),is_active
(true, false)。ENUM 和 BOOLEAN 类型天生就是低基数的。低基数列作为索引的主要问题是“筛选效率低下”:
索引选择性差 (Poor Selectivity)
数据库优化器在决定是否使用索引时,会评估其“选择性”。一个好的索引应该能帮你快速排除掉大部分不符合条件的数据。
举个例子:
假设一个 orders
表有1000万条记录,其中 status
列有3个值:’processing’, ‘shipped’, ‘completed’。
SELECT * FROM orders WHERE status = 'shipped';
status = 'shipped'
的记录可能占了总数的三分之一,也就是大约333万条。索引维护成本 (Maintenance Overhead)
索引不是免费的。每次对表进行 INSERT
, UPDATE
, DELETE
操作时,如果动到了索引列,数据库也需要同步更新索引本身。对于一个写操作频繁的表,一个几乎用不上的低效索引反而会拖慢整体的写入性能。
结论: 当一个索引不能有效地区分数据时,它的存在就弊大于利。这就是“不要给低基数列加索引”这个建议的根本原因。
虽然低基数索引通常是低效的,但在以下几种情况下,给它们建立索引却是非常明智和高效的:
数据分布极不均衡 (Skewed Data Distribution)
这是最常见也最重要的例外情况。虽然列的基数很低,但如果某个值的出现频率极低,为它建索引就非常有用。
举例:
一个 users
表有1亿用户,有一个 is_banned
(true/false) 字段。
is_banned
是 false
。is_banned
是 true
(被封禁的用户)。SELECT * FROM users WHERE is_banned = true;
这个查询的选择性就极高。索引可以快速定位到那一小部分被封禁的用户,性能会远超全表扫描。作为覆盖索引 (Covering Index) 的一部分
当一个查询所需的所有列都包含在索引中时,数据库引擎就无需回表,可以直接从索引中获取所有数据返回。这被称为“覆盖索引”。
举例:
你想统计每种订单状态的数量:SELECT status, COUNT(*) FROM orders GROUP BY status;
status
列上建立一个索引 INDEX(status)
。status
索引,而完全不需要触碰庞大的主表数据,就能完成统计。这会带来巨大的性能提升。作为复合索引 (Composite Index) 的一部分
当低基数列是复合索引的非前导列时,它通常是非常有用的。
举例:
你的查询条件通常是 WHERE corp_id = ? AND status = ?
。
corp_id
是一个高基数列(有很多公司)。status
是一个低基数列。INDEX(corp_id, status)
是非常高效的。corp_id
快速筛选出很少一部分数据,然后再在这个小范围内根据 status
进行查找。此时 status
的低基数问题就不再是瓶颈。场景 | 是否建议加索引 | 原因 |
---|---|---|
单独查询,且数据均匀分布 | 不建议 | 索引选择性差,优化器可能放弃使用,且有维护成本。 |
单独查询,但数据分布极不均衡 | 强烈建议 | 对稀有值的查询效率极高。 |
查询只涉及该列和少数几列 | 建议 | 可以创建覆盖索引,避免回表,极大提升性能。 |
作为复合索引的一部分,与其他高选择性列组合 | 强烈建议 | 复合索引可以大大提高多条件查询的效率。 |
实践法则:
SELECT status, COUNT(*) FROM your_table GROUP BY status;
来了解值的分布情况。EXPLAIN
:在你认为需要加索引的查询前加上 EXPLAIN
,看看数据库的执行计划。确认它是否会使用你创建的索引(key
列),以及扫描的行数(rows
列)是否显著减少。总而言之,“不要给ENUM/低基数列加索引”是一个有用的“经验法则”,它提醒我们警惕低基数列的索引陷阱。但它绝不是一条必须遵守的铁律。 真正的数据库优化需要结合具体业务场景、查询模式和数据分布来进行综合判断。
MySQL表添加业务唯一性约束(Unique Constraint)通常是正确且非常推荐的做法。
这不仅仅是数据库层面的一个设置,更是保证业务逻辑严谨性和数据完整性的关键手段。
NULL
值的处理NULL
值。因为在索引层面,NULL 被认为是一个不确定的值,NULL 不等于任何值,包括另一个NULL。如果你希望某个字段要么唯一,要么为空,那么 UNIQUE 约束是合适的。结论是:应该加。在设计表结构时,应该主动思考哪些字段或字段组合是业务上的“天然唯一标识”,并为它们添加 UNIQUE 约束。