表的连接方式介绍(NESTED LOOP, SORT MERGE JOIN, HASH JOIN )
循环嵌套链接(NESTED LOOP)
嵌套循环链接的内部处理的流程如下。
- Oracle 优化器根据基于规则RBO或基于成本CBO的原则,选择两个表中的一个作为驱动表,并指定其为外部表。
- Oracle 优化器再将另外一个表指定为内部表。
- Oracle从外部表中读取第一行,然后和内部表中的数据逐一进行对比,所有匹配的记录放在结果集中。
- Oracle读取外部表中的第二行,再和内部表中的数据逐一进行对比,所有匹配的记录添加到结果集中。
- 重复上述步骤,直到外部表中的所有记录全部处理完。
- 最后产生满足要求的结果集。
使用嵌套循环链接是一种从结果集中提取第一批记录最快速的方法。在驱动行源表(就是正在查找的记录)较小或者内部行源表已链接的列有惟一的索引或高度可选的非惟一索引时, 嵌套循环链接效果是比较理想的。嵌套循环链接比其他链接方法有优势,它可以快速地从结果集中提取第一批记录,而不用等待整个结果集完全确定下来。这样,在理想情况下,终端用户就可以通过查询屏幕查看第一批记录,而在同时读取其他记录。不管如何定义链接的条件或者模式,任何两行记录源可以使用嵌套循环链接,所以嵌套循环链接是非常灵活的。
然而,如果内部行源表(读取的第二张表)已链接的列上不包含索引或者索引不是高度可选时,嵌套循环链接效率是很低的。如果驱动表的记录非常庞大时,其他的链接方法可能更加有效。一般在nested loop中, 驱动表满足条件结果集不大,被驱动表的链接字段要有索引,这样就有nested loop。如果驱动表返回记录太多,就不适合nested loops了。如果链接字段没有索引,则适合走hash join,因为不需要索引。可以通过在SQL语句中添加HINTS,强制Oracle优化器产生嵌套循环链接的执行计划。
嵌套循环示例
被驱动表连接列创建索引
SQL> create index idx_t2 on t2(col2);
Index created.
当前数据库版本
SQL> select * from v$version;
BANNER
————————————————————————————————————————
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 – 64bit Production
PL/SQL Release 11.2.0.4.0 – Production
CORE 11.2.0.4.0 Production
TNS for Linux: Version 11.2.0.4.0 – Production
NLSRTL Version 11.2.0.4.0 – Production
查询方法 一
SQL> select /*+ ordered use_nl(t2)*/ t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2;
Execution Plan
———————————————————-
Plan hash value: 1054738919
—————————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————————
| 0 | SELECT STATEMENT | | 3 | 60 | 6 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 3 | 60 | 6 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 3 | 60 | 6 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | T1 | 3 | 45 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IDX_T2 | 1 | | 0 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 5 | 1 (0)| 00:00:01 |
—————————————————————————————
Predicate Information (identified by operation id):
—————————————————
4 – access("T1"."COL2"="T2"."COL2")
查询方法二
SQL> select /*+ use_nl(t1 t2)*/ t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2;
Execution Plan
———————————————————-
Plan hash value: 1054738919
—————————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————————
| 0 | SELECT STATEMENT | | 3 | 60 | 6 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 3 | 60 | 6 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 3 | 60 | 6 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | T1 | 3 | 45 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IDX_T2 | 1 | | 0 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 5 | 1 (0)| 00:00:01 |
—————————————————————————————
Predicate Information (identified by operation id):
—————————————————
4 – access("T1"."COL2"="T2"."COL2")
排序合并链接( SORT MERGE JOIN )
排序合并链接内部处理的流程如下。
优化器判断第一个源表是否已经排序,如果已经排序,则到第3步,否则到第2步。
第一个源表排序。
优化器判断第二个源表是否已经排序,如果已经排序,则到第5步,否则到第4步。
第二个源表排序。
已经排过序的两个源表进行合并操作,并生成最终的结果集。
在缺乏数据的选择性或者可用的索引时,或者两个源表都过于庞大(所选的数据超过表记录数的5%)时,排序合并链接将比嵌套循环链接更加高效。排列合并链接需要比较大的临时内存块,以用于排序,这将导致在临时表空间占用更多的内存和磁盘I/O。
排序合并连接示例
SQL> select /*+ ordered use_merge(t2)*/ t1.col1,t1.col2,t2.col3 from t1,t2 where
t1.col2=t2.col2;
Execution Plan
———————————————————-
Plan hash value: 412793182
—————————————————————————-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————-
| 0 | SELECT STATEMENT | | 3 | 60 | 8 (25)| 00:00:01 |
| 1 | MERGE JOIN | | 3 | 60 | 8 (25)| 00:00:01 |
| 2 | SORT JOIN | | 3 | 45 | 4 (25)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 3 | 15 | 4 (25)| 00:00:01 |
| 5 | TABLE ACCESS FULL| T2 | 3 | 15 | 3 (0)| 00:00:01 |
—————————————————————————-
Predicate Information (identified by operation id):
—————————————————
4 – access("T1"."COL2"="T2"."COL2")
filter("T1"."COL2"="T2"."COL2")
Note
—–– dynamic sampling used for this statement (level=2)
笛卡尔链接 ( CARTESIAN JOIN )
笛卡尔链接是指在SQL语句中没有写出表链接的条件,优化器把第一个表的每一条记录和第二个表的所有记录相链接。如果第一个表的记录数为m,第二个表的记录数为m,则会产生m×n条记录数。
笛卡尔链接示例
下面的查询,未指名链接条件,就会产生笛卡尔链接。
SQL> select t1.col1,t1.col2,t2.col3 from t1,t2;
9 rows selected.
Execution Plan
———————————————————-
Plan hash value: 787647388
—————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 9 | 162 | 9 (0)| 00:00:01 |
| 1 | MERGE JOIN CARTESIAN| | 9 | 162 | 9 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | T1 | 3 | 45 | 3 (0)| 00:00:01 |
| 3 | BUFFER SORT | | 3 | 9 | 6 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | T2 | 3 | 9 | 2 (0)| 00:00:01 |
—————————————————————————–
Note
—–– dynamic sampling used for this statement (level=2)
由于笛卡尔链接会导致性能很差的SQL,因此一般也很少用到。
哈希链接( HASH JOIN )
当内存能够提供足够的空间时,哈希(HASH)链接是Oracle优化器通常的选择。哈希链接中,优化器根据统计信息,首先选择两个表中的小表,在内存中建立这张表的基于链接键的哈希表;优化器再扫描表链接中的大表,将大表中的数据与哈希表进行比较,如果有相关联的数据,则将数据添加到结果集中。
当表链接中的小表能够完全cache到可用内存的时候,哈希链接的效果最佳。哈希链接的成本只是两个表从硬盘读入到内存的成本。但是,如果哈希表过大而不能全部cache到可用内存时,优化器将会把哈希表分成多个分区,再将分区逐一cache到内存中。当表的分区超过了可用内存时,分区的部分数据就会临时地写到磁盘上的临时表空间上。因此,分区的数据写磁盘时,比较大的区间(EXTENT)会提高I/O性能。当哈希表构建完成后,进行下面的处理。
第二个大表进行扫描。
如果大表不能完全cache到可用内存的时候,大表同样会分成很多分区。
大表的第一个分区cache到内存。
对大表第一个分区的数据进行扫描,并与哈希表进行比较,如果有匹配的记录,添加到结果集里面。
与第一个分区一样,其他的分区也类似处理。
所有的分区处理完后,Oracle对产生的结果集进行归并,汇总,产生最终的结果。
当哈希表过大或可用内存有限,哈希表不能完全Cache到内存。随着满足链接条件的结果集的增加,可用内存会随之下降,这时已经Cache到内存的数据可能会重新写回到硬盘去。如果出现这种情况,系统的性能就会下降。当链接的两个表是用等值链接并且表的数据量比较大时,优化器才可能采用哈希链接。哈希链接是基于CBO的。只有在数据库初始化参数HASH_JOIN_ENABLED设为True,并且为参数PGA_AGGREGATE_TARGET设置了一个足够大的值的时候,Oracle才会使用哈希链接。HASH_AREA_SIZE是向下兼容的参数,但在Oracle 9i之前的版本中应当使用HASH_AREA_SIZE。当使用ORDERED提示时,FROM子句中的第一张表将用于建立哈希表。可以通过在SQL语句中添加HINTS /+ use_hash(a b)/ 强制Oracle优化器产生哈希链接的执行计划。
SQL> select /*+ use_hash(t1 t2)*/t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2;
Execution Plan
———————————————————-
Plan hash value: 1838229974
—————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————
| 0 | SELECT STATEMENT | | 3 | 60 | 6 (0)| 00:00:01 |
|* 1 | HASH JOIN | | 3 | 60 | 6 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 3 | 15 | 3 (0)| 00:00:01 |
—————————————————————————
Predicate Information (identified by operation id):
—————————————————
1 – access("T1"."COL2"="T2"."COL2")
当缺少有用的索引时,哈希链接比嵌套循环链接更加有效。哈希链接也可能比嵌套循环链接更快,因为处理内存中的哈希表比检索B_树索引更加迅速。
反连接( Anti Join )
反连接是一种特殊的连接类型,当做子查询展开时,Oracle经常会把那些外部where条件为NOT EXISTS、NOT IN、<>ALL的子查询转换成对应的反连接。
反连接示例
分别执行以下三条SQL
select * from t1 where col2 not in (select col2 from t2);
select * from t1 where col2 <>all(select col2 from t2);
select * from t1 where not exists (select 1 from t2 where col2=t1.col2);
SQL> select * from t1 where col2 not in (select col2 from t2);
COL1 CO
———- —
3 C
Execution Plan
———————————————————-
Plan hash value: 1275484728
—————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————
| 0 | SELECT STATEMENT | | 3 | 51 | 6 (0)| 00:00:01 |
|* 1 | HASH JOIN ANTI NA | | 3 | 51 | 6 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 3 | 6 | 3 (0)| 00:00:01 |
—————————————————————————
Predicate Information (identified by operation id):
—————————————————
1 – access("COL2"="COL2")
SQL> select * from t1 where col2 <>all(select col2 from t2);
COL1 CO
———- —
3 C
Execution Plan
———————————————————-
Plan hash value: 1275484728
—————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————
| 0 | SELECT STATEMENT | | 3 | 51 | 6 (0)| 00:00:01 |
|* 1 | HASH JOIN ANTI NA | | 3 | 51 | 6 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| T2 | 3 | 6 | 3 (0)| 00:00:01 |
—————————————————————————
Predicate Information (identified by operation id):
—————————————————
1 – access("COL2"="COL2")
SQL> select * from t1 where not exists (select 1 from t2 where col2=t1.col2);
COL1 CO
———- —
3 C
Execution Plan
———————————————————-
Plan hash value: 1534930707
—————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 3 | 51 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS ANTI | | 3 | 51 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_T2 | 1 | 2 | 0 (0)| 00:00:01 |
—————————————————————————–
Predicate Information (identified by operation id):
—————————————————
3 – access("COL2"="T1"."COL2")
由于表t1 t2的各自连接列col2上均没有NULL值,在这种情况下,上面三个SQL其实是等价的。所有的执行计划里面都有ANTI,说明在执行上面的SQL时,的确是用的反连接。
当表的连接列中有了NULL值的时候,这三个SQL就不完全等价了,这种影响又细分为两种情况。
1.t1表的连接列上出现NULL值
SQL> insert into t1 values (4,null);
1 row created.
SQL> commit;
Commit complete.
SQL> select * from t1;
COL1 COL2
———- —–
1 A
2 B
3 C
4
–重新执行刚才的三个SQL
SQL> select * from t1 where col2 not in (select col2 from t2);
COL1 COL2
———- —–
3 C
SQL> select * from t1 where col2 <>all(select col2 from t2);
COL1 COL2
———- —–
3 C
SQL> select * from t1 where not exists (select 1 from t2 where col2=t1.col2);
COL1 COL2
———- —–
3 C
4
2.T2表的连接列出现NULL
SQL> delete from t1 where col1=4;
1 row deleted.
SQL> insert into t2 values (null,‘E2’);
1 row created.
SQL> commit;
Commit complete.
SQL> select * from t2;
COL2 COL3
—– —-
A A2
B B2
D D2
E2
–重新执行上面的三个SQL
SQL> select * from t1 where col2 not in (select col2 from t2);
no rows selected
SQL> select * from t1 where col2 <>all(select col2 from t2);
no rows selected
SQL> select * from t1 where not exists (select 1 from t2 where col2=t1.col2);
COL1 COL2
———- —–
3 C
结论:
(1) 当表的连接列出现了NULL值,上述范例中的SQL就不等价了。
(2) NOT IN 和 <>ALL 对NULL值敏感,这意味着NOT IN后面的子查询或常量集合一旦有NULL值出现,则整个SQL的执行结果就会为NULL,不会包含任何记录
(3) NOT EXISTS对NULL值不敏感,也就是说NULL值对NOT EXISTS的执行结果不会有什么影响。
为了解决NOT IN 和 <>ALL 对NULL值敏感,Oracle推出了改良的反连接,被称为Null-Aware Anti Join,执行计划中出现的HASH JOIN ANTI NA,这个关键字NA就是Null-Aware的缩写,这里oracle就是要告知所采用的不是普通的hash反连接,而是改良的反连接,是可以处理null值的。Oracle是否启用Null-Aware Anti Join受隐含参数_OPTIMIZER_NULL_AWARE_ANTIJOIN控制,默认是TRUE,表示启用Null-Aware Anti Join。
改良反连接示例
如果把该隐含参数改为FALSE,则Oracle不能再用改良反连接,而又因为NOT IN对NULL值敏感,所以Oracle此时也不能用普通的反连接。
SQL> alter session set "_OPTIMIZER_NULL_AWARE_ANTIJOIN" = false;
Session altered.
SQL> select * from t1 where col2 <>all(select col2 from t2);
COL1 CO
———- —
3 C
Execution Plan
———————————————————-
Plan hash value: 895956251
—————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————
| 0 | SELECT STATEMENT | | 1 | 15 | 5 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| T2 | 3 | 6 | 2 (0)| 00:00:01 |
—————————————————————————
Predicate Information (identified by operation id):
—————————————————
1 – filter( NOT EXISTS (SELECT 0 FROM "T2" "T2" WHERE
LNNVL("COL2"<>:B1)))
3 – filter(LNNVL("COL2"<>:B1))
可以看到这里的执行计划走了FILTER,而不再是HASH JOIN ANTI NA。
半连接( Semi Join )
半连接是一种特殊的连接类型,与反连接一样,Oracle数据库里面也没有相关的关键字可以在SQL文本中专门表示半连接。半连接和普通的内连接不同,半连接会去重。
半连接示例
分别执行以下三条SQL
select * from t1 where col2 in (select col2 from t2);
select * from t1 where col2=any (select col2 from t2);
select * from t1 where exists (select 1 from t2 where col2=t1.col2);
SQL> select * from t1 where col2 in (select col2 from t2);
COL1 CO
———- —
1 A
2 B
Execution Plan
———————————————————-
Plan hash value: 3783859632
—————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 3 | 51 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 3 | 51 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_T2 | 3 | 6 | 0 (0)| 00:00:01 |
—————————————————————————–
SQL> select * from t1 where col2=any (select col2 from t2);
COL1 CO
———- —
1 A
2 B
Execution Plan
———————————————————-
Plan hash value: 3783859632
—————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 3 | 51 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 3 | 51 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_T2 | 3 | 6 | 0 (0)| 00:00:01 |
—————————————————————————–
SQL> select * from t1 where exists (select 1 from t2 where col2=t1.col2);
COL1 CO
———- —
1 A
2 B
Execution Plan
———————————————————-
Plan hash value: 3783859632
—————————————————————————–
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————–
| 0 | SELECT STATEMENT | | 3 | 51 | 3 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 3 | 51 | 3 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T1 | 3 | 45 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_T2 | 3 | 6 | 0 (0)| 00:00:01 |
—————————————————————————–
上述三个SQL的执行结果是一样的,而且它们的执行计划的现实内容均有关键字SEMI,这个关键字就说明Oracle执行这三个SQL是在用半连接。
主要表连接方式比较
特性 | 嵌套循环连接 (NESTED LOOP) | 排序合并连接 (SORT MERGE) | 哈希连接 (HASH JOIN) |
---|---|---|---|
优化器提示 | USE_NL |
USE_MERGE |
USE_HASH |
连接条件 | 任何连接条件 | 主要用于非等值连接(<,<=,>,>=) | 仅用于等值连接(=) |
资源消耗 | CPU、磁盘I/O | 内存、临时空间 | 内存、临时空间 |
最佳适用场景 | - 高选择性索引 - 限制性搜索 |
- 缺乏有效索引 - 大表连接 |
- 缺乏索引 - 数据仓库环境 |
首次返回速度 | (最快) | (较慢) | (较慢) |
整体吞吐量 | |||
内存需求 | |||
索引依赖 | 高度依赖 | 不依赖 | 不依赖 |
表的连接方式介绍(NESTED LOOP, SORT MERGE JOIN, HASH JOIN )的更多相关文章
- Oracle 表的连接方式(1)-----Nested loop join和 Sort merge join
关系数据库技术的精髓就是通过关系表进行规范化的数据存储,并通过各种表连接技术和各种类型的索引技术来进行信息的检索和处理. 表的三种关联方式: nested loop:从A表抽一条记录,遍历B表查找匹配 ...
- Nested Loop,Sort Merge Join,Hash Join
三种连接工作方式比较: Nested loops 工作方式是从一张表中读取数据,访问另一张表(通常是索引)来做匹配,nested loops适用的场合是当一个关联表比较小的时候,效率会更高. Merg ...
- 数据库多表连接方式介绍-HASH-JOIN
1.概述 hash join是一种数据库在进行多表连接时的处理算法,对于多表连接还有两种比较常用的方式:sort merge-join 和 nested loop. 为了比较清楚的介绍hash joi ...
- SQL Tuning 基础概述06 - 表的关联方式:Nested Loops Join,Merge Sort Join & Hash Join
nested loops join(嵌套循环) 驱动表返回几条结果集,被驱动表访问多少次,有驱动顺序,无须排序,无任何限制. 驱动表限制条件有索引,被驱动表连接条件有索引. hints:use_n ...
- [转]Apache HTTP Server 与 Tomcat 的三种连接方式介绍
首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接.事实上 Tomcat 本身已经提供了 HTTP 服务,该服务默认的端口是 8080,装好 tomcat 后通过 8080 端 ...
- Oracle 表的连接方式(2)-----HASH JOIN的基本机制1
我们对hash join的常见误解,一般包括两个: 第一个误解:是我们经常以为hash join需要对两个做join的表都做全表扫描 第二个误解:是经常以为hash join会选择比较小的表做buil ...
- sort merge join,hash join,netsloop join
Join Operations ? SORT-MERGE JOIN – Sorts tables on the join key and then merges them together – Sor ...
- Oracle 表的连接方式
1. 连接说明 ① Oracle一次只能连接两个表.不管查询中有多少个表,Oracle 在连接中一次仅能操作两张表. ② 当执行多个表的连接时,优化器从一个表开始,将它与另一个表连接:然后将中间结果与 ...
- Oracle 表的连接方式(2)-----HASH JOIN的基本机制3
HASH JOIN的模式 hash join有三种工作模式,分别是optimal模式,onepass模式和multipass模式,分别在v$sysstat里面有对应的统计信息: SQL> sel ...
- Oracle 表的连接方式(2)-----HASH JOIN的基本机制2
Hash算法原理 对于什么是Hash算法原理?这个问题有点难度,不是很好说清楚,来做一个比喻吧:我们有很多的小猪,每个的体重都不一样,假设体重分布比较平均(我们考虑到公斤级别),我们按照体重来分,划分 ...
随机推荐
- git库移植
记一次个人项目移植到组织项目的git应用,留爪. 1. 首先保证你本地有一份完整的库 2. 在 gitee 组织里新建一份裸库 3. 本地库移除所有远程库 git remote //查看所有远程库 g ...
- Ant Design Pro 中 点击子菜单的时候,其他菜单不自动收起来
记录一波自己在这段时间碰到的一个Ant Design Pro 的坑: 每次点击菜单都会将其他菜单自动收起来,导致一系列的用户体验不佳. 设置defaultOpenAll: true后依然不管用 经过各 ...
- 我理解的伽马校正(Gamma Correction
写在前面 我相信几乎所有做图像处理方面的人都听过伽马校正(Gamma Correction)这一个名词,但真正明白它是什么.为什么要有它.以及怎么用它的人其实不多.我也不例外.最初我查过一些资料,但很 ...
- 能详细地讲讲stm32该怎么学吗?
作为一个在嵌入式领域摸爬滚打了好几年的老兵,我想分享一下我学习STM32的心路历程和方法论.坦白说,刚开始接触STM32时,我也是一脸懵逼.机械专业毕业的我转行做嵌入式,第一份工作被调剂到电子部门,实 ...
- rabbitmq分布式事务
Rabbitmq解决分布式事务的原理:可靠消息最终一致性方案 需要保证以下三要素 1.确认生成者一定要将消息可靠投递到MQ[采用MQ的异步confirm消息确认机制] public interface ...
- LVGL基础对象的框架、思维与概念,以及部分源码解析
概念: Lvgl虽然是以C语言进行编程开发,但其中借鉴了CSS当中编程思想,引入了类与对象的概念,其中lvgl的基础单位为控件,等同于HTML5当中的标签. Lvgl当中的"类"是 ...
- NOIP集训 P4137 Rmq Problem / mex 题解
前置指使:可持久化线段树 题解:P4137 Rmq Problem / mex 有一个长度为 \(n\) 的数组 \(\{ a_1,a_2,...,a_n \}\) . \(m\) 次询问,每次询问一 ...
- Java编程--简单的Factory程序(工厂设计模式)
Factory类不是接口.抽象类,就是普通的类. Factory就像一个工厂一样,可以返回很多对象. 子类在继承.实现抽象类和接口后由Factory类处理,由于子类可能会有多个,Factory根据客户 ...
- 【数学建模经验分享】 | 2025美赛E题F奖论文分享 | 微分方程模型 | 种族竞争狩猎模型 | Lotka-Volterra 方程拓展
0 前言 昨晚(准确说是今天)凌晨三点正在熬夜写计网作业突然收到队友发来消息说美赛成绩出来了,查了一下拿到F奖,感到非常欣喜.但欣喜之余其实也有一些对于没有拿到O奖的遗憾,毕竟在刚提交完论文的时候就感 ...
- 【工具】JS脚本|网页任意视频倍速播放(包括MOOC、本地视频、其他的视频)
实际发布时间:2022-12-14 22:54:52. csdn禁止浏览器脚本相关博客了,就只能重新发到这儿了. 2024/12/14更新:更新了常见问题Q&A,可以配合食用. 只要浏览器 ...