陸續更新中…
Mybatis
1、 什麼是Mybatis,工作原理
是一個半ORM(對象關係映射)框架,封裝了JDBC
基本工作原理:
先封裝SQL,接着調用JDBC操作數據庫,最後把返回的結果封裝成Java類
2、優點
- 基於sql編程,比較靈活,sql寫在xml中,和程序代碼解耦;支持動態sql
- 與JDBC相比,減少了代碼量
- 與各種數據庫兼容,JDBC支持的Mybatis都支持
3、缺點
- sql語句編寫工作量大
- sql語句依賴數據庫,移植性差
4、與Hibernate比較
- Mybatis是一個半ORM框架,因爲sql語句需要編寫;使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關係模型直接獲取,是全自動的
- Mybatis直接編寫sql,靈活度高,適合對關係數據模型要求不高的場景
- Hibernate對象/關係映射能力強,數據庫無關性好,適用對關係模型要求高的場景,可以節省代碼,提高效率
5、#{}和${}的區別
#{}是預編譯處理,會被替換爲?,調用PreparedStatement的set方法來賦值
${}是變量佔位符,會被替換成變量的值
使用#{}可以有效防止SQL注入
6、預編譯
過程
- 執行預編譯語句
- 設置變量
- 執行語句
如果需要再次執行,那麼就不需要第一步了
好處
- 避免SQL注入,因爲SQL已經編譯完成,結構已經固定
- 提高SQL執行效率。當發送一條SQL語句給服務器時,服務器首先校驗SQL的語法是否正確,然後編譯成可執行的函數,最後才執行。其中前兩步的時間可能比執行時間還要長。如果需要多次執行insert語句,每次只是參數不同,使用預編譯的話只對SQL語句進行一次語法校驗和編譯,所以效率高
7、Dao接口的工作原理
Dao接口即Mapper接口
接口的全限名就是映射文件中的namespace;接口的方法名就是映射文件中的id值
當調用接口方法時,接口全限名+方法名可唯一定位一個MapperSatement,所以接口的方法不能重載
Mybatis運行時使用JDK動態代理爲Mapper接口生成代理對象proxy,proxy攔截接口方法,執行MapperStatement所代表的sql,然後返回執行結果
8、一級、二級緩存
- 一級緩存:其存儲作用域爲SqlSession,每個SqlSession的實例都有一個基於PerpetualCache的HashMap;當Session flush或close之後,Session中的所有Cache都被情況;默認打開
- 二級緩存與一級緩存機制相同,不同在於其存儲作用域爲Mapper(Namespace),多個SqlSession類的實例對象可以共用二級緩存,並且可自定義存儲源。默認不打開,使用二級緩存屬性類需要實現Serializable序列化接口
- 對於緩存數據更新機制,當某一級作用域進行了CUD操作後,默認該作用域下所有select中的緩存將被clear
Mysql
數據類型
MySQL支持多種類型,大致可以分爲三類:數值、日期/時間和字符串(字符)類型
整數類型
分爲兩種:
整數:tinyint、smallint、mediumint、int、bigint,分別佔用8、16、24、32、64位
實數:float、double、decimal
字符串:varchar(變長)、char(定長)、blob、text
varchar:存儲可變長;需要額外字節記錄長度(長度<=255用1個,大於用2個)。
適合場景:列的最大長度比平均長度大很多;列更新少;使用了像UTF-8這樣複雜的字符集
char:mysql會刪除末尾空格
適合場景:存儲很短的字符串,因爲不需要額外字節記錄長度;所有值接近同一長度;對於經常變更的數據也不易產生碎片
日期和時間類型
datetime:8個字節
timestap:4個字節
位數據類型
bit
set
連接的使用
- INNER JOIN內連接:獲取兩個表中字段匹配的記錄,可以直接用JOIN
- LEFT JOIN左連接:獲取左表所有記錄
- RIGHT JOIN右連接:獲取右表所有記錄
三大範式
https://www.cnblogs.com/wsg25/p/9615100.html
第一範式(1NF)
每個字段必須是不可拆分的最小單元,強調列的原子性
第二範式(2NF)
https://blog.csdn.net/ljp812184246/article/details/50706596
滿足1NF後,所有字段都必須完全依賴主鍵,不能有任何一列與主鍵沒有關係
假定選課關係表爲SelectCourse(學號, 姓名, 年齡, 課程名稱, 成績, 學分),關鍵字爲組合關鍵字(學號, 課程名稱),因爲存在如下決定關係:
(學號, 課程名稱) → (姓名, 年齡, 成績, 學分)
這個數據庫表不滿足第二範式,因爲存在如下決定關係:
(課程名稱) → (學分)
(學號) → (姓名, 年齡)
不符合2NF會存在以下問題:
- 數據冗餘
- 更新異常:如果調整某課程的學分,那麼所有行都要調整,否則會出現學分不同的情況
- 插入異常:如果要開設新課程,暫時沒有人選修,由於沒有學號關鍵字,因此無法插入
- 刪除異常:如果這批學生已經結課,這些記錄就應該刪除,但是同時課程信息也被刪除了
第三範式(3NF)
滿足2NF後,所有字段只與主鍵直接相關而不是間接相關,不能包含依賴傳遞
(學號) → (姓名, 年齡, 所在學院, 學院地點, 學院電話)
這個數據庫是符合2NF的,但是不符合3NF,因爲存在如下決定關係:
(學號) → (所在學院) → (學院地點, 學院電話)
即存在非關鍵字段"學院地點"、"學院電話"對關鍵字段"學號"的傳遞函數依賴。
優點:減少數據冗餘
缺點:對於查詢需要多個表進行關聯,寫效率降低,很難進行索引優化
delete、truncate、drop
delete和truncate只刪除數據,drop是刪除整個表(結構和數據)
delete刪除過程是一行一行刪除,在日誌保存刪除記錄,能夠恢復,且保留標識計數值。
truncate是一次性將所有數據都刪除,不能恢復,標識計數值重置
delete是DML,這個操作會被放到 rollback segment中,事務提交後才生效。如果有相應的 tigger,執行的時候將被觸發。truncate和drop是DLL,立即執行
速度:drop> truncate > delete
鎖
https://www.cnblogs.com/leedaily/p/8378779.html
-
表級鎖
直接鎖定整張表,其他進程無法進行寫操作。如果是寫鎖,則讀操作也不允許。MyISAM採用
兩種模式:表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)。
加鎖:lock table study write/read [local];
解鎖:unlock tables;
-
加了**“local”**選項,其作用就是在滿足MyISAM表併發插入條件的情況下,允許其他用戶在表尾併發插入記錄;
-
給表顯式加表鎖時,必須同時取得所有涉及到表的鎖,並且MySQL不支持鎖升級。也就是說,在執行LOCK TABLES後,只能訪問顯式加鎖的這些表,不能訪問未加鎖的表
-
如果加的是讀鎖,那麼只能執行查詢操作,而不能執行更新操作
-
-
行級鎖
InnoDB支持行級鎖和表級鎖,默認是行級鎖
- 共享鎖(S):又稱讀鎖。若事務T對數據對象A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。
- 排他鎖(X):又稱寫鎖。允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的數據集共享讀鎖和排他寫鎖。若事務T對數據對象A加上X鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖。
注意:
排他鎖指的是一個事務在一行數據加上排他鎖後,其他事務不能再在其上加其他的鎖。InnoDB引擎默認的修改數據語句:update,delete,insert都會自動給涉及到的數據加上排他鎖,select語句默認不會加任何鎖類型(加排他鎖可以使用select …for update語句,加共享鎖可以使用select … lock in share mode語句)
-
頁級鎖
一次鎖定相鄰的一組記錄
表級鎖 | 行級鎖 | 頁級鎖 | |
---|---|---|---|
開銷 | 小 | 大 | 適中 |
加鎖時間 | 快 | 慢 | 適中 |
鎖定粒度 | 最大 | 最小 | 適中 |
併發度 | 最低 | 最高 | 適中 |
大表優化
單表優化
一般以整型值爲主的表在千萬級
以下,字符串爲主的表在五百萬
以下是沒有太大問題的
字段
- 儘量使用較小的整數類型,儘量不使用INT,非負加上UNSIGNED
- varchar長度只分配真正需要的空間
- 使用枚舉或整數替代字符串
- 儘量使用
TIMESTAMP
而非DATETIME
- 使用整型存儲IP
- 單表不要有太多字段,20以內
- 避免使用null,很難查詢優化且佔用額外內存空間
索引
- 在經常需要搜索、根據範圍進行搜索的列上創建索引,因爲索引已經排序,其指定的範圍是連續的
- 在
WHERE
和ORDER BY
命令上涉及的列建立索引,可根據EXPLAIN
來查看用了索引還是全表掃描 - 字符字段只建前綴索引,且最好不要做主鍵
- 儘量擴展索引而不是新建索引
- 對於只有很少數據值的列也不應該增加索引 。比如性別列
- 對於那些定義爲text, image和bit數據類型的列不應該增加索引。這是因爲,這些列的數據量要麼相當大,要麼取值很少。
- 當修改性能遠遠大於檢索性能時,不應該創建索引。這是因爲,修改性能和檢索性能是互相矛盾的。當增加索引時,會提高檢索性能,但是會降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。因此,當修改性能遠遠大於檢索性能時,不應該創建索引
優化sql
-
在where中避免使用
-
!= 或 **<>**操作符
-
對字段進行null值判斷
-
使用or來連接條件。如:
select id from t where num=10 or num=20
可以這樣查詢:select id from t where num=10
union all
select id from t where num=20 -
避免對字段進行表達式操作
num/2=1改爲num=1*2
這些會使引擎放棄使用索引而進行全表掃描
-
-
少用join
-
下面的查詢也將導致全表掃描:
select id from t where name like ‘%abc%’
若要提高效率,可以考慮全文檢索
垂直拆分
根據業務相關性進行拆分
優點:行數據變小,一個數據塊能存放更多的數據,減少磁盤I/O
缺點:主鍵冗餘;會引起表的join操作;事務處理複雜
水平拆分
分表:解決單表大量數據的查詢性能問題
分庫:解決單臺數據庫的高併發問題。根據業務不同進行切分
分區:就是把一張表的數據分成多個區塊,這些區塊可以在一個磁盤上,也可以在不同的磁盤上,分區後,表面上還是一張表,但數據散列在多個位置,這樣一來,多塊硬盤同時處理不同的請求,從而提高磁盤 I/O 讀寫性能,實現比較簡單。 包括水平分區和垂直分區。
主從複製
https://blog.csdn.net/qq_30299977/article/details/100087045
原理:
在MySQL中有一種叫做bin的二進制日誌,這個日誌文件裏面記錄了關於此數據庫的所有修改的sql語句(包括insert,update,delete,grant等等)。而主從複製就是利用這個二進制bin日誌,在主庫上創建一個用戶,從數據庫通過此用戶去讀取bin日誌,然後再在從數據庫上再執行一次。
方式:
-
同步複製
主服務器在將更新的數據寫入它的二進制日誌(bin-log)文件中後,必須等待驗證所有的從服務器的更新數據是否已經複製到其中,之後纔可以自由處理其它進入的事務處理請求
-
異步複製
主服務器在將更新的數據寫入它的二進制日誌(bin-log)文件中後,無需等待驗證更新數據是否已經複製到從服務器中,就可以自由處理其它進入的事務處理請求。
-
半同步複製
主服務器在將更新的數據寫入它的二進制日誌(bin-log)文件中後,只需**等待驗證其中一臺**從服務器的更新數據是否已經複製到其中,就可以自由處理其它進入的事務處理請求,其他的從服務器不用管
過程:
- 主服務器啓用
實現:https://www.jianshu.com/p/5c6d6ffa98af
數據庫索引
http://blog.codinglabs.org/articles/theory-of-mysql-index.html
優缺點
優點:
- 加快數據檢索速度
- 使用索引可以在查詢的過程中,使用優化隱藏器,提高系統性能
缺點:
- 創建和維護索引需要耗費時間,且隨着數據量增加而增加
- 索引佔用物理空間
- 當對錶中數據進行insert、delete、update時候,索引也需要動態維護
普通索引
允許重複,加快查詢速度
創建索引
1、CREATE INDEX index_name ON table_name(col_name(length))
2、ALTER TABLE table_name ADD INDEX index_name(col_name(length))
3、CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX indexName (username(length))
);
刪除索引
DROP INDEX [indexName] ON table_name;
如果是CHAR,VARCHAR類型,length可以小於字段實際長度;如果是BLOB和TEXT類型,必須指定 length。
唯一索引
它與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。不可以被其他表引用爲外鍵
1、CREATE UNIQUE INDEX indexName ON table(col_name(length))
2、ALTER TABLE mytable ADD UNIQUE index_name(col_name(length))
3、CREATE TABLE mytable(
ID INT NOT NULL,
usrname VARCHAR(16) NOT NULL,
UNIQUE INDEX indexName (username(length))
);
主鍵索引
在數據庫表關係圖中自動爲主鍵創建主鍵索引,它是唯一索引的特定類型,不能爲空,可以被其他表引用爲外鍵
聯合索引
https://juejin.im/post/5e57ac99e51d45270e212534
聚集索引(聚簇索引)
數據行的物理順序與鍵值的邏輯順序相同。一個表只能包含一個聚集索引。
聚集索引的葉子節點存放的就是對應的數據,
如果某索引不是聚集索引,則表中行的物理順序與鍵值的邏輯順序不匹配。與非聚集索引相比,聚集索引通常提供更快的數據訪問速度
InnoDB使用聚集索引,MyISAM使用非聚集索引
B樹
又叫平衡多路查找樹。一棵m階的B樹 (m叉樹)的特性:
-
如果整棵樹不只有一個結點,則根結點至少有2個子結點
-
中間結點有k-1個關鍵字和k個子結點,其中ceil(m / 2)<= k <= m(ceil(x)是一個取上限的函數)
-
所有葉子結點都在同一層
-
非葉子結點包含n個關鍵字信息 (P1,K1,P2,K2,P3,…,Kn,Pn+1),其中:
a) Ki (i=1…n)爲關鍵字,且關鍵字按順序升序排序K(i-1)< Ki。
b) Pi爲指向子結點的接點,且指針P(i)指向的子樹中的所有結點的關鍵字均小於Ki,但都大於K(i-1)。
c) 關鍵字的個數n必須滿足: [ceil(m / 2)-1]<= n <= m-1。
B樹相對於平衡二叉樹來說,每個節點的關鍵字增多了,樹的層數變少,那麼查找次數就少了,搜索磁盤的效率變高了
索引數據結構
採用B+樹,比B樹更充分的利用了節點空間,查詢速度更穩定
- 每個結點最多有m個關鍵字(B樹最多m-1),非葉子結點的子樹指針與關鍵字個數相同
- 所有葉子結點包含了全部的關鍵字信息,且按照關鍵字順序相連
- 所有數據僅存在葉子結點
- 爲所有葉子結點增加一個鏈指針
- 非葉子結點的子樹指針P[i],指向關鍵字值屬於[K[i], K[i+1])的子樹(B-樹是開區間);
選用B+樹的原因:
- 由於B+樹在非葉子節點上不包含數據信息,因此單個節點能夠存放更多的key。 數據存放的更加緊密,具有更好的空間局部性。
- B+樹查詢性能穩定,因爲必須要搜尋到葉子結點
- B+樹的葉子結點都是相鏈的,因此對整棵樹的遍歷只需要一次線性遍歷葉子結點即可。而且由於數據順序排列並且相連,所以便於區間查找和搜索。而B樹則需要進行每一層的遞歸遍歷。相鄰的元素可能在內存中不相鄰,所以緩存命中性沒有B+樹好。
SQL注入
舉個例子:
後臺java代碼拼接sql如下:
public List getInfo(String title){
StringBuffer buf = new StringBuffer();
buf.append("select id,title from study where title=’ “).append(title).append(” ’ ");
}
前端頁面有輸入框如下:
主題:___________
如果輸入爲 ’ or ‘1’='1
最後拼接結果如下:
select id,title from study where title = ’ ’ or ‘1’=‘1’
那麼查詢結果就是所有數據,這就是sql注入。
防止措施
-
PreparedStatement
採用預編譯語句集,它內置了處理SQL注入的能力,只要使用它的setXXX方法傳值即可。
-
使用正則表達式過濾傳入的參數
MVCC
MVCC(Multiversion Concurrency Control)即多版本併發控制技術,它使得大部分支持行鎖的事務引擎,不再單純使用行鎖來進行數據庫的併發控制,而是把數據庫的行鎖與行的多個版本結合起來,只需要很小的開銷,就可以實現非鎖定讀,讀寫不衝突,從而大大提高數據庫系統的併發性能。
InnoDB的可重複讀隔離通過MVCC解決了髒讀、不可重複讀、幻讀。
https://blog.csdn.net/sanyuesan0000/article/details/90235335
只解決了讀操作的幻讀,對於修改操作依然存在幻讀。解決幻讀的方法是MVCC+next-key locks。next-key lock是行鎖的一種,實現相當於record lock(記錄鎖) + gap lock(間隙鎖);其特點是不僅會鎖住記錄本身(record lock的功能),還會鎖定一個範圍(gap lock的功能)
具體實現
不同引擎的實現不同,典型的有樂觀併發控制和悲觀併發控制。以InnoDB爲例。下面是REPEATABLE READ隔離級別下MVCC的具體操作:
InnoDB的MVCC,是通過在每行記錄後面保存兩個隱藏的列來實現的,這兩個列分別保存了這個行的創建版本號和刪除版本號(這個刪除並不只是指那些delete操作,也包括了update,update實質是新插入一行,並且標記原有行爲刪除,這樣才能讓某一些事務只能讀到以前的值)。每開始一個新的事務,系統版本號(可以理解爲事務的ID)就會自動遞增,事務開始時刻的系統版本號會作爲事務的ID。
當一個update的事務提交執行以後,通常是新增一行,然後附上自己的事務id,再標記之前的那行數據的刪除id。
讀事務執行時,只能讀取到同時滿足下面兩個條件的行:
(1)創建id<=當前id(讀之前的數據,在本事務之後的事務更新的數據都不能讀到)
(2)刪除id>=當前id(在本事務之後的事務刪除的數據不影響本次讀取)
總結
InnoDB-MVCC是一種系統行爲,在REPEATABLE READ隔離級別下,它通過樂觀併發控制解決了該隔離級別所不能解決的幻讀,但是前提是這些都得依託於事務的封裝。儘管如此,它還是無法完全解決一些併發業務場景下的問題,並且過多的事務使用會嚴重影響系統的性能,這就需要通過用戶行爲去約束(最開始所提到的樂觀鎖)。
MVCC並非樂觀鎖,但是InnoDB存儲引擎所實現的MVCC是樂觀的
引擎區別
-
MyISAM是非事務安全的,不支持外鍵,而InnoDB是事務安全的,支持外鍵
-
MyISAM鎖的粒度是表級的,而InnoDB支持行級鎖
-
MyISAM效率上要優於InnoDB,小型應用可以考慮使用MyISAM。爲什麼快?
因爲在進行select的時候,InnoDB要維護的東西比較多
1)InnoDB要緩存數據塊,MyISAM只緩存索引塊
2)InnoDB尋址要映射到塊,然後到行,而MyISAM記錄的直接是文件的OFFSET,定位更快
3)InnoDB要維護MVCC一致 -
MyISAM表保存成文件形式,跨平臺使用更加方便
-
MyISAM 只支持表級鎖,InnoDB 支持行級鎖和表級鎖,默認是行級鎖 ,行鎖大幅度提高了多用戶併發操作的性能 。InnoDB 比較適合於插入和更新操作比較多的情況,而 MyISAM則適合用於頻繁查詢的情況。
另外,InnoDB 表的行鎖也不是絕對的,如果在執行一個 SQL 語句時,MySQL 不能確定要掃描的範圍,InnoDB 表同樣會鎖全表,例如 update table set num=1 where name like “%aaa%”。 -
MyISAM: select count(
*
) from table,MyISAM 只要簡單的讀出保存好的行數。因爲MyISAM 內置了一個計數器,count(
*
)時它直接從計數器中讀。
InnoDB:不保存表的具體行數,也就是說,執行 select count(*) from table 時,InnoDB要掃描一遍整個表來計算有多少行。
事務的實現原理
https://www.cnblogs.com/kismetv/p/10331633.html
ACID是事務的四個特性
ACID
原子性Atomicity
一個事務是最小工作單元,整個事務中的操作要麼全部成功,要麼全部失敗回滾。undo log是事務實現原子性和隔離性的基礎。當事務對數據庫進行修改時,InnoDB會生成相應的undo log,如果事務失敗或者回滾,就可以利用undo log的信息將數據回滾
一致性Consistency
事務開始前和結束後,數據庫的完整性沒有被破壞
隔離性Isolation
多個事務併發執行時,事務之間是互相隔離的,不能互相干擾。鎖+MVCC
持久性Durability
事務一旦提交,對數據庫的改變是永久性的,不能回滾。redo log
事務的隔離級別
-
Read Uncommitted讀未提交
隔離級別最低。一個事務B會讀到另一個事務A更新後但未提交的數據,如果A回滾,那麼B讀到的數據就是髒數據,這就是髒讀。
-
Read Committed
一個事務從開始直到提交之前,所做的任何修改對其他事務都是不可見的。 在事務B內,多次讀同一數據,在這個事務還沒有結束時,如果事務A恰好修改了這個數據,那麼,在第一個事務中,兩次讀取的數據就可能不一致,這就是不可重複讀(Non Repeatable Read)的問題
-
Repeatable Read(MySQL默認)
解決了髒讀和不可重複讀的問題。
問題: 在事務B中讀取數據,另外一個事務A插入一條新記錄,當A再次讀取時,沒有數據,試圖更新這條不存在的記錄時,竟然能成功,並且,再次讀取同一條記錄,它就神奇地出現了。這就是幻讀(Phantom Read
-
Serializable
最嚴格的隔離級別,所有事務按照次序依次執行,因此,髒讀、不可重複讀、幻讀都不會出現。
但是,由於事務是串行執行,所以效率會大大下降,應用程序的性能會急劇降低。如果沒有特別重要的情景,一般都不會使用Serializable隔離級別。
默認隔離級別
在MySQL中,如果使用InnoDB,默認的隔離級別是Repeatable Read。
數據恢復
1、配置my.ini
server_id = 1 log_bin = mysql-bin.log #設置log文件保存路徑,默認爲mysql的data目錄下 max_binlog_size = 512M #最大log binlog_format = row binlog_row_image = full
2、show binary logs;
查看最新log
3、show binlog events in 'mysql-bin.000106;
可以看到228建立了一個test表,475刪除了test
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-WUth4NAA-1588228909170)(mysql/image-20191212104847047.png)]
4、打開一個cmd,進入data目錄,執行下列命令
終止位置要是402,因爲創建test的命令是在402結束
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3LbI7WbK-1588228909178)(mysql/image-20191212105214496.png)]
5、進入mysql,執行 source e:u.sql;
常用命令
創建數據庫:create database people;
刪除數據庫:drop database people;
選擇數據庫:use people;
創建數據表:
CREATE TABLE `crawl`.`people` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
PRIMARY KEY (`id`)
);
刪除表數據:delete from success_killed;
刪除表:drop table seckill;
插入數據:insert into people (field1,field2) values (value1,value2);
更新數據:update people set filed1=value1;
刪除數據:delete from people where filed1=value1;