Oracle收集对表收集统计信息导致全表扫描直接路径读?
direct path read深入解析
前言
最近碰到一件很奇葩的事情,因为某条SQL执行缓慢,原因是走了笛卡尔(两组大数据结果集),而且笛卡尔还是NL的一个部分,要循环31M次。
很容易发现是统计信息没有导致的,收集完就好了。
结果过了个51假期回来有人反馈其他的SQL慢了,看了下慢的SQL涉及到的表就刚好是上次收集统计信息的表。
统计问题SQL的历史执行情况,发现时间刚好在那天收集完后开始变慢。咋回事。
后边发现慢的原因是对收集的那两张表做全表扫描时候用了直接路径读(DPR)。暂定为表A和表B。SQL的语法结构是对A,B重复做2次left join,所以慢的SQL中A,B表会被扫描各3次,全部都是DPR。
???收集统计信息会导致DPR???
事件概要
对事情做一个简述,不展开来说,引出研究和得到的结论就行。
- SQL变慢,发现时间刚好是收集相关表的统计信息之后变慢的
- 收集cursor_awr('&sql_id')可以得到执行计划变过的
- 使用hint回退原来的执行计划,还是慢,综合对比无论是新旧执行计划,都走了全表扫描(DPR),statistics_level=all收集详细的执行情况也是发现主要时间花在DRP上(很奇怪就是以前应该也慢才对,毕竟表A、B都很大,而且各3次全表扫)
- ashrpti、awrsqrpt相关sql的信息,发现快的时候物理读远远小于慢的时候的物理读
- 通过session级别禁用DPR之后,反而更慢了。产生gc类等待,以及"db file sequential read"和"db file scattered read"等。
因此基本可以确认全表扫描导致DPR变慢的,而导致DPR的原因应该就是收集了表的统计信息。
研究过程
首先百度搜索和mos上都找遍了,没有类似的案例。
关于DPR的很容易可以搜索到一些关键的信息,这里自行搜索就行(有些文章的结论就并不完全正确)。
我们从DPR的产生条件出发引出实验内容。
第一,大表是DPR的前置条件,那么什么样的表才算是大表呢?
Oracle有个隐含参数控制的这个阀值,'_small_table_threshold',单位是块,简称为STT。
第二,超过表的VLOT的值,无论是否设置10949,均为发生DPR。(_serial_direct_read的关闭能否阻止尚未验证)。
第三,表已经被缓存在buffer cache中的块占比,超过表50%的话,不会发生DPR。
第四,表的脏块率,超过25%的脏块也不会发生DPR。
为了研究方便,使用sys用户创建视图查询隐含参数。
create or replace view h$parameter
as
select a.ksppinm name,
a.ksppdesc description,
b.ksppstvl session_value,
c.ksppstvl system_value
from x$ksppi a,x$ksppcv b,x$ksppsv c
where a.indx=b.indx and a.indx=c.indx;
在我的环境下(11.2.0.4.0),查询的结果如下:
set line 500
col name for a50
col session_value for a50
col system_value for a50
select name,session_value,system_value from h$parameter where name in
('_db_block_buffers','_very_large_object_threshold','_small_table_threshold','_direct_read_decision_statistics_driven','_statistics_based_srf_enabled'); NAME SESSION_VALUE SYSTEM_VALUE
-------------------------------------------------- -------------------------------------------------- --------------------------------------------------
_db_block_buffers 32830 32830
_small_table_threshold 656 656
_very_large_object_threshold 500 500
_direct_read_decision_statistics_driven TRUE TRUE
_statistics_based_srf_enabled TRUE TRUE
这里关于STT以及VLOT和MTT的计算方法,有个公式
STT = _small_table_threshold
MTT = * _small_table_threshold
VLOT = (_very_large_object_threshold/) * _db_block_buffers
算得的几个具体的数值:
STT = 656
MTT = 3280
VLOT = 164150
那么我们可以构造一张表,循环对表insert一波数据,然后全表扫描看看是否发生DPR,如果发生DPR了,打印出插入了多少行数据。如果没发生DPR,那么truncate表,重新insert知道满足DPR为止。
先构造一张表T:
create table t (id int,name varchar2(2000)) tablespace test;
然后用以下这么个函数实现,函数改造自某个大神,使用sys用户创建(后边的实验都用sys用户)
CREATE OR REPLACE FUNCTION GET_ADR_TRSH(P_STEP IN NUMBER DEFAULT 1,
P_START IN NUMBER DEFAULT 0,
P_STOP IN NUMBER DEFAULT NULL)
RETURN NUMBER IS
L_PRD NUMBER;
L_CNT NUMBER;
L_BLOCKS NUMBER := 0;
L_START NUMBER := P_START;
BEGIN
EXECUTE IMMEDIATE 'truncate table t';
LOOP
INSERT
INTO T
SELECT level,rpad('a',1800,'+')
FROM DUAL
CONNECT BY LEVEL <= P_STEP + L_START;
COMMIT;
L_BLOCKS := L_BLOCKS + P_STEP + L_START;
L_START := 0;
--EXECUTE IMMEDIATE 'analyze table t delete statistics';
--EXECUTE IMMEDIATE 'analyze table t compute statistics';
EXECUTE IMMEDIATE 'alter system flush buffer_cache';
SELECT /*+ full(t) */
COUNT(*)
INTO L_CNT
FROM T;
SELECT VALUE
INTO L_PRD
FROM V$SEGMENT_STATISTICS
WHERE OWNER = USER
AND OBJECT_NAME = 'T'
AND STATISTIC_NAME = 'physical reads direct';
EXIT WHEN(L_PRD > 0);
END LOOP;
RETURN L_BLOCKS;
END;
/
说明一下:因为我的表结构是name字段2000字节,而块大小为8192字节,因此这个insert的数据每个块刚好只能容纳4行。
再运行以下语句,会得到插入多少行发生DPR的结果。
set serveroutput on
DECLARE
L_TRSH NUMBER;
BEGIN
L_TRSH := GET_ADR_TRSH();
DBMS_OUTPUT.PUT_LINE(L_TRSH);
END;
/
我的环境跑的结果如下:
SYS@zkm> set serveroutput on
SYS@zkm> DECLARE
2 L_TRSH NUMBER;
3 BEGIN
4 L_TRSH := GET_ADR_TRSH();
5 DBMS_OUTPUT.PUT_LINE(L_TRSH);
6 END;
7 /
2489 PL/SQL procedure successfully completed. SYS@zkm> select count(*) from t; COUNT(*)
----------
2489
表示我insert了2489行数据后,全表扫描的话会产生DPR。
SYS@zkm> select dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid) block#,count(*) from t group by dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid) order by 2; FILE# BLOCK# COUNT(*)
---------- ---------- ----------
6 131 4
...省略大部分内容...
6 766 4
6 767 4
6 851 1 623 rows selected.
总共623行,代表T表有623个快,由于在第623个块产生已经满足DPR了,因此第623个块insert了一行数据就停止了。
那么,这个和我们前边的大表阀值STT=656有区别,这是怎么回事?
我们知道,表在没有统计信息的时候,dba_tables.blocks这个字段是空的,那么全表扫描的时候,不知道表的块数(姑且用Object_Size表示)怎么和STT=656做比较来确定是否DPR呢?
结合我前言中遇到的问题,我猜测是用的dba_segments.blocks这个值(收集统计信息后走了DPR嘛)。后来找到一篇文章,里边的大佬也是说用的dba_segments.blocks。
但是我在做实验的时候其实是不对的。准确的说,根据我的实验结果,Object_Size应该使用的是L3段头块中"Extent Control Header"下边的#blocks below处的值。
我们接着上边的实验往下验证。
首先确定L3段头块的信息:
SYS@zkm> select HEADER_FILE,HEADER_BLOCK from dba_segments where owner='SYS' and segment_name='T'; HEADER_FILE HEADER_BLOCK
----------- ------------
6 130
查看dba_segments.blocks这个值和L3里边"Extent Control Header"下边的#blocks below处的值,我们来做个比较:
SYS@zkm> select blocks from dba_segments where owner='SYS' and segment_name='T'; BLOCKS
----------
768
SYS@zkm> select value from v$diag_info where name like '%De%'; VALUE
--------------------------------------------------------------------------------
/u01/app/oracle/diag/rdbms/zkm/zkm/trace/zkm_ora_8267.trc SYS@zkm> alter system dump datafile 6 block 130; System altered. SYS@zkm> !more /u01/app/oracle/diag/rdbms/zkm/zkm/trace/zkm_ora_8267.trc
...省略部分内容...
Extent Control Header
-----------------------------------------------------------------
Extent Header:: spare1: 0 spare2: 0 #extents: 21 #blocks: 768
last map 0x00000000 #maps: 0 offset: 2716
Highwater:: 0x01800380 ext#: 20 blk#: 128 ext size: 128
#blocks in seg. hdr's freelists: 0
#blocks below: 748
mapblk 0x00000000 offset: 20
Unlocked
--------------------------------------------------------
Low HighWater Mark :
Highwater:: 0x01800300 ext#: 19 blk#: 128 ext size: 128
#blocks in seg. hdr's freelists: 0
#blocks below: 640
mapblk 0x00000000 offset: 19
Level 1 BMB for High HWM block: 0x01800301
Level 1 BMB for Low HWM block: 0x01800281
--------------------------------------------------------
Segment Type: 1 nl2: 1 blksz: 8192 fbsz: 0
L2 Array start offset: 0x00001434
First Level 3 BMB: 0x00000000
L2 Hint for inserts: 0x01800081
Last Level 1 BMB: 0x01800301
Last Level II BMB: 0x01800081
Last Level III BMB: 0x00000000
Map Header:: next 0x00000000 #extents: 21 obj#: 96513 flag: 0x10000000
Inc # 0
Extent Map
-----------------------------------------------------------------
0x01800080 length: 8
0x01800088 length: 8
0x01800090 length: 8
0x01800098 length: 8
0x018000a0 length: 8
0x018000a8 length: 8
0x018000b0 length: 8
0x018000b8 length: 8
0x018000c0 length: 8
0x018000c8 length: 8
0x018000d0 length: 8
0x018000d8 length: 8
0x018000e0 length: 8
0x018000e8 length: 8
0x018000f0 length: 8
0x018000f8 length: 8
0x01800100 length: 128
0x01800180 length: 128
0x01800200 length: 128
0x01800280 length: 128
0x01800300 length: 128 Auxillary Map
--------------------------------------------------------
Extent 0 : L1 dba: 0x01800080 Data dba: 0x01800083
Extent 1 : L1 dba: 0x01800080 Data dba: 0x01800088
Extent 2 : L1 dba: 0x01800090 Data dba: 0x01800091
Extent 3 : L1 dba: 0x01800090 Data dba: 0x01800098
Extent 4 : L1 dba: 0x018000a0 Data dba: 0x018000a1
Extent 5 : L1 dba: 0x018000a0 Data dba: 0x018000a8
Extent 6 : L1 dba: 0x018000b0 Data dba: 0x018000b1
Extent 7 : L1 dba: 0x018000b0 Data dba: 0x018000b8
Extent 8 : L1 dba: 0x018000c0 Data dba: 0x018000c1
Extent 9 : L1 dba: 0x018000c0 Data dba: 0x018000c8
Extent 10 : L1 dba: 0x018000d0 Data dba: 0x018000d1
Extent 11 : L1 dba: 0x018000d0 Data dba: 0x018000d8
Extent 12 : L1 dba: 0x018000e0 Data dba: 0x018000e1
Extent 13 : L1 dba: 0x018000e0 Data dba: 0x018000e8
Extent 14 : L1 dba: 0x018000f0 Data dba: 0x018000f1
Extent 15 : L1 dba: 0x018000f0 Data dba: 0x018000f8
Extent 16 : L1 dba: 0x01800100 Data dba: 0x01800102
Extent 17 : L1 dba: 0x01800180 Data dba: 0x01800182
Extent 18 : L1 dba: 0x01800200 Data dba: 0x01800202
Extent 19 : L1 dba: 0x01800280 Data dba: 0x01800282
Extent 20 : L1 dba: 0x01800300 Data dba: 0x01800302
-------------------------------------------------------- Second Level Bitmap block DBAs
--------------------------------------------------------
DBA 1: 0x01800081 End dump data blocks tsn: 7 file#: 6 minblk 130 maxblk 130
可以看出dba_segments.blocks为768,而"Extent Control Header"下边的#blocks below处的值为748。
那么其实Object_Size的值是等于748的。
前边提到的T表有623个块意义在当前就显得不是特别重要的,因为Oracle的判断基准以Object_size=748,并不是实际上的623个块。
如何验证Object_size的值是否准确呢?新开一个session(就叫s1会话吧),作如下操作:
SYS@zkm> select value from v$diag_info where name like '%De%'; VALUE
--------------------------------------------------------------------------------
/u01/app/oracle/diag/rdbms/zkm/zkm/trace/zkm_ora_8363.trc SYS@zkm> alter system flush buffer_cache; System altered. SYS@zkm> / System altered. SYS@zkm> / System altered. SYS@zkm> alter session set events 'trace[NSMTIO] disk=medium'; Session altered. SYS@zkm> SELECT /*+ full(t) */ COUNT(*) FROM T; COUNT(*)
----------
2489
查看trc文件内容:
NSMTIO: qertbFetch:[MTT < OBJECT_SIZE < VLOT]: Checking cost to read from caches(local/remote) and checking storage reduction factors (OLTP/EHCC Comp)
NSMTIO: kcbdpc:DirectRead: tsn: 7, objd: 96513, objn: 96511
ckpt: 1, nblks: 748, ntcache: 1, ntdist:0
NSMTIO: Additional Info: VLOT=164150
Object# = 96513, Object_Size = 748 blocks
SqlId = 4x7n0h69zwnuk, plan_hash_value = 2966233522, Partition# = 0
可以直接看到:
Object_Size = 748 blocks
VLOT=164150
这两个值都和前边得到的结果一致。
从其他内容看,我们可以得到判断条件是
当满足[MTT < OBJECT_SIZE < VLOT]的时候,会检查cost(比如cache率,前边提到的第三),本地还是远程之类的等。
实际上,MTT的值为3280不可能<Object_size,MTT在11gR2已经废弃。
大量实验可以得出结论:可以看到在11.2版本下,小于SST不走DirectRead,大于VLOT一定走DirectRead,介于SST和VLOT的表,需要考虑是否为本地表或者远程表
然后这里最后走了DPR了(DirectRead)。
其他信息:
object_id=96511,data_object_id=96513
ntcache: 1表示缓存了一个块
值得一提的是,如果T表存在统计信息,也就是dba_tables.blocks有值的话,Oracle会直接使用该值作为Object_size的值。我们验证下,
新开一个session,收集统计信息之后。
SYS@zkm> analyze table t compute statistics; Table analyzed. SYS@zkm> select blocks from dba_tables where owner='SYS' and table_name='T' union all select blocks from dba_segments where owner='SYS' and segment_name='T'; BLOCKS
----------
748 --dba_tables.blocks
768 --dba_segments.blocks
刚好和原来一样,不过你可以尝试truncate表T, 他的值还是用748,但其实为0了。这里实验过程不写出来了。
这个其实可以验证收集统计信息很有可能导致DPR的发生了。
接下来我们验证下前边第三点:
第三,表已经被缓存在buffer cache中的块占比,超过表50%的话,不会发生DPR。
首先,删除统计信息(后边说明原因)
然后用如下语句查询当前表被缓存在buffer cache的块的数量。
SYS@zkm> select INST_ID,sum(NUM_BUF) from x$kcboqh where obj# in (select DATA_OBJECT_ID from dba_objects where owner='SYS' and object_name='T') group by inst_id; INST_ID SUM(NUM_BUF)
---------- ------------
1 5 SYS@zkm> analyze table t delete statistics; Table analyzed.
表示实例1中,表T有5个块被缓存。
由于Object_Size = 748,一半为374。因此想办法将缓存的块变为373,看是否发生DPR,然后在缓存多一个块,变为374,看是否发生DPR。
用SELECT /*+ full(t) */ COUNT(*) FROM T where rownum<=xxxx;
由于1个块包含4行,其中373块实际上还要减掉L3号块,因此读取372个块即可,共1488行。但实际上查询的结果有问题,我之前一篇文章有研究过全表扫描下用rownum后如何缓存块,还是测试不够充分。
不过这里暂不做研究,最终我调试出来的rownum可以为1472。
开多另外一个session,将多块读设置为1,禁用动态采样,在这个session级别上禁用DPR。
直接复制的:
set arraysize 5000
alter system flush buffer_cache;
alter session set "_serial_direct_read"=NEVER;
alter session set db_file_multiblock_read_count=1;
SELECT /*+ full(t) dynamic_sampling(t 0) */ COUNT(*) FROM T where rownum<=1476;
select INST_ID,sum(NUM_BUF) from x$kcboqh where obj# in (select DATA_OBJECT_ID from dba_objects where owner='SYS' and object_name='T') group by inst_id;
SYS@zkm> set arraysize 5000
SYS@zkm> alter system flush buffer_cache; System altered. SYS@zkm> alter session set "_serial_direct_read"=NEVER; Session altered. SYS@zkm> alter session set db_file_multiblock_read_count=1; Session altered. SYS@zkm> SELECT /*+ full(t) dynamic_sampling(t 0) */ COUNT(*) FROM T where rownum<=1472; COUNT(*)
----------
1472 SYS@zkm> select INST_ID,sum(NUM_BUF) from x$kcboqh where obj# in (select DATA_OBJECT_ID from dba_objects where owner='SYS' and object_name='T') group by inst_id; INST_ID SUM(NUM_BUF)
---------- ------------
1 373
回到s1会话,执行:
SYS@zkm> SELECT /*+ full(t) */ COUNT(*) FROM T; COUNT(*)
----------
2489 SYS@zkm>
SYS@zkm>
SYS@zkm>
SYS@zkm> / COUNT(*)
----------
2489 trc多出来的信息: *** 2020-05-12 11:10:11.356
NSMTIO: qertbFetch:[MTT < OBJECT_SIZE < VLOT]: Checking cost to read from caches(local/remote) and checking storage reduction factors (OLTP/EHCC Comp)
NSMTIO: kcbdpc:DirectRead: tsn: 7, objd: 96513, objn: 96511
ckpt: 1, nblks: 748, ntcache: 373, ntdist:0
NSMTIO: Additional Info: VLOT=164150
Object# = 96513, Object_Size = 748 blocks
SqlId = 4x7n0h69zwnuk, plan_hash_value = 2966233522, Partition# = 0
其中ntcache=373刚好就是我们控制的缓存373。
这里也只走了DPR,那么我们多缓存一个块,是否就不会用DPR了呢?
SYS@zkm> set arraysize 5000
SYS@zkm> alter system flush buffer_cache; System altered. SYS@zkm> alter session set "_serial_direct_read"=NEVER; Session altered. SYS@zkm> alter session set db_file_multiblock_read_count=1; Session altered. SYS@zkm> SELECT /*+ full(t) */ COUNT(*) FROM T where rownum<=1476; COUNT(*)
----------
1476 SYS@zkm> select INST_ID,sum(NUM_BUF) from x$kcboqh where obj# in (select DATA_OBJECT_ID from dba_objects where owner='SYS' and object_name='T') group by inst_id; INST_ID SUM(NUM_BUF)
---------- ------------
1 374
回到s1,
SYS@zkm> SELECT /*+ full(t) */ COUNT(*) FROM T; COUNT(*)
----------
2489 trc多出来的信息: *** 2020-05-12 11:11:28.249
NSMTIO: qertbFetch:[MTT < OBJECT_SIZE < VLOT]: Checking cost to read from caches(local/remote) and checking storage reduction factors (OLTP/EHCC Comp)
NSMTIO: kcbdpc:NoDirectRead:[CACHE_READ]: tsn: 7, objd: 96513, objn: 96511
ckpt: 0, nblks: 748, ntcache: 374, ntdist:0
NSMTIO: Additional Info: VLOT=164150
Object# = 96513, Object_Size = 748 blocks
SqlId = 4x7n0h69zwnuk, plan_hash_value = 2966233522, Partition# = 0
当ntcache为Object_size的一半的时候,即表T有一半的块缓存在buffer cache中时候,不会做DPR。
奇怪的是,如果你收集了统计信息,无论cache率多高,都只会做直接路径读。这点尚未搞明白为什么。
这个时候,如果禁用参数"_statistics_based_srf_enabled"=false的话,
再次s1执行观察trc的话,输出会变为如下显示:
*** 2020-05-12 22:37:50.181
NSMTIO: qertbFetch:NoDirectRead:[- STT < OBJECT_SIZE < MTT]:Obect's size: 748 (blocks), Threshold: MTT(3283 blocks),
_object_statistics: enabled, Sage: disabled,
Direct Read for serial qry: enabled(::::::), Ascending SCN table scan: FALSE
flashback_table_scan: FALSE, Row Versions Query: FALSE
SqlId: 4x7n0h69zwnuk, plan_hash_value: 2966233522, Object#: 96513, Parition#: 0
这个时候的Object_size确实就是<MTT的值了。再次设置为true的话,判断cache率有变正常了。
关于这个隐含参数,网络和mos几乎没有资料介绍。
根据实验和网上资料我们可以总结,
1.统计信息的有无只会影响到Object_size,当然可以通过"_direct_read_decision_statistics_driven"控制是否使用统计信息来影响DPR
2.当Object_size < STT的话,一定不走DPR
3.当Object_size > VLOT的话,一定走DPR(未排除参数_serial_direct_read是否影响)
4.当Object_size介于MTT和VLOT之间的时候,会使用成本计算(Checking cost to read from caches(local/remote) and checking storage reduction factors (OLTP/EHCC Comp))去判断是否DPR。
其中,MTT<Object_size实际上不准确(因此才说11gR2MTT是废弃的)。由于要使用成本代价,因此可以认为STT < Object_size < VLOT的时候,使用成本计算。比如包括缓存率的成本等。
5."_direct_read_decision_statistics_driven"默认为true,当设置为false,从观察到的结果上看,MTT被启用了,从上边实验看原因是因为Oracle直接判断- STT < OBJECT_SIZE < MTT了,这个时候无法判断是否成本计算被禁用了。毕竟不再有"MTT < OBJECT_SIZE < VLOT".有时间可以构造出这么一个条件出来观察看看。
参考
http://www.itpub.net/thread-1815281-1-1.html
https://www.talkwithtrend.com/Article/218485
Oracle收集对表收集统计信息导致全表扫描直接路径读?的更多相关文章
- mysql不会使用索引,导致全表扫描情况
不会使用索引,导致全表扫描情况 1.不要使用in操作符,这样数据库会进行全表扫描,推荐方案:在业务密集的SQL当中尽量不采用IN操作符 2.not in 使用not in也不会走索引推荐方案:用not ...
- 如何优雅的使用 参数 is null而不导致全表扫描(破坏索引)
相信大家在很多实际业务中(特别是后台系统)会使用到各种筛选条件来筛选结果集 首先添加测试数据 ), Age INT) go CREATE INDEX idx_age ON TempList (Age) ...
- Oracle 11g全表扫描以Direct Path Read方式执行
在Oracle Database 11g中有一个新特性,全表扫描可以通过直接路径读的方式来执行(Direct Path Read),这是一个合理的变化,如果全表扫描的大量数据读取是偶发性的,则直接路径 ...
- [转载]会引起全表扫描的几种SQL
查询语句的时候尽量避免全表扫描,使用全扫描,索引扫描!会引起全表扫描的几种SQL如下 1.模糊查询效率很低: 原因:like本身效率就比较低,应该尽量避免查询条件使用like:对于like ‘%... ...
- 会引起全表扫描的几种SQL 以及sql优化 (转)
出处: 查询语句的时候尽量避免全表扫描,使用全扫描,索引扫描!会引起全表扫描的几种SQL如下 1.模糊查询效率很低: 原因:like本身效率就比较低,应该尽量避免查询条件使用like:对于like ‘ ...
- MYSQl 全表扫描以及查询性能
MYSQl 全表扫描以及查询性能 -- 本文章仅用于学习,记录 一. Mysql在一些情况下全表检索比索引查询更快: 1.表格数据很少,使用全表检索会比使用索引检索更快.一般当表格总数据小于10行并且 ...
- MySQL查询优化:LIMIT 1避免全表扫描
在某些情况下,如果明知道查询结果只有一个,SQL语句中使用LIMIT 1会提高查询效率. 例如下面的用户表(主键id,邮箱,密码): create table t_user(id int primar ...
- SQL 数据优化索引建suo避免全表扫描
首先什么是全表扫描和索引扫描?全表扫描所有数据过一遍才能显示数据结果,索引扫描就是索引,只需要扫描一部分数据就可以得到结果.如果数据没建立索引. 无索引的情况下搜索数据的速度和占用内存就会比用索引的检 ...
- MySql避免全表扫描【转】
原文地址:http://blog.163.com/ksm19870304@126/blog/static/37455233201251901943705/ 对查询进行优化,应尽量避免全表扫描,首先应考 ...
随机推荐
- Java实现 LeetCode 214 最短回文串
214. 最短回文串 给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串.找到并返回可以用这种方式转换的最短回文串. 示例 1: 输入: "aacecaaa" 输出 ...
- Java实现 洛谷 P1015 回文数(N进制回文数)
输入输出样例 输入样例#1: 10 87 输出样例#1: STEP=4 import java.util.Scanner; public class 回文数2 { public static void ...
- Mybatis缓存及延迟加载策略
一:引言 通过前面几篇的文章介绍了Mybatis的一对一.一对多.多对多关系的配置及实现,可是大家发现了吗?在执行关联查询的时候,直接会把当前查询的主表里包含的副表也查询后封装到对象里,其实在实际开发 ...
- “造轮运动”之 ORM框架系列(三)~ 干货呈上
这一趴里面,我就来正式介绍一下CoffeeSQL的干货. 首先要给CoffeeSQL来个定位:最开始就是由于本人想要了解ORM框架内部的原理,所以就四处搜寻有关的博客与学习资料,就是在那个夏天 ...
- KVM Web管理平台 WebVirtMgr
WebVirtMgr介绍 WebVirtMgr是一个KVM管理平台,让kvm管理变得更为可视化,对中小型kvm应用场景带来了更多方便.WebVirtMgr采用几乎纯Python开发,其前端是基于Pyt ...
- 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...
- .Net Core微服务入门全纪录(一)——项目搭建
前言 写这篇博客主要目的是记录一下自己的学习过程,只能是简单入门级别的,因为水平有限就写到哪算哪吧,写的不对之处欢迎指正. 什么是微服务? 关于微服务的概念解释网上有很多... 个人理解,微服务是一种 ...
- python基础:如何使用 pip 安装第三方库
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 在这个生活中处处都是大数据和人工智能的时代,总是能在各种角落看到 Pyth ...
- [转] 浅谈C++中的那些内存泄露
点击阅读原文 尽管学过C语言.可是C++里面的一些基础还是不太懂,还须要再掌握. 对于内存泄露,我的个人理解就是程序在执行过程中,自己开辟了空间,用完这块空间后却没有释放. 今晚上我就犯了这种低级错误 ...
- Python函数&异常处理
1. 函数基础 1.1 参数和返回值 1.1.1 参数 位置参数.关键字参数 def my_func1(x, y, z): print(x+y+z, "计算结束") my_func ...