物理读之LRU(最近最少被使用)的深入解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: LRU算法,不但在操作系统中有,在数据库中也有,下面我以Oracle数据库的LRU算法为例给大家分享,解析它背后的原理。

一组LRU链表包括LRU主链,LRU辅助链,LRUW主链,LRUW辅助链,称为一个WorkSet(工作组)如下图:
3.png
sys@ZMDB> selectCNUM_SET,CNUM_REPL,ANUM_REPL,CNUM_WRITE,ANUM_WRITE from x$kcbwds whereCNUM_SET>0;

CNUM_SET CNUM_REPL ANUM_REPL CNUM_WRITE ANUM_WRITE


15221      15221       3796          0          0
15221      15221       3783          0          0

CNUM_SET:工作组总的buffer总数量
CNUM_REPL:工作组中LRU的buffer总数量(主LRU+辅LRU)
ANUM_REPL:工作组中辅LRU总BUFFER的数量

通过隐含参数查到BUFFER的总的个数是30442,正好与上面的CNUM_SET=15221+15221
sys@ZMDB>@?/rdbms/admin/show_para
Enter value for p: _db_block_buffers
old 12: AND upper(i.ksppinm) LIKEupper('%&p%')
new 12: AND upper(i.ksppinm) LIKEupper('%_db_block_buffers%')

P_NAME P_DESCRIPTION P_VALUE ISDEFAULT ISMODIFIEDISADJ


_db_block_buffers Number of database blocks cached inmemory: hidden 30442 TRUE FALSE FALSE

                                      Parameter

我们用以下语句查下数据库中buffer所在LRU的状态
sys@ZMDB> select lru_flag,count(*) from x$bh group by lru_flag;

LRU_FLAG COUNT(*)


     6        208
     2         10
     4      7122
     8     15199

0 7646

我们对LRU_FLAG=6,2,4,8,0等做出解释,举个例子,对于6是什么含义呢?
首先要在x$bh中找到lru_flag=6的任意的一个BUFFER
sys@ZMDB> select LRU_FLAG,LOWER(BA)from x$bh where lru_flag=6 andrownum=1;

LRU_FLAG LOWER(BA)


     6 0000000081dae000

DUMP buffer_cache中BH信息,如下命令:
sys@ZMDB>alter session set events'immediate trace name buffers level 1';

Session altered.
ys@ZMDB> col value for a85
sys@ZMDB> select * from v$diag_info where name='Default TraceFile';

INST_ID NAME VALUE


1 Default Trace File /u01/app/oracle/diag/rdbms/zmdb/zmdb/trace/zmdb_ora_13235.trc

通过BA=81dae000搜索trace文件,
/u01/app/oracle/diag/rdbms/zmdb/zmdb/trace/zmdb_ora_13235.trc
得到如下内容:
BH (0x81fe7e38) file#: 1 rdba: 0x0040ace1 (1/44257) class: 1 ba:0x81dae000
set: 6 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc:0,25
dbwrid: 0 obj: 421 objn: 423 tsn: 0 afn: 1hint: f
hash: [0x9ef9d710,0x853f8da8] lru:[0x81fe7df0,0x81fe8050]
lru-flags: moved_to_tail on_auxiliary_list
ckptq: [NULL] fileq: [NULL] objq: [NULL]objaq: [NULL]
st: CR md: NULL fpin: 'kdswh06: kdscgr' tch:1
cr: [scn: 0x0.80350f4d],[xid: 0x0.0.0],[uba:0x0.0.0],[cls: 0x0.80350f4d],[sfl: 0x0],[lc: 0x0.8034c532]
flags: block_written_once redo_since_read

LRU_FLAG=6的意思是lru-flags: moved_to_tail on_auxiliary_list,就是向LRU的辅助链表的尾部移动,这有可能是SMON从LRU的主链表上的非脏块、TCH<=1并且状态是非PIN的BUFFER被挂接到LRU辅助链表的尾部。
根据以上的方法同理可以解释出LRU_FLAG的含义:
LRU_FLAG
0==>LRU-主链冷端的头部,这个比较特殊他在DUMP没有显示LRU_FLAG

2==>LRU-主链冷端的尾部,lru-flags:moved_to_tail

4==>LRU-辅助链,lru-flags:on_auxiliary_list

6==>LRU-辅助链的尾部,lru-flags:moved_to_tail on_auxiliary_list

8==>LUR-主链热端,lru-flags:hot_buffer

当发生物理读时,Oracle会从LRU辅助链表找空闲的BUFFER,然后把LRU辅助的链上的BUFFER挂接到LRU主链的冷端头,实验如下:
首先要保证有LRU辅助链上的BUFFER,即有LRU_FLAG=6或LRU_FLAG=4,如果数据库刚刚启来,可能没有LRU_FLAG=6、LRU_FLAG=4,那需要做大量的物理读操作,才会有LRU_FLAG=6或LRU_FLAG=4
sys@ZMDB> alter system flush buffer_cache;

System altered.

sys@ZMDB> selectlru_flag,count(*) from x$bh group by lru_flag;

LRU_FLAG COUNT(*)


     6        208
     4     30009
     0          2

第一次DUMP整个BUFFER CACHE:
sys@ZMDB> alter session set events'immediate trace name bufferslevel 1';
/u01/app/oracle/diag/rdbms/zmdb/zmdb/trace/zmdb_ora_13480.trc

发生物理读
gyj@ZMDB> conn gyj/gyj
Connected.
gyj@ZMDB> set autot on;
gyj@ZMDB> select id,name,dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid)block# from gyj_t1 where id=1;

    ID NAME                                FILE#     BLOCK#

     1 gyj1                                    7        139

Execution Plan

Plan hash value: 59758809


| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

| 0 | SELECT STATEMENT | | 1 | 14 | 68 (0)| 00:00:01 |

|* 1 | TABLE ACCESS FULL| GYJ_T1 | 1| 14 | 68 (0)| 00:00:01 |

Predicate Information(identified by operation id):

1 - filter("ID"=1)

Statistics

      1 recursive calls
      1 db block gets
    254 consistent gets
    248 physical reads
      0 redo size
    733 bytes sent via SQL*Net to client
    523 bytes received via SQL*Net from client
      2 SQL*Net roundtrips to/from client
      0 sorts (memory)
      0 sorts (disk)
      1 rows processed

sys@ZMDB> selectLRU_FLAG,lower(BA),TCH from x$bh where file#=7 and dbablk=139;

LRU_FLAG LOWER(BA) TCH


     0 000000007d1b2000          1
     4 0000000078558000          0
     4 0000000085f68000          0

物理读完成后,再次dump整个buffer cache,
sys@ZMDB>alter session set events'immediate trace name buffers level 1';

/u01/app/oracle/diag/rdbms/zmdb/zmdb/trace/zmdb_ora_13511.trc

拿BA=7d1b2000,搜索第一次DUMP的trace文件
/u01/app/oracle/diag/rdbms/zmdb/zmdb/trace/zmdb_ora_13480.trc

BH (0x7d3e8098) file#: 3 rdba:0x00c0586b (3/22635) class: 34 ba: 0x7d1b2000
set: 5 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc:0,25
dbwrid:0 obj: -1 objn: 0 tsn: 2 afn: 3 hint: f
hash: [0x9efa7570,0x9efa7570] lru:[0x7f7f5d30,0x7d3e8050]
lru-flags: on_auxiliary_list
ckptq: [NULL] fileq: [NULL] objq: [NULL]objaq: [NULL]
st: FREE md: NULL fpin: 'ktuwh03: ktugnb'tch: 0 lfb: 33
flags:

拿BA=7d1b2000,搜索第二次DUMP的trace文件
/u01/app/oracle/diag/rdbms/zmdb/zmdb/trace/zmdb_ora_13511.trc

BH (0x7d3e8098) file#: 7 rdba:0x01c0008b (7/139) class: 1 ba: 0x7d1b2000
set: 5 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc:0,25
dbwrid: 0 obj: 22919 objn: 19567 tsn: 7 afn:7 hint: f
hash: [0x787e4bd8,0x9e4cda50] lru:[0x7f7f5d30,0x7d3e8050]
ckptq: [NULL] fileq: [NULL] objq:[0x9a88e518,0x7d3e8078] objaq: [0x9a88e508,0x7d3e8088]
st: XCURRENT md: NULL fpin: 'kdswh11:kdst_fetch' tch: 1
flags: only_sequential_access
LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN:[0xffff.ffffffff] HSUB: [65535]

从上面的两个trace可以得出结论ba: 0x7d1b2000
从lru-flags:on_auxiliary_list(LRU_FLAG=4)到LRU-主链冷端的头部,这个比较特殊在DUMP没有显示LRU_FLAG(LRU_FLAG=0)

观察LRUTCH>=2时冷端移到热端

1、BUFFER手动设为100M

ALTER SYSTEM SETmemory_max_target=0 scope=spfile;
ALTER SYSTEM SET memory_target=0;
alter system set sga_target=0;

create table gyj1_t80 (idint,name char(2000));

create table gyj2_t80 (idint,name char(2000));

begin
for i in 1 .. 30000
loop

insert into gyj1_t80 values(i,'gyj'||i);

commit;
end loop;
end;
/

SQL> SQL> selectbytes/1024/1024||'M' from dba_segments where segment_name='GYJ1_T80' andowner='GYJ';

BYTES/1024/1024||'M'

80M

begin
for i in 1 .. 30000
loop

insert into gyj2_t80 values(i,'gyj'||i);

commit;
end loop;
end;
/

create index idx_gyj1_t80m ongyj1_t80(id);

create index idx_gyj2_t80m ongyj2_t80(id);

SQL> show user;
USER is "GYJ"
SQL> conn / as sysdba
Connected.
SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.

第一次dump
SQL> alter session set events'immediate trace name buffers level1';

Session altered.

SQL> select * fromv$diag_info where name='Default Trace File';

INST_ID NAME


VALUE

     1 Default Trace File

/u01/app/oracle/diag/rdbms/jfdb/jfdb/trace/jfdb_ora_7210.trc

发生一个物理读走索引
set autot on
selectid,name,dbms_rowid.rowid_relative_fno(rowid)file#,dbms_rowid.rowid_block_number(rowid) block# from gyj1_t80 where id=1;

SQL> selectid,name,dbms_rowid.rowid_relative_fno(rowid)file#,dbms_rowid.rowid_block_number(rowid) block# from gyj1_t80 where id=1;

    ID NAME                      FILE#     BLOCK#

     1 gyj1                          5        581

select LRU_FLAG,lower(BA),TCHfrom x$bh where file#=5 and dbablk=581;
SQL> select LRU_FLAG,lower(BA),TCH,decode(state,0,'free',1,'xcur',2,'scur'
2 ,3,'cr', 4,'read',5,'mrec',6,'irec',7,'write',8,'pi',9,'memory',10,'mwrite',11,
3 'donated', 12,'protected', 13,'securefile', 14,'siop',15,'recckpt', 16, 'flashf
4 ree', 17, 'flashcur', 18,'flashna') from x$bh where file#=5 anddbablk=581;

LRU_FLAG LOWER(BA) TCH DECODE(STA


     0 000000009fca8000          1 xcur

SQL> selectLRU_FLAG,lower(BA),TCH from x$bh where file#=5 and dbablk=581;

LRU_FLAG LOWER(BA) TCH


     0 000000009fca8000          5



SQL> set autot traceonly;
SQL> select /+ index(G) / count(name) fromgyj1_t80 G where id<=8000;

SQL> selectLRU_FLAG,lower(BA),TCH from x$bh where file#=5 and dbablk=581;

LRU_FLAG LOWER(BA) TCH


     0 000000009fca8000          6

再次发生物理读,此时LRU_FLAG=0变为8,同时TCH=8重置为0
SQL>select LRU_FLAG,lower(BA),TCH from x$bh where file#=5 and dbablk=581;

LRU_FLAG LOWER(BA) TCH


     0000000009fca8000          8

SQL> select LRU_FLAG,lower(BA),TCH from x$bh where file#=5 anddbablk=581;

LRU_FLAG LOWER(BA) TCH


     8000000009fca8000          0

BH (0x9ffe02a8) file#: 5 rdba: 0x01400245 (5/581) class: 1 ba:0x9fca8000
set: 5 pool: 3 bsz: 8192bsi: 0 sflg: 2 pwc: 15,19
dbwrid: 0 obj: 13537 objn:13537 tsn: 5 afn: 5 hint: f
hash:[0xb6a86de0,0xb6a86de0] lru: [0x9ffe0260,0x9ffe9a60]
lru-flags: hot_buffer
ckptq: [NULL] fileq: [NULL]objq: [0x9ffe0618,0x9ffe0028] objaq: [0x9ffe0628,0x9ffe0038]
st: XCURRENT md: NULL fpin:'kdswh05: kdsgrp' tch: 0
flags:
LRBA: [0x0.0.0] LSCN:[0x0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]

当TCH=0时,再发生大量物理读,地址为9fca8000的BUFFER就被重用了,彻底从BUFFER消失

SQL> selectLRU_FLAG,lower(BA),TCH from x$bh where file#=5 and dbablk=581;

LRU_FLAG LOWER(BA) TCH


     8 000000009fca8000          0

SQL> select LRU_FLAG,lower(BA),TCH from x$bh wherefile#=5 and dbablk=581;

no rows selected
通过实验,我们更清楚地了解到物理读LRU的基本流程,可以进一步理解物理读内部的LRU算法。

相关文章
|
3月前
|
存储 关系型数据库 MySQL
深入解析MySQL数据存储机制:从表结构到物理存储
深入解析MySQL数据存储机制:从表结构到物理存储
327 1
|
4月前
|
开发者 图形学 Java
揭秘Unity物理引擎核心技术:从刚体动力学到关节连接,全方位教你如何在虚拟世界中重现真实物理现象——含实战代码示例与详细解析
【8月更文挑战第31天】Unity物理引擎对于游戏开发至关重要,它能够模拟真实的物理效果,如刚体运动、碰撞检测及关节连接等。通过Rigidbody和Collider组件,开发者可以轻松实现物体间的互动与碰撞。本文通过具体代码示例介绍了如何使用Unity物理引擎实现物体运动、施加力、使用关节连接以及模拟弹簧效果等功能,帮助开发者提升游戏的真实感与沉浸感。
113 1
|
4月前
|
缓存 算法 前端开发
深入理解缓存淘汰策略:LRU和LFU算法的解析与应用
【8月更文挑战第25天】在计算机科学领域,高效管理资源对于提升系统性能至关重要。内存缓存作为一种加速数据读取的有效方法,其管理策略直接影响整体性能。本文重点介绍两种常用的缓存淘汰算法:LRU(最近最少使用)和LFU(最不经常使用)。LRU算法依据数据最近是否被访问来进行淘汰决策;而LFU算法则根据数据的访问频率做出判断。这两种算法各有特点,适用于不同的应用场景。通过深入分析这两种算法的原理、实现方式及适用场景,本文旨在帮助开发者更好地理解缓存管理机制,从而在实际应用中作出更合理的选择,有效提升系统性能和用户体验。
221 1
|
4月前
|
开发者 图形学 Java
Unity物理引擎深度揭秘:从刚体碰撞到软体模拟,全面解析实现复杂物理交互的技巧与秘诀,助你打造超真实游戏体验
【8月更文挑战第31天】物理模拟在游戏开发中至关重要,可让虚拟世界更真实。Unity作为强大的跨平台游戏引擎,内置物理系统,支持从刚体碰撞到布料模拟的多种功能。通过添加Rigidbody组件,可实现物体受力和碰撞;使用AddForce()施加力;通过关节(如Fixed Joint)连接刚体以模拟复杂结构。Unity还支持软体物理,如布料和绳索模拟,进一步增强场景丰富度。掌握这些技术,可大幅提升游戏的真实感和玩家体验。
176 0
|
4月前
《黑神话:悟空》中的物理模拟与碰撞检测技术解析
【8月更文第26天】《黑神话:悟空》是一款备受期待的动作角色扮演游戏,以其精致的画面和丰富的物理效果而闻名。为了实现游戏中的真实感和互动性,开发团队使用了先进的物理引擎和碰撞检测系统。本文将深入探讨《黑神话:悟空》中的物理模拟与碰撞检测技术,并通过一些伪代码示例来展示其实现细节。
192 0
|
6月前
|
存储 监控 NoSQL
Redis中的LRU淘汰策略深入解析
Redis的内存管理关键在于处理数据增长与有限内存的矛盾,LRU策略被广泛用于此。LRU基于“不常访问的数据未来访问可能性小”的假设,淘汰最近最少使用的数据。Redis通过双向链表实现,但并非严格LRU,而是采样算法以平衡性能和精度。用户可通过调整`maxmemory-samples`等参数优化。尽管LRU简单高效,但无法区分数据重要性和访问频率,可能误淘汰重要数据。合理设置参数、结合其他策略、监控调优是优化LRU使用的关键。
150 1
|
6月前
|
运维 内存技术
计算机网络:物理层中的数字传输系统全景概览解析
计算机网络:物理层中的数字传输系统全景概览解析
102 0
|
7月前
|
缓存 算法 Java
数据结构~缓存淘汰算法--LRU算法(Java的俩种实现方式,万字解析
数据结构~缓存淘汰算法--LRU算法(Java的俩种实现方式,万字解析
|
7月前
|
缓存 算法 前端开发
前端开发者必知的缓存淘汰策略:LRU算法解析与实践
前端开发者必知的缓存淘汰策略:LRU算法解析与实践
218 0
|
7月前
|
存储 SQL 关系型数据库
drds逻辑表与物理解析
drds逻辑表与物理解析
123 5

推荐镜像

更多
下一篇
DataWorks