lru優化
1. 請教高手:oracle臨時表創建優化
http://www.upschool.com.cn/e/1836/2007/16/10264717_1.shtml
關於Oracle臨時表數據cache的研究
Global Temporary Table是Oracle 8i中出現的特性,可以用於存儲事務或會話中的臨時數據。它的出現大大方便了開發人員。但是在使用上面,由於它本身的特性,一直存在一些問題。
簡單說一下臨時表,它的數據只對調用它的會話可見,一個會話是無法訪問其他會話中的臨時表的數據。可以在創建時指定它是事務級的還是會話級的。它被創建在用戶的默認臨時表空間上,在創建時不會分配段,而是在會話中第一次insert的時候從零時表空間分配數據段。DML時,不會產生redo log,但是會產生undo log。並且無法生成臨時表或者臨時表上索引的統計信息(勢必會影響CBO下的查詢計劃)。
下面研究一下臨時表的數據是如何存儲,又是如何獲得的,如何cache在內存中的:
我們知道,對於普通表(regular tables),第一對表進行掃描時,會將掃描到的數據放到buffer cache中,以便以後如果有其他事務需要掃描相同數據時,直接從內存中讀,而不產生disk read。並且,同時在LUR和MUR鏈中記錄一個點,以決定這些數據什麼時候被page out。
那對於臨時表呢?以前我是這樣認為的,既然臨時表的數據只對會話可見,那它的數據就不應該放在公用的buffer cache中,而放在每個會話的PGA裡面更合適。那這個觀點正確嗎?我查了很多資料,並沒有明確的指出臨時表的數據應該是cache在內存的哪一塊。由於這些都是Oracle internal的東東,沒有任何公布的資料可查,我們下面來做一些試驗來看看Oracle到底怎麼管理臨時表的數據的。
臨時表中的數據到底cache在哪裡?
首先,創建測試用的對象:
創建一張普通表:
CREATE TABLE pga_ttt (
A VARCHAR2(100)
);
給表插入測試數據:
INSERT INTO pga_ttt values(1);
創建臨時表:
CREATE GLOBAL TEMPORARY TABLE PGA_TEST
(
A VARCHAR2(3000),
B VARCHAR2(2000),
C VARCHAR2(2000)
)
ON COMMIT PRESERVE ROWS;
測試過程:
1. 修改db cache size為一個較小的值:
ALTER SYSTEM SET DB_CACHE_SIZE = 50M;
2. 重起資料庫:
STARTUP FORCE
這時的資料庫的內存中應該是比較干凈的。
3. 先看一下一些什麼對象已經cache在buffer cache中了:
SELECT DISTINCT objd FROM v$bh ORDER BY objd;
OBJD
----
2
3
6
7
8
... ...
4294967294
4294967295
這些對象應該都是系統啟動時載入的一些系統對象。
4. 另外啟動兩個會話,分別執行以下語句:
INSERT INTO PGA_TEST VALUES(1, 1, 1);
SELECT * FROM PGA_TEST;
5. 再看下buffer cache中的對象:
SELECT DISTINCT objd FROM v$bh ORDER BY objd;
OBJD
----
2
3
6
7
... ...
6693385
6693513
4294967294
4294967295
這時,可以發現多出兩個對象來了。但不能確定和pga_test有什麼關系,也許又是兩個系統對象。
6. 查一下pga_test的object number
SELECT object_id FROM dba_objects WHERE object_name = 'PGA_TEST';
53513
比較失望L,這個object number和剛才那兩個新cache到buffer cache中的object number並不相同。但是這還並不能說明臨時表的數據一定沒有cache到buffer cache中去。
接下來繼續測試,將buffer cache mp出來!
7. Dump出buffer cache
用level 3將整個buffer cache都mp出來,這將會產生一個比較大的trace文件(折就是為什麼要把buffer cache設小一些的原因)
oradebug setmypid
oradebug mp buffers 3
8. 打開trace文件
在trace文件中,找到一下兩段:
· 第一段:
BH (183EBEEC) file#: 201 rdba: 0x0066220a (1/2499082) class: 1 ba: 18114000
set: 3 blksize: 8192 bsi: 0 set-flg: 2 pwbcnt: 574
dbwrid: 0 obj: 6693385 objn: 53513 tsn: 3 afn: 201
hash: [201d7a88,201d7a88] lru: [183ebff0,183ebe90]
ckptq: [NULL] fileq: [NULL] objq: [1ea81d98,183ec044]
st: XCURRENT md: NULL tch: 2
flags: buffer_dirty temp_data gotten_in_current_mode redo_since_read
LRBA: [0x0.0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]
buffer tsn: 3 rdba: 0x0066220a (1/2499082)
scn: 0x0000.00550a09 seq: 0x00 flg: 0x08 tail: 0x0a090600
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
Hex mp of block: st=0, typ_found=1
Dump of memory from 0x18114000 to 0x18116000
18114000 0000A206 0066220A 00550A09 08000000 [....."f...U.....]
... ...
... ...
... ...
18115830 20202020 20202020 20202020 20202020 [ ]
Repeat 123 times
18115FF0 20202020 20202020 20202020 0A090600 [ ....]
Block header mp: 0x0066220a
Object id on Block? Y
seg/obj: 0x662209 csc: 0x00.00 itc: 2 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0006.01b.00000c9d 0x0080142b.088a.20 ---- 1 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
data_block_mp,data header at 0x1811405c
===============
tsiz: 0x1fa0
hsiz: 0x14
pbl: 0x1811405c
bdba: 0x0066220a
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x824
avsp=0x810
tosp=0x810
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x824
block_row_mp:
tab 0, row 0, @0x824
tl: 6012 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
... ...
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
col 1: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
... ...
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
col 2: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
... ...
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
end_of_block_mp
· 第二段:
BH (183EC04C) file#: 201 rdba: 0x0066228a (1/2499210) class: 1 ba: 18118000
set: 3 blksize: 8192 bsi: 0 set-flg: 2 pwbcnt: 574
dbwrid: 0 obj: 6693513 objn: 53513 tsn: 3 afn: 201
hash: [201e8d88,183e6a5c] lru: [183ec150,183ebff0]
ckptq: [NULL] fileq: [NULL] objq: [1ea81dd0,183ec1a4]
st: XCURRENT md: NULL tch: 2
flags: buffer_dirty temp_data gotten_in_current_mode redo_since_read
LRBA: [0x0.0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]
buffer tsn: 3 rdba: 0x0066228a (1/2499210)
scn: 0x0000.00550a08 seq: 0x00 flg: 0x08 tail: 0x0a080600
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
Hex mp of block: st=0, typ_found=1
Dump of memory from 0x18118000 to 0x1811A000
18118000 0000A206 0066228A 00550A08 08000000 [....."f...U.....]
... ...
... ...
... ...
18119830 20202020 20202020 20202020 20202020 [ ]
Repeat 123 times
18119FF0 20202020 20202020 20202020 0A080600 [ ....]
Block header mp: 0x0066228a
Object id on Block? Y
seg/obj: 0x662289 csc: 0x00.00 itc: 2 flg: O typ: 1 - DATA
fsl: 0 fnx: 0x0 ver: 0x01
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.014.00000c58 0x008044a3.060a.08 ---- 1 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
data_block_mp,data header at 0x1811805c
===============
tsiz: 0x1fa0
hsiz: 0x14
pbl: 0x1811805c
bdba: 0x0066228a
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x824
avsp=0x810
tosp=0x810
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x824
block_row_mp:
tab 0, row 0, @0x824
tl: 6012 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
... ...
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
col 1: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
... ...
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
col 2: [2000]
31 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
... ...
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
end_of_block_mp
這兩段都是cache在buffer cache中的數據段。注意他們的基礎系統中的以下內容:
obj: 6693385 objn: 53513
obj: 6693513 objn: 53513
object number是53513,這正是pga_test的object number,對比一下後面數據塊的內容:
col 0: [2000]
31 20 20 20 ...(即』1 』)
col 1: [2000]
31 20 20 20 ...(即』1 』)
col 2: [2000]
31 20 20 20 ...(即』1 』)
正是從pga_test中的掃描到的數據。
再看objn前面的數字,是不是很眼熟?對了,這就是從v$bh中查到的在掃描臨時表後buffer cache中多出兩個對象的object number!這因該是為了保持各自會話的數據的獨立,Oracle創建了一個系統臨時對象(叢v$bh中看,這個臨時對象時屬於sys用戶的,不屬於當前用戶的),保持了與臨時表相同的結構,然後在buffer中開辟了一片區域,以系統臨時對象的名義存放各自數據,使之相互不影響。同時我們還可以留意到它們的LRU值是不同的。
另外看下他們的flags:
flags: buffer_dirty temp_data gotten_in_current_mode redo_since_read
buffer_dirty:因為臨時表需要先插入數據,所以被置了dirty標志;
temp_data:臨時表在第一次INSERT時,才在臨時表空間上分配臨時段,所以是屬於臨時數據;
gotten_in_current_mode:顯然,當前會話這在獲取各自臨時表中的數據,因此是當前被獲取模式下;
redo_since_read:這個不是十分明白。因為臨時表的DML是不會產生redo log的,會產生undo log,同時會產生針對這些undo的redo log(而不是臨時表的)。
現在,我們基本上可以得出這樣的推論:
推論1:臨時表的數據是cache在buffer cache中的。並且,為了保持各自會話的數據獨立,在buffer cache中為各個會話開辟一片區域來cache它們各自的數據。
以上推論可以和普通表來做一個對比。
在兩個會話中分別查詢普通表:
SELECT * FROM pga_ttt;
Dump 出cache buffer:
oradebug setmypid
oradebug mp buffers 3
查詢普通表的object number
SELECT object_id FROM dba_objects WHERE object_name = 'PGA_TTT';
53514
看看trace文件中的內容:雖然我們在兩個會話中都掃描了這張表,但是buffer cache只有一段它的數據段:
BH (18BE658C) file#: 5 rdba: 0x014097be (5/38846) class: 1 ba: 18810000
set: 3 blksize: 8192 bsi: 0 set-flg: 2 pwbcnt: 64
dbwrid: 0 obj: 53514 objn: 53514 tsn: 5 afn: 5
hash: [202153d8,202153d8] lru: [18be6690,18be6530]
ckptq: [NULL] fileq: [NULL] objq: [18be6584,18be66e4]
st: XCURRENT md: NULL tch: 2
flags: only_sequential_access
LRBA: [0x0.0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]
buffer tsn: 5 rdba: 0x014097be (5/38846)
scn: 0x0000.005513e7 seq: 0x01 flg: 0x06 tail: 0x13e70601
frmt: 0x02 chkval: 0xa15f type: 0x06=trans data
Hex mp of block: st=0, typ_found=1
Dump of memory from 0x18810000 to 0x18812000
18810000 0000A206 014097BE 005513E7 06010000 [[email protected].....]
... ...
... ...
... ...
18810080 00000000 00000000 00000000 00000000 [................]
Repeat 502 times
18811FF0 00000000 2C000000 31010101 13E70601 [.......,...1....]
Block header mp: 0x014097be
Object id on Block? Y
seg/obj: 0xd10a csc: 0x00.5513bd itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x14097b9 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0006.01b.00000c9d 0x0080142b.088a.21 --U- 1 fsc 0x0000.005513e7
0x02 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
data_block_mp,data header at 0x18810064
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x18810064
bdba: 0x014097be
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x1f93
avsp=0x1f7b
tosp=0x1f7b
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x1f93
block_row_mp:
tab 0, row 0, @0x1f93
tl: 5 fb: --H-FL-- lb: 0x1 cc: 1
col 0: [ 1] 31
end_of_block_mp
另外,檢查它的基礎信息,這時obj和objn是相同的,都是ojb$表中對應的object#。
會話之間的臨時表數據是否可以復用?
以上的問題應該可以告一個段落了。下面我想到另外一個問題:如果各個會話的寫入臨時表中數據都一樣,那麼會話之間的數據能不能復用呢(即從其他會話的buffer cache中得到一份數據拷貝,而不需要讀寫臨時表空間)?
其實,這個問題應該可以通過一個比較簡單的測試來推斷:
1. 在兩個不同的會話中分別向臨時表插入數據:
INSERT INTO PGA_TEST VALUES(1, 1, 1);
2. 然後刷新buffer cache,將buffer中的數據都寫入到磁碟中去:
ALTER SYSTEM FLUSH BUFFER_CACHE;
以上語句之適用於10g,如果是9i,可以用下面的語句:
ALTER SYSTEM SET EVENTS 『IMMEDIATE TRACE NAME FLUSH_CACHE』;
3. 分別在兩個會話中查詢臨時表:
· SESSION 1:
SQL> SET AUTOT TRACE
SQL> SELECT * FROM PGA_TEST;
Execution Plan
----------------------------------------------------------
Plan hash value: 30787903
--------------------------------------
| Id | Operation | Name |
--------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| PGA_TEST |
--------------------------------------
Note
-----
- rule based optimizer used (consider using cbo)
Statistics
----------------------------------------------------------
165 recursive calls
0 db block gets
20 consistent gets
7 physical reads
0 redo size
6533 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
1 rows processed
可以看到第一個會話產生了physical read,顯然是從臨時數據段上讀取了數據。
再在這個會話上查詢一次:
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 30787903
--------------------------------------
| Id | Operation | Name |
--------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| PGA_TEST |
--------------------------------------
Note
-----
- rule based optimizer used (consider using cbo)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
6533 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
可以看到,以及沒有physical read了,這時不是動臨時段上讀取數據了,而是直接讀取buffer cache了。
· SESSION 2:
SQL> SET AUTOT TRACE
SQL> SELECT * FROM pga_test;
Execution Plan
----------------------------------------------------------
Plan hash value: 30787903
--------------------------------------
| Id | Operation | Name |
--------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| PGA_TEST |
--------------------------------------
Note
-----
- rule based optimizer used (consider using cbo)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
3 consistent gets
2 physical reads
0 redo size
6533 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
請注意,盡管SESSION 1已經是從buffer cache中讀取數據了,但是SESSION 2還是有physical read,說明它還是從自己所分配到的臨時段上讀取的數據,而不是其他會話的!
再作一次查詢:
SQL> /
Execution Plan
----------------------------------------------------------
Plan hash value: 30787903
--------------------------------------
| Id | Operation | Name |
--------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| PGA_TEST |
--------------------------------------
Note
-----
- rule based optimizer used (consider using cbo)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
6533 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
這時就是從它自己的buffer cache中讀取數據了。
推論2:每個會話在第一次INSERT是分配臨時段,他們的數據讀寫是完全獨立的,不會有任何聯系,即使他們的數據內容完全一樣。
我們再與普通表進行對比:
在第一個SESSION中查詢普通表:
SQL> SELECT * FROM pga_ttt;
Execution Plan
----------------------------------------------------------
Plan hash value: 3071005808
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 52 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| PGA_TTT | 1 | 52 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------
Statistics
----------------------------------------------------------
163 recursive calls
0 db block gets
24 consistent gets
15 physical reads
0 redo size
407 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
1 rows processed
這時產生了physical read,說明數據是從磁碟讀取的。
再在第一個SESSION查詢這張表:
SQL> SELECT * FROM pga_test;
Execution Plan
----------------------------------------------------------
Plan hash value: 30787903
--------------------------------------
| Id | Operation | Name |
--------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS FULL| PGA_TEST |
--------------------------------------
Note
-----
- rule based optimizer used (consider using cbo)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
6533 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
希望對你有幫助。
2. MySQL怎麼禁止使用緩存以及復雜多表查詢的索引優化問題
1、query_cache_size=0 已經禁用了查詢緩存,但表數據可能緩存了,flush tables試試,不過操作系統還有一個硬碟緩存,想跟第一次查詢之前的狀態一致恐怕只能每次重啟
2、group by與order by不會用索引的,索引最大的用處就是減小磁碟IO,也就是where時盡量少磁碟IO並讀出所有滿足條件的記錄
3. linux luks 影響速度嗎
一、CPU
首先從CPU說起。
你仔細檢查的話,有些伺服器上會有的一個有趣的現象:你cat /proc/cpuinfo時,會發現CPU的頻率竟然跟它標稱的頻率不一樣:
#cat /proc/cpuinfo
processor : 5
model name : Intel(R) Xeon(R) CPU E5-2620 0 @2.00GHz
cpu MHz : 1200.000
這個是Intel E5-2620的CPU,他是2.00G * 24的CPU,但是,我們發現第5顆CPU的頻率為1.2G。
這是什麼原因呢?
這些其實都源於CPU最新的技術:節能模式。操作系統和CPU硬體配合,系統不繁忙的時候,為了節約電能和降低溫度,它會將CPU降頻。這對環保人士和抵制地球變暖來說是一個福音,但是對MySQL來說,可能是一個災難。
為了保證MySQL能夠充分利用CPU的資源,建議設置CPU為最大性能模式。這個設置可以在BIOS和操作系統中設置,當然,在BIOS中設置該選項更好,更徹底。由於各種BIOS類型的區別,設置為CPU為最大性能模式千差萬別,我們這里就不具體展示怎麼設置了。
然後我們看看內存方面,我們有哪些可以優化的。
i) 我們先看看numa
非一致存儲訪問結構 (NUMA : Non-Uniform Memory Access) 也是最新的內存管理技術。它和對稱多處理器結構 (SMP : Symmetric Multi-Processor) 是對應的。簡單的隊別如下:
如圖所示,詳細的NUMA信息我們這里不介紹了。但是我們可以直觀的看到:SMP訪問內存的都是代價都是一樣的;但是在NUMA架構下,本地內存的訪問和非 本地內存的訪問代價是不一樣的。對應的根據這個特性,操作系統上,我們可以設置進程的內存分配方式。目前支持的方式包括:
--interleave=nodes
--membind=nodes
--cpunodebind=nodes
--physcpubind=cpus
--localalloc
--preferred=node
簡而言之,就是說,你可以指定內存在本地分配,在某幾個CPU節點分配或者輪詢分配。除非 是設置為--interleave=nodes輪詢分配方式,即內存可以在任意NUMA節點上分配這種方式以外。其他的方式就算其他NUMA節點上還有內 存剩餘,Linux也不會把剩餘的內存分配給這個進程,而是採用SWAP的方式來獲得內存。有經驗的系統管理員或者DBA都知道SWAP導致的資料庫性能 下降有多麼坑爹。
所以最簡單的方法,還是關閉掉這個特性。
關閉特性的方法,分別有:可以從BIOS,操作系統,啟動進程時臨時關閉這個特性。
a) 由於各種BIOS類型的區別,如何關閉NUMA千差萬別,我們這里就不具體展示怎麼設置了。
b) 在操作系統中關閉,可以直接在/etc/grub.conf的kernel行最後添加numa=off,如下所示:
kernel /vmlinuz-2.6.32-220.el6.x86_64 ro root=/dev/mapper/VolGroup-root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=VolGroup/root rd_NO_MD quiet SYSFONT=latarcyrheb-sun16 rhgb crashkernel=auto rd_LVM_LV=VolGroup/swap rhgb crashkernel=auto quiet KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM numa=off
另外可以設置 vm.zone_reclaim_mode=0盡量回收內存。
c) 啟動MySQL的時候,關閉NUMA特性:
numactl --interleave=all mysqld
當然,最好的方式是在BIOS中關閉。
ii) 我們再看看vm.swappiness。
vm.swappiness是操作系統控制物理內存交換出去的策略。它允許的值是一個百分比的值,最小為0,最大運行100,該值默認為60。vm.swappiness設置為0表示盡量少swap,100表示盡量將inactive的內存頁交換出去。
具體的說:當內存基本用滿的時候,系統會根據這個參數來判斷是把內存中很少用到的inactive 內存交換出去,還是釋放數據的cache。cache中緩存著從磁碟讀出來的數據,根據程序的局部性原理,這些數據有可能在接下來又要被讀 取;inactive 內存顧名思義,就是那些被應用程序映射著,但是 長時間 不用的內存。
我們可以利用vmstat看到inactive的內存的數量:
#vmstat -an 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free inact active si so bi bo in cs us sy id wa st
1 0 0 27522384 326928 1704644 0 0 0 153 11 10 0 0 100 0 0
0 0 0 27523300 326936 1704164 0 0 0 74 784 590 0 0 100 0 0
0 0 0 27523656 326936 1704692 0 0 8 8 439 1686 0 0 100 0 0
0 0 0 27524300 326916 1703412 0 0 4 52 198 262 0 0 100 0 0
通過/proc/meminfo 你可以看到更詳細的信息:
#cat /proc/meminfo | grep -i inact
Inactive: 326972 kB
Inactive(anon): 248 kB
Inactive(file): 326724 kB
這里我們對不活躍inactive內存進一步深入討論。 Linux中,內存可能處於三種狀態:free,active和inactive。眾所周知,Linux Kernel在內部維護了很多LRU列表用來管理內存,比如LRU_INACTIVE_ANON, LRU_ACTIVE_ANON, LRU_INACTIVE_FILE , LRU_ACTIVE_FILE, LRU_UNEVICTABLE。其中LRU_INACTIVE_ANON, LRU_ACTIVE_ANON用來管理匿名頁,LRU_INACTIVE_FILE , LRU_ACTIVE_FILE用來管理page caches頁緩存。系統內核會根據內存頁的訪問情況,不定時的將活躍active內存被移到inactive列表中,這些inactive的內存可以被 交換到swap中去。
一般來說,MySQL,特別是InnoDB管理內存緩存,它佔用的內存比較多,不經常訪問的內存也會不少,這些內存如果被Linux錯誤的交換出去了,將 浪費很多CPU和IO資源。 InnoDB自己管理緩存,cache的文件數據來說佔用了內存,對InnoDB幾乎沒有任何好處。
所以,我們在MySQL的伺服器上最好設置vm.swappiness=0。
我們可以通過在sysctl.conf中添加一行:
echo vm.swappiness = 0 /etc/sysctl.conf
並使用sysctl -p來使得該參數生效。
三、文件系統
最後,我們看一下文件系統的優化
i) 我們建議在文件系統的mount參數上加上noatime,nobarrier兩個選項。
用noatime mount的話,文件系統在程序訪問對應的文件或者文件夾時,不會更新對應的access time。一般來說,Linux會給文件記錄了三個時間,change time, modify time和access time。
我們可以通過stat來查看文件的三個時間:
stat libnids-1.16.tar.gz
File: `libnids-1.16.tar.gz'
Size: 72309 Blocks: 152 IO Block: 4096 regular file
Device: 302h/770d Inode: 4113144 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access : 2008-05-27 15:13:03.000000000 +0800
Modify: 2004-03-10 12:25:09.000000000 +0800
Change: 2008-05-27 14:18:18.000000000 +0800
其中access time指文件最後一次被讀取的時間,modify time指的是文件的文本內容最後發生變化的時間,change time指的是文件的inode最後發生變化(比如位置、用戶屬性、組屬性等)的時間。一般來說,文件都是讀多寫少,而且我們也很少關心某一個文件最近什 么時間被訪問了。
所以,我們建議採用noatime選項,這樣文件系統不記錄access time,避免浪費資源。
現在的很多文件系統會在數據提交時強制底層設備刷新cache,避免數據丟失,稱之為write barriers。但是,其實我們資料庫伺服器底層存儲設備要麼採用RAID卡,RAID卡本身的電池可以掉電保護;要麼採用Flash卡,它也有自我保 護機制,保證數據不會丟失。所以我們可以安全的使用nobarrier掛載文件系統。設置方法如下:
對於ext3, ext4和 reiserfs文件系統可以在mount時指定barrier=0;對於xfs可以指定nobarrier選項。
ii) 文件系統上還有一個提高IO的優化萬能鑰匙,那就是deadline。
在 Flash技術之前,我們都是使用機械磁碟存儲數據的,機械磁碟的尋道時間是影響它速度的最重要因素,直接導致它的每秒可做的IO(IOPS)非常有限, 為了盡量排序和合並多個請求,以達到一次尋道能夠滿足多次IO請求的目的,Linux文件系統設計了多種IO調度策略,已適用各種場景和存儲設備。
Linux的IO調度策略包括:Deadline scheler,Anticipatory scheler,Completely Fair Queuing(CFQ),NOOP。每種調度策略的詳細調度方式我們這里不詳細描述,這里我們主要介紹CFQ和Deadline,CFQ是Linux內 核2.6.18之後的默認調度策略,它聲稱對每一個 IO 請求都是公平的,這種調度策略對大部分應用都是適用的。但是如果資料庫有兩個請求,一個請求3次IO,一個請求10000次IO,由於絕對公平,3次IO 的這個請求都需要跟其他10000個IO請求競爭,可能要等待上千個IO完成才能返回,導致它的響應時間非常慢。並且如果在處理的過程中,又有很多IO請 求陸續發送過來,部分IO請求甚至可能一直無法得到調度被 餓死 。而deadline兼顧到一個請求不會在隊列中等待太久導致餓死,對資料庫這種應用來 說更加適用。
實時設置,我們可以通過
echo deadline /sys/block/sda/queue/scheler
來將sda的調度策略設置為deadline。
我們也可以直接在/etc/grub.conf的kernel行最後添加elevator=deadline來永久生效。
CPU方面:
關閉電源保護模式
內存:
vm.swappiness = 0
關閉numa
文件系統:
用noatime,nobarrier掛載系統
4. activity背景圖佔用內存太大怎麼優化
使用圖片緩存技術
在你應用程序的UI界面載入一張圖片是一件很簡單的事情,但是當你需要在界面上載入一大堆圖片的時候,情況就變得復雜起來。在很多情況下,(比如使用ListView, GridView 或者 ViewPager 這樣的組件),屏幕上顯示的圖片可以通過滑動屏幕等事件不斷地增加,最終導致OOM。
為了保證內存的使用始終維持在一個合理的范圍,通常會把被移除屏幕的圖片進行回收處理。此時垃圾回收器也會認為你不再持有這些圖片的引用,從而對這些圖片進行GC操作。用這種思路來解決問題是非常好的,可是為了能讓程序快速運行,在界面上迅速地載入圖片,你又必須要考慮到某些圖片被回收之後,用戶又將它重新滑入屏幕這種情況。這時重新去載入一遍剛剛載入過的圖片無疑是性能的瓶頸,你需要想辦法去避免這個情況的發生。
這個時候,使用內存緩存技術可以很好的解決這個問題,它可以讓組件快速地重新載入和處理圖片。下面我們就來看一看如何使用內存緩存技術來對圖片進行緩存,從而讓你的應用程序在載入很多圖片的時候可以提高響應速度和流暢性。
內存緩存技術對那些大量佔用應用程序寶貴內存的圖片提供了快速訪問的方法。其中最核心的類是LruCache (此類在android-support-v4的包中提供) 。這個類非常適合用來緩存圖片,它的主要演算法原理是把最近使用的對象用強引用存儲在 LinkedHashMap 中,並且把最近最少使用的對象在緩存值達到預設定值之前從內存中移除。
在過去,我們經常會使用一種非常流行的內存緩存技術的實現,即軟引用或弱引用 (SoftReference or WeakReference)。但是現在已經不再推薦使用這種方式了,因為從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠。另外,Android 3.0 (API Level 11)中,圖片的數據會存儲在本地的內存當中,因而無法用一種可預見的方式將其釋放,這就有潛在的風險造成應用程序的內存溢出並崩潰。
5. 游戲服務端大訪問量大並發的優化解決方案
所有的對象都放在內存,20萬用戶以下無壓力。
如果游戲的用戶很多,例如超過50萬,內存就會不夠,可使用演算法來淘汰一些數據。
流程:收到用戶請求 - 在內存查找用戶對象 - 如果不存在就從資料庫中載入- 放入內存cache-如果cache中的用戶超過20萬 - 用LRU演算法淘汰最古老的用戶數據。
避免同步的IO操作,所有會發生寫資料庫的操作:例如角色獲得了經驗,要更新資料庫;這類和游戲邏輯相關、安全性要求不高的保存操作,一律用非同步操作,由後台的資料庫保存線程定期保存。
流程:如果要保存到資料庫 - 檢查該對象是否已有標志為在保存隊列中 - 如果為假 - 將對象放入保存隊列。 後台保存線程的流程:從隊列中獲取要保存的對象 - 保存 - 置保存標志位為假。
內存cache + 非同步保存模式,並發 每秒1000+ 不會有任何壓力,而且正常情況下每個請求的處理時間不會超過50毫秒。
郵件操作一定產生大量IO操作,而且都是同步操作,可用上面的cache機制處理,或者專門的郵件伺服器。
如果是DNF之類的格鬥類游戲,因為對系統響應的時間要求特別高,50毫秒都嫌慢,這種情況下,瓶頸是在網路上,可用UDP包來解決。搜索UDP,有大量文檔。
如果用戶數是海量的,例如超過500萬,或者對並發的要求更高,例如每秒5000+次請求,這種指標明顯超過了單機的處理能力,這個時候就必須採用分布式結構,使用多台伺服器。可參照EJB二次遠程調用的原理實現多機分布式結構,搜索EJB,也有大量文檔。
沒事不要用c或者c++寫游戲伺服器端,c#和java這類歷史悠久、有大量工具包、程序員一抓一大把的語言最好。性能不是問題,少BUG、穩定、開發周期短才是最重要的。
6. 如何對Android客戶端性能優化
為什麼我們的App需要優化,最顯而易見的時刻:用戶say,什麼狗屎,刷這么久都沒反應,取關卸載算了。
這跟什麼有關,我們先蒼白的反駁下,尼瑪用戶設備老舊網又爛,關我屁事,根本不用優化。可是,老闆拍板了,施壓給CTO,然後CTO又來找你:Y的今天必須給我想辦法優化了,不然不準回家。
好吧,為什麼從UI的表象上看,App又卡又慢而且還錯亂。我們試著來剖析下吧。
題外話:把minSDK改到4.0+,去特么的low用戶,連手機都不願意換,還能指望它能給你帶來多少營收么,直接pass掉吧。4.0前的系統bug不少,不能為了彌補這些bug而降低了整體的高性能。
好了,讓我們先從UI說起:
首先要明白的是UI的繪制流程:measure-layout-draw,measure與layout都需要for loop所有的子控制項,匯集起來才能完成繪制,布局。所以子控制項越多,所消耗的時間越長(inflate,layout_weight,relative,多層嵌套等),減少不必要的子控制項或層級,是相當有必要的。你可以通過merge,viewstub這些標簽來減少層級嵌套。如果你的空間觀念沒那麼好,可以用HierarchyViewer工具來檢查。
對於Listview或者GridView這種多item的組件來說,復用item可以減少inflate次數,通過setTag,getTag的ViewHolder方式實現復用,這里要注意的是,holder中的控制項最好reset後再賦值,避免圖片,文字錯亂。
對於ViewPager第一次顯示時卡頓以及左右滑動卡頓,有以下幾種優化方式:
ViewPager同時緩存page數最好為最小值3,如果過多,那麼第一次顯示時,ViewPager所初始化的pager就會很多,這樣pager累積渲染耗時就會增多,看起來就卡。
每個pager應該只在顯示時才載入網路或資料庫(UserVisibleHint=true),最好不要預載入數據,以免造成浪費
圖片顯示不出來或者載入時間太長,怎麼辦?分兩部分,下載速度,載入速度。
對於下載,要控制好同時下載的最大任務數(平均速度慢),同時給InputStream再包一層緩沖流會更快(如BufferedInputStream)。
對於載入速度,我們要知道一點,雖然下載的圖片可能只有幾百K,但是decode成bitmap所佔用的內存可是成倍的,盡可能的減小圖片size是根本因素,讓服務端提供不同解析度的圖片才是最好的解決方案,內存總有耗盡的時刻,別老想著大解析度會更清晰,實際就只有150*150的空間,非給弄張1000*1000的圖片是不恰當的。另外論載入速度:內存>硬碟>網路,合理的使用內存緩存也是關鍵。假如自己寫不好,沒關系,有那麼多開源的圖片緩存框架,不用自己操心。
再說緩存
有很多種緩存方式,也不用Stay列舉了,我們要說的是搭配使用。
比方說,以前我們一直在用強引用,HashMap,後來我們發現占內存,我們就用軟引用,弱引用來及時回收,再後來因為回收機制不可控,所以又有了lrucache,disklrucache通過演算法來平衡內存與硬碟緩存。隨著android版本的推進與演化,我們也應該擁抱變化。如果你的App里還有軟引用,弱引用的地方,不妨再check下。
比方說網路+資料庫。網路我們一般都是去主動獲取,而非被動接受。那如果說數據是重復的或者未更改的呢?那我們去取一次網路數據有什麼意義呢?我的解決方案是給每個activity或fragment或每個組件設置一個最大請求間隔,比如一個listview,第一次請求數據時,保存一份到資料庫,並記下時間戳,當下次重新初始化時,判斷是否超過最大時間間隔(如5分鍾),如果沒有,只載入資料庫數據,不需要再做網路請求。當然,還有一些隱式的http請求框架會緩存伺服器數據,在一定時間內不再請求網路,或者當伺服器返回304時將之前緩存的數據直接返回。
反正也說到網路了,那我們也來說說
現在有很多現成HTTP框架供我們使用,我們幾乎只用寫配置就可以搞定一個url請求,但是這里有很多需要服務端配合的,比如:json數據格式,WebP代替jpg,支持斷點續傳,多個請求合並成一個,盡量不做重定向,伺服器緩存以及負載均衡等。
對客戶端本身,除了上述的實現,我們還需要合理的緩存,控制最大請求並發量,及時取消已失效的請求,過濾重復請求,timeout時間設置,請求優先順序設置等。
優化可不是一個人的事,實現一個功能簡單,但是想優化重構,那是很不容易的事。需要多方面的預判與聯調。合理的假設與實踐是優化最重要的手段。
說完這些具體的點,我們再來說說一些常識,或者稱之為代碼規范。
你要知道for loop中不要聲明臨時變數,不到萬不得已不要在裡面寫try catch。
明白垃圾回收機制,避免頻繁GC,內存泄漏,OOM(有機會專門說)
合理使用數據類型,比如StringBuilder代替String,(筆試題最常見的是str+="str"中有幾個對象) ,少用枚舉enum,少用父類聲明(List,Map)
如果你有頻繁的new線程,那最好通過線程池去execute它們,減少線程創建開銷。
你要知道單例的好處,並正確的使用它。
多用常量,少用顯式的"action_key",並維護一個常量類,別重復聲明這些常量。
如果可以,至少要弄懂設計模式中的策略模式,組合模式,裝飾模式,工廠模式,觀察者模式,這些能幫助你合理的解耦,即使需求頻繁變更,你也不用害怕牽一發而動全身。需求變更不可怕,可怕的是沒有在寫代碼之前做合理的設計。
當然還有很多很多,Stay所說的也只是一個大的輪廓,還是需要自己不斷的嘗試。會開發寫代碼跟會做產品的區別還是蠻大的,僅僅是態度就能刷死80%的碼農了。當你碰到一些需要優化的地方,耐心的去分析,時間的累積會讓你成為真正的工程師。
另外優化也沒有絕對的完美,每一次優化都是基於當前的環境來做的,要明白溝通是最好的優化,不盲從,不隨便,三思而後行。
Android上如何做性能優化的?大概寫三年代碼就能差不多知道了。
7. 那個redis的lru是僅僅內存中被排除還是包括持久化的也被刪除了
首先花一些較少的篇幅介紹Redis的基本安裝和使用,然後將深入Redis所支持的數據結構主要講解Redis底層設計對這些數據結構的支撐,接下來會介紹Redis的主要配置優化事項,最後介紹Redis的集群搭建方式(基於3.X版本)和實施案例。
8. mysql有基於LRU緩沖池,其它輔助緩存如memcached和redis的意義應該就不需要了,還是有其它需要的理由
1、首先明確是不是一定要上緩存,當前架構的瓶頸在哪裡,若瓶頸真是資料庫操作上,再繼續往下看。
2、明確memcached和redis的區別,到底要使用哪個。前者終究是個緩存,不可能永久保存數據(LRU機制),支持分布式,後者除了緩存的同時也支持把數據持久化到磁碟等,redis要自己去實現分布式緩存(貌似最新版本的已集成),自己去實現一致性hash。因為不知道應用場景,不好說一定要用memcache還是redis,說不定用mongodb會更好,比如在存儲日誌方面。
3、緩存量大但又不常變化的數據,比如評論。
4、思路是對的,清晰明了,讀DB前,先讀緩存,如果有直接返回,如果沒有再讀DB,然後寫入緩存層並返回。
5、考慮是否需要主從,讀寫分離,考慮是否分布式部署,考慮是否後續水平伸縮。
6、想要一勞永逸,後續維護和擴展方便,那就將現有的代碼架構優化,按你說的替換資料庫組件需要改動大量代碼,說明當前架構存在問題。可以利用現有的一些框架,比如SpringMVC,將應用層和業務層和資料庫層解耦。再上緩存之前把這些做好。
7、把讀取緩存等操作做成服務組件,對業務層提供服務,業務層對應用層提供服務。
8、保留原始資料庫組件,優化成服務組件,方便後續業務層靈活調用緩存或者是資料庫。
9、不建議一次性全量上緩存,最開始不動核心業務,可以將邊緣業務先換成緩存組件,一步步換至核心業務。
10、刷新內存,以memcached為例,新增,修改和刪除操作,一般採用lazy load的策略,即新增時只寫入資料庫,並不會馬上更新Memcached,而是等到再次讀取時才會載入到Memcached中,修改和刪除操作也是更新 資料庫,然後將Memcached中的數據標記為失效,等待下次讀取時再載入。
大方向兩種方案:
1、腳本同步:自己寫腳本將資料庫數據寫入到redis/memcached。這就涉及到實時數據變更的問題(mysql row binlog的實時分析),binlog增量訂閱Alibaba 的canal ,以及緩存層數據 丟失/失效 後的數據同步恢復問題。
2、業務層實現:先讀取nosql緩存層,沒有數據再讀取mysql層,並寫入數據到nosql。nosql層做好多節點分布式(一致性hash),以及節點失效後替代方案(多層hash尋找相鄰替代節點),和數據震盪恢復了。
9. Linux上MySQL優化提升性能 哪些可以優化的關閉NUMA特性
一、CPU
首先從CPU說起。
你仔細檢查的話,有些伺服器上會有的一個有趣的現象:你cat /proc/cpuinfo時,會發現CPU的頻率竟然跟它標稱的頻率不一樣:
#cat /proc/cpuinfo
processor : 5
model name : Intel(R) Xeon(R) CPU E5-2620 0 @2.00GHz
cpu MHz : 1200.000
這個是Intel E5-2620的CPU,他是2.00G * 24的CPU,但是,我們發現第5顆CPU的頻率為1.2G。
這是什麼原因呢?
這些其實都源於CPU最新的技術:節能模式。操作系統和CPU硬體配合,系統不繁忙的時候,為了節約電能和降低溫度,它會將CPU降頻。這對環保人士和抵制地球變暖來說是一個福音,但是對MySQL來說,可能是一個災難。
為了保證MySQL能夠充分利用CPU的資源,建議設置CPU為最大性能模式。這個設置可以在BIOS和操作系統中設置,當然,在BIOS中設置該選項更好,更徹底。由於各種BIOS類型的區別,設置為CPU為最大性能模式千差萬別,我們這里就不具體展示怎麼設置了。
然後我們看看內存方面,我們有哪些可以優化的。
i) 我們先看看numa
非一致存儲訪問結構 (NUMA : Non-Uniform Memory Access) 也是最新的內存管理技術。它和對稱多處理器結構 (SMP : Symmetric Multi-Processor) 是對應的。簡單的隊別如下:
如圖所示,詳細的NUMA信息我們這里不介紹了。但是我們可以直觀的看到:SMP訪問內存的都是代價都是一樣的;但是在NUMA架構下,本地內存的訪問和非 本地內存的訪問代價是不一樣的。對應的根據這個特性,操作系統上,我們可以設置進程的內存分配方式。目前支持的方式包括:
--interleave=nodes
--membind=nodes
--cpunodebind=nodes
--physcpubind=cpus
--localalloc
--preferred=node
簡而言之,就是說,你可以指定內存在本地分配,在某幾個CPU節點分配或者輪詢分配。除非 是設置為--interleave=nodes輪詢分配方式,即內存可以在任意NUMA節點上分配這種方式以外。其他的方式就算其他NUMA節點上還有內 存剩餘,Linux也不會把剩餘的內存分配給這個進程,而是採用SWAP的方式來獲得內存。有經驗的系統管理員或者DBA都知道SWAP導致的資料庫性能 下降有多麼坑爹。
所以最簡單的方法,還是關閉掉這個特性。
關閉特性的方法,分別有:可以從BIOS,操作系統,啟動進程時臨時關閉這個特性。
a) 由於各種BIOS類型的區別,如何關閉NUMA千差萬別,我們這里就不具體展示怎麼設置了。
b) 在操作系統中關閉,可以直接在/etc/grub.conf的kernel行最後添加numa=off,如下所示:
kernel /vmlinuz-2.6.32-220.el6.x86_64 ro root=/dev/mapper/VolGroup-root rd_NO_LUKS LANG=en_US.UTF-8 rd_LVM_LV=VolGroup/root rd_NO_MD quiet SYSFONT=latarcyrheb-sun16 rhgb crashkernel=auto rd_LVM_LV=VolGroup/swap rhgb crashkernel=auto quiet KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM numa=off
另外可以設置 vm.zone_reclaim_mode=0盡量回收內存。
c) 啟動MySQL的時候,關閉NUMA特性:
numactl --interleave=all mysqld
當然,最好的方式是在BIOS中關閉。
ii) 我們再看看vm.swappiness。
vm.swappiness是操作系統控制物理內存交換出去的策略。它允許的值是一個百分比的值,最小為0,最大運行100,該值默認為60。vm.swappiness設置為0表示盡量少swap,100表示盡量將inactive的內存頁交換出去。
具體的說:當內存基本用滿的時候,系統會根據這個參數來判斷是把內存中很少用到的inactive 內存交換出去,還是釋放數據的cache。cache中緩存著從磁碟讀出來的數據,根據程序的局部性原理,這些數據有可能在接下來又要被讀 取;inactive 內存顧名思義,就是那些被應用程序映射著,但是 長時間 不用的內存。
我們可以利用vmstat看到inactive的內存的數量:
#vmstat -an 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free inact active si so bi bo in cs us sy id wa st
1 0 0 27522384 326928 1704644 0 0 0 153 11 10 0 0 100 0 0
0 0 0 27523300 326936 1704164 0 0 0 74 784 590 0 0 100 0 0
0 0 0 27523656 326936 1704692 0 0 8 8 439 1686 0 0 100 0 0
0 0 0 27524300 326916 1703412 0 0 4 52 198 262 0 0 100 0 0
通過/proc/meminfo 你可以看到更詳細的信息:
#cat /proc/meminfo | grep -i inact
Inactive: 326972 kB
Inactive(anon): 248 kB
Inactive(file): 326724 kB
這里我們對不活躍inactive內存進一步深入討論。 Linux中,內存可能處於三種狀態:free,active和inactive。眾所周知,Linux Kernel在內部維護了很多LRU列表用來管理內存,比如LRU_INACTIVE_ANON, LRU_ACTIVE_ANON, LRU_INACTIVE_FILE , LRU_ACTIVE_FILE, LRU_UNEVICTABLE。其中LRU_INACTIVE_ANON, LRU_ACTIVE_ANON用來管理匿名頁,LRU_INACTIVE_FILE , LRU_ACTIVE_FILE用來管理page caches頁緩存。系統內核會根據內存頁的訪問情況,不定時的將活躍active內存被移到inactive列表中,這些inactive的內存可以被 交換到swap中去。
一般來說,MySQL,特別是InnoDB管理內存緩存,它佔用的內存比較多,不經常訪問的內存也會不少,這些內存如果被Linux錯誤的交換出去了,將 浪費很多CPU和IO資源。 InnoDB自己管理緩存,cache的文件數據來說佔用了內存,對InnoDB幾乎沒有任何好處。
所以,我們在MySQL的伺服器上最好設置vm.swappiness=0。
我們可以通過在sysctl.conf中添加一行:
echo vm.swappiness = 0 /etc/sysctl.conf
並使用sysctl -p來使得該參數生效。
三、文件系統
最後,我們看一下文件系統的優化
i) 我們建議在文件系統的mount參數上加上noatime,nobarrier兩個選項。
用noatime mount的話,文件系統在程序訪問對應的文件或者文件夾時,不會更新對應的access time。一般來說,Linux會給文件記錄了三個時間,change time, modify time和access time。
我們可以通過stat來查看文件的三個時間:
stat libnids-1.16.tar.gz
File: `libnids-1.16.tar.gz'
Size: 72309 Blocks: 152 IO Block: 4096 regular file
Device: 302h/770d Inode: 4113144 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access : 2008-05-27 15:13:03.000000000 +0800
Modify: 2004-03-10 12:25:09.000000000 +0800
Change: 2008-05-27 14:18:18.000000000 +0800
其中access time指文件最後一次被讀取的時間,modify time指的是文件的文本內容最後發生變化的時間,change time指的是文件的inode最後發生變化(比如位置、用戶屬性、組屬性等)的時間。一般來說,文件都是讀多寫少,而且我們也很少關心某一個文件最近什 么時間被訪問了。
所以,我們建議採用noatime選項,這樣文件系統不記錄access time,避免浪費資源。
現在的很多文件系統會在數據提交時強制底層設備刷新cache,避免數據丟失,稱之為write barriers。但是,其實我們資料庫伺服器底層存儲設備要麼採用RAID卡,RAID卡本身的電池可以掉電保護;要麼採用Flash卡,它也有自我保 護機制,保證數據不會丟失。所以我們可以安全的使用nobarrier掛載文件系統。設置方法如下:
對於ext3, ext4和 reiserfs文件系統可以在mount時指定barrier=0;對於xfs可以指定nobarrier選項。
ii) 文件系統上還有一個提高IO的優化萬能鑰匙,那就是deadline。
在 Flash技術之前,我們都是使用機械磁碟存儲數據的,機械磁碟的尋道時間是影響它速度的最重要因素,直接導致它的每秒可做的IO(IOPS)非常有限, 為了盡量排序和合並多個請求,以達到一次尋道能夠滿足多次IO請求的目的,Linux文件系統設計了多種IO調度策略,已適用各種場景和存儲設備。
Linux的IO調度策略包括:Deadline scheler,Anticipatory scheler,Completely Fair Queuing(CFQ),NOOP。每種調度策略的詳細調度方式我們這里不詳細描述,這里我們主要介紹CFQ和Deadline,CFQ是Linux內 核2.6.18之後的默認調度策略,它聲稱對每一個 IO 請求都是公平的,這種調度策略對大部分應用都是適用的。但是如果資料庫有兩個請求,一個請求3次IO,一個請求10000次IO,由於絕對公平,3次IO 的這個請求都需要跟其他10000個IO請求競爭,可能要等待上千個IO完成才能返回,導致它的響應時間非常慢。並且如果在處理的過程中,又有很多IO請 求陸續發送過來,部分IO請求甚至可能一直無法得到調度被 餓死 。而deadline兼顧到一個請求不會在隊列中等待太久導致餓死,對資料庫這種應用來 說更加適用。
實時設置,我們可以通過
echo deadline /sys/block/sda/queue/scheler
來將sda的調度策略設置為deadline。
我們也可以直接在/etc/grub.conf的kernel行最後添加elevator=deadline來永久生效。
CPU方面:
關閉電源保護模式
內存:
vm.swappiness = 0
關閉numa
文件系統:
用noatime,nobarrier掛載系統
IO調度策略修改為deadline。
10. 在Android開發中,有哪些好的內存優化方式
可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統數據結構。通常的HashMap的實現方式更加消耗內存,因為它需要一個額外的實例對象來記錄Mapping操作。另外,SparseArray更加高效,在於他們避免了對key與value的自動裝箱(autoboxing),並且避免了裝箱後的解箱。
2. 避免在Android裡面使用Enum
Android官方培訓課程提到過「Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.」,具體原理請參考《Android性能優化典範(三)》,所以請避免在Android裡面使用到枚舉。
3. 減小Bitmap對象的內存佔用
Bitmap是一個極容易消耗內存的大胖子,減小創建出來的Bitmap的內存佔用可謂是重中之重,,通常來說有以下2個措施:
inSampleSize:縮放比例,在把圖片載入內存之前,我們需要先計算出一個合適的縮放比例,避免不必要的大圖載入。
decode format:解碼格式,選擇ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差異
4.Bitmap對象的復用
縮小Bitmap的同時,也需要提高BitMap對象的復用率,避免頻繁創建BitMap對象,復用的方法有以下2個措施
LRUCache : 「最近最少使用演算法」在Android中有極其普遍的應用。ListView與GridView等顯示大量圖片的控制項里,就是使用LRU的機制來緩存處理好的Bitmap,把近期最少使用的數據從緩存中移除,保留使用最頻繁的數據,
inBitMap高級特性:利用inBitmap的高級特性提高Android系統在Bitmap分配與釋放執行效率。使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經存在的內存區域,新解碼的Bitmap會嘗試去使用之前那張Bitmap在Heap中所佔據的pixel data內存區域,而不是去問內存重新申請一塊區域來存放Bitmap。利用這種特性,即使是上千張的圖片,也只會僅僅只需要佔用屏幕所能夠顯示的圖片數量的內存大小
4. 使用更小的圖片
在涉及給到資源圖片時,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用更小的圖片。盡量使用更小的圖片不僅可以減少內存的使用,還能避免出現大量的InflationException。假設有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖時會因為內存不足而發生InflationException,這個問題的根本原因其實是發生了OOM。
5.StringBuilder
在有些時候,代碼中會需要使用到大量的字元串拼接的操作,這種時候有必要考慮使用StringBuilder來替代頻繁的「+」。
6.避免在onDraw方法裡面執行對象的創建
類似onDraw等頻繁調用的方法,一定需要注意避免在這里做創建對象的操作,因為他會迅速增加內存的使用,而且很容易引起頻繁的gc,甚至是內存抖動。
7. 避免對象的內存泄露
類的靜態變數持有大數據對象
靜態變數長期維持到大數據對象的引用,阻止垃圾回收。
非靜態內部類存在靜態實例
非靜態內部類會維持一個到外部類實例的引用,如果非靜態內部類的實例是靜態的,就會間接長期維持著外部類的引用,阻止被回收掉。
資源對象未關閉
資源性對象比如(Cursor,File文件等)往往都用了一些緩沖,我們在不使用的時候,應該及時關閉它們, 以便它們的緩沖及時回收內存。它們的緩沖不僅存在於java虛擬機內,還存在於java虛擬機外。 如果我們僅僅是把它的引用設置為null,而不關閉它們,往往會造成內存泄露。
解決辦法: 比如SQLiteCursor(在析構函數finalize(),如果我們沒有關閉它,它自己會調close()關閉), 如果我們沒有關閉它,系統在回收它時也會關閉它,但是這樣的效率太低了。 因此對於資源性對象在不使用的時候,應該調用它的close()函數,將其關閉掉,然後才置為null. 在我們的程序退出時一定要確保我們的資源性對象已經關閉。 程序中經常會進行查詢資料庫的操作,但是經常會有使用完畢Cursor後沒有關閉的情況。如果我們的查詢結果集比較小, 對內存的消耗不容易被發現,只有在常時間大量操作的情況下才會復現內存問題,這樣就會給以後的測試和問題排查帶來困難和風險,記得try catch後,在finally方法中關閉連接
Handler內存泄漏
Handler作為內部類存在於Activity中,但是Handler生命周期與Activity生命周期往往並不是相同的,比如當Handler對象有Message在排隊,則無法釋放,進而導致本該釋放的Acitivity也沒有辦法進行回收。