1.6.SQL運維篇
運維這塊逆天只能說夠用,並不能說擅長,所以這篇就當拋磚之用,歡迎補充和糾錯
PS:再說明下CentOS優化策略
這部分的內容來源:首先這塊逆天不是很擅長,所以主要是參考網上的DBA文章,之后請教了下運維相關的朋友,大家辯證看就行了,我只能保證90%的准確度(具體看業務)
1.6.1.概念
1.RAID系
RAID:磁盤冗余隊列
把多個容量小的磁盤組成一組容量更大的磁盤,並提供數據冗余來保證數據完整性的技術
RAID0
:數據條帶(好處:成本低,應用:數據備份)
需要硬盤數>=2,數據沒有冗余或修復功能,只是多個小容量變成大容量的功能
RAID1:磁盤鏡像(好處:數據安全、讀很快)
磁盤的數據鏡像到另一個磁盤上,最大限度的保證系統的可靠性和可修復性
RAID5
:分布式奇偶校驗磁盤陣列(好處:性價比高,缺點:兩塊磁盤失效則整個卷的數據都無法恢復,應用:從數據庫)
把數據分散到多個磁盤上,如果任何一個盤數據失效都可以從奇偶校驗塊中重建
RAID10:分片鏡像(優點:讀寫性能良好,相對RAID5重建更簡單速度更快,缺點:貴)
對磁盤先做RAID1之后對兩組RAID1的磁盤再做RAID0
RAID級別 | 特點 | 備份 | 盤數 | 讀 | 寫 |
---|---|---|---|---|---|
RAID0 |
便宜,讀寫快,不安全 | 沒有 | N | 快 | 快 |
RAID1 |
貴,高速讀,最安全 | 有 | 2N | 快 | 慢 |
RAID5 |
性價比高,讀快,安全 | 有 | N+1 | 快 | 取決於最慢盤 |
RAID10 |
貴,高速,安全 | 有 | 2N | 快 | 快 |
2.SAN和NAS
SAN:通過專用高速網將一個或多個網絡存儲設備和服務器連接起來的專用存儲系統
通過光纖連接到服務器,設備通過塊接口訪問,服務器可以將其當做硬盤使用
NAS:連接在網絡上, 具備資料存儲功能的裝置,以數據為中心,將存儲設備與服務器徹底分離,集中管理數據,從而釋放帶寬、提高性能、降低總擁有成本、保護投資。其成本遠遠低於使用服務器存儲,而效率卻遠遠高於后者
使用網絡進行連接,通過基於文件協議(NFS、SMB)來訪問
PS:網絡存儲一般都是用來搭建開發環境或者數據庫備份
3.QPS和TPS
QPS
(Queries Per Second):每秒鍾處理的請求數(一般都是查詢,但DML、DDL也包括)
eg:
10ms
處理1個sql,1s處理100個sql,那么QPS<=100
(100ms
處理1個sql,QPS<=10)
TPS
(Transactions Per Second):每秒鍾系統能夠處理的交易或事務的數量(每秒事務數|消息數
)
一個事務是指一個客戶機向服務器發送請求然后服務器做出反應的過程。客戶機在發送請求時開始計時,收到服務器響應后結束計時,以此來計算使用的時間和完成的事務個數
PS:QPS看的多些
1.6.2.常見問題
1.超高的CPU|內存使用率:容易因CPU|內存資源耗盡而宕機
PS:如果是CPU密集型:需要更好的CPU;需要更大的並發量:需要更多的CPU(WEB項目)
MySQL有同一數據中多次寫操作合並為一次寫操作
2.並發量大:容易導致數據庫連接數被占滿
PS:MySQL的
max_connections
默認是100(根據硬件條件調整)
3.磁盤IO:導致性能直線下降(熱點數據內存放不下時
)
解決:定期整理磁盤碎片、
RAID增強傳統硬盤
、SSD
、Fusion-io
(PCIe)、網絡存儲NAS or ASN
PS:SSD應用於存在大量隨機IO
或解決單線程IO瓶頸
的場景
4.網卡流量(網絡):容易出現無法連接數據庫的現象
解決:
- 減少從服務器的數量
- 分級緩存(防止同一時間緩存的大量失效)
- 避免使用
select *
進行查詢(減少傳輸過程中的無用字節)- 分離業務網絡和服務器網絡
5.大表定義:單表數據量超過千萬行 or 表數據文件超過10G
問題:大表更容易出現慢查詢、DDL也很慢也容易導致其他問題
解決:分庫分表(拆分為多個小表)
PS:分庫分表前可以對大表的歷史數據進行歸檔(冷熱數據隔離)【核心:歸檔時間點的選擇】
DDL影響的補充說明:
建索引很慢
,而且會引起長時間的主從延遲修改表結構需要長時間鎖表
- 引起長時間的主從延遲
- 影響正常的數據操作
分庫分表容易出現的問題:
- 分表主鍵的選擇
- 不能保證id是全局唯一,這時候可以使用諸如
雪花算法
來解決
- 不能保證id是全局唯一,這時候可以使用諸如
- 跨庫跨表的join問題
- 事物問題(分布式事物誕生了)
PS:不太影響的案例:日志表(insert
和select
很多,很少delete和update)
6.大事務定義:運行時間較長,操作數據比較多的事物
問題:
- 鎖定太多的數據,造成大量的阻塞和鎖超時
- 回滾需要的時間很長(又得鎖一段時間了)
- 執行時間長,容易導致主從的延遲
解決:- 避免一次處理大量數據(分批處理)
- 去除在事物中不必要的select語句(一般都是事物中使用過多查詢導致的)
- PS:select完全可以在事物外查詢,事物專注於寫
SQL標准中定義的4種隔離級別:
- 未提交讀(
read uncommited
) - 已提交讀(
read commited
)- 不可重復讀
- 可重復讀(
repeatable read
)innodb的默認隔離級別
- 可串行化(
serializable
) - PS:隔離性低到高,
並發性高到低
PS:查看事物隔離級別-show variables like '%iso%';
,設置會話的隔離級別:set session tx_isolation='read-committed'
擴展:CentOS優化策略(MySQL服務器)
1.內核相關(/etc/sysctl.conf
)
查看默認值:sysctl -a
tcp相關設置:
# 三次握手listen的最大限制
net.core.somaxconn = 65535 # 默認是128
# 當網絡接受速率大於內核處理速率時,允許發送到隊列中的包數
net.core.netdev_max_backlog = 65535 # 默認是1000
# Linux隊列的最大半連接數(超過則丟包)
net.ipv4.tcp_max_syn_backlog = 65535 # 默認是128(不適合Web服務器)
PS:這邊只是一個參考,自己可以根據環境適當降低(最大端口數一般都是65535)
注意:如果是Web服務器,
net.ipv4.tcp_max_syn_backlog
不宜過大(容易有synflood攻擊的安全問題),net.ipv4.tcp_tw_recycle
和net.ipv4.tcp_tw_reuse
不建議開啟
加快tcp鏈接回收的幾個參數:
# TCP等待時間,加快tcp鏈接回收
net.ipv4.tcp_fin_timeout = 10 # 默認60
# 把發起關閉,但關閉沒完成的TCP關閉掉
net.ipv4.tcp_tw_recycle = 1 # 默認0(不適合Web服務器)
# 允許待關閉的socket建立新的tcp
net.ipv4.tcp_tw_reuse = 1 # 默認0(不適合Web服務器)
PS:net.ipv4.tcp_tw_reuse
擴展說明:主動調用closed的一方才會在接收到對端的ACK后進入time_wait狀態
參考文章:
https://blog.csdn.net/weixin_41966991/article/details/81264095
緩存區大小的最大值和默認值:
net.core.wmem_default = 87380 # 默認212992
net.core.wmem_max = 16777216 # 默認212992
net.core.rmem_default = 87380 # 默認212992
net.core.rmem_max = 16777216 # 默認212992
PS:每個socket
都會有一個rmem_default
大小的緩存空間(如果設置了setsockopt
則就是多少,最大不超過rmem_max
)
減少失效連接所占用的系統資源:
# 對於tcp失效鏈接占用系統資源的優化,加快資源回收效率
# 鏈接有效時間(單位s)
net.ipv4.tcp_keepalive_time = 120 # 默認7200
# tcp未獲得相應時重發間隔(單位s)
net.ipv4.tcp_keepalive_intvl = 30 # 默認75
# 重發數量(單位s)
net.ipv4.tcp_keepalive_probes = 3 # 默認9
內存相關參數:
# 共享單個共享內存下的最大值
kernel.shmmax = 4294967295 # 最大為物理內存-1byte
# 除非虛擬內存全部占滿,否則不使用交換分區(為了性能)
# (free -m ==> Swap)
vm.swappiness = 0 # 默認30
PS:kernel.shmmax
設置的足夠大,一般就是為了容納整個innodb的緩沖池
eg:
4G = 4*1024 M = 4*1024*1024 KB = 4*1024*1024*1024 byte = 4294967296 - 1 = 4294967295
PS:unsigned int
=>[0, 2^32)
=>[0,4294967296)
=>[0,4294967295]
巧不,一樣的值
2.資源限制(/etc/security/limit.conf
)
打開文件數的限制(追加到配置后即可)
# [*|%] [soft|hard] [type_item] [value]
* soft nofile 65536
* hard nofile 65535
默認值:ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 3548
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024 《《看這
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 3548
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
PS:一般來說是夠用了,但是一個遇到大型數據庫可能就不夠看了(多表多庫配置高)
*:所有用戶有效、soft:當前系統生效、hard:系統中所能設置的最大值、nofile:所限制的資源是打開文件的最大數、65536:數值
【重啟才生效】
3.磁盤調度策略(/sys/block/devname/queue/scheduler
)
現在默認策略就是deadline
,所以不用優化了【對數據庫支持很不錯】
PS:通過cat /sys/block/sda/queue/scheduler
查看([這個就是設置的值]
)
noop [deadline] cfq
如果不是可以通過:echo deadline > /sys/block/sda/queue/scheduler
來設置
cfq
:會在隊列中插入一些不必要的請求,會導致相應時間加長,一般桌面系統用的比較多
noop
:實現了一個FIFO隊列,像電梯工作一樣對IO請求進行組織,當有一個新請求到來時會合並到最近請求之后,以此保證請求同一介質(傾向於餓死讀而利於寫)一般閃存設備
、RAM
、嵌入式系統
用的比較多
deadline
:確保了在一個截止時間內去服務請求(可調整)默認讀期限短於寫期限(防止寫操作因為不能被讀而出現餓死的現象)
4.文件系統
Win:NTFS
,Linux:EXT3|4、XFS
Linux現在基本上都是選擇XFS
,如果是EXT3
、EXT4
還需要設置一下:/etc/fstab
(慎重)
/dev/sda1/ext4 noatime,nodiratime,data=writeback 1 1
PS:noatime
表示不記錄訪問時間,nodiratime
不記錄目錄的訪問時間(可以減少一些寫的操作)
不同的日志策略:
data=[wtiteback|ordered|journal]
- writeback:只有原數據寫入日志,原數據寫入和數據寫入並不是同步的(最快)PS:Innodb有自己的事務日志,所以是最好的選擇
- ordered:只會記錄原數據,但提供了一些一致性的保證,在寫原數據之前會先寫數據,使他們保持一致(比writeback慢但更安全)
- journal:提供了原子日志的一種行為,在數據寫入到最終位置之前,將記錄到日志中(最慢,對Innodb來說是沒有必要)
課后拓展:
TPS、並發用戶數、吞吐量關系
https://www.cnblogs.com/zhengah/p/4532156.html
針對Mysql所在linux服務器的系統優化參數
https://blog.csdn.net/qq_40999403/article/details/80666102
網絡優化之net.ipv4.tcp_tw_recycle參數
https://blog.csdn.net/chengm8/article/details/51668992
linux socket 緩存: core rmem_default rmem_max
https://blog.csdn.net/penzchan/article/details/41682411
Linux上的free命令詳解、swap機制
http://www.cnblogs.com/xiaojianblogs/p/6254535.html
磁盤IO過高時的處理辦法
https://www.cnblogs.com/wjoyxt/p/4808024.html
文件系統對性能的影響
https://blog.csdn.net/qq_30353203/article/details/78197870
1.6.3.MySQL配置參數
建議:優先從數據庫設計和SQL優化着手,然后才是配置優化和存儲引擎的選擇,最后才是硬件提升
設計案例:
列太多
不行,關聯太多
也不行(10個以內),不恰當的分區表
,使用了外鍵
分區表:一個服務器下,邏輯上還是一個表,物理存儲上分成了多個表(類似於SQLServer的水平分庫)
PS:分庫分表:物理和邏輯上都拆分成多個表了
之前講環境的時候簡單說了下最基礎的
[mysqld]
# 獨立表空間: 每一個表都有一個.frm表描述文件,還有一個.ibd文件
innodb_file_per_table=on
# 不對連接進行DNS解析(省時)
skip_name_resolve=on
# 配置sql_mode
sql_mode='strict_trans_tables'
然后說SQL_Mode
的時候簡單說了下全局參數
和會話參數
的設置方法:MySQL的SQL_Mode修改小計
- 全局參數設置:
set global 參數名=參數值;
- 只對新會話有效,重啟后失效
- 會話參數設置:
set [session] 參數名=參數值
- 只對當前會話有效,其他會話不影響
這邊繼續說下其他幾個影響較大的配置參數:(對於開發人員來說,簡單了解即可,這個是DBA的事情了)
1.安全相關配置
expire_logs_days
:自動清理binlog- PS:一般最少保存7天(具體根據業務來)
max_allowed_packet
:配置MySQL接收包的大小- PS:默認太小。如果配置了主從,需要配置成一樣大(防止丟包)
skip_name_resolve
:禁用DNS查找(這個我們之前說過了,主要是提速)- PS:如果啟用了,那么進行用戶授權時,只能通過
ip
或者ip段
或者本機host出現過的域名
進行授權- 用
*
的是沒影響的
- 用
- PS:如果啟用了,那么進行用戶授權時,只能通過
sysdata_is_now
:保證sysdate()返回確定性日期- PS:如果主從使用了binlog的
statement
模式,sysdata的結果會不一樣,最后導致數據不一致- 類似的問題還有很多,eg:獲取最后一次id的時候(
last_insert_id()
) - 擴:現在MySQL有了
Mixed
模式
- 類似的問題還有很多,eg:獲取最后一次id的時候(
- PS:如果主從使用了binlog的
read_only
:一般用戶只能讀數據,只有root用戶可以寫:- PS:推薦在從庫中開啟,這樣就只接受從主庫中的寫操作,其它只讀
- 從庫授權的時候不要授予超級管理員的權限,不然這個參數相當於廢了
skip_slave_start
:禁用從庫(Slave
)自動恢復- MySQL在重啟后會自動啟用復制,這個可以禁止
- PS:不安全的崩潰后,復制過去的數據可能也是不安全的(手動啟動更合適)
sql_mode
:設置MySQL的SQL模式(這個上次說過,默認是寬松的檢測,這邊再補充幾個)strict_trans_tables
:對所有支持事物類型的表做嚴格約束:- 最常見,主要對事物型的存儲引擎生效,其他的沒效果
- PS:如果插入數據不符合規范,則中斷當前操作
no_engine_subtitution
:建表的時候指定不可用存儲引擎會報錯only_full_group_by
:檢驗group by
語句的合法性- 要求在在分組查詢語句中,把所有沒有使用聚合函數的列,列出來
- eg:
select count(url),name from file_records group by url;
- 使用了name字段,name不是聚合函數,那必須在group by中寫一下
ansi_quotes
:不允許使用雙引號來包含字符串- PS:防止數據庫遷移的時候出錯
- PS:生存環境下最好不要修改,容易報錯對業務產生影響(嚴格變寬松沒事)
PS:一般SQL_Mode
是測試環境相對嚴格(strict_trans_tables,only_full_group_by,no_engine_subtitution,ansi_quotes
),線上相對寬松(strict_trans_tables
)
補充說下sysdate()
和now()
的區別:(看個案例就懂了)
PS:對於一個語句中調用多個函數中
now()
返回的值是執行時刻的時間,而sysdate()
返回的是調用該函數的時間
MariaDB [(none)]> select sysdate(),sleep(2),sysdate();
+---------------------+----------+---------------------+
| sysdate() | sleep(2) | sysdate() |
+---------------------+----------+---------------------+
| 2019-03-28 09:09:29 | 0 | 2019-03-28 09:09:31 |
+---------------------+----------+---------------------+
1 row in set (2.001 sec)
MariaDB [(none)]> select now(),sleep(2),now();
+---------------------+----------+---------------------+
| now() | sleep(2) | now() |
+---------------------+----------+---------------------+
| 2019-03-28 09:09:33 | 0 | 2019-03-28 09:09:33 |
+---------------------+----------+---------------------+
1 row in set (2.000 sec)
2.內存相關
sort_buffer_size
:每個會話使用的排序緩沖區大小- PS:每個連接都分配這么多eg:1M,100個連接==>100M(默認是全部)
join_buffer_size
:每個會話使用的表連接緩沖區大小- PS:給每個join的表都分配這么大,eg:1M,join了10個表==>10M
binlog_cache_size
:每個會話未提交事物的緩沖區大小read_rnd_buffer_size
:設置索引緩沖區大小read_buffer_size
:對MyISAM全表掃描時緩沖池大小(一般都是4k的倍數)- PS:對臨時表操作的時候可能會用到
read_buffer_size
的擴充說明:
現在基本上都是Innodb存儲引擎了,大部分的MyISAM的配置就不用管了,但是這個還是需要配置下的
引入下臨時表知識擴展:
- 系統使用臨時表:
- 不超過16M:系統會使用
Memory
表 - 超過限制:使用
MyISAM
表
- 不超過16M:系統會使用
- 自己建的臨時表:(可以使用任意存儲引擎)
create temporary table tb_name(列名 類型 類型修飾符,...)
PS:現在知道為啥配置read_buffer_size
了吧(系統使用臨時表的時候,可能會使用MyISAM
)
3.IO相關參數
主要看看Innodb
的IO
相關配置
事物日志:(總大小:Innodb_log_file_size * Innodb_log_files_in_group
)
- 事物日志大小:
Innodb_log_file_size
- 事物日志個數:
Innodb_log_files_in_group
日志緩沖區大小:Innodb_log_buffer_size
一般日志先寫到緩沖區中,再刷新到磁盤(一般32M~128M就夠了)
知識擴展:redo Log
內存中緩沖區的大小:(字節為單位)
show variables like 'innodb_log_buffer_size';
- PS:以字節為單位,每隔1s就會把數據存儲到磁盤上
show variables like 'innodb_log_files_in_group';
- PS:有幾個就產生幾個
ib_logfile
文件(默認是2)
- PS:有幾個就產生幾個
日志刷新頻率:Innodb_flush_log_at_trx_commit
- 0:每秒進行一次日志寫入緩存,並刷新日志到磁盤(最多丟失1s)
- 1:每次交執事物就把日志寫入緩存,並刷新日志到磁盤(默認)
- 2:每次事物提交就把日志寫入緩存,每秒刷新日志到磁盤(推薦)
刷新方式:Innodb_flush_method=O_DIRECT
關閉操作系統緩存(避免了操作系統和Innodb雙重緩存)
如何使用表空間:Innodb_file_per_table=1
為每個innodb建立一個單獨的表空間(這個基本上已經成為通用配置了)
是否使用雙寫緩存:Innodb_doublewrite=1
(避免發生頁數據損壞)
- 默認是開啟的,如果出現寫瓶頸或者不在意一些數據丟失可以不開啟(開啟后性能↑↑)
- 查看是否開啟:
show variables like '%double%';
設置innodb緩沖池大小:innodb_buffer_pool_size
如果都是innodb存儲引擎,這個參數的設置可以這樣來算:(一般都是內存的
75%
)
查看命令:show global variables like 'innodb_buffer_pool_size';
PS:緩存數據和索引(直接決定了innodb性能) 課后拓展:https://www.cnblogs.com/wanbin/p/9530833.html
innodb緩存池實例的個數:innodb_buffer_pool_instances
PS:主要目的為了減少資源鎖增加並發。
每個實例的大小=總大小/實例的個數
一般來說,每個實例大小不能小於1G,而且個數不超過8個
4.其他服務器參數
sync_binlog
:控制MySQL如何像磁盤中刷新binlog- 默認是0,MySQL不會主動把緩存存儲到磁盤,而是靠操作系統
- PS:為了數據安全,建議主庫設置為1(效率也容易降低)
- 還是那句話:一般不去管,具體看業務
- 控制內存臨時表大小:
tmp_table_size
andmax_heap_table_size
- PS:建議保持兩個參數一致
max_connections
:設置最大連接數- 默認是100,可以根據環境調節,太大可能會導致內存溢出
Sleep
等待時間:一般設置為相同值(通過連接參數區分是否是交互連接)interactive_timeout
:設置交互連接的timeout時間wait_timeout
:設置非交互連接的timeout時間
擴展工具:pt-config-diff
使用參考:pt-config-diff u=root,p=pass,h=localhost /etc/my.conf
eg:比較配置文件和服務器配置
pt-config-diff /etc/my.cnf h=localhost --user=root --password=pass
3 config differences
Variable /etc/my.cnf mariadb2
========================= =========== ========
max_connect_errors 2 100
rpl_semi_sync_master_e... 1 OFF
server_id 101 102
課后拓展:https://www.cndba.cn/leo1990/article/2789
擴展:常見存儲引擎
常見存儲引擎:
- MyISAM:不支持事物,表級鎖
- 索引存儲在內存中,數據放入磁盤
- 文件后綴:
frm、MYD、MYI
Innodb
:事物級存儲引擎,支持行級鎖和事物ACID特性- 同時在內存中緩存索引和數據
- 文件后綴:
frm、ibd
Memory
:表結構保存在磁盤文件中,表內容存儲在內存中- Hash索引、B-Tree索引
- PS:容易丟失數據(重啟后數據丟失,表結構依舊存在)
CSV
:一般都是作為中間表- 以文本方式存儲在文件中,不適合大表
- frm(表結構)、CSV(表內容)、CSM(元數據,eg:表狀態、數據量)
- PS:不支持索引(engine=csv),所有列不能為Null
- 詳細可以查看上次寫的文章:小計:協同辦公衍生出的需求
- Archive:數據歸檔(壓縮)
- 文件:
.frm
(存儲表結構)、.arz
(存儲數據) - 只支持
insert
和select
操作 - 只允許在自增ID列上加上索引
適合場景:日志類
(省空間)
- 文件:
- Federated:建立遠程連接表(性能不怎樣,默認禁止)
- 本地不存儲數據(數據全部在遠程服務器上)
- 本地需要保存表結構和遠程服務器的連接信息
- PS:類似於SQLServer的鏈接服務器
逆天點評:除非你有100%的理由,否則全選innodb
,特別不建議混合使用
Memory存儲引擎
Memory存儲引擎:
- 支持
Hash
和BTree
兩種索引- Hash索引:等值查找(默認)
- Btree索引:范圍查找
create index ix_name using btree on tb_name(字段,...)
- PS:不同場景下的不同選擇,性能差異很大
- 所有字段類型都等同於固定長度,且不支持
Text
和Blog
等大字段類型- eg:
varchar(100)
等價於>char(100)
- eg:
- 存儲引擎使用表級鎖
- PS:性能不見得比innodb好
- 大小由
max_heap_table_size
決定(默認16M)- PS:如果想存大點,就得改參數(對已經存在的表不生效,需要重建才行)
- 常用場景(
數據易丟失,要保證數據可再生
)- 緩存周期性聚合數據的結果
- 用於查找或者映射的表(eg:郵編和地區的對應表)
- 保存數據分析中產生的中間表
PS:現在基本上都是redis了,如果不使用redis的小項目可以考慮(eg:官網、博客...)
文章拓展:
OLAP、OLTP的介紹和比較
https://www.cnblogs.com/hhandbibi/p/7118740.html
now()與sysdate()
http://blog.itpub.net/22664653/viewspace-752576/
https://stackoverflow.com/questions/24137752/difference-between-now-sysdate-current-date-in-mysql
binlog三種模式的區別(row,statement,mixed)
https://blog.csdn.net/keda8997110/article/details/50895171/
MySQL-重做日志 redo log -原理
https://www.cnblogs.com/cuisi/p/6525077.html
詳細分析MySQL事務日志(redo log和undo log)
https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html
innodb_flush_method的性能差異與File I/O
https://blog.csdn.net/melody_mr/article/details/48626685
InnoDB關鍵特性之double write
https://www.cnblogs.com/geaozhang/p/7241744.html
存儲引擎的擴展
1.簡單回顧
上節在最后的時候說了下存儲引擎,這邊簡單回顧下:
存儲引擎 | 是否支持事物 | 文字說明 |
---|---|---|
MyISAM |
不支持 | MySQL5.6以前的默認存儲引擎 |
CSV |
不支持 | 用CSV格式來存儲數據(一般當中間表) |
Archive |
不支持 | 只能查詢和添加數據(一般記錄日志用) |
Memory |
不支持 | 數據只存儲在內存中(容易丟失) |
innodb |
支持(行級鎖) | 現在基本上都使用這個 |
NDB |
支持(行級鎖) | MySQL集群才使用(內存型,數據會持久化一份) |
補充說明:
Archive
存儲引擎的數據會用zlib
來壓縮,而且只支持在自增ID上添加索引NDB
存儲引擎的數據存儲在磁盤中(熱數據存儲在內存中),支持Ttree索引和集群- 場景:數據需要完全同步(這些后面會繼續說的)
2.常見場景
提一個場景:innodb
表無法在線修改表結構的時候怎么解決?
先看下Innodb不支持在線修改表結構
都有哪些情況:(主要從性能方面考慮)
- 第一次創建
全文索引
和添加空間索引
(MySQL5.6
以前版本不支持)- 全文索引:
create fulltext index name on table(列,...);
- 空間索引:
alter table geom add spatial index(g);
- 全文索引:
- 刪除主鍵或者添加自增列
- PS:innodb存儲就是按照主鍵進行順序存儲的(這時候需要重新排序)
- 刪除主鍵:
alter table 表名 drop primary key
- 加自增列:
alter table 表名 add column id int auto_increment primary key
- 修改列類型、修改表字符集
- 修改列類型:
alter table 表名 modify 列名 類型 類型修飾符
- 修改字符集:
alter table 表名 character set=utf8mb4
- 修改列類型:
PS:DDL不能並發執行(表級鎖)長時間的DDL操作會導致主從不一致
DDL沒法進行資源限制,表數據多了容易占用大量存儲IO空間(空間不夠就容易執行失敗)
3.解決方案
安裝:yum install percona-toolkit
or apt-get install percona-toolkit
PS:離線包:
https://www.percona.com/downloads/percona-toolkit/LATEST/
命令:pt-online-schema-change 選項 D=數據庫,t=表名,u=用戶名,p=密碼
原理:先創建一個類型修改完的表,然后把舊表數據copy過去,然后刪除舊表並重命名新表
查看幫助文檔:pt-online-schema-change --help | more
官方文檔:https://www.percona.com/doc/percona-toolkit/LATEST/pt-online-schema-change.html
PS:一般就--alter
和--charset
用的比較多(--execute
代表執行)
常用:pt-online-schema-change --alter "DDL語句" --execute D=數據庫,t=表名,u=用戶名,p=密碼
eg:添加新列:
pt-online-schema-change --alter "add 列名 類型" --execute D=數據庫,t=表名,u=用戶名,p=密碼
知識回顧:
- 添加字段:add
alter table tb_name add 列名 數據類型 修飾符 [first | after 列名];
- PS:SQLServer沒有
[first | after 列名]
- 修改字段:alter、change、modify
- 修改字段名:
alter table tb_name change 舊列名 新列名 類型 類型修飾符
- 修改字段類型:
alter table tb_name modify 列名 類型 類型修飾符
- 添加默認值:
alter table tb_name alter 列名 set default df_value
- 修改字段名:
- 刪除字段:drop
alter table tb_name drop 字段名
4.InnoDB專欄
寫在前面的概念:排它鎖(別名:獨占鎖、寫鎖)、共享鎖(別名:讀鎖)
4.1.innoDB是如何實現事物的?
事物4大特性:A(原子性)C(一致性)I(隔離性)D(持久性)
innodb事務日志主要就是redo log
(重做日志)和undo log
(回滾日志)
事物特性 | innodb實現方式 |
---|---|
原子性(A) | 回滾日志(undo log ):用於記錄數據修改前的狀態 |
一致性(C) | 重做日志(redo log ):用於記錄數據修改后的狀態 |
隔離性(I) | 鎖(lock ):用於資源隔離(共享鎖 + 排他鎖) |
持久性(D) | 重做日志(redo log ) + 回滾日志(undo log ) |
我畫個轉賬案例:
4.2.innodb讀
操作是否會阻塞寫
操作?
一般情況下:查詢
需要對資源添加共享鎖(讀鎖) | 修改
需要對資源添加排它鎖(寫鎖)
是否兼容 | 寫鎖 |
讀鎖 |
---|---|---|
寫鎖 |
不兼容 | 不兼容 |
讀鎖 |
不兼容 | 兼容 |
PS:共享鎖和共享鎖之間是可以共存的(讀的多並發)理論上講讀操作和寫操作應該相互阻塞
而innodb
看起來卻仿佛打破了這個常規,看個案例:
1.啟動一個事物,但是不提交
2.在另一個連接中查詢
PS:理論上獨占鎖沒提交時是不能讀操作的,但innodb
做了優化,會查詢undo log
(未修改前的數據)中的記錄來提高並發性
3.提交事物后再查詢,這時候就看到更新后的數據了
PS:這個就是innodb的MVCC
(多版本並發控制)
知識拓展:
【推薦】Mysql的InnoDB事務多版本並發控制如何實現(MVCC)
https://www.cnblogs.com/aspirant/p/6920987.html
https://blog.csdn.net/u013007900/article/details/78641913
https://www.cnblogs.com/dongqingswt/p/3460440.html
https://www.jianshu.com/p/a3d49f7507ff
https://www.jianshu.com/p/a03e15e82121
https://www.jianshu.com/p/5a9c1e487ddd
基於mysql全文索引的深入理解
https://www.cnblogs.com/dreamworlds/p/5462018.html
【推薦】MySQL中的全文索引(InnoDB存儲引擎)
https://www.jianshu.com/p/645402711dac
innodb的存儲結構
https://www.cnblogs.com/janehoo/p/6202240.html
深入淺出空間索引:為什么需要空間索引
https://www.cnblogs.com/mafeng/p/7909426.html
常見的空間索引方法
https://blog.csdn.net/Amesteur/article/details/80392679
【推薦】pt-online-schema-change解讀
https://www.cnblogs.com/xiaoyanger/p/6043986.html
pt-online-schema-change使用說明、限制與比較
https://www.cnblogs.com/erisen/p/5971416.html
pt-online-schema-change使用注意要點
https://www.jianshu.com/p/84af8b8f040b
詳細分析MySQL事務日志(redo log和undo log)
https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html
1.6.4.MySQL權限相關
1.賬號權限設置
之前在SQL環境篇的時候簡單提了一下權限設置(點我回顧),現在再說說常用的權限知識:
1.2.創建賬號
用戶組成格式:用戶名@可訪問控制的列表
- 用戶名:一般16字節
- 以
UTF-8
為例:1英文字符 = 1字節,1中文 = 3字節
- 以
- 可訪問控制列表:
%
:所有ip都可訪問(一般都這么干的,數據比較重要的推薦使用第二種)192.168.1.%
:192.168.1
網段的ip都可以訪問- 這個不包含
localhost
(數據庫本地服務器不能訪問)
- 這個不包含
localhost
:只能通過數據庫服務器進行本地訪問
1.創建命令:create user 用戶名@ip identified by '密碼';
PS:可以使用
\h create user
來查看幫助文檔
2.查看當前用戶:select user();
PS:MariaDB
查看當前數據庫有哪些用戶:select user,password,host from mysql.user;
MySQL:
select user,authentication_string,host from mysql.user;
3.修改密碼:alter user user() identified by '密碼';
4.另類思路:我一般都是直接在表中插入數據(MySQL是authentication_string
)
eg:
insert into mysql.user(user,host,password) values("用戶名","%",password("密碼"));
PS:修改密碼:
update mysql.user set
password=password('新密碼') where user='用戶名';
知識拓展:ERROR 1045 (28000): Access denied for user 'mysql'@'localhost'
1.3.常用權限
權限類別 | 語句 | 說明文字 |
---|---|---|
admin |
create user |
創建新用戶權限 |
- | grant option |
為用戶設置權限 |
- | super |
設置服務器權限 |
DDL |
create |
創建數據庫和表 |
- | alter |
修改表結構權限 |
- | index |
創建和刪除索引 |
- | drop |
刪除數據庫和表 |
DML |
select |
查詢表數據權限 |
- | insert |
插入表數據權限 |
- | update |
刪除表數據權限 |
- | execute |
可執行存儲過程 |
- | delete |
刪除表數據權限 |
補充說明:super
:如設置全局變量等系統語句,一般DBA會有這個權限
PS:MariaDB查看數據庫支持哪些權限:show privileges;
1.4.用戶授權
權限這個東西大家都懂,一般都是最小權限
授權命令如下:grant 權限列表 on 數據庫.表 to 用戶名@ip
PS:開發的時候可能為了省事這么設置:
grant all [privileges] on 數據庫.* to 用戶名@'%';
正規點一般這么設置:
- 線上:
grant select,insert,update on 數據庫.* to 用戶名@ip
- 開發:
grant select,insert,update,index,alter,create on 數據庫.* to 用戶名@ip段
PS:查看當前用戶權限:show grants for 用戶名;
,刷新數據庫權限:flush privileges;
以前可以在授權的時候直接創建用戶(加一段
identified by '密碼'
),新版本好像分開了
1.5.權限收回
命令如下:revoke 權限列表 on 數據庫.表 from 用戶名@ip
eg:
revoke create,alter,delete from django.* from dnt@'%'
(是from
而不是on
)
2.數據庫賬號安全
這個了解即可,我也是剛從DBA朋友那邊了解到的知識(MySQL8.0
),基本上用不到的,簡單羅列下規范:
- 只給最小的權限(線上權限基本上都是給最低的(防黑客))
- 密碼強度限制(MySQL高版本默認有限制,主要針對MariaDB)
- 密碼有期限(謹慎使用,不推薦線上用戶設置有效期)
- 歷史密碼不可用(不能重復使用舊密碼)
- PS:現在用BAT的產品來修改密碼基本上都是不讓使用上次的密碼
設置前三次使用過的密碼不能再使用:create user@'%'identified by '密碼' password history 3;
PS:設置用戶密碼過期:alter user 用戶名@ip password expire;
3.遷移問題
經典問題:如何從一個實例遷移數據庫賬號到另一個實例?
- eg:老集群 > 新集群
官方文檔:https://www.percona.com/doc/percona-toolkit/LATEST/pt-show-grants.html
3.1.版本相同
數據庫備份下,然后在新環境中恢復
然后導出用戶創建和授權語句:eg:pt-show-grants -u=root,-p=密碼,-h=服務器地址 -P=3306
擴展文章:pt-show-grants的使用(eg:
pt-show-grants --host=192.168.36.123 --port=3306 --user=root --password=密碼
)
生成的腳本大致是這樣的:(把腳本放新服務器中執行即可)
CREATE USER IF NOT EXISTS 'mysql.sys'@'localhost';
ALTER USER 'mysql.sys'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE' REQUIRE NONE
PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;GRANT SELECT ON `sys`.`sys_config` TO 'mysql.sys'@'localhost';
GRANT TRIGGER ON `sys`.* TO 'mysql.sys'@'localhost';
GRANT USAGE ON *.* TO 'mysql.sys'@'localhost';
-- Grants for 'root'@'%'
CREATE USER IF NOT EXISTS 'root'@'%';
ALTER USER 'root'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9' REQUIRE NONE PASSWORD EXPI
RE DEFAULT ACCOUNT UNLOCK;GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
3.2.版本不同
可以使用上面的方法,但是需要使用mysql_upgrade
升級下系統表(適用:低版本到高版本)但是推薦使用生成SQL腳本