shared pool 和buffer pool 详解(之二, Cache Buffers LRU Chain、Cache Buffers LRU Chain闩锁竞争与解决)

【深入解析--eygle】学习笔记

1.1.2  Cache BuffersLRU Chain闩锁竞争与解决

当用户进程需要读数据到Buffer Cache时或Cache Buffer根据LRU算法进行管理等,就不可避免的要扫描LRU  List获取可用Buffer或更改Buffer状态,我们知道,Oracle的Buffer Cache是共享内存,可以为众多并发进程并发访问,所以在搜索的过程中必须获取Latch(Latch是Oracle的一种串行锁机制,用于保护共享内存结构),锁定内存结构,防止并发访问损坏内存中的数据(我们必须认识到对于数据的访问、Buffer的存取就意味着多次的Latch访问,而过于严重的Latch竞争常常是系统的瓶颈所在)。

这个用于锁定LRU的Latch就是我们经常见到的Cache Buffers Lru Chain。

select addr, latch#,name, gets, misses, immediate_gets, immediate_misses

from v$latch

where name = ‘cache buffers lru chain‘;

16:48:46 [email protected]>select addr, latch#, name, gets, misses, immediate_gets,immediate_misses

17:19:05   2   from v$latch

17:19:05   3  where name = ‘cache buffers lru chain‘;

ADDR                 LATCH# NAME                        GETS     MISSES IMMEDIATE_GETS IMMEDIATE_MISSES

-------------------------- ------------------------- ------ ---------- ------------------------------

0000000060019F08        150 cache buffers lru chain    29129          2          25156                0

17:19:06 [email protected] SQL>

可以从v$latch_children视图察看当前各子Latch使用情况:

select addr,

child#,

name,

gets,

misses,

immediate_gets   igets,

immediate_missesimisses

fromv$latch_children

where name= ‘cache buffers lru chain‘;

17:19:06 [email protected] SQL>select addr,

17:46:11  2         child#,

17:46:11  3         name,

17:46:11  4         gets,

17:46:11  5         misses,

17:46:11  6         immediate_gets   igets,

17:46:11  7         immediate_misses imisses

17:46:11  8    from v$latch_children

17:46:11  9   where name = ‘cache bufferslru chain‘;

ADDR             CHILD# NAME                     GETS     MISSES      IGETS   IMISSES

---------------- ------- ----------------------------- ---------- ---------- ----------

0000000077572578      16 cache buffers lru chain      0         0          0          0

00000000775724B0      15 cache buffers lru chain     16         0          1          0

0000000077556FE0      14 cache buffers lru chain      0         0          0          0

0000000077556F18      13 cache buffers lru chain     16         0          1          0

000000007753BA48      12 cache buffers lru chain      0         0          0          0

000000007753B980      11 cache buffers lru chain     16         0          1          0

00000000775204B0      10 cache buffers lru chain      0         0          0          0

00000000775203E8       9 cache buffers lru chain     16         0          1          0

0000000077504F18       8 cache buffers lru chain      0         0          0          0

0000000077504E50       7 cache buffers lru chain     16         0          1          0

00000000774E9980       6 cache buffers lru chain      0         0          0          0

00000000774E98B8       5 cache buffers lru chain  29417         2      25624          0

00000000774CE3E8       4 cache buffers lru chain      0         0          0          0

00000000774CE320       3 cache buffers lru chain     16         0          1          0

00000000774B2E50       2 cache buffers lru chain      0         0          0          0

00000000774B2D88       1 cache buffers lru chain     16         0          1          0

16 rows selected.

17:46:13 [email protected] SQL>

如果该Latch竞争激烈,通常有如下方法可以采用:

(1)  适当增大BufferCache,这样可以减少读数据到Buffer Cache的机会,减少扫描Lru List的竞争。

(2)  可以适当增加LRULatch的数量,修改_db_block_lru_latches参数可以实现,但是该参数通常来说是足够的,除非在Oracle Support的建议下或确知该参数将带来的影响,否则不推荐修改。

(3)  通过多缓冲池技术,可以减少不希望的数据老化和全表扫描等操作对于Default池的冲击,从而可以减少竞争。

(4)  优化SQL,减少数据读取,从而减少对于LRU List的扫描。

2.1.3 Cache Buffer Chain闩锁竞争与解决

在LRU和Dirty List这两个内存结构之外,Buffer Cache的管理还存在另外两个重要的数据结构:Hash Bucket和Cache Buffer Chain。

1.Hash Bucket和Cache Buffer Chain

我们可以想象,如果所有的Buffer Cache中的所有Buffer都通过同一个结构管理,当需要确定某个Block在Buffer中是否存在时,将需要遍历整个结构,性能会相当低下。

为了提高效率,Oracle引入了Bucket的数据结构,Oracle把管理的所有的Buffer通过一个内部的Hash算法运算后存放到不同Hash Bucket中,这样通过Hash Bucket进行分割之后,众多的Buffer被分布到一定数量的Bucket之中,当用户需要在Buffer中定位数据是否存在时,只需要通过同样的算法获得Hash值,然后到相应的Bucket中查找少量的Buffer即可确定。

每个Buffer的存放的Bucket由Buffer的数据块地址(DBA,Data Block Address)运算决定。在Bucket内部,通过Cache Buffer Chain(它是一个双向链表)将所有的Buffer通过BufferHeader信息联系起来。

Buffer Header存放的是对应数据块的概要信息,包括数据块的文件号、块地址、状态等。在判断数据块在Buffer中是否存在,通过检查Buffer header即可确定

让我们通过一个现实的场景来回顾一下这个过程。

如果大家去过老一点的图书馆,查找过手工索引,你可能记得这样的场景:树立在你面前的是一排柜子(那是相当的壮观),柜子又被分为很多小的抽屉,抽屉上按照不同的分类方法标注了相关信息,比如按开头字母顺序,如果我们要查询Oracle相关书籍,就需要找到标记有“O”的 抽 屉 。打 开 抽 屉 ,我们 会 看 到 一 系 列 的 卡 片 ,这 些 卡 片 通 常 被 一 根铁闩串起来(通常就是一个铁丝),每根卡片上会记录相关书籍的信息,可能包括书籍名称、作者、ISBN号、出版日期等,当然这些卡片上还存储了一个重要的信息,就是书籍存放的书架位置信息,有了这个信息,通过翻阅这些卡片,就可以快速地找到想要的书籍,并且
在 需 要 时 能 够 快 速 从 图 书

馆浩如烟海的图书中找到我们需要的一本。

在这里,图书馆就是我们的Buffer Cache,这个Cache可能因为“图书数量”的增加而不断扩大;每个抽屉都是一个Bucket,这个Bucket中存放了根据一定的分类方式(也就是通过Hash运算)归入的图书信息,也就是Buffer Header;抽屉中的每张卡片就是一个Buffer Header,这些Buffer  Header上记录了关于数据块的重要信息,如DBA等;这些卡片在Bucket中,通过一个铁闩串接起来,这就是Cache Buffer Chain

由于每个抽屉只有一根铁闩,如果很多同学都想翻阅这个链上的卡片,那么就产生了Cache  Buffer  Chain的竞争,先来到那个同学持有了Latch就能不停的翻阅,其他同学只好不停的来检查,当然如果检查次数多了(超过了_spin_count), 也 可 以 去 休 息 室 小 憩 一 会 , 再来和其他同学争夺。

从Oracle 9i开始,对于Cache Buffer Chain的只读访问,其Latch可以被共享。也就是说,如果大家都只是翻一翻卡片,那么大家可以一起读,但是如果有人要借走这本书,那么就只能独享这个Latch了。这就是Buffer Cache与Latch竞争。

由于Buffer根据Buffer  Header进行散列,最终决定存入那一个Hash  Bucket,那么Hash Bucket的数量在一定程度上决定了每个Bucket中Buffer数量的多少,也就间接影响了搜索的性能。所以在不同版本中,Oracle一直在修改算法,优化Hash Bucket的数量。我们可以想象, Bucket的数量多一些,那么在同一时间就可以有更多的同学可以拿到不同的抽屉,进行数据访问;但是更多的抽屉,显然需要更多的存放空间,更多的管理成本,所以优化在什么时候都不是简单的一元方程。

Hash Bucket的设置受一个隐含参数_ DB_BLOCK_HASH_BUCKETS的影响。在Oracle 7和Oracle 8中,该参数缺省值为DB_BLOCK_BUFFERS/4的下一个素数。

通过以上的讨论可以知道,对应每个Bucket,只存在一个Chain,当用户试图搜索Cache Buffer Chain时,必须首先获得Cache Buffer Chain Latch。那么Cache BufferChain Latch的设置就同样值得研究了

我们看到,从Oracle  8i 开始,_db_block_hash_buckets 的数量较以前增加了8 倍,而_db_block_hash_latchs的数量增加有限,这意味着,每个Latch需要管理多个Bucket,但是由于Bucket数量的多倍增加,每个Bucket上的Block数量得以减少,从而使少量Latch管理更多Bucket成为可能。

总结一下上图中所描述的内容。

(1)  从Oracle8i开始,Bucket的数量比以前大大增加;通过增加的Bucket的“稀 释 ”使得每个Bucket上的Buffer数量大大减少。

(2)  在Oracle8i之前,_db_block_hash_latches的数量和Bucket的数量是一致的,每个Latch管理一个Bucket;从Oracle 8i开始每个Latch需要管理多个Bucket,由于每个Bucket上的Buffer数量大大降低,所以Latch的性能反而得到了提高。

(3)  每个Bucket存在一条Cache Buffer Chain。

(4)  Buffer Header上存在指向具体Buffer的指针。

2.X$BH与Buffer Header

Buffer  Header数据,可以从数据库的字典表中查询得到,这张字典表是:X$BH。X$BH中的BH就是指Buffer Headers,每个Buffer在x$bh中都存在一条记录

Buffer  Header中存储每个Buffer容纳的数据块的文件号、块地址、状态等重要信息,可以将Buffer Header看作是Buffer的“名片”,通过这张名片,Buffer的重要信息得以展现;

下图包含了Buffer Header上记录的信息,Hash Bucket上的Chian连接起这些BH。

根据Buffer  Header上记录的这些信息,结合dba_extents视图,可以很容易地找到每个Buffer对应的对象信息:

12:01:56 SQL>desc v$bh;

Name                    Null?    Type

----------------------- --------------------------

FILE#                            NUMBER

BLOCK#                           NUMBER

CLASS#                           NUMBER

STATUS                           VARCHAR2(10)

XNC                              NUMBER

FORCED_READS                     NUMBER

FORCED_WRITES                    NUMBER

LOCK_ELEMENT_ADDR                RAW(8)

LOCK_ELEMENT_NAME                NUMBER

LOCK_ELEMENT_CLASS               NUMBER

DIRTY                            VARCHAR2(1)

TEMP                             VARCHAR2(1)

PING                             VARCHAR2(1)

STALE                            VARCHAR2(1)

DIRECT                           VARCHAR2(1)

NEW                              CHAR(1)

OBJD                             NUMBER

TS#                              NUMBER

LOBID                            NUMBER

CACHEHINT                        NUMBER

12:07:19 SQL>

X$BH中还有一个重要字段TCH,TCH为Touch的缩写,表征一个Buffer的访问次数, Buffer被访问的次数越多,说明该Buffer越“抢手”,也就可能存在热点块竞争的问题。

以下通过几个简单的查询。以下查询用于获得当前数据库最繁忙的Buffer:

SELECT *

FROM(SELECT addr, ts#, file#, dbarfil, dbablk, tch

FROM x$bh

ORDER BY tch DESC)

WHERE ROWNUM < 11;

再结合dba_extents中的信息,可以查询得到这些热点Buffer都来自哪些对象:

查询热点块对象:

[email protected] SQL>SELECT e.owner, e.segment_name,e.segment_type

FROMdba_extents e,

(SELECT *

FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch

FROM x$bh

ORDER BY tch DESC)

WHERE ROWNUM < 11) b

WHEREe.relative_fno = b.dbarfil

ANDe.block_id <= b.dbablk

AND e.block_id + e.blocks > b.dbablk;

OWNER                 SEGMENT_NAME                   SEGMENT_TYPE

--------------------------------------------------- -----------------

SYS                   C_USER#                        CLUSTER

SYS                   I_USER1                        INDEX

SYS                   JOB$                           TABLE

SYS                   JOB$                           TABLE

SYS                   SCHEDULER$_CLASS               TABLE

SYS                   SCHEDULER$_CLASS_PK            INDEX

SYS                   SCHEDULER$_JOB                 TABLE

SYS                   SCHEDULER$_JOB                 TABLE

SYS                   SCHEDULER$_JOB                 TABLE

SYS                  SCHEDULER$_LIGHTWEIGHT_JOB    TABLE

10 rows selected.

12:16:13 [email protected] SQL>

除了查询X$BH之外,其实也可以从Buffer Cache的转储信息中,看到Buffer Header的具体内容,以下信息来自Level 1级的Buffer Dump:

*** ACTION NAME:() 2014-07-23 13:58:11.585

Dump of buffer cache at level 1 for tsn=2147483647rdba=0

BH (0x69fd83a8) file#: 2 rdba: 0x00804d72(2/19826) class: 4 ba: 0x69c08000

set: 3pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,25

dbwrid: 0obj: 58298 objn: -1 tsn: 1 afn: 2 hint: f

hash:[0x77f8d3b0,0x77f8d3b0] lru: [0x69fd85c0,0x69fd8360]

lru-flags:debug_dump on_auxiliary_list

ckptq:[NULL] fileq: [NULL] objq: [NULL] objaq: [NULL]

st: CR md:NULL tch: 1

cr: [scn:0x0.197ed5],[xid: 0x0.0.0],[uba: 0x0.0.0],[cls: 0x0.198787],[sfl: 0x0],[lc:0x0.0]

flags:

BH (0x6a3f1e88) file#: 1 rdba: 0x004084d5(1/34005) class: 1 ba: 0x6a2bc000

set: 3pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,25

dbwrid: 0obj: 37 objn: 37 tsn: 0 afn: 1 hint: f

hash:[0x77f8d3c0,0x77f8d3c0] lru: [0x6a3f20a0,0x6a3f1e40]

lru-flags:debug_dump hot_buffer

ckptq:[NULL] fileq: [NULL] objq: [0x6a3f20c8,0x6a3f1e68] objaq:[0x6a3f20d8,0x6a3f1e78]

st:XCURRENT md: NULL fpin: ‘kdiwh100: kdircys‘ tch: 3

flags:only_sequential_access

LRBA:[0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]

BH (0x6d7de638) file#: 2 rdba: 0x008109aa(2/68010) class: 4 ba: 0x6d4ae000

set: 3pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,25

dbwrid: 0obj: 71803 objn: -1 tsn: 1 afn: 2 hint: f

hash:[0x77f8d3f0,0x77f8d3f0] lru: [0x6d7de5f0,0x6d7de850]

lru-flags:debug_dump

ckptq:[NULL] fileq: [NULL] objq: [NULL] objaq: [NULL]

st: CR md:NULL tch: 1

cr: [scn:0x0.197ed5],[xid: 0x0.0.0],[uba: 0x0.0.0],[cls: 0x0.199105],[sfl: 0x0],[lc:0x0.0]

flags:

在Oracle 10g之前,数据库的等待事件中,所有Latch等待被归入Latch Free等待事件中,在Statspack的report中,如果在Top 5等待事件中看到Latch Free这一等待处于较高的位置,就需要DBA介入进行研究和解决。

3.热点块竞争与解决

接下来将通过一个环境的实际情况对热点块的问题进行分析和探讨。

Top 5等待事件都值得关注。注意到这个数据库中Latch Free是最严重的竞争。

由于Latch  Free是一个汇总等待事件,我们需要从v$latch视图获得具体的Latch竞争主要是由哪些Latch引起的。

如果cache buffers chains正是主要的Latch竞争。实际上,如果数据库在繁忙的时段,基本上处于停顿状态,大量进程等待latchfree竞争,这些获得Session的等待事件可以很容易地从v$session_wait视图中查询得到:

16:10:22 [email protected] SQL>select SID,EVENT,SEQ#from v$session_wait;

SIDEVENT                                                             SEQ#

---------------------------------------------------------------------- ----------

1SQL*Net message to client                                          7166

2pmon timer                                                        5913

3rdbms ipc message                                                18352

4 VKTM Logical Idle Wait                                               1

5rdbms ipc message                                                 5912

6DIAG idle wait                                                   17714

7rdbms ipc message                                                 5923

8DIAG idle wait                                                   17704

9rdbms ipc message                                                 5913

10rdbms ipc message                                                 9958

11rdbms ipc message                                                11632

12rdbms ipc message                                                22003

13smon timer                                                          410

14rdbms ipc message                                                   28

15rdbms ipc message                                                 7335

16rdbms ipc message                                                 17710

18rdbms ipc message                                                  549

20rdbms ipc message                                                  565

21rdbms ipc message                                                  3404

22rdbms ipc message                                                  546

26Streams AQ: qmn coordinator idle wait                              1270

28Streams AQ: waiting for time management or cleanup tasks              3

30Streams AQ: qmn slave idle wait                                     712

32rdbms ipc message                                                 5828

33SQL*Net message from client                                         32

35rdbms ipc message                                                 4902

36SQL*Net message from client                                        7734

37Space Manager: slave idle wait                                      125

38Space Manager: slave idle wait                                      246

40jobq slave wait                                                     54

41jobq slave wait                                                     54

45SQL*Net message from client                                         429

47Space Manager: slave idle wait                                       65

33 rows selected.

16:10:30 [email protected] SQL>

如果需要具体确定热点对象,可以从v$latch_children中查询具体的子Latch信息:

SELECT *

FROM(SELECT addr,

child#,

gets,

misses,

sleeps,

immediate_gets   igets,

immediate_misses imiss,

spin_gets        sgets

FROM v$latch_children

WHERE NAME = ‘cache buffers chains‘

ORDER BY sleeps DESC)

WHEREROWNUM < 11;

_value,address, piece;

X$BH中还存在另外一个关键字段HLADDR,即Hash Chain Latch Address,这个字段可以和v$latch_child.addr进行关联,这样就可以把具体的Latch竞争和数据块关联起来,再结合dba_extents视图,就可以找到具体的热点竞争对象。

到具体热点竞争对象之后,可以进一步地结合v$sqlarea或者v$sqltext视图,找到频繁操作这些对象的SQL,对其进行优化,就可以缓解或解决热点块竞争的问题。通过以下查询可以实现以上的思想,获取当前持有最热点数据块的Latch及Buffer信息:

SELECT b.addr,

a.ts#,

a.dbarfil,

a.dbablk,

a.tch,

b.gets,

b.misses,

b.sleeps

FROM(SELECT *

FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch, hladdr

FROM x$bh

ORDER BY tch DESC)

WHERE ROWNUM < 11) a,

(SELECT addr, gets, misses, sleeps

FROM v$latch_children

WHERE NAME = ‘cache buffers chains‘) b

WHEREa.hladdr = b.addr;

ADDR                    TS#    DBARFIL    DBABLK        TCH       GETS    MISSES     SLEEPS

---------------- ---------- ---------- -------------------- ---------- ---------- ----------

0000000077BF8A38          2          3        144        249       5274          0          0

0000000077FEF1F8          2          3        208        248       6702          0          0

0000000077F9FCF0          2          3        272        247       5198          0          0

0000000077FF0900          0          1       2017        246     15293          0          0

0000000077BD1018          2          3       176        245       5187          0          0

0000000077BE5128          2          3        160        245       5034          0          0

0000000077FC77D8          2          3        240        244       6431          5          3

0000000077BEEDB0          0          1       2016        244       9761          0          0

0000000077FDB0E8          2          3        224        244       6161          0          0

0000000077BBD708          2          3        192        242       5805          0         0

利用前面提到的SQL,可以找到这些热点Buffer的对象信息:

SELECT distinct e.owner, e.segment_name,e.segment_type

FROMdba_extents e,

(SELECT *

FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch

FROM x$bh

ORDER BY tch DESC)

WHERE ROWNUM < 11) b

WHEREe.relative_fno = b.dbarfil

ANDe.block_id <= b.dbablk

ANDe.block_id + e.blocks > b.dbablk;

16:18:29 [email protected] SQL>/

OWNER        SEGMENT_NAME                  SEGMENT_TYPE

------------- -----------------------------------------------

SYS          _SYSSMU1_1240252155$          TYPE2 UNDO

SYS          _SYSSMU2_111974964$           TYPE2 UNDO

SYS          _SYSSMU5_4011504098$          TYPE2 UNDO

SYS          _SYSSMU9_3945653786$          TYPE2 UNDO

SYS          _SYSSMU6_3654194381$          TYPE2 UNDO

SYS          _SYSSMU4_1126976075$          TYPE2 UNDO

SYS          _SYSSMU8_3612859353$          TYPE2 UNDO

SYS          _SYSSMU10_3271578125$         TYPE2 UNDO

SYS          _SYSSMU3_4004931649$          TYPE2 UNDO

SYS          _SYSSMU7_4222772309$          TYPE2 UNDO

10 rows selected.

16:25:56 [email protected] SQL>

结合v$sqltext或v$sqlarea,可以找到操作这些对象的相关SQL,让我们继续查询:

break on hash_value skip 1

SELECT /*+ rule */

hash_value,sql_text

FROMv$sqltext

WHERE(hash_value, address) IN

(SELECT a.hash_value, a.address

FROM v$sqltext a,

(SELECT DISTINCT a.owner, a.segment_name, a.segment_type

FROM dba_extents a,

(SELECT dbarfil, dbablk

FROM (SELECT dbarfil,dbablk

FROM x$bh

ORDER BY tchDESC)

WHERE ROWNUM < 11)b

WHERE a.relative_fno = b.dbarfil

AND a.block_id <= b.dbablk

AND a.block_id + a.blocks > b.dbablk) b

WHERE upper(a.sql_text) LIKE ‘%‘ || b.segment_name || ‘%‘

AND b.segment_type = ‘TABLE‘)

ORDER BYhash_value, address, piece;

找到这些SQL之后,剩下的问题就简单了,可以通过优化SQL减少数据的访问,避免或优化某些容易引起争用的操作(如connect by等操作)来减少热点块竞争。

shared pool 和buffer pool 详解(之二, Cache Buffers LRU Chain、Cache Buffers LRU Chain闩锁竞争与解决)

时间: 2024-10-18 05:28:26

shared pool 和buffer pool 详解(之二, Cache Buffers LRU Chain、Cache Buffers LRU Chain闩锁竞争与解决)的相关文章

Protocol Buffer技术详解(语言规范)

Protocol Buffer技术详解(语言规范) 该系列Blog的内容主体主要源自于Protocol Buffer的官方文档,而代码示例则抽取于当前正在开发的一个公司内部项目的Demo.这样做的目的主要在于不仅可以保持Google文档的良好风格和系统性,同时再结合一些比较实用和通用的用例,这样就更加便于公司内部的培训,以及和广大网友的技术交流.需要说明的是,Blog的内容并非line by line的翻译,其中包含一些经验性总结,与此同时,对于一些不是非常常用的功能并未予以说明,有兴趣的开发者

Protocol Buffer技术详解(Java实例)

Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发团队中目前主要使用的开发语言就是C++.Java和Python,其中Python主要用于编写各种工具程序.然而为了保证该篇Blog的完整性和独立性,我仍然会将上一篇Blog中已经出现的内容再一次赘述,同时对于Java中特有的部分也会着重介绍.          一.生成目标语言代码.      下面

MySQL之SQL优化详解(二)

目录 MySQL之SQL优化详解(二) 1. SQL的执行顺序 1.1 手写顺序 1.2 机读顺序 2. 七种join 3. 索引 3.1 索引初探 3.2 索引分类 3.3 建与不建 4. 性能分析Explain MySQL之SQL优化详解(二) 1. SQL的执行顺序 1.1 手写顺序 1.2 机读顺序 2. 七种join 3. 索引 3.1 索引初探 是什么: 排好序的快速查找数据结构 两个主要的索引结构: B+tree 索引和哈希索引. 如何建: 1. ALTER TABLE table

[顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功)

原文:[顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功) [顶]ORACLE PL/SQL编程详解之二: PL/SQL块结构和组成元素(为山九仞,岂一日之功) 继上四篇:ORACLE PL/SQL编程之八:把触发器说透                ORACLE PL/SQL编程之六:把过程与函数说透(穷追猛打,把根儿都拔起!)                [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) [推荐]

logback logback.xml常用配置详解(二)&lt;appender&gt;

logback 常用配置详解(二) <appender> <appender>: <appender>是<configuration>的子节点,是负责写日志的组件. <appender>有两个必要属性name和class.name指定appender名称,class指定appender的全限定名. 1.ConsoleAppender: 把日志添加到控制台,有以下子节点: <encoder>:对日志进行格式化.(具体参数稍后讲解 ) &

logback 常用配置详解(二) &lt;appender&gt;

logback 常用配置详解(二) <appender> <appender>: <appender>是<configuration>的子节点,是负责写日志的组件. <appender>有两个必要属性name和class.name指定appender名称,class指定appender的全限定名. 1.ConsoleAppender: 把日志添加到控制台,有以下子节点: <encoder>:对日志进行格式化.(具体参数稍后讲解 ) &

【Hibernate步步为营】--双向关联一对一映射详解(二)

很不好意思,有两天时间没有更新博客文章了,不写文章的日子还真是感觉很空洞啊,养成了写文章的恶习想改也改不掉啊.说点题外话,前两天收到一位朋友的私信,邀请笔者写一篇有关OWS的文章,用来研究图标工具的一种技术,很荣幸收到这位朋友的邀请,但是因为这几天开发的项目着急上线所以暂时没有时间去研究,只能等这周末了,利用周末的时间来研究然后更新类似的技术文章. 回到文章的正题,上篇文章讨论了双向主键关联,它其实是一对一主键关联的一种特殊情况,想要实现双向的关联就必须在映射文件的两端同时配置<one-to-o

iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)

这里接着前文<iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)>,主要是干货环节,列举了如何基于 PhotoKit 与 AlAssetLibrary 封装出通用的方法. 三. 常用方法的封装 虽然 PhotoKit 的功能强大很多,但基于兼容 iOS 8.0 以下版本的考虑,暂时可能仍无法抛弃 ALAssetLibrary,这时候一个比较好的方案是基于 ALAssetLibrary 和 PhotoKit 封装出一系列模拟系统 Asset 类的自定义类,然后在其中封装好兼容 A

iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)

一. 概况 本文接着 iOS 开发之照片框架详解,侧重介绍在前文中简单介绍过的 PhotoKit 及其与 ALAssetLibrary 的差异,以及如何基于 PhotoKit 与 AlAssetLibrary 封装出通用的方法. 这里引用一下前文中对 PhotoKit 基本构成的介绍: PHAsset: 代表照片库中的一个资源,跟 ALAsset 类似,通过 PHAsset 可以获取和保存资源 PHFetchOptions: 获取资源时的参数,可以传 nil,即使用系统默认值 PHAssetCo