mysql 一对多 关联一条最新的数据_不得不会的mysql锁
6. 多表之間的關系
如圖,實際業務數據庫中的表之間都是有關系的,我們接下來主要要學習的就是如何分析表關系及建立表關系。
分類表
create table category( cid varchar(32) primary key, cname varchar(100) );商品表
create table product( pid varchar(32) primary key, pname varchar(40), price double );?訂單表
create table orders( oid varchar(32) primary key, totalprice double );訂單項表
create table orderitem( oid varchar(50), pid varchar(50) );6.1 表與表之間的關系
表與表之間的關系,說的就是表與表之間數據的關系。
1)一對一關系:一夫一妻
2)一對多關系:會員和訂單
3)多對多關系(需要中間表實現):商品和訂單
6.2 外鍵
如何表示表與表之間的關系呢?就是使用外鍵約束表示的。
要想理解外鍵,我們先去理解表的角色:主表和從表(需要建立關系才有了主從表的角色區分)
注意:一般的設計表結構中, 避免使用外鍵
6.3 一對一關系
在實際工作中,一對一在開發中應用不多,因為一對一完全可以創建成一張表
案例:一個丈夫只能有一個妻子
CREATE TABLE wife( id INT PRIMARY KEY , wname VARCHAR(20), sex CHAR(1) ); CREATE TABLE husband( id INT PRIMARY KEY , hname VARCHAR(20), sex CHAR(1) );6.4 一對多
案例:一個分類對應多個商品
總結:有外鍵的就是多的一方。
注意事項:一對多關系和一對一關系的創建很類似,唯一區別就是外鍵不唯一。
一對多關系創建:
? ?- 添加外鍵列
? ?- 添加外鍵約束
案例:
? ?- 在商品表中添加一條記錄,該記錄的cid在分類表中不存在
? ?- 在分類表中,刪除一條記錄,這條記錄在商品表中有外鍵關聯
6.5 多對多
同一個商品對應多個訂單,一個訂單對應多個商品
注意事項:
- 需要中間表去完成多對多關系的創建 - 多對多關系其實就是兩個一對多關系的組合
多對多關系創建:
- 創建中間表,并在其中創建多對多關系中兩張表的外鍵列 - 在中間表中添加外鍵約束 - 在中間表中添加聯合主鍵約束
7.多表關聯查詢
我們已經學會了如何在一張表中讀取數據,這是相對簡單的,但是在真正的應用中經常需要從多個數據表中讀取數據。本章節我們將向大家介紹如何使用 MySQL 的 JOIN 在兩個或多個表中查詢數據。你可以在 SELECT, UPDATE 和 DELETE 語句中使用 MySQL 的 JOIN 來聯合多表查詢。
JOIN 按照功能大致分為如下三類:
- CROSS JOIN(交叉連接)
- INNER JOIN(內連接或等值連接)。
- OUTER JOIN(外連接)
建表語句:
insert into category (cid,cname) values ('c001','家電');insert into category (cid,cname) values ('c002','服飾');insert into category (cid,cname) values ('c003','化妝品');insert into product(pid,pname,price,cid) values ('p001','聯想',5000,'c001');insert into product(pid,pname,price,cid) values ('p002','海爾',3000,'c001');insert into product(pid,pname,price,cid) values ('p003','雷神',5000,'c001');insert into product(pid,pname,price,cid) values ('p004','阿迪',1000,'c002');insert into product(pid,pname,price,cid) values ('p005','耐克',1200,'c002');insert into product(pid,pname,price,cid) values ('p006','NB',800,'c002');insert into product(pid,pname,price,cid) values ('p007','彪馬',600,'c002');insert into product(pid,pname,price,cid) values ('p008','雪花秀',1500,'c003');insert into product(pid,pname,price,cid) values ('p009','悅詩風吟',1100,'c003');7.1 交叉連接
關鍵字:CROSS JOIN
交叉連接也叫笛卡爾積連接。笛卡爾積是指在數學中,兩個集合X和Y的笛卡尓積(Cartesian product),又稱直積,表示為X*Y,第一個對象是X的成員而第二個對象是Y的所有可能有序對的其中一個成員。
交叉連接的表現:行數相乘、列數相加
隱式交叉連接
SELECT * FROM A, B
顯式交叉連接
SELECT * FROM A CROSS JOIN B
7.2 內連接
關鍵字:INNER JOIN
內連接也叫等值連接,內聯接使用比較運算符根據每個表共有的列的值匹配兩個表中的行。
隱式內連接
SELECT * FROM A,B WHERE A.id = B.id
顯式內連接
SELECT * FROM A INNER JOIN B ON A.id = B.id
7.3 外連接
外聯接可以是左向外聯接、右向外聯接或完整外部聯接。
也就是說外連接又分為:左外連接、右外連接、全外連接。
外連接需要有主表或者保留表的概念。
在 FROM子句中指定外聯接時,可以由下列幾組關鍵字中的一組指定:
1. 左外連接
LEFT JOIN 或者 LEFT OUTER JOIN
SELECT * FROM A LEFT JOIN B ON A.id = B.id
案例:查詢分類信息,關聯查詢商品信息
2. 右外連接
RIGHT JOIN 或者 RIGHT OUTER JOIN
SELECT * FROM A RIGHT JOIN B ON A.id = B.id
3. 全外連接(mysql不支持)
FULL JOIN 或 FULL OUTER JOIN
SELECT * FROM A FULL JOIN B ON A.id = B.id
外連接總結
通過業務需求,分析主從表
如果使用LEFT JOIN,則主表在它左邊
如果使用RIGHT JOIN,則主表在它右邊
查詢結果以主表為主,從表記錄匹配不到,則補null
8. mysql鎖
8.1 mysql鎖介紹
按照鎖的粒度來說,MySQL主要包含三種類型(級別)的鎖定機制:
- 全局鎖:鎖的是整個database。由MySQL的SQL layer層實現的 - 表級鎖:鎖的是某個table。由MySQL的SQL layer層實現的 - 行級鎖:鎖的是某行數據,也可能鎖定行之間的間隙。由某些存儲引擎實現,比如InnoDB。
按照鎖的功能來說分為:共享讀鎖和排他寫鎖。
按照鎖的實現方式分為:悲觀鎖和樂觀鎖(使用某一版本列或者唯一列進行邏輯控制)
表級鎖和行級鎖的區別:
表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,并發度最低;
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,并發度也最高
8.2 mysql表級鎖
1)表級鎖介紹
由MySQL SQL layer層實現
MySQL的表級鎖有兩種:
一種是表鎖。一種是元數據鎖(meta data lock,MDL)。
MySQL 實現的表級鎖定的爭用狀態變量:
mysql> show status like 'table%';
table_locks_immediate:產生表級鎖定的次數;
table_locks_waited:出現表級鎖定爭用而發生等待的次數;
2)表鎖介紹
① 表鎖有兩種表現形式:
表共享讀鎖(Table Read Lock)是讀取操作創建的鎖。其他用戶可以并發讀取數據,但任何事務都不能對數據進行修改(獲取數據上的排他鎖),直到已釋放所有共享鎖。 如果事務T對數據A加上共享鎖后,則其他事務只能對A再加共享鎖,不能加排他鎖。獲準共享鎖的事務只能讀數據,不能修改數據。
表獨占寫鎖(Table Write Lock)如果事務T對數據A加上排他鎖后,則其他事務不能再對A加任任何類型的封鎖。獲準排他鎖的事務既能讀數據,又能修改數據。
② 手動增加表鎖:lock table 表名稱 read(write),表名稱2 read(write),其他;
③ 查看表鎖情況:show open tables;
④ 刪除表鎖:unlock tables;
3)表鎖演示
環境準備
CREATE TABLE mylock ( ?id int(11) NOT NULL AUTO_INCREMENT, ?NAME varchar(20) DEFAULT NULL, ?PRIMARY KEY (id) ); INSERT INTO mylock (id,NAME) VALUES (1, 'a'); INSERT INTO mylock (id,NAME) VALUES (2, 'b'); INSERT INTO mylock (id,NAME) VALUES (3, 'c'); INSERT INTO mylock (id,NAME) VALUES (4, 'd');讀鎖演示
寫鎖演示:
讀鎖和寫鎖都是行級鎖,InnoDB的行鎖是通過給索引上的索引項加鎖來實現的,如果沒有索引,InnoDB將通過隱藏的聚簇索引來對記錄加鎖,InnoDB行鎖分為3中情形:
Record Lock:對索引項加鎖。
Gap Lock:對索引項之間的“間隙”、第一條記錄前的“間隙”或最后一條記錄后的“間隙”加鎖。
Next-key Lock:前兩種的結合,對記錄及其前面的間隙加鎖。?
InnoDB這種行鎖的實現特點意味著,如果不通過索引條件檢索數據,那么InnoDB將對表中的所有記錄加鎖,實際效果跟鎖表一樣。
4)元數據鎖介紹
MDL不需要顯式使用,在訪問一個表的時候會被自動加上。
MDL的作用是,保證讀寫的正確性。你可以想象一下,如果一個查詢正在遍歷一個表中的數據,而執行期間另一個線程對這個表結構做變更,刪了一列,那么查詢線程拿到的結果跟表結構對不上,肯定是不行的。
因此,在 MySQL 5.5 版本中引入了 MDL,當對一個表做增刪改查操作的時候,加 MDL 讀鎖;當要對表做結構變更操作的時候,加 MDL 寫鎖。
讀鎖之間不互斥,因此你可以有多個線程同時對一張表增刪改查。
讀寫鎖之間、寫鎖之間是互斥的,用來保證變更表結構操作的安全性。因此,如果有兩個線程要同時給一個表加字段,其中一個要等另一個執行完才能開始執行。
5)元數據鎖演示
我們可以看到 session A 先啟動,這時候會對表 t 加一個 MDL 讀鎖。由于 session B 需要的也是 MDL 讀鎖,因此可以正常執行。
之后 session C 會被 blocked,是因為 session A 的 MDL 讀鎖還沒有釋放,而 session C 需要 MDL 寫鎖,因此只能被阻塞。
如果只有 session C 自己被阻塞還沒什么關系,但是之后所有要在表 t 上新申請 MDL 讀鎖的請求也會被 session C 阻塞。前面我們說了,所有對表的增刪改查操作都需要先申請 MDL 讀鎖,就都被鎖住,等于這個表現在完全不可讀寫了。
你現在應該知道了,事務中的 MDL 鎖,在語句執行開始時申請,但是語句結束后并不會馬上釋放,而會等到整個事務提交后再釋放。
8.3 mysql行級鎖
1. 行級鎖介紹
MySQL的行級鎖,是由存儲引擎來實現的,這里我們主要講解InnoDB的行級鎖。
InnoDB的行級鎖,按照鎖定范圍來說,分為三種:
- 記錄鎖(Record Locks):鎖定索引中一條記錄。
- 間隙鎖(Gap Locks):要么鎖住索引記錄中間的值,要么鎖住第一個索引記錄前面的值或者最后一個索引記錄后面的值。 - Next-Key Locks:是索引記錄上的記錄鎖和在索引記錄之前的間隙鎖的組合。
InnoDB的行級鎖,按照功能來說,分為兩種:
- 共享鎖(S):允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。
- 排他鎖(X):允許獲得排他鎖的事務更新數據,阻止其他事務取得相同數據集的共享讀鎖和排他寫鎖。
對于UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);
對于普通SELECT語句,InnoDB不會加任何鎖,事務可以通過以下語句顯示給記錄集加共享鎖或排他鎖。
手動添加共享鎖(S):
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
手動添加排他鎖(x):
SELECT * FROM table_name WHERE ... FOR UPDATE
InnoDB也實現了表級鎖,也就是意向鎖,意向鎖是mysql內部使用的,不需要用戶干預。
意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
意向鎖和行鎖可以共存,意向鎖的主要作用是為了【全表更新數據】時的性能提升。否則在全表更新數據時,需要先檢索該范是否某些記錄上面有行鎖。
| ?共享鎖(S) ? | ?兼容 ? | ?沖突 ? | ?兼容 ? | ?沖突 ? |
| ?排他鎖(X) ? | ?沖突 ? | ?沖突 ? | ?沖突 ? | ?沖突 ? |
| ?意向共享鎖(IS) ? | ?兼容 ? | ?沖突 ? | ?兼容 ? | ?兼容 ? |
| ?意向排他鎖(IX) ? | ?沖突 ? | ?沖突 ? | ?兼容 ? | ?兼容 ? |
InnoDB行鎖是通過給索引上的索引項加鎖來實現的,因此InnoDB這種行鎖實現特點意味著:只有通過索引條件檢索的數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!
Innodb所使用的行級鎖定爭用狀態查看:
mysql> show status like 'innodb_row_lock%';
- Innodb_row_lock_current_waits:當前正在等待鎖定的數量; - Innodb_row_lock_time:從系統啟動到現在鎖定總時間長度; - Innodb_row_lock_time_avg:每次等待所花平均時間; - Innodb_row_lock_time_max:從系統啟動到現在等待最常的一次所花的時間; - Innodb_row_lock_waits:系統啟動后到現在總共等待的次數;
對于這5個狀態變量,比較重要的主要是:
- Innodb_row_lock_time_avg(等待平均時長) - Innodb_row_lock_waits(等待總次數) - Innodb_row_lock_time(等待總時長)這三項。
尤其是當等待次數很高,而且每次等待時長也不小的時候,我們就需要分析系統中為什么會有如此多的等待,然后根據分析結果著手指定優化計劃。
2. InnoDB行鎖演示
創建表及索引
create table test_innodb_lock (a int(11),b varchar(16)) engine=innodb;create index test_innodb_a_idx on test_innodb_lock(a);create index test_innodb_lock_b_idx on test_innodb_lock(b);行鎖定基本演示
| ?1 ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? |
| ?2 ? | ?mysql> update test_innodb_lock set b = ?'b1' where a = 1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 ?Changed: 1 Warnings: 0 更新,但是不提交 ? | |
| ?3 ? | ?mysql> update test_innodb_lock set b = ?'b2' where a = 1; 被阻塞,等待 ? | |
| ?4 ? | ?mysql> commit; Query OK, 0 rows affected ?(0.05 sec) 提交 ? | |
| ?5 ? | ?mysql> update test_innodb_lock set b = ?'b2' where a = 1; Query OK, 0 rows affected (36.14 sec) Rows matched: 1 ?Changed: 0 Warnings: 0 解除阻塞,更新正常進行 ? |
無索引升級為表鎖演示
| ?1 ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? |
| ?2 ? | ?mysql> update test_innodb_lock set b = ?'2' where b = 2000; Query OK, 1 row affected (0.02 sec) Rows matched: 1 ?Changed: 1 Warnings: 0 ? | ?mysql> update test_innodb_lock set b = ?'3' where b = 3000; 被阻塞,等待 ? |
| ?3 ? | ?mysql> commit; Query OK, 0 rows ?affected (0.10 sec) ? | |
| ?4 ? | ?mysql> update test_innodb_lock set b = ?'3' where b = 3000; Query OK, 1 row affected (1 min 3.41 sec) Rows matched: 1 ?Changed: 1 Warnings: 0 阻塞解除,完成更新 ? |
間隙鎖帶來的插入問題演示
| ?1 ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? |
| ?2 ? | ?mysql> select * from test_innodb_lock; ?| a | b | | 1 | b2 | | 3 | 3 | | 4 | 4000 | | 5 | 5000 | | 6 | 6000 | | 7 | ?7000 | | 8 | 8000 | | 9 | 9000 | | 1 | b1 | 9 rows in set (0.00 sec) ? | |
| ?3 ? | ?mysql> update test_innodb_lock set b = ?a * 100 where a < 4 and a > 1; Query OK, 1 row affected (0.02 sec) Rows ?matched: 1 Changed: 1 Warnings: 0 ? | |
| ?4 ? | ?mysql> insert into test_innodb_lock ?values(2,'200'); 被阻塞,等待 ? | |
| ?5 ? | ?mysql> commit; Query OK, 0 rows ?affected (0.02 sec) ? | |
| ?6 ? | ?mysql> insert into test_innodb_lock ?values(2,'200'); Query OK, 1 row affected (38.68 sec) 阻塞解除,完成插入 ? |
使用共同索引不同數據的阻塞示例
| ?1 ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? |
| ?2 ? | ?mysql> update test_innodb_lock set b = ?'bbbbb' where a = 1 and b = 'b2'; Query OK, 1 row affected (0.00 sec) Rows ?matched: 1 Changed: 1 Warnings: 0 ? | |
| ?3 ? | ?mysql> update test_innodb_lock set b = ?'bbbbb' where a = 1 and b = 'b1'; 被阻塞 ? | |
| ?4 ? | ?mysql> commit; Query OK, 0 rows ?affected (0.02 sec) ? | |
| ?5 ? | ?mysql> update test_innodb_lock set b = ?'bbbbb' where a = 1 and b = 'b1'; Query OK, 1 row affected (42.89 sec) Rows ?matched: 1 Changed: 1 Warnings: 0 session 提交事務,阻塞去除,更新完成 ? |
死鎖演示
| ?1 ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? | ?mysql> set autocommit=0; Query OK, 0 ?rows affected (0.00 sec) ? |
| ?2 ? | ?mysql> update t1 set id = 110 where id ?= 11; Query OK, 0 rows affected (0.00 sec) Rows matched: 0 Changed: 0 ?Warnings: 0 ? | |
| ?3 ? | ?mysql> update t2 set id = 210 where id ?= 21; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 ?Warnings: 0 ? | |
| ?4 ? | ?mysql>update t2 set id=2100 where ?id=21; 等待sessionb釋放資源,被阻塞 ? | |
| ?5 ? | ?mysql>update t1 set id=1100 where ?id=11; Query OK,0 rows affected (0.39sec) Rows matched: 0 Changed: 0 ?Warnings:0 等待sessiona釋放資源,被阻塞 ? | |
| ?兩個 session 互相等等待對方的資源釋放之后才能釋放自己的資源,造成了死鎖 ? |
9. mysql事務
9.1 事務介紹
在MySQL中的事務是由存儲引擎實現的,而且支持事務的存儲引擎不多,我們主要講解InnoDB存儲引擎中的事務。事務處理可以用來維護數據庫的完整性,保證成批的 SQL 語句要么全部執行,要么全部不執行。
事務用來管理DDL、DML、DCL操作,比如 insert,update,delete 語句,默認是自動提交的。
一般來說,事務是必須滿足4個條件(ACID):
① Atomicity(原子性):構成事務的的所有操作必須是一個邏輯單元,要么全部執行,要么全部不執行。
② Consistency(一致性):數據庫在事務執行前后狀態都必須是穩定的或者是一致的。
③ Isolation(隔離性):事務之間不會相互影響。
? ?由鎖機制和MVCC機制來實現的
? ?MVCC:優化讀寫性能(讀不加鎖、讀寫不沖突)
④ Durability(持久性):事務執行成功后必須全部寫入磁盤。
9.2 事務開啟
在MySQL命令行的默認設置下,事務都是自動提交的,即執行SQL語句后就會馬上執行COMMIT操作。因此要顯式地開啟一個事務務須使用命令BEGIN或START TRANSACTION,或者執行命令SET AUTOCOMMIT=0,用來禁止使用當前會話的自動提交。
常見的操作有以下三個:
① BEGIN或START TRANSACTION;顯式地開啟一個事務;
② COMMIT也可以使用COMMIT WORK,不過二者是等價的。COMMIT會提交事務,并使已對數據庫進行的所有修改稱為永久性的;
③ ROLLBACK有可以使用ROLLBACK WORK,不過二者是等價的?;貪L會結束用戶的事務,并撤銷正在進行的所有未提交的修改;
9.3 事務并發問題
在事務的并發操作中可能會出現一些問題:
① 丟失更新:一個事務更新之后,另一個事務也更新了,但是第二個事務回滾了,則第一個事務也被回滾了。
② 臟讀:一個事務讀取到另一個事務未提交的數據。
③ 不可重復讀:一個事務因讀取到另一個事務已提交的update或者delete數據。導致對同一條記錄讀取兩次以上的結果不一致。
④ 幻讀:一個事務因讀取到另一個事務已提交的insert數據。導致對同一張表讀取兩次以上的結果不一致。
9.4 事務隔離級別
1)四種隔離級別(SQL92標準)
現在來看看MySQL數據庫為我們提供的四種隔離級別(由低到高):
① Read uncommitted (讀未提交):最低級別,任何情況都無法保證。
② Read committed (RC,讀已提交):可避免臟讀的發生。
③ Repeatable read (RR,可重復讀):可避免臟讀、不可重復讀的發生。
(注意事項:InnoDB的RR還可以解決幻讀,主要原因是Next-Key鎖,只有RR才能使用Next-Key鎖)
④ Serializable (串行化):可避免臟讀、不可重復讀、幻讀的發生。
(由MVCC降級為Locking-Base CC)
2)默認隔離級別
大多數數據庫的默認隔離級別是Read Committed,比如Oracle、DB2等。
MySQL數據庫的默認隔離級別是Repeatable Read。
3)如何查看和設置隔離級別
在MySQL數據庫中查看當前事務的隔離級別:
select @@tx_isolation;
在MySQL數據庫中設置事務的隔離級別:
set [glogal | session] transaction isolation level 隔離級別名稱;
set tx_isolation=’隔離級別名稱;’
4)注意事項
隔離級別越高,越能保證數據的完整性和一致性,但是對并發性能的影響也越大。
對于多數應用程序,可以優先考慮把數據庫系統的隔離級別設為Read Committed。它能夠避免臟讀取,而且具有較好的并發性能。盡管它會導致不可重復讀、幻讀這些并發問題,在可能出現這類問題的個別場合,可以由應用程序采用悲觀鎖或樂觀鎖來控制。
總結
以上是生活随笔為你收集整理的mysql 一对多 关联一条最新的数据_不得不会的mysql锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 元素周期表排列的规律_中考化学:金属活动
- 下一篇: oracle 事务_从Oracle到PG