程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> mysql存儲進程事務治理簡析

mysql存儲進程事務治理簡析

編輯:MySQL綜合教程

mysql存儲進程事務治理簡析。本站提示廣大學習愛好者:(mysql存儲進程事務治理簡析)文章只能為提供參考,不一定能成為您想要的結果。以下是mysql存儲進程事務治理簡析正文


ACID:Atomic、Consistent、Isolated、Durable
存儲法式供給了一個絕佳的機制來界說、封裝和治理事務。
1,MySQL的事務支撐
1)MySQL的事務支撐不是綁定在MySQL辦事器自己,而是與存儲引擎相干:
Sql代碼

MyISAM:不支撐事務,用於只讀法式進步機能
InnoDB:支撐ACID事務、行級鎖、並發
Berkeley DB:支撐事務
MyISAM:不支撐事務,用於只讀法式進步機能
InnoDB:支撐ACID事務、行級鎖、並發
Berkeley DB:支撐事務 2) 隔離級別:

隔離級別決議了一個session中的事務能夠對另外一個session的影響、並發session對數據庫的操作、一個session中所見數據的分歧性
ANSI尺度界說了4個隔離級別,MySQL的InnoDB都支撐:
Java代碼

READ UNCOMMITTED:最初級其余隔離,平日又稱為dirty read,它許可一個事務讀取還沒commit的數據,如許能夠會進步機能,然則dirty read能夠不是我們想要的
READ COMMITTED:在一個事務中只許可曾經commit的記載可見,假如session中select還在查詢中,另外一session此時insert一筆記錄,則新添加的數據弗成見
REPEATABLE READ:在一個事務開端後,其他session對數據庫的修正在本領務中弗成見,直到本領務commit或rollback。在一個事務中反復select的成果一樣,除非本領務中update數據庫。
SERIALIZABLE:第一流其余隔離,只許可事務串行履行。為了到達此目標,數據庫會鎖住每行曾經讀取的記載,其他session不克不及修正數據直到前一事務停止,事務commit或撤消時才釋放鎖。
READ UNCOMMITTED:最初級其余隔離,平日又稱為dirty read,它許可一個事務讀取還沒commit的數據,如許能夠會進步機能,然則dirty read能夠不是我們想要的
READ COMMITTED:在一個事務中只許可曾經commit的記載可見,假如session中select還在查詢中,另外一session此時insert一筆記錄,則新添加的數據弗成見
REPEATABLE READ:在一個事務開端後,其他session對數據庫的修正在本領務中弗成見,直到本領務commit或rollback。在一個事務中反復select的成果一樣,除非本領務中update數據庫。
SERIALIZABLE:第一流其余隔離,只許可事務串行履行。為了到達此目標,數據庫會鎖住每行曾經讀取的記載,其他session不克不及修正數據直到前一事務停止,事務commit或撤消時才釋放鎖。 可使用以下語句設置MySQL的session隔離級別:

Sql代碼

set transaction isolation level {read uncommitted | read committed | repeatable read | serializable}
set transaction isolation level {read uncommitted | read committed | repeatable read | serializable}

MySQL默許的隔離級別是REPEATABLE READ,在設置隔離級別為READ UNCOMMITTED或SERIALIZABLE時要當心,READ UNCOMMITTED會招致數據完全性的嚴重成績,而SERIALIZABLE會招致機能成績並增長逝世鎖的機率
3)事務治理語句:
Sql代碼

START TRANSACTION:開端事務,autocommit設為0,假如曾經有一個事務在運轉,則會觸發一個隱蔽的COMMIT
COMMIT:提交事務,保留更改,釋放鎖
ROLLBACK:回滾本領務對數據庫的一切更改,然後停止事務,釋放鎖
SAVEPOINT savepoint_name:創立一個savepoint辨認符來ROLLBACK TO SAVEPOINT
ROLLBACK TO SAVEPOINT savepoint_name:回滾到從savepoint_name開端對數據庫的一切更改,如許就許可回滾事務中的一部門,包管更改的一個子集被提交
SET TRANSACTION:許可設置事務的隔離級別
LOCK TABLES:許可顯式的鎖住一個或多個table,會隱式的封閉以後翻開的事務,建議在履行LOCK TABLES語句之前顯式的commit或rollback。我們普通所以普通在事務代碼裡不會應用LOCK TABLES
START TRANSACTION:開端事務,autocommit設為0,假如曾經有一個事務在運轉,則會觸發一個隱蔽的COMMIT
COMMIT:提交事務,保留更改,釋放鎖
ROLLBACK:回滾本領務對數據庫的一切更改,然後停止事務,釋放鎖
SAVEPOINT savepoint_name:創立一個savepoint辨認符來ROLLBACK TO SAVEPOINT
ROLLBACK TO SAVEPOINT savepoint_name:回滾到從savepoint_name開端對數據庫的一切更改,如許就許可回滾事務中的一部門,包管更改的一個子集被提交
SET TRANSACTION:許可設置事務的隔離級別
LOCK TABLES:許可顯式的鎖住一個或多個table,會隱式的封閉以後翻開的事務,建議在履行LOCK TABLES語句之前顯式的commit或rollback。我們普通所以普通在事務代碼裡不會應用LOCK TABLES

2,界說事務
MySQL默許的行動是在每條SQL語句履行後履行一個COMMIT語句,從而有用的將每條語句自力為一個事務。
在龐雜的運用場景下這類方法就不克不及知足需求了。
為了翻開事務,許可在COMMIT和ROLLBACK之前多條語句被履行,我們須要做以下兩步(也能夠工資是兩種方法):
1, 設置MySQL的autocommit屬性為0,默許為1
2,應用START TRANSACTION語句顯式的翻開一個事務(然後autocommit屬性會主動被設置為0)
假如曾經翻開一個事務,則SET autocommit=0不會起感化,由於START TRANSACTION會隱式的提交session中一切以後的更改,停止已有的事務,並翻開一個新的事務。
應用SET AUTOCOMMIT語句的存儲進程例子:
Sql代碼

delimiter $$
use test$$
create procedure t_insert_table()
begin
/** 標志能否失足 */
declare t_error int default 0;
/** 假如湧現sql異常,則將t_error設置為1後持續履行前面的操作 */
declare continue handler for sqlexception set t_error=1; -- 失足處置
/** 顯示的開啟事務,啟動它後,autocommit值會主動設置為0 */
start transaction;
insert into t_bom_test(parent_id,child_id) values('C','XXXX');
insert into t_trigger_test(name,age) values('zhangsan',34);
/** 標志被轉變,表現事務應當回滾 */
if t_error=1 then
rollback; -- 事務回滾
else
commit; -- 事務提交
end if;
end$$
delimiter ;
delimiter $$
use test$$
create procedure t_insert_table()
begin
/** 標志能否失足 */
declare t_error int default 0;
/** 假如湧現sql異常,則將t_error設置為1後持續履行前面的操作 */
declare continue handler for sqlexception set t_error=1; -- 失足處置
/** 顯示的開啟事務,啟動它後,autocommit值會主動設置為0 */
start transaction;
insert into t_bom_test(parent_id,child_id) values('C','XXXX');
insert into t_trigger_test(name,age) values('zhangsan',34);
/** 標志被轉變,表現事務應當回滾 */
if t_error=1 then
rollback; -- 事務回滾
else
commit; -- 事務提交
end if;
end$$
delimiter ;

平日COMMIT或ROLLBACK語句履行時才完成一個事務,然則有些DDL語句等會隱式觸發COMMIT,所以應當在事務中盡量罕用或留意一下:
Sql代碼

ALTER FUNCTION
ALTER PROCEDURE
ALTER TABLE
BEGIN
CREATE DATABASE
CREATE FUNCTION
CREATE INDEX
CREATE PROCEDURE
CREATE TABLE
DROP DATABASE
DROP FUNCTION
DROP INDEX
DROP PROCEDURE
DROP TABLE
UNLOCK TABLES
LOAD MASTER DATA
LOCK TABLES
RENAME TABLE
TRUNCATE TABLE
SET AUTOCOMMIT=1
START TRANSACTION
ALTER FUNCTION
ALTER PROCEDURE
ALTER TABLE
BEGIN
CREATE DATABASE
CREATE FUNCTION
CREATE INDEX
CREATE PROCEDURE
CREATE TABLE
DROP DATABASE
DROP FUNCTION
DROP INDEX
DROP PROCEDURE
DROP TABLE
UNLOCK TABLES
LOAD MASTER DATA
LOCK TABLES
RENAME TABLE
TRUNCATE TABLE
SET AUTOCOMMIT=1
START TRANSACTION


3,應用Savepoint
應用savepoint回滾不免有些機能消費,普通可以用IF改寫
savepoint的優越應用的場景之一是“嵌套事務”,你能夠願望法式履行一個小的事務,然則不願望回滾裡面更年夜的事務:
Sql代碼

CREATE PROCEDURE nested_tfer_funds
(in_from_acct INTEGER,
in_to_acct INTEGER,
in_tfer_amount DECIMAL(8,2))
BEGIN
DECLARE txn_error INTEGER DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN
SET txn_error=1;
END
SAVEPINT savepint_tfer;
UPDATE account_balance
SET balance=balance-in_tfer_amount
WHERE account_id=in_from_acct;
IF txn_error THEN
ROLLBACK TO savepoint_tfer;
SELECT 'Transfer aborted';
ELSE
UPDATE account_balance
SET balance=balance+in_tfer_amount
WHERE account_id=in_to_acct;
IF txn_error THEN
ROLLBACK TO savepoint_tfer;
SELECT 'Transfer aborted';
END IF:
END IF;
END;
CREATE PROCEDURE nested_tfer_funds
(in_from_acct INTEGER,
in_to_acct INTEGER,
in_tfer_amount DECIMAL(8,2))
BEGIN
DECLARE txn_error INTEGER DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN
SET txn_error=1;
END
SAVEPINT savepint_tfer;
UPDATE account_balance
SET balance=balance-in_tfer_amount
WHERE account_id=in_from_acct;
IF txn_error THEN
ROLLBACK TO savepoint_tfer;
SELECT 'Transfer aborted';
ELSE
UPDATE account_balance
SET balance=balance+in_tfer_amount
WHERE account_id=in_to_acct;
IF txn_error THEN
ROLLBACK TO savepoint_tfer;
SELECT 'Transfer aborted';
END IF:
END IF;
END;

4,事務和鎖
事務的ACID屬性只能經由過程限制數據庫的同步更改來完成,從而經由過程對修正數據加鎖來完成。
直到事務觸發COMMIT或ROLLBACK語句時鎖才釋放。
缺陷是前面的事務必需等後面的事務完成能力開端履行,吞吐量跟著期待鎖釋放的時光增加而遞加。
MySQL/InnoDB經由過程行級鎖來最小化鎖競爭。如許修正統一table裡其他行的數據沒無限制,並且讀數據可以一直沒有期待。
可以在SELECT語句裡應用FOR UPDATE或LOCK IN SHARE MODE語句來加下行級鎖
Sql代碼

SELECT select_statement options [FOR UPDATE|LOCK IN SHARE MODE]
SELECT select_statement options [FOR UPDATE|LOCK IN SHARE MODE]

FOR UPDATE會鎖住該SELECT語句前往的行,其他SELECT和DML語句必需期待該SELECT語句地點的事務完成
LOCK IN SHARE MODE同FOR UPDATE,然則許可其他session的SELECT語句履行並許可獲得SHARE MODE鎖
逝世鎖:
逝世鎖產生於兩個事務互相期待彼此釋放鎖的情形
當MySQL/InnoDB檢討到逝世鎖時,它會強迫一個事務rollback並觸發一條毛病新聞
對InnoDB而言,所選擇的rollback的事務是完成任務起碼的事務(所修正的行起碼)
Java代碼

mysql > CALL tfer_funds(1,2,300);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

逝世鎖在任何數據庫體系裡都能夠產生,然則對MySQL/InnoDB這類行級鎖數據庫而言能夠性絕對較少。
可以經由過程應用分歧的次序來鎖row或table和讓事務堅持盡量短來削減逝世鎖的頻率。
假如逝世鎖不輕易debug,你可以向你的法式中添加一些邏輯來處置逝世鎖偏重試事務,但這部門代碼多了今後很難保護
所以,比擬好的防止逝世鎖的方法是在做任何修正之前按必定的次序添加行級鎖,如許就可以防止逝世鎖:
Java代碼

CREATE PROCEDURE tfer_funds3
(from_account INT, to_account INT, tfer_amount NUMERIC(10,2))
BEGIN
DECLARE local_account_id INT;
DECLARE lock_cursor CURSOR FOR
SELECT account_id
FROM account_balance
WHERE account_id IN (from_account, to_account)
ORDER BY account_id
FOR UPDATE;
START TRANSACTION;
OPEN lock_cursor;
FETCH lock_cursor INTO local_account_id;
UPDATE account_balance
SET balance=balance-tfer_amount
WHERE account_id=from_account;
UPDATE account_balance
SET balance=balance+tfer_amount
WHERE account_id=to_account;
CLOSE lock_cursor;
COMMIT;
END;

設置逝世鎖ttl: innodb_lock_wait_timeout,默許為50秒
假如你在一個事務中混雜應用InnoDB和非InnoDB表,則MySQL不克不及檢測到逝世鎖,此時會拋出“lock wait timeuot”1205毛病
悲觀所和消極鎖戰略:
消極鎖:在讀取數據時鎖住那幾行,其他對這幾行的更新須要比及消極鎖停止時能力持續
悲觀所:讀取數據時不鎖,更新時檢討能否數據曾經被更新過,假如是則撤消以後更新
普通在消極鎖的期待時光太長而不克不及接收時我們才會選擇悲觀鎖
消極鎖的例子:
Java代碼

CREATE PROCEDURE tfer_funds
(from_account INT, to_account INT,tfer_amount NUMERIC(10,2),
OUT status INT, OUT message VARCHAR(30))
BEGIN
DECLARE from_account_balance NUMERIC(10,2);
START TRANSACTION;
SELECT balance
INTO from_account_balance
FROM account_balance
WHERE account_id=from_account
FOR UPDATE;
IF from_account_balance>=tfer_amount THEN
UPDATE account_balance
SET balance=balance-tfer_amount
WHERE account_id=from_account;
UPDATE account_balance
SET balance=balance+tfer_amount
WHERE account_id=to_account;
COMMIT;
SET status=0;
SET message='OK';
ELSE
ROLLBACK;
SET status=-1;
SET message='Insufficient funds';
END IF;
END;

悲觀鎖的例子:
Java代碼

CREATE PROCEDURE tfer_funds
(from_account INT, to_account INT, tfer_amount NUMERIC(10,2),
OUT status INT, OUT message VARCHAR(30) )
BEGIN
DECLARE from_account_balance NUMERIC(8,2);
DECLARE from_account_balance2 NUMERIC(8,2);
DECLARE from_account_timestamp1 TIMESTAMP;
DECLARE from_account_timestamp2 TIMESTAMP;
SELECT account_timestamp,balance
INTO from_account_timestamp1,from_account_balance
FROM account_balance
WHERE account_id=from_account;
IF (from_account_balance>=tfer_amount) THEN
-- Here we perform some long running validation that
-- might take a few minutes */
CALL long_running_validation(from_account);
START TRANSACTION;
-- Make sure the account row has not been updated since
-- our initial check
SELECT account_timestamp, balance
INTO from_account_timestamp2,from_account_balance2
FROM account_balance
WHERE account_id=from_account
FOR UPDATE;
IF (from_account_timestamp1 <> from_account_timestamp2 OR
from_account_balance <> from_account_balance2) THEN
ROLLBACK;
SET status=-1;
SET message=CONCAT("Transaction cancelled due to concurrent update",
" of account" ,from_account);
ELSE
UPDATE account_balance
SET balance=balance-tfer_amount
WHERE account_id=from_account;
UPDATE account_balance
SET balance=balance+tfer_amount
WHERE account_id=to_account;
COMMIT;
SET status=0;
SET message="OK";
END IF;
ELSE
ROLLBACK;
SET status=-1;
SET message="Insufficient funds";
END IF;
END$$

5,事務設計指南
1,堅持事務短小
2,盡可能防止事務中rollback
3,盡可能防止savepoint
4,默許情形下,依附於消極鎖
5,為吞吐量請求刻薄的事務斟酌悲觀鎖
6,顯示聲明翻開事務
7,鎖的行越少越好,鎖的時光越短越好
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved