一、理論:
1.內存優化原則:
a.將盡量多的內存分配給mysql做緩存,但要給操作系統和其他程序預留足夠的內存,否則將產生SWAP頁交換,影響自身性能
b.MyISAM的數據文件讀取依賴於操作系統自身的IO緩存,因此,如果有MyISAM表,就要預留更多的內存給操作系統做IO緩存
c.排序區、連接區等緩存是分配給每個數據庫會話專用的,其默認值的設置要根據最大連接數合理分配。不能設置太大否則在並發連接較高時會導致物理內存耗盡
2.MyISAM內存優化:
a.key_buffer_size決定MyISAM索引塊緩存區的大小,它直接影響MyISAM表的存取效率。可以在mysql參數文件中設置key_buffer_size的值,對於一般MyISAM的數據庫建議至少將1/4可用內存分配給key_buffer_size
key buffer使用率計算公式如下:
1 - ( (key_blocks_unused * key_cache_block_size) / key_buffer_size )
在80%左右合適。大於80%將因索引緩存不足導致性能下降,小於80%會導致內存浪費
b.使用多個索引緩存:
1)mysql通過各session共享key buffer提高了MyISAM索引存取的性能,但它並不能消除session間key buffer間的競爭。
3.調整LRU策略:
a.通過調節key_cache_division_limit來控制多大比例的緩存用做warm子表。
在最後:
N * key_cache_age_threshold / 100次緩存命准內未被訪問過,就會被降級到warm子表
b.調整read_buffer_size和read_md_buffer_size
c.如帶用order by子句,可以適當增大read_rnd_buffer_size的值。但read_rnd_buffer_size的值也是按session分配的
4.InnoDB內存優化:
a.InnoDB用一塊內存區做IO緩存池,該緩存池不僅用來緩存InnoDB的索引塊,而且也用來緩存InnoDB的數據塊。與MyISAM不同
b.在內部,InnoDB緩存池邏輯上由free list、flush list、LRU list組成。顧名思義,free list是空閒緩存塊列表,flush list是需要緩新到磁盤的緩存塊列表,而LRU list是InnoDB正在使用的緩存塊,它是InnoDB buffer pool的核心。
InnoDB使用LRU算法與MyISAM的‘中點插入策略’LRU算法類似。
髒頁的刷新
c.可以通過調整InnoDB buffer pool的大小,改變young sublist和old sublist的分配比例、控制髒緩存的刷新活動、使用多個InnoDB緩存池等方法來優化InnoDB的性能
5.innodb_buffer_pool_size的設置:
a.innodb_buffer_pool_size決定InnoDB存儲引擎表數據和索引數據的最大緩存區大小。innodb buffer pool同時為數據塊和索引塊提供數據緩存,若innodb_buffer_pool_size值越大,緩存命中率越高,訪問InnoDB表需要的磁盤I/O就越少,性能也就越高。
可以通過:
mysqladmin -S /tmp/mysql.sock ext | grep -i innodb_buffer_pool
查看buffer pool的使用情況
可用以下公式InnoDB緩存池的命中率:
( 1- innodb_buffer_pool_reads/innodb_buffer_pool_read_request) * 100
若太低則應該擴充內存、增加innodb_buffer_pool_size的值
6.調整innodb_old_blocks_time的設置:
a.innodb_old_blocks_time參數決定了緩存數據塊由old sublist轉移到young sublist的快慢,當一個緩存數據塊被插入到midpoint(old sublist)後,至少要在old sublist停留超過innodb_old_blocks_time(ms)後,才有可能被轉移到new sublist.
可以根據InnoDB Monitor的輸出信息來調整innodb_old_blocks_time的值,在進行表掃描時,如果non-youngs/s很低,young/s很高,就應考慮將innodb_old_blocks_time適當調大,以防止表掃描將真正的熱數據淘汰,此值可以進行動態設置
7.調整緩存池數量,減少內部對緩存池數據結構的爭用:
a.InnoDB的緩存系統引入了innodb_buffer_pool_instances配置參數。對於較大的緩存池,適當增大此參數的值,可以降低並發導致的內部緩存訪問沖突,改善性能。InnoDB緩存系統會將參數innodb_buffer_pool_size指定大小的緩存平分為innodb_buffer_pool_instances個buffer pool.
8.控制innodb buffer刷新,延長數據緩存時間,減緩磁盤I/O
a.innodb buffer pool的刷新快慢主要取決於兩個參數
1)innodb_max_dirty_pages_pct:控制緩存池中髒頁的最大比例,默認值是75%,如果髒頁的數量達到或超過該值,innoDB後台線程將開台緩存刷新
2)innodb_io_capacity:在某種程度上代表磁盤中每秒可完成的I/O次數,對於轉速較低的磁盤,可將innodb_io_capacity降低。對於固態磁盤和由多個磁盤組成的陣列,innodb_io_capacity的值可適當增大
3)如無法增大緩存池,應將innodb_max_dirty_pages_pct的值調小,將innodb_io_capactity的值提高,以加快髒頁的刷新
9.InnoDB doublewrite:
a.在做恢復時,如果發現不一致的頁,InnoDB會用系統表空間double buffer區的相應副本來恢復數據頁
b.由於同步到doublewrite buffer是對連續磁盤空間的順序寫,因此開啟雙寫對性能的影響並不太大。對需要高性能並且可以容忍丟失數據的應用,可將innodb_doublewrite=0來關閉雙寫以滿足性能
10.調整用戶服務線程排序緩存區:
a.若查看show global status看到sort_merge_passes的值很大,則可以調整參數sort_buffer_size的值來調整排序緩存區,以改善order by子句或group子句的sql性能
b.可通過調整join_buffer_size的值來改善沒使用索引的查詢
c.最好的策略是設置較小的全局join_buffer_size,對較復雜的操作session單獨設置join_buffer_size
11.InnoDB log機制及優化:
a.innodb_flush_log_at_trx_commit的設置:
0:每秒觸發一次,可滿足持久化要求(效率最高,但最不安全)
1:每個事務提交時立刻將緩存中的redo日志回寫到日志文件,並調用操作系統fsync刷新IO緩存(默認值,效率最低,但最安全)
2:每個事務提交時,InnoDB立刻將redo日志回寫到日志文件,但並不馬上調用fsync來刷新IO緩存,而是每秒只做一次磁盤IO緩存刷新操作(性能和數據安全性都在中間)
12.設置log file size,控制檢查點:
a.可以通過一些方法計算innodb每小時產生的日志量並估計合適的innodb_log_file_size值
13.調整innodb_log_buffer_size:
a.可以通過增大innodb_log_buffer_size來減少日志寫磁盤操作,從而提高事務處理的性能
14.調整max_connections,提高並發連接:
a.max_connections控制允許連接到mysql數據庫的最大數量,默認值是151
15.調整back_log:
a.如果需要數據庫在較短時間內處理大量連接請求,可適當增大back_log的值
16.調整table_open_cache:
a.在未執行flush tables命令的情況下,如果mysql狀態變量opened_tables的值較大,就說明table_open_cache設置的太小,應適當增大
17.調整thread_cache_size:
a.可以通過計算cache的失敗率threads_created/connections來衡量thread_cached_size的值是否合適。此值越接近1,說明線程cache命中率越低,應考慮適當增加thread_cache_size的值
18.innodb_lock_wait_timeout的設置:
a.innodb_lock_wait_timeout可以控制innodb事務等待行鎖的時間,默認值是50ms.
b.對於需要快速反饋的交互式OLTP應用,可以將行鎖等待超時時間調小,以避免事務長時間掛起
c.對於後台運行的批處理操作,可以將行鎖等待超時時間調大,以避免發生大的回滾操作
二、實踐:
mysql> use sakila; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show engine innodb status \G; *************************** 1. row *************************** Type: InnoDB Name: Status: ===================================== 151102 7:13:01 INNODB MONITOR OUTPUT ===================================== Per second averages calculated from the last 15 seconds ----------------- BACKGROUND THREAD ----------------- srv_master_thread loops: 113 1_second, 113 sleeps, 7 10_second, 47 background, 47 flush srv_master_thread log flush and writes: 113 ---------- SEMAPHORES ---------- OS WAIT ARRAY INFO: reservation count 18, signal count 18 Mutex spin waits 8, rounds 240, OS waits 7 RW-shared spins 11, rounds 330, OS waits 11 RW-excl spins 0, rounds 0, OS waits 0 Spin rounds per wait: 30.00 mutex, 30.00 RW-shared, 0.00 RW-excl ------------ TRANSACTIONS ------------ Trx id counter B0A Purge done for trx's n:o < 920 undo n:o < 0 History list length 103 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 0, not started MySQL thread id 28, OS thread handle 0x7f5dbdfe6700, query id 568 localhost root show engine innodb status -------- FILE I/O -------- I/O thread 0 state: waiting for i/o request (insert buffer thread) I/O thread 1 state: waiting for i/o request (log thread) I/O thread 2 state: waiting for i/o request (read thread) I/O thread 3 state: waiting for i/o request (read thread) I/O thread 4 state: waiting for i/o request (read thread) I/O thread 5 state: waiting for i/o request (read thread) I/O thread 6 state: waiting for i/o request (write thread) I/O thread 7 state: waiting for i/o request (write thread) I/O thread 8 state: waiting for i/o request (write thread) I/O thread 9 state: waiting for i/o request (write thread) Pending normal aio reads: 0 [0, 0, 0, 0] , aio writes: 0 [0, 0, 0, 0] , ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0 Pending flushes (fsync) log: 0; buffer pool: 0 586 OS file reads, 55 OS file writes, 48 OS fsyncs 0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s ------------------------------------- INSERT BUFFER AND ADAPTIVE HASH INDEX ------------------------------------- Ibuf: size 1, free list len 0, seg size 2, 0 merges merged operations: insert 0, delete mark 0, delete 0 discarded operations: insert 0, delete mark 0, delete 0 Hash table size 553229, node heap has 1 buffer(s) 0.00 hash searches/s, 0.00 non-hash searches/s --- LOG --- Log sequence number 13476957 Log flushed up to 13476957 Last checkpoint at 13476957 0 pending log writes, 0 pending chkp writes 32 log i/o's done, 0.00 log i/o's/second ---------------------- BUFFER POOL AND MEMORY ---------------------- Total memory allocated 274726912; in additional pool allocated 0 Dictionary memory allocated 182244 Buffer pool size 16383 Free buffers 15829 Database pages 553 Old database pages 224 Modified db pages 0 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 0, not young 0 0.00 youngs/s, 0.00 non-youngs/s Pages read 553, created 0, written 27 0.00 reads/s, 0.00 creates/s, 0.00 writes/s No buffer pool page gets since the last printout Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: 553, unzip_LRU len: 0 I/O sum[0]:cur[0], unzip sum[0]:cur[0] -------------- ROW OPERATIONS -------------- 0 queries inside InnoDB, 0 queries in queue 1 read views open inside InnoDB Main thread process no. 62305, id 140040552920832, state: waiting for server activity Number of rows inserted 5, updated 0, deleted 0, read 12 0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s ---------------------------- END OF INNODB MONITOR OUTPUT ============================ 1 row in set (0.00 sec) ERROR: No query specified mysql> set global hot_cache.key_buffer_size = 128* 1024; Query OK, 0 rows affected (0.00 sec) mysql> set global hot_cache.key_buffer_size = 0; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'key_buffer_size'; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | key_buffer_size | 33554432 | +-----------------+----------+ 1 row in set (0.00 sec) mysql> set global key_buffer_size = 0; ERROR 1438 (HY000): Cannot drop default keycache mysql> show warnings; +-------+------+------------------------------+ | Level | Code | Message | +-------+------+------------------------------+ | Error | 1438 | Cannot drop default keycache | +-------+------+------------------------------+ 1 row in set (0.00 sec) mysql> cache index sales,sales2 in hot_cache; +---------------+--------------------+----------+-------------------------------------+ | Table | Op | Msg_type | Msg_text | +---------------+--------------------+----------+-------------------------------------+ | sakila.sales | assign_to_keycache | Error | Table 'sakila.sales' doesn't exist | | sakila.sales | assign_to_keycache | status | Operation failed | | sakila.sales2 | assign_to_keycache | Error | Table 'sakila.sales2' doesn't exist | | sakila.sales2 | assign_to_keycache | status | Operation failed | +---------------+--------------------+----------+-------------------------------------+ 4 rows in set (0.00 sec) mysql> set global key_cache_division_limit = 70; Query OK, 0 rows affected (0.00 sec) mysql> set global hot_cache.key_cache_division_limit = 70; Query OK, 0 rows affected (0.00 sec) mysql> show global variables like '%innodb_old_blocks_pct%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_old_blocks_pct | 37 | +-----------------------+-------+ 1 row in set (0.01 sec) mysql> show global variables like '%doublewrirte%'; Empty set (0.00 sec) mysql> show global variables like '%doublewrite%'; +--------------------+-------+ | Variable_name | Value | +--------------------+-------+ | innodb_doublewrite | ON | +--------------------+-------+ 1 row in set (0.01 sec) mysql> select @a1 := variable_value as a1; ERROR 1054 (42S22): Unknown column 'variable_value' in 'field list' mysql> select @a1 := variable_value as a1 -> from information_schema.global_status -> where variable_name = 'innodb_os_log_written' -> union all -> select sleep(60) -> union all -> select @a2 := variable_value as a2 -> from information_schema.global_status -> where variable_name = 'innodb_os_log_written'; +------+ | a1 | +------+ | 9216 | | 0 | | 9216 | +------+ 3 rows in set (1 min 0.02 sec) mysql> select round((@a2-@a1)/1024/1024 / @@innodb_log_files_in_group) as MB; +------+ | MB | +------+ | 0 | +------+ 1 row in set (0.01 sec)