mysql性能的處理方式:
異步(日志記錄),備份(從庫),版本號(事務),標識(鎖),多線程(鏈接池),批量處理(批量排序按索引查詢),內存(各種buffer),排序(索引),盡可能的記錄標識(異常重啟之后根據標識判斷分情況還原處理)
遺忘的概念:
1.鎖
全局鎖:是給整個庫加鎖
表鎖:給整個表加鎖
行鎖:給掃描到的所有行加鎖(一個沒有索引的update會給整個表現有的行加鎖)
行鎖加鎖和釋放的原則:
? ? ? 讀已提交隔離級別下
? ? ? select * from t where d = 5 for update (d列沒索引)(就算d列有索引根據掃描的過程也可能掃描到多行)
? ? ? sql執行過程中:掃描全表,這時掃描到的行都加鎖 掃描到的間歇也加間歇鎖 就是next_key lock
? ? ? sql執行完之后:不滿足條件的行 d != 5 的行 next_key lock都會被去掉
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? d = 5 的行還是得等到事務提交時行鎖才釋放
間歇鎖:給掃描到的索引區間加鎖
行鎖用來鎖定涉及到的行(保證一致性),間歇鎖用來鎖定涉及到的區間(防止幻讀)可以根據業務場景反推為什么這么用鎖
2.mvcc處理事務,是根據版本號對比處理,不一樣的事務加版本號的時機不一樣
3.事務的隔離級別是就當前事務而言的
4. MVCC控制事務的讀取的可見性,鎖是用來控制讀寫造成的沖突,鎖和mvcc共同實現事務的效果
5.索引是一種數據結構,數據行都在主鍵索引下邊,普通索引下是主鍵id
數據行 數據頁的概念
6.可重復讀級別下會觸發"間歇鎖"
7.只有當前讀才有幻讀的問題? 有了間歇鎖當前讀也不會出現幻讀 因為數據insert不了
普通的讀mvcc就能解決,當前讀需要next_key lock解決
8.可重復讀就解決了幻度,串行化是通過表鎖解決的
三范式? 表設計? 字段屬性選用? 索引設計
了解HBASE
分庫 分表的利弊
一、Mysql的結構
1.mysql中數據庫與數據庫實例的區別
數據庫是文件的集合,是依照某種數據模型組織起來存放于二級存儲器中的數據的集合
數據庫實例是程序,所有對數據庫的操作必須在數據庫實例下進行
2.mysql數據庫的組成
連接池、管理服務和工具的組件、sql接口組件、查詢分析器、優化器、緩沖組件、存儲引擎、物理文件
存儲引擎:是基于底層物理結構的實現,是基于表的不是數據庫
3.innodb的介紹
特點:支持行鎖設計、mvcc、外鍵、提供一致性非鎖定讀、能有效利用內存和cpu
3.1innodb的架構
包括:多個后臺線程、和內存池(緩存)
后臺線程保證內存池中的數據和緩存中的數據一致,將緩存中修改過的數據刷新到數據庫,同時將數據最新的數據同步到緩存
3.1.1后臺線程
Master Thread 將緩存池中的數據異步刷新到磁盤,保證數據一致性
IO Thread innodb中大量使用AIO來操作數據庫讀寫,write、read、insertBuffer、logThread
Purge Thread
3.1.2innodb內存
innodb的緩存
為了彌補磁盤操作較慢的問題,innodb對數據庫加了一步緩存操作
1.讀取數據,在數據庫讀取頁的操作時,首先將從磁盤讀到的頁放到緩存池當中,這個過程稱將頁“FIX”在緩存池中,下次讀取相同的頁時先判斷該頁是否在緩存池中。若在緩存池中則命中直接讀取該頁,若沒有則從磁盤中讀取。
2.修改數據,首先修改在緩存池中的頁,然后再以一定的頻率刷到磁盤上,不是每次更新都會觸發緩存和磁盤的同步,而是通過一種Checkpoint的機制。
連接池
HikariCP:目前速度最快的連接池,優化了代理和攔截器,簡化了代碼 新增了fastList,適合做監控
Durid:經過阿里大規模數據的檢驗比較可靠
DBCP/C3P0速度比價慢
按照效率排序的話,count(字段)<count(主鍵 id)<count(1)≈count(*)
二、sql的執行過程
2.1查詢sql的執行過程
連接器(連接數據庫)、(緩存執行請求先走緩存(8.0之前))、分析器(分析請求類型、表、列 檢查sql的正確性)、優化器(選擇高效率的執行策略)、執行器(調用存儲引擎的接口獲取數據)
2.2修改sql的執行過程
update T set c=c+1 where id = 2? 數據表使用了innodb
同樣是經歷 連接器 緩存 分析器 優化器 執行器
先從緩存或磁盤中取出id=2的數據,讓后將取出的c的值加一
將修改后的這條數據更新到innodb的內存中同時將更新的內容更新到redo日志中,此時redo是prepare狀態,然后告知執行器執行完了,隨時可以提交事務
然后執行器生成這波操作的binlog日志),當當前這個事務提交后redo log和binlog同時完成寫入
這里redo log 和 binlog是分兩階段提交的,先是準備后是提交,兩個日志要么都寫入成功要么都寫入不成功
redo log 和 binlog的區別:
redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實現的,所有引擎都可以使用。
redo log是物理日志(保證事務的持久性),記錄的是“在某個數據頁上做了什么修改”;binlog 是邏輯日志,記錄的是這個語句的原始邏輯,比如“給 ID=2 這一行的 c 字段加 1 ”。
redo log 是循環寫的,空間固定會用完;binlog 是可以追加寫入的。“追加寫”是指 binlog 文件寫到一定大小后會切換到下一個,并不會覆蓋以前的日志。
Binlog有兩種模式,statement 格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新后都有
測試:從redo log功能角度解釋一下innodb的緩存
check point 的觸發時機:redo log 要寫滿了、緩存不足、空閑時間、MySQL關閉
2.3change buffer
使用場景:
1.當要修改一條數據,
2.這條數據在內存中不存在
3.先將修改的操作記錄在change buffer中
4.當再次讀取這條數據時,先將數據讀取到內存中再用change buffer中記錄的邏輯來修改內存中的數據
如果內存中存在就直接修改內存了? 唯一索引因為必須要讀取數據判斷數據是否沖突所以不適用這種場景,change buffer 就是為了減少磁盤的讀取io? rodo log是為了減少寫io
思考問題點:
1.innodb中 緩存,change buffer,redo log ,磁盤的關系
2.change buffer的使用場景(普通索引,寫操作大于讀操作)
2.4查詢中的排序算法(臨時表)
核心:通過sort_buffer將order by的列存起來然后做排序 再根據排序好的列來取數據拼成結果集
查詢的列很少 走內存臨時表
select word from t where time <2020-05-06 order by word;
內存臨時表的儲存引擎不是innodb所以需要“位置"這種數據
如果有分頁,會將符合條件的數據都查出然后計算需要返回多少行到多少行
如果where條件和要排序的列屬于同一個索引那就不需要排序,因為拿到的數據就是有序的
查詢的列很多就根據索引走磁盤
select city,name,age,id_card, address,time from t where city = "杭州" order by name;
大查詢會不會把MySQL內存打爆?
不會
mysql采用的策略是“邊讀邊發”,mysql引入netBuffer的概念,一塊內存空間,每次將讀取到的數據放入netBuffer中netBuffer滿了之后調用網絡接口將數據發送出去然后將netBuffer清空進入下一輪的讀取,如果本地網絡棧寫滿了就等待直到網絡棧重新可寫再次發送
三、數據庫的事務
讀未提交 讀已提交 可重復讀 可重復讀是站在當前事務的角度來說的
事務的屬性:acid 原子性? 一致性 隔離性 持久性
事務解決的問題:臟讀 不可重復讀 幻讀
四個隔離級別:
讀未提交:可以讀取到未交的數據
讀已提交:可以讀取到提交后的數據
可重復讀:在A事務中對某條數據讀取了兩次,這兩次中間B事務對該條數據做了修改,但是A事務兩次讀取到的還是一樣的數據(MySQL默認的隔離級別)
串行化:讀加讀鎖,寫加寫鎖
我們來看看在不同的隔離級別下,事務 A 會有哪些不同的返回結果,也就是圖里面 V1、V2、V3 的返回值分別是什么。
若隔離級別是“讀未提交”, 則 V1 的值就是 2。這時候事務 B 雖然還沒有提交,但是結果已經被 A 看到了。因此,V2、V3 也都是 2。
若隔離級別是“讀提交”,則 V1 是 1,V2 的值是 2。事務 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。
若隔離級別是“可重復讀”,則 V1、V2 是 1,V3 是 2。之所以 V2 還是 1,遵循的就是這個要求:事務在執行期間看到的數據前后必須是一致的。
若隔離級別是“串行化”,則在事務 B 執行“將 1 改成 2”的時候,會被鎖住。直到事務 A 提交后,事務 B 才可以繼續執行。所以從 A 的角度看, V1、V2 值是 1,V3 的值是 2。
隔離級別的實現方式:視圖(read-view)和鎖、回滾日志
數據庫里會創建一個視圖,訪問的時候以視圖的邏輯為準
讀未提交:直接返回記錄上的最新值
讀已提交:在SQL語句執行的時候創建
可重復讀:在事務開始的時候創建
串行化:直接通過加鎖的方式來避免并行訪問,當讀寫鎖沖突的時候,后一個事務等前一個事務執行完在執行
回滾日志:
MySQL的每次變更都會記錄一條對應的回滾操作,記錄上的最新值可以通過回滾操作得到前一個 狀態的值
當一個事務回滾時,可一個通過回滾操作恢復數據
當沒有視圖需要回滾日志時,回滾日志就會刪除
事務的詳解
請仔細看一遍:第八講事務到底是隔離的還是不隔離的
MVCC一行數據只有一個記錄不是有多個紀錄,但是會記錄當前最新的版本號,如果想看到之前的版本需要根據undo log 來計算
行的版本號和事務的id是一致的
可重復讀的事務里有修改語句修改語句是當前讀
MVCC適用于“可重復讀”,思考 讀未提交,讀已提交,insert語句 是怎么處理可見性的
長事務對MySQL的影響?
一個事務的執行時間過長,在整個事務中回滾段一直未被清理,大量的回滾日志占用很多磁盤空間
同時還占用鎖資源,極大加重了整個庫的壓力
幻讀的概念
讀到了“新插入的數據行”
同一個事務中兩次查詢第二次查到了新插入的數據行(普通的查詢是“快照讀”,“當前讀”的情況下才會出現“幻讀”)
最后一行出現了幻讀
四、索引
InnoDB 的數據是保存在主鍵索引上的
平衡二叉樹 B+樹? https://www.toutiao.com/a6624750730471277059/
索引覆蓋 :想要查詢的結果集就是索引不用回表
索引最左原則:ABC索引 適合 A? AB ABC 也是適合 like "xx%"
索引下推 :聯合索引 "name,age"? select * from custom where name like "張%" and age = 10;
為什么主鍵自增要好?
1.自增起始數據小占用空間小
2.索引采用的是B+樹,數據自增可以減少“分裂”和“合并”
? 分裂:當出現中間數據時,B+樹會觸發分裂,MySQL也會申請新的數據將原來數據頁中分出的數據拷貝過去,這個過程比較消耗資源同時原來的數據頁未被使用完浪費了空間
? 合并:當相鄰的兩個數據頁刪除了數據,可能觸發合并是分裂的逆過程同樣消耗性能
為什么B+樹適合建索引?
1.B+樹非葉子節點沒有關鍵字指針只存有關鍵字,非葉子節點可以存放更多的數據,所有樹的高度比較低,查詢的速度更快
2.非葉子節點存放的都是索引,數據都存放在葉子節點,方便掃庫也適合區間查詢,像B樹非葉子節點也存放有數據,找到具體的數據需要進行中序遍歷才可以
普通索引和唯一索引在查詢性能上的差異大不大?
不大
1.唯一索引在索引樹上讀到符合條件的數據就不再讀取
2.普通索引是讀取到符合條件的數據之后會接著讀取下一條,直到讀到不符合條件的數據為止
3.innodb數據的讀取是以頁為單位來讀取的,一次會將一頁數據從磁盤中全部讀出一頁默認大小16kb
所以差別不大
字符串索引的三個小技巧:
1.采用前綴索引,將字符串的前幾個值作為索引,可以減少空間占用
2.像身份證號這種數據 前面的數字都相同后邊幾位不同,存儲的時候可以倒著存儲然后用前綴索引
3.將長字符串取hash值通過hash值建立索引
where條件中有函數不走索引:
select * from tradelog where tradeid=110717;(tradeild列是varchar類型有索引,但是這條語句卻沒走索引)
select * from tradelog where? CAST(tradid AS signed int) = 110717;(因為tardeild是varchar類型所以優化器需要將110717轉換為字符串類型,然后再執行,轉換使用了函數所以不適用索引)
兩個表的字符集不同,一個是 utf8,一個是 utf8mb4,所以做表關聯查詢的時候用不上關聯字段的索引
兩個表字符集不同在優化器會使用函數修改sql將關聯的那一列的數據的字符集修改為一樣的,因為使用了函數所以不走索引
五、數據庫鎖
全局鎖:FTWRL將整個庫設置成只讀狀態,增刪改,建表,修改表的操作都不能執行,影響范圍很大,同時也影響主從同步;如果是innodb可以使用事務(可重復讀)也可以實現多次讀是一樣的數據
表鎖:
顯示加鎖,lock tables T read/write
非顯示加鎖,MDL 當訪問表時(增刪改查)數據庫默認加上表鎖讀鎖,當對數據表有結構修改時默認加上表鎖寫鎖,讀鎖之間不互斥可以有多個,讀鎖和讀鎖,寫鎖和讀鎖之間互斥
修改表結構導致鎖表的問題 就是因為讀寫鎖互斥導致的,寫鎖沒有獲取到會一直阻塞后邊的請求,當寫鎖獲取到后 執行了修改表結構的語句 后邊的業務語句才能執行
如何安全的給表加字段?
1.執行alter table語句之前確定沒有長事務
2.執行語句中加入等待時間,在等待時間內沒有獲取到寫鎖,先不執行 讓業務語句執行;
行鎖:給掃描到的所有行加鎖,行鎖是存儲引擎層面的概念不同的存儲引擎有不同的行鎖實現方式,MyISAM沒有行鎖
事務中的行鎖,在sql執行時創建在事務提交后釋放
行鎖死鎖的例子:
如何解決死鎖問題?
1.設置鎖的等待時間 innodb_lock_wait_timeout 超過時間 第一個被鎖住的線程要過 50s默認 才會超時退出,然后其他線程才有可能繼續執行
2.設置死鎖檢測 參數 innodb_deadlock_detect 設置為 on 發現死鎖后,主動回滾死鎖鏈條中的某一個事務,讓其他事務得以繼續執行
3.也可以從數據表的設計上避免,像進行賬號金額的操作,將一個賬號改為多個賬號,每次需要處理余額可以隨機選取一個賬號來處理,最終所有的賬號余額的和為總賬號的余額,這樣減少了行死鎖的概率;
當前讀的概念:
修改語句是當前讀,是先查詢再修改
即使一個修改語句在可重復讀的事務里,也會去讀取最新的已提交的數據
修改語句同時會給涉及的行加寫鎖,(沒有事務的情況下)語句執行完鎖釋放
select語句后加for update也是同樣的操作(當前讀 加寫鎖)
mysql是如何運用行鎖、間歇鎖解決幻讀的?
update t set c = 5 where d = 2;
update語句會添加寫鎖
假如d列沒有索引,就會觸發全局掃描,mysql會給所有掃描到的所有行加鎖
insert into t (id , c , d),(1,4,2);
新插入一條d=5的數據,新插入的數據沒有被掃描到不會加鎖(容易引發幻讀)
間歇鎖:將涉及到的行的區間給加鎖(包括兩端)
下圖中間歇鎖 鎖定的區間有(-負無窮,0],(0,5],(5,10],(10,15],(15,20],(20,25],(25,正無窮]
有了間歇鎖就能解決幻讀的問題
間歇鎖的兩原則,兩優化,一個bug
原則 1:加鎖的基本單位是 next-key lock。希望你還記得,next-key lock 是前開后閉區間。
原則 2:查找過程中訪問到的對象才會加鎖。
優化 1:索引上的等值查詢,給唯一索引加鎖的時候,next-key lock 退化為行鎖。
優化 2:索引上的等值查詢,向右遍歷時且最后一個值不滿足等值條件的時候,next-key lock 退化為間隙鎖。
一個 bug:唯一索引上的范圍查詢會訪問到不滿足條件的第一個值為止。
間歇鎖鎖住行的范圍更大,更容易導致死鎖
六、表空間
1.innodb中表數據的存儲方式
表結構:MySQL8.0前是存放在單獨的文件里,8.0之后是存儲在系統數據表里
表數據:可以通過配置選擇 一種是存儲在共享表空間,一種是一張表一個文件的形式存儲
為什么刪除了表數據表空間大小沒變?
mysql中數據是以數據頁的形式存儲
當刪除一行數據時,會將這個行標記為可復用
當刪除也一頁數據時會將這個頁標記為可復用
所以刪除數據后占用的表空間不會變
如果真正的想收縮表空間可以通過重建表的方式實現,重建的過程中會重建索引重新分配內存
? ? ? 命令:alter table A engine=InnoDB? 這是一個DDL的過程全程不能用寫和修改的操作
? ? ? mysql5.7之后有online DDL的操作 重建表的過程中允許添加和修改,它會把修改和添加的操作記錄到日志文件中,重建完成之后將日志里的邏輯執行一遍
MDL:表級鎖
DML:指的是增刪改查的操作
DDL:指的是修改表結構的操作
七、性能分析
一條簡單的查詢語句查詢速度很慢
mysql> CREATE TABLE `t` (
? `id` int(11) NOT NULL,
? `c` int(11) DEFAULT NULL,
? PRIMARY KEY (`id`)
) ENGINE=InnoDB;
select * from t where id=1;
1.等DML鎖
show processlist 可以查看當前sql語句的執行情況
是表被鎖住了,找到鎖表的線程直接kill掉
2.等flush
flush tables t;
本身flush執行速度很快,可是flush如果被其他事物阻塞了,flush就會阻塞新的查詢語句
其他事務中對t表有操作,flush會等待其他事務執行完再對t表進行flush
這個時候還是通過 show processlist命令查看當前sql執行情況
3.等待行鎖
select * from t where id = 2; select語句默認不加讀鎖
select * from t where id = 2lock in share mode;顯式加讀鎖
當前的行在其他事務中有寫鎖,這條語句就會等待寫鎖釋放才能讀取
4.數據量很大也沒有索引
5.一致性讀,讀取的是老版本的數據,其他事務做了大量修改,需要執行大量的回滾日志才能看到老數據;
短連接風暴
一般的短連接模式就是,連接數據庫之后執行很少的sql后就斷開,再次需要的時候再次連接
風險:一旦數據庫處理得慢一些,連接數就會暴漲超過了max_connections其他業務就不能連接數據庫
弊端:MySQL 建立連接的過程,成本是很高的。除了正常的網絡連接三次握手外,還需要做登錄權限判斷和獲得這個連接的數據讀寫權限。
處理:show prossicelist 查看空閑的連接直接kill掉
? ? ? ? ? 為設置wait timeout
慢查詢性能問題
在 MySQL 中,會引發性能問題的慢查詢,大體有以下三種可能:
索引沒有設計好;創建索引,修改表結構會觸犯表鎖 但是現在執行 online DDL,同時可以采用在備份庫中執行alter table 然后切換主備? 再在另外一個庫中執行alter table
SQL 語句沒寫好; sql優化、行鎖、表鎖、flush
MySQL 選錯了索引。 可以強制使用某個索引
如何判斷一個數據庫還能否正常使用?
select 1 判斷進程是否正常
select * from t 判斷查詢是否正常
查看updateTime(類型timestamp)字段 判斷修改是否正常
可以通過命令查看磁盤io時間,如果頻繁出現超時就是有性能問題
庫里的表越多連接是否就越慢?
否
連接的過程中服務端就三個功能:TCP握手,用戶校驗,獲取權限
這三個功能點和表的數據量無關所以不會影響速度
客戶端會有補全表名,補全庫名的功能,這個功能需要需要構建本地哈希表 這個和表的數量有關
sql優化
1.查詢一個大表沒有索引
建立索引
2.關聯查詢的是一個冷數據大表沒有索引
t2表數據行超過200萬行,b字段沒有索引
select * from t1 join t2 on (t1.b=t2.b) where t2.b>=1 and t2.b<=2000;
改為:
create temporary table temp_t(id int primary key, a int, b int, index(b))engine=innodb;
insert into temp_t select * from t2 where b>=1 and b<=2000;
select * from t1 join temp_t on (t1.b=temp_t.b);
大表索引占用空間較大,又是冷數據所以沒必要建立索引
可以建立臨時表,給臨時表建立索引
讓sql可以走BKA算法
3.保潔商家打查詢案例BAK
查詢封裝商家基本信息和商家其他信息
crm_custom? 商家基本信息表
custom_id
crm_custom_sign? 商家簽約表
custom_id
service_id 201
crm_custom_level? 商家等級表
custom_id
crm_custom_category_status 商家狀態表
custom_id
其他表
原始sql:
select * from crm_custom a left join crm_custom_sign b on a.custom_id = b.custom_id
left join crm_custom_level c on a.custom_id = c.custom_id left join crm_custom_category_status d on a.custom_id = d.custom_id
where b.service_id = 201 limit0,30;
一百多萬數據得執行八個小時
優化后:
customIds =
select a.custom_id from crm_custom a left join crm_custom_sign b where a.custom_id = b.custom_id where b.service_id = 201 order by a.custom_id desc limit 0,30 (crm_custom_sign 的custom_id有索引)? 這一步走了索引覆蓋
customBeans=
select * from crm_custom from custom_id in (customIds);
這一步走了主鍵索引而且參數是有序的 BKA
for循環customBeans 根據customId去各個表中獲取想要列?
這一步走了普通索引
優化后這個流程十分鐘
4.分庫分表后使用臨時表優化查詢
t 表通f列做key分發到24個庫上
select * from t where a > M order by b limit 0,100;
由于a不是key所以會去24個庫分別執行這個sql
正常思路:在24個庫執行完sql拿到 24*100 = 2400條數據 然后通過程序按照b字段進行排序,然后取出前100條
使用臨時表的方式:
在24個庫中執行sql取出2400條數據
選擇一個庫創建臨時表 create temporaty t_temp
將查詢到的2400放入臨時表
在臨時表上執行sql select * from t_temp where a>M order by b limit 0,100;
這種場景使用臨時表的好處:
分庫后如果數據量較大又有 join order by 這樣的復雜查詢 如果使用程序處理對內存 CPU要求很高
如果使用臨時表能夠直接使用mysql來處理 mysql本身有很好的優化方案
5.盡量使用普通索引不使用唯一索引
6.limit分頁參數過大為什么很慢?
原因:select * from t limit 300000,10; 需要掃描 300000+10行
? ? ? ? ? select * from t limit 0,10; 需要掃描10行
解決方法:
記錄上次查詢的最大id:a
select * from t where id > a order by id asc limit 0,10;
八、主備一致和讀寫分離
A庫:主庫? B庫:備庫(m-s結構)
核心是通過binlog來實現主備一致
1.在備庫B上通過change master命令,設置主庫A的IP、端口、用戶名、密碼以及從哪個位置開始請求binlog日志,這個位置包含文件名和日志偏移量
2.在備庫上執行startslave命令這時候會啟動兩個線程,io_thread 和 sql_thread,其中io_thread負責與主庫建立連接
3.主庫校驗完用戶名和密碼之后通過備份庫B傳過來的位置,從本讀取binlog發送給B
4.備份庫B拿到binlog后開始寫本地文件,叫"中轉日志"relay log
5.sql_threda讀取中轉日志,解析日志里的命令然后執行
A和B庫互為主備關系(雙M結構)
MySQL 在 binlog 中記錄了這個命令第一次執行時所在實例的 server id
從節點 A 更新的事務,binlog 里面記的都是 A 的 server id;
傳到節點 B 執行一次以后,節點 B 生成的 binlog 的 server id 也是 A 的 server id;
再傳回給節點 A,A 判斷到這個 server id 與自己的相同,就不會再處理這個日志。所以,死循環在這里就斷掉了。
一般情況下將從庫設置為only read,但是主備同步使用的是super 用戶,只讀對super用戶無效
binlog記錄日志的三種形式
1.statement 記錄執行的sql
2.row 根據主鍵記錄數據的變化
3.mixed是statement和row的綜合(根據情況選擇使用statement或者row)
三種方式的比較:
安全:
delete from t where a = 1 and b > "2020.02.02" limit 1;(a有索引,b也有索引)
如果采用statement 同樣一條sql在不一樣的庫中可能刪除了不一樣的行,因為走了不一樣的索引
如果采用row就不會出現這樣的問題
如果采用mixed數據庫會根據安全情況主動選擇row
性能:
statement:只需要記錄相關sql就好了
row:需要記錄所有涉及到的行 如果一條sql涉及十萬行就得記錄十萬行
主備延遲的原因
備庫的同步在一段時間內完全被堵住
1.主庫起了一個大事務(包括大表的DDL、or一個事務操作很多行)
2.備庫起了一個長事務,長時間占用線程
未完全堵住延遲時間較長
1.主庫是多線程,從庫是單線程,從庫跟不上更新的速度
2.還有就是一些主從復制的策略也會影響
在主從的基礎上可以實現讀寫分離
如何解決讀寫分離中從庫延遲的問題?(數據同步的延遲)
1.強制走主庫
對應一致要求特別高的數據讀寫都走主庫
2.sleep
讀取從庫的數據前先sleep一下保證數據已經同步完畢
3.判斷主備無延遲方案
第一種方法:每次從庫執行查詢請求前,先判斷 seconds_behind_master 是否已經等于 0。如果還不等于 0 ,那就必須等到這個參數變為 0 才能執行查詢請求(不完全準 單位是秒)
第二種方法,對比位點確保主備無延遲:保證binlog同步完也都執行了
Master_Log_File 和 Read_Master_Log_Pos,表示的是讀到的主庫的最新位點;
Relay_Master_Log_File 和 Exec_Master_Log_Pos,表示的是備庫執行的最新位點。
如果 Master_Log_File 和 Relay_Master_Log_File、Read_Master_Log_Pos 和 Exec_Master_Log_Pos 這兩組值完全相同,就表示接收到的日志已經同步完成。
第三種方法,對比 GTID 集合確保主備無延遲:確認binlog都同步完也都執行了
Auto_Position=1 ,表示這對主備關系使用了 GTID 協議。
Retrieved_Gtid_Set,是備庫收到的所有日志的 GTID 集合;
Executed_Gtid_Set,是備庫所有已經執行完成的 GTID 集合。
如果這兩個集合相同,也表示備庫接收到的日志都已經同步完成。
(第二種、第三種方法比一種準 但是還不夠 因為主庫執行完的日志可能還沒完全提交過來)
4.配合semi-sync
簽收機制
事務提交的時候,主庫把 binlog 發給從庫;
從庫收到 binlog 以后,發回給主庫一個 ack,表示收到了;
主庫收到這個 ack 以后,才能給客戶端返回“事務完成”的確認。
判斷主備無延遲方案? 配合semi-sync? 配合使用可以保證主從的一致
5.GTID方案
數據庫開啟GTID模式
等待最新的事務同步完再執行查詢
九、join語句的執行過程
select * from t1 straight_join t2 on (t1.a=t2.a);? t2 的a字段不是主鍵但是有索引
sql執行過程
1.從t1表中讀取一行數據R
2.從數據行R中取出字段a到t2表中去找
3.找到符合條件的行,和R組成一行作為結果集的一部分
4.重復執行1到3知道t1表的結尾循環結束
分析:
t1表是驅動表? t2是被驅動表
1.對t1表做了全表掃描 全表行數h1
2.而對每一行R,根據R中的a去t2表搜索走了索引,假如a在t2表中不重復,那么t2表業掃描了h1行
3.所以整個流程掃描了2h1行數據
上邊這個sql中假如t1表有N行? t2表有M行
時間復雜度為? o(n) = N + N*2*log2M;
N對時間復雜度的影響是主要的 所有需要小表驅動大表
select * from t1 straight_join t2 on (t1.a=t2.b); t2的b字段不是主鍵也沒有索引
t1表N行,t2表M行
sql執行過程
最笨的思路:全局掃描t1表,逐行讀取R,取出R中的a值,全局掃描t2表逐條取出R2將R2中的b值和a值比較符合則拼裝結果集,這樣t2表需要全局掃描N次? 總共掃描的行為 N+N*M;
mysql真正的思路:
1.全局掃描t1表將t1表全部放入線程級的內存join_buffer
2.全局掃描t2表,逐條和join_buffer中的數據做對比得出符合條件的數據
兩張表都只掃描一次 總共掃描的行數是:N+M
如果t1表太大join_buffer一次裝不下怎么辦?
答:那就分多次裝
每多裝一次t2表就需要多做一次全局掃描,用t2表中的所有數據和每個join_buffer做對比
總共掃描的行:N+k*M
可以表示為:N + λ*N*M
所以N對行數的影響還是最大還是應該:小表驅動大表
如果不強制性指定驅動關系,優化器會自動設置小表驅動大表
進一步的優化BKA?
十、臨時表的解釋
內存表的概念:
1.數據都保存在內存中,系統重啟時數據會被清空但是表結構還在
2.內存表只能使用memory存儲引擎
其他屬性和正常的數據表沒有區別
臨時表的概念:
1.臨時表是相對于事務而言的,一個臨時表只對一個事務可見,兩個事務中有臨時表的名稱相同是互不影響的, session 結束的時候,會自動刪除臨時表
2.臨時表數據是寫在磁盤上的,可以使用各種存儲引擎
3.臨時表可以與普通表同名。
4.session A 內有同名的臨時表和普通表的時候,show create 語句,以及增刪改查語句訪問的是臨時表。
5.show tables 命令不顯示臨時表
十一、分表
1.分表的key選取
1.按時間字段分表,方便擴張
2.按全局主鍵分表,方便查詢
3.結合可以預估范圍的字段具體分析,根據業務有的也可按地域屬性劃分
分區分表:
CREATE TABLE `t` (
`ftime` datetime NOT NULL,
`c` int(11) DEFAULT NULL,
KEY (`ftime`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
PARTITION BY RANGE (YEAR(ftime))
(PARTITION p_2017 VALUES LESS THAN (2017) ENGINE = InnoDB,
PARTITION p_2018 VALUES LESS THAN (2018) ENGINE = InnoDB,
PARTITION p_2019 VALUES LESS THAN (2019) ENGINE = InnoDB,
PARTITION p_others VALUES LESS THAN MAXVALUE ENGINE = InnoDB);
insert into t values('2017-4-1',1),('2018-4-1',1);
我在表 t 中初始化插入了兩行記錄,按照定義的分區規則,這兩行記錄分別落在 p_2018 和 p_2019 這兩個分區上。
可以看到,這個表包含了一個.frm 文件和 4 個.ibd 文件,每個分區對應一個.ibd 文件。也就是說:
對于引擎層來說,這是 4 個表;
對于 Server 層來說,這是 1 個表
分表后鎖性能的分析:
間隙鎖會被阻斷
表鎖也只能作用于掃描到的分區(有多個分區相當于有多個表)
分區的應用場景
1.較大的歷史記錄表,可以按時間分區
2.分區之后業務代碼更簡潔
分庫分表
訂單系統分庫分表拆分案例
整體大的步驟:
1.原來的庫保持不同,業務上雙寫
2.引入es同步數據庫表數據
3.非key字段為條件的查詢如何處理
? 1)實時性要求不高的走es查詢
? 2)數據一致性要求高的走原來的全量的庫