MySQL筆記彙總
目錄
1 MySQL背景介紹
1.1 關於MySQL
官方文件:https://dev.mysql.com/doc/refman/8.0/en/
MySQL是Oracle公司開發、釋出和支援的最流行的開源SQL資料庫管理系統。
【主要特點】
- 開源
- 使用BTree索引
- 支援多執行緒,對多核CPU效能可以達到更好的發揮
- 用C和C++編寫
1.2 MySQL8.0新特性
-
資料字典
詳情參考:https://cloud.tencent.com/developer/article/1123363
資料字典存放MySQL元資訊:表結構、資料庫名或表名、欄位的資料型別、檢視、索引、表字段資訊、儲存過程、觸發器等。
新版本改進:
- 將所有原先存放於資料字典檔案中的資訊,全部存放到資料庫系統表中(檔案-->表)【提升查詢資料速度】
- 對INFORMATION_SCHEM,mysql,sys系統庫中的儲存引擎做了改進,原先使用MyISAM儲存引擎的資料字典表都改為使用InnoDB儲存引擎存放。
-
更換新的身份認證外掛caching_sha2_password【預設使用】,但由於與客戶端相容性不太好,大多數使用者回退到了mysql_native_password版本
-
Innodb增強:
-
自增列【消除了以往重啟例項自增列不連續的問題】
-
可禁用死鎖檢測
一個新的動態變數,
innodb_deadlock_detect
,可用於禁用死鎖檢測。在高併發性系統上,當多個執行緒等待同一鎖時,死鎖檢測會導致減速。有時,禁用死鎖檢測並依賴於innodb_lock_wait_timeout
在發生死鎖時設定事務回滾。
-
2 CentOS 7.6 安裝MySQL
2.1 環境準備
首先centos7 已經不支援mysql(大概是因為收費),所以內部集成了mariadb,而安裝mysql的話會和mariadb的檔案衝突,所以需要先解除安裝掉mariadb
- 解除安裝mariadb
rpm -qa | grep mariadb
rpm -e --nodeps mariadb-libs-5.5.60-1.el7_5.x86_64
centos7 內部集成了mariadb,而安裝mysql的話會和mariadb的檔案衝突,所以需要先解除安裝掉mariadb。
【如果之前安裝了mysql需要先解除安裝】
yum remove mysql*
刪除安裝目錄
whereis mysql
rm -rf /usr/share/mysql
-
安裝MySQL
-
獲取yum源(MySQL官網)
-
安裝yum源
rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
-
檢視各版本啟動狀況
yum repolist all | grep mysql
預設開啟最新版8.0
-
[調整命令】禁用8.0,開啟5.7
yum-config-manager --disable mysql80-community yum-config-manager --enable mysql57-community
命令在yum-utils 包裡,安裝既可以解決無法找到yum-config-manager命令:
yum -y install yum-utils
-
安裝mysqll
yum -y install mysql-community-server
-
2.2 配置MySQL遠端連線
-
檢視mysql版本
mysql -V
-
啟動mysql&&設定開機自啟
systemctl start mysqld systemctl enable mysqld
-
檢視預設生成密碼
grep 'temporary password' /var/log/mysqld.log
-
登入修改密碼
mysql -uroot -p
-
修改密碼
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NoPassword564925080!'; mysql5.7之後預設安裝了密碼安全檢查外掛(validate_password),預設密碼檢查策略要求密碼必須包含:大小寫字母、數字和特殊符號,並且長度不能少於8位。否則會提示ERROR 1819 (HY000): Your password does not satisfy the current policy requirements錯誤.
-
授權遠端登入使用者
預設的密碼加密方式是:caching_sha2_password,而現在很多客戶端工具還不支援這種加密認證方式,連線測試的時候就會報錯:client does not support authentication protocol requested by server; consider upgrading MySQL client
CREATE USER 'noneplus'@'%' IDENTIFIED BY 'Noneplus564925080!'; GRANT ALL ON *.* TO 'noneplus'@'%'; //修改認證方式為mysql_native_password ALTER USER 'noneplus'@'%' IDENTIFIED WITH mysql_native_password BY 'Noneplus564925080!'; flush privileges;
-
開放伺服器3306埠
-
遠端連線
3 MySQL基礎知識儲備
3.1 常用命令
登入
mysql -u root -p
資料庫相關命令
show databases;
create database database_name;
use database_name;
drop database_name;
資料庫表相關命令
【資料庫和資料庫表相關命令都屬於DDL資料定義語言】
show tables; [先切換到指定資料庫]
//建立表
CREATE TABLE `user_info` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '使用者id',
`username` varchar(10) DEFAULT NULL COMMENT '使用者姓名',
`password` varchar(20) DEFAULT NULL COMMENT '使用者密碼',
`age` int(5) DEFAULT NULL COMMENT '年齡',
`email` varchar(20) DEFAULT NULL COMMENT '郵箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8 COMMENT='使用者資訊表'
//查看錶定義
show create table user_info \G;
drop table user_info;
//刪除表字段
alter table user_info column age;
//修改表
Alter table user_info modify username varchar(15);
//增加表的欄位
alter table user_info add column gender int(1);
//欄位改名
alter table user_info change age age1 int(3);
//modify,add,change都可以進行排序
Alter table user_info modify username varchar(15) first; //放在最前面
alter table user_info add column gender int(1) after age;//欄位至於age之後
//修改表名
alter table user_info rename person_info;
增刪改查命令
【增刪改查屬於DML資料操作語言】
插入
INSERT INTO user_info(username,password,age,email) VALUES('hq','123456789',22,'[email protected]')
更新
UPDATE user_info SET username='hq',age=23,email='[email protected]' WHERE id=5
刪除
DELETE FROM user_info WHERE id=6
查詢
SELECT * FROM user_info WHERE id = 6
SELECT * FROM user_info WHERE id = 6 and age<30
排序【預設升序】
SELECT * FROM user_info ORDER BY ID DESC LIMIT 10 //查詢最後十條資料
SELECT * FROM user_info ORDER BY ID DESC LIMIT 10,20 //查詢最後20條資料的前10條
統計資料總條數
SELECT COUNT(1) FROM user_info;
統計最大值,最小值,求和
SELECT MAX(age),MIN(age) ,SUM(age) FROM user_info;
表連線查詢
select ename,deptname from emp,dept where emp.deptno=dept.deptno;
3.2 常用資料型別
數值型別
整數型別 | 位元組 | 最小值 | 最大值 |
---|---|---|---|
tinyint | 1 | 有符號-128 無符號0 | 有符號127 無符號255 |
smallint | 2 | 有符號-32768 無符號0 | 有符號32767 無符號65535 |
mediumint | 3 | 有符號-800w 無符號0 | 有符號800w 無符號167w |
int,integer | 4 | 有符號-21億 無符號0 | 有符號21億 無符號42億 |
bigint | 8 | 有符號-92w兆 無符號0 | 有符號92w兆 無符號184w兆 |
int(5)指定顯式寬度【不顯式指定預設int(11)】,當數值寬度小於五位的時候,預設填滿。zerofill指用0填充。
unsigned表示是否帶符號
浮點數型別 | 位元組 | 最小值 | 最大值 |
---|---|---|---|
float | 4 | ||
double | 8 |
定點數型別 | 位元組 | 最小值 | 最大值 |
---|---|---|---|
decimal(M,D) | M+2 | 有符號-128 無符號0 | 有符號127 無符號255 |
表示一共顯示M位數字,包括整數位和小數位,其中D位代表小數點有幾位
decimal不指定精度預設整數位為10,小數位為0.
日期時間型別
型別 | 位元組 | 最小值 | 最大值 |
---|---|---|---|
date | 4 | 1000-01-01 | 9999-12-31 |
datetime | 8 | 1000-01-01 00:00:00 | 9999-12-31 23:59:59 |
timestamp | 4 | 1970010108001 | 2038年的某個時刻 |
記錄系統當前時間可用timestamp,支援不同地方的時區差異
TIMESTAMP 儲存的時間範圍 1970-01-01 00:00:01 ~ 2038-01-19-03:14:07
字串型別
型別 | 描述 |
---|---|
char | 0-255位元組 |
varchar | 0-65535位元組 |
tinyblob | 0-255位元組 |
blob | 0-65535位元組 |
mediumblob | 0-16772150位元組 |
longblob | 0-4294967295位元組 |
tinytext | 0-255位元組 |
text | 0-65535位元組 |
mediumtext | 0-16772150位元組 |
longtext | 0-4294967295位元組 |
varbinary(M) | 0-M位元組 |
binary(M) | 0-M位元組 |
3.3 運算子
算術運算子
算術運算子 | 說明 |
---|---|
+ | 加法運算 |
- | 減法運算 |
* | 乘法運算 |
/ | 除法運算,返回商 |
% | 求餘運算,返回餘數 |
比較運算子
比較運算子 | 說明 |
---|---|
= | 等於 |
< | 小於 |
<= | 小於等於 |
> | 大於 |
>= | 大於等於 |
<=> | 安全的等於,不會返回 UNKNOWN |
<> 或!= | 不等於 |
IS NULL 或 ISNULL | 判斷一個值是否為 NULL |
IS NOT NULL | 判斷一個值是否不為 NULL |
LEAST | 當有兩個或多個引數時,返回最小值 |
GREATEST | 當有兩個或多個引數時,返回最大值 |
BETWEEN AND | 判斷一個值是否落在兩個值之間 |
IN | 判斷一個值是IN列表中的任意一個值 |
NOT IN | 判斷一個值不是IN列表中的任意一個值 |
LIKE | 萬用字元匹配 |
REGEXP | 正則表示式匹配 |
邏輯運算子
邏輯運算子 | 說明 |
---|---|
NOT 或者 ! | 邏輯非 |
AND 或者 && | 邏輯與 |
OR 或者 || | 邏輯或 |
XOR | 邏輯異或【相同為0,不同為1】 |
位運算子
位運算子 | 說明 |
---|---|
| | 按位或 |
& | 按位與 |
^ | 按位異或 |
<< | 按位左移 |
>> | 按位右移 |
~ | 按位取反,反轉所有位元 |
運算子優先順序
優先順序由低到高排列 | 運算子 |
---|---|
1 | =(賦值運算)、:= |
2 | II、OR |
3 | XOR |
4 | &&、AND |
5 | NOT |
6 | BETWEEN、CASE、WHEN、THEN、ELSE |
7 | =(比較運算)、<=>、>=、>、<=、<、<>、!=、 IS、LIKE、REGEXP、IN |
8 | | |
9 | & |
10 | <<、>> |
11 | -(減號)、+ |
12 | *、/、% |
13 | ^ |
14 | -(負號)、〜(位反轉) |
15 | ! |
4 開發規範
4.1 設計規範
正規化
-
第一正規化:無重複的列
-
第二正規化:屬性完全依賴於主鍵
-
第三正規化:屬性不能傳遞依賴其他非主屬性
正規化的作用是避免資料冗餘(資料重複)。
正規化的問題
按照正規化設計出來的表在資料冗餘的問題雖然得到解決,但是會生成許多表,導致了表數量的複雜性,其二,查詢資料的時候,多表查詢的時間遠遠高於單表查詢的時間。
反正規化
正規化的目的是減小資料冗餘,而反正規化指的是在一定程度上允許資料冗餘,目的是加快資料操作。
對比
正規化與反正規化是一場時間和空間的較量,滿足正規化節省空間,滿足反正規化加快操作速度。
在滿足正規化設計資料庫的前提條件下,再根據具體的業務需求完成反正規化的設計。
4.2 命名規範
小寫+下劃線,不能使用保留關鍵字【!!!】
【MySQL物件名預設規定大小寫敏感,且在生產環境中MySQL通常執行在Linux系統下,Linux系統本身也是大小寫敏感的。】
【https://dev.mysql.com/doc/mysqld-version-reference/en/keywords-8-0.html建議在設計資料表之後逐一排查有沒有使用關鍵字。】
4.3 欄位規範
原則:
- 儘可能選擇儲存空間最小的欄位【栗子:IP轉化為整型儲存】、
- 非負型資料優先使用無符號儲存
1,char VS varchar
char 定長 浪費空間 查詢速度快
varchar 變長 節省空間 查詢速度較慢
出於儲存空間的考慮,優先選擇varchar
2,避免使用text,blob,如果一定要使用,單獨出擴充套件表(通常這類資料會考慮使用NoSQL來儲存)
【MySQL記憶體臨時表不支援text,blob這樣的大資料型別,只能使用磁碟臨時表完成,並且會導致二次查詢】
3,同財務相關的最好使用定點數decimal
4,日期型別選擇
- DATETIME:記錄年月日時分秒,表示的時間範圍最大
- 如果記錄的日期要讓不同時區的人使用,使用TIMESTAMP
5 B+樹索引
5.1 什麼是索引?
索引是一種資料結構,具體表現在查詢演算法上。
5.2 索引目的
提高查詢效率
【類比字典和借書】
如果要查“mysql”這個單詞,我們肯定需要定位到m字母,然後從下往下找到y字母,再找到剩下的sql。如果沒有索引,那麼你可能需要把所有單詞看一遍才能找到你想要的。
去圖書館借書也是一樣,如果你要借某一本書,一定是先找到對應的分類科目,再找到對應的編號,這是生活中活生生的例子,通用索引,可以加快查詢速度,快速定位。
5.3 B樹
結構特徵:每個節點可包含多個子節點,葉子節點位於同一層(每個節點儲存索引和資料)
使用用法:B樹為磁碟預讀設計,其特徵相對於二叉樹降低了高度,減少IO次數(樹的高度等於IO次數)
5.3 B+樹
結構特徵:只在葉子節點儲存資料,且葉子節點有序排列,通過鏈指標相連(只有葉子節點儲存資料,其他節點都只儲存索引,單次IO能載入更多節點)
使用用法:B樹解決了磁碟IO問題,而B+樹通過資料結構優化和區間訪問加快了元素的查詢效率
5.4 原理分析
索引儲存位置
索引本身也很大,所以儲存在磁碟中,需要載入到記憶體中執行。
故:索引結構優劣標準:磁碟I/O次數
區域性性原理和磁碟預讀
區域性性原理:當一個數據被用到,其附近的資料很可能會馬上用到
磁碟預讀:由於儲存介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,磁碟的存取速度往往是主存的幾百分分之一,因此為了提高效率,要儘量減少磁碟I/O。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的資料放入主存。
B樹如何利用磁碟預讀功能
B樹的節點大小和磁碟的IO大小是進行過匹配的,一次IO可以讀取一整個節點的大小。這樣就能有效減少IO次數。
【如果節點大小和B樹大小不對齊,那麼同一頁節點可能需要兩次IO讀取】
綜上所述,B樹解決的核心問題是IO次數的問題
為什麼B+樹比B樹更適合作為索引結構
B樹解決了磁碟IO的問題但沒有解決元素遍歷複雜的問題。
B+樹的葉子節點用鏈指標相連,極大提高區間訪問速度。【比如查詢50到100的記錄,查出50後,順著指標遍歷即可】
B+樹的葉子結點可以存哪些東西
可能是整行資料,也可能是主鍵的值。
前者被稱為聚簇索引,後者稱為非聚簇索引。
聚簇索引更快!!!
為什麼???聚簇索引已經查到整行資料了,而非聚簇索引還可能根據主鍵值再進行查詢一次。
例外:覆蓋索引——資料直接從索引中取得。
6,SQL優化
SQL優化背景
開發專案上線初期,由於業務資料量相對較少,一些SQL的執行效率對程式執行效率的影響不太明顯,而開發和運維人員也無法判斷SQL對程式的執行效率有多大,故很少針對SQL進行專門的優化,而隨著時間的積累,業務資料量的增多,SQL的執行效率對程式的執行效率的影響逐漸增大,此時對SQL的優化就很有必要。
- SQL優化發生在業務量達到一定規模的時候
- 目的是優化SQL的執行效率
6.1 優化範圍
- 硬體資源
- 作業系統引數,資料庫引數配置
- SQL語句,索引優化
6.2 SQL優化
- 資料庫設計優化【規範,前期設計】
- SQL語句優化
- 索引優化
- 讀寫分離,分庫分表
6.3 慢查詢語句
慢查詢:10s無返回結果,定義為慢查詢
SHOW STATUS LIKE "slow_queries";
SHOW VARIABLES LIKE "long_query_time";//可以顯示當前慢查詢時間
set long_query_time=1 ;//可以修改慢查詢時間
6.4 常用優化方法
-
避免全表掃描(考慮在 where 及 order by 涉及的列上建立索引)
-
儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描
select id from t where num is null 可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢: select id from t where num=0
-
應儘量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描
-
應儘量避免在 where 子句中使用 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
-
in 和 not in 也要慎用,否則會導致全表掃描
select id from t where num in(1,2,3) 對於連續的數值,能用 between 就不要用 in 了: select id from t where num between 1 and 3
-
應儘量避免在 where 子句中對欄位進行表示式操作,這將導致引擎放棄使用索引而進行全表掃描
select id from t where num/2=100 應改為: select id from t where num=100*2
-
應儘量避免在where子句中對欄位進行函式操作,這將導致引擎放棄使用索引而進行全表掃描
select id from t where substring(name,1,3)='abc'--name以abc開頭的id 應改為: select id from t where name like 'abc%'
-
很多時候用 exists 代替 in 是一個好的選擇
select num from a where num in(select num from b) 用下面的語句替換: select num from a where exists(select 1 from b where num=a.num)
-
索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率(5)
-
儘量使用數字型欄位,若只含數值資訊的欄位儘量不要設計為字元型,這會降低查詢和連線的效能,並會增加儲存開銷
-
儘可能的使用 varchar 代替 char ,因為首先變長欄位儲存空間小,可以節省儲存空間
-
任何地方都不要使用 select * from t ,用具體的欄位列表代替“*”,不要返回用不到的任何欄位
-
儘量避免使用遊標,因為遊標的效率較差,如果遊標操作的資料超過1萬行,那麼就應該考慮改寫
7 事務和鎖
7.1 事務
7.1.1 事務存在的原因
事務存在的目的:保證使用者對資料操作對資料是安全的。(比如說銀行卡餘額)
7.1.2 事務的特性——ACID
原子性:一個事務要麼全部執行,要麼不執行
一致性:事務開始和結束時,資料保持一致
隔離性:事務之間互不影響
永續性:事務操作的結果具有永續性
7.1.3 關於髒讀,不可重複讀,幻讀
-
髒讀
事務A讀取了事務B中尚未提交的資料。如果事務B回滾,則A讀取使用了錯誤的資料。
【一個事物在讀的時候,禁止讀取未提交的事務】
-
不可重複讀
不可重複讀是指在一個事務範圍內多次查詢卻返回了不同的資料值,這是由於存在查詢間隔,被另一個事務修改並提交了。
【一個事物在讀的時候,禁止任何事務寫】
-
幻讀
在事務A多次讀取過程中,事務B對資料進行了新增操作,導致事務A多次讀取的資料不一致。
【一個事物加上表級鎖,禁止任何操作的併發】
小結:
髒讀是讀取了尚未提交的資料,不可重複讀是讀取了不停更新的資料(修改),幻讀是指讀取了不停更新的資料(新增)。
7.1.4 關於事務隔離級別
目的:避免髒讀,不可重複讀,幻讀
讀未提交:一個事務可以讀到另一個事務尚未提交的資料。也就是髒讀,避免髒讀的方式:
讀提交:一個事務要等另一個事務提交後才能讀取資料。但會導致一個事務中相同查詢出現不同的結果。也就是不可重複讀。避免不可重複讀的方式:
重複讀(RR,MySQL預設級別):就是在開始讀取資料時,不允許修改操作。但會導致由於允許insert操作導致的事務結果出現不同。也就是幻讀,避免幻讀的方式::
序列化:序列化使事務序列順序執行,但會大大降低併發效能。
7.2 鎖機制
7.2.1 併發控制 控制的是什麼?
併發問題:某個時間點兩次或兩次以上同一請求的結果不一致。
當程式的使用者超過兩個人時,就有機率產生併發問題。當程式的使用者變多,產生併發問題的概率就會隨之上升。
總的來說,併發控制就是控制資料的一致性。
7.2.2 共享鎖和排他鎖
Innodb實現了兩種型別的行鎖:共享鎖,排他鎖。
共享鎖:所有使用者都可讀取當前記錄,但不可修改當前記錄
select * from table lock in share mode
排它鎖(悲觀鎖):當前使用者可進行增刪改查,其他使用者無法進行任何操作(MySQL的增刪改操作預設加了排他鎖,查無任何鎖)
【為什麼在Innodb中使用索引?】
Innodb行鎖並不是鎖記錄而是鎖索引,優先鎖主鍵索引,其次鎖非主鍵索引(比如唯一索引),如果沒有索引,就需要通過全表掃描來找到當前記錄,就相當於表鎖了。(這也是為什麼需要進行索引優化的原因)
意向共享鎖和意向排他鎖
Innodb雖然使用行鎖,但並沒有廢棄表鎖。
【行鎖和表鎖】
MyISAM儲存引擎使用的是表鎖,而Innodb增加了行鎖。並不意味著Innodb徹底拋棄了表鎖。
關於行鎖,較小的粒度導致其高併發,但也因較小的粒度導致加鎖慢,開銷大,會出現死鎖情況。
關於表鎖,較大的粒度在高併發上的表現很弱,但同時粒度較大,加鎖塊,開銷小,不會出現死鎖情況。
沒有完美的技術,只有合適的解決方案。在高併發場景下使用行鎖而忍受一些問題本質上是一種權衡。
【意向鎖的背景衝突】
意向鎖的出現本質上是解決行鎖和表鎖矛盾的問題。
事務A獲得了表中某一行的共享鎖,事務B申請了表的寫許可權,這時候就會產生矛盾。
【關於意向鎖】
首先,意向鎖是一種表鎖。
意向共享鎖:事務獲得表中的某一行的共享鎖前,需要先獲得整張表的意向共享鎖。
意向排他鎖:事務獲得表中的某一行的排他鎖前,需要先獲得整張表的意向排他鎖。
意向鎖的加鎖過程是自動完成的。
【意向鎖的共享問題】
意向鎖是表鎖,它的互斥性是針對表級別的事務,比如一個事務要獲取一張表的寫許可權。所以意向鎖對於表級別的事務是互斥的。但是對於行級別的事務是共享的,也就是說,一個意向鎖可以被多個行級別的事務所持有。
7.3 死鎖
關於死鎖抖音上有一個非常好玩的小視訊:
面試官問:解釋一下死鎖,解釋明白了就發offer
應聘者答:先發offer,發了offer再解釋
死鎖本質上就是持有鎖和釋放鎖的問題,就像這個視訊裡描述的,面試官在聽到死鎖的解釋後,才會釋放offer這個鎖,而應聘者是得到offer後才會釋放死鎖解釋這個鎖。offer和對死鎖的解釋就可以類比兩個鎖。
死鎖的狀態就是互相等待。
7.4 樂觀鎖與悲觀鎖
樂觀鎖和悲觀鎖並不是鎖的具體實現,而是併發控制的兩種策略,或者說是抽象。
樂觀鎖(適合多讀場景)
- 樂觀鎖本質上是沒有鎖的。
- 執行流程,先讀取資料,然後在更新前檢查在讀取至更新這段時間資料是否被修改
- 未修改:直接更新資料
- 已修改:重新讀取,再次提交更新(或者放棄操作)
為什麼樂觀鎖適合多讀場景?
樂觀鎖是一種更新前的檢查機制,相對於悲觀鎖來說在多讀場景下可以減少鎖的效能開銷,對於多寫場景,樂觀鎖會一直進入已修改,重新讀取,再次提交的迴圈,反而帶來更多的資源消耗。
悲觀鎖(適合多寫場景)
- 讀取資料的時候上鎖(其他使用者就無法讀取),直到本次資料更新完成才會釋放鎖。在多寫場景下,能保證較高的資料一致性。
【總的來說,樂觀鎖回滾重試,悲觀鎖阻塞事務】