1. 程式人生 > 其它 >檢視、觸發器、儲存過程、流程控制等MySQL小知識點補充

檢視、觸發器、儲存過程、流程控制等MySQL小知識點補充

檢視、觸發器、儲存過程、流程控制等MySQL小知識點補充

SQL注入問題

SQL注入:利用特殊符號的組合產生特殊的含義,避開正常的業務邏輯
問題:
1.輸對使用者名稱就可以成功登入
select * from userinfo where name='jason' -- kasdjksajd' and pwd=''

2.不需要對的使用者名稱和密碼也可以登入成功
select * from userinfo where name='xyz' or 1=1 -- aksdjasldj' and pwd='' 

#SQL注入問題產生的原因:手動拼接關鍵資料
#解決辦法:交給execute處理即可
SQL = " select * from userinfo where name=%s and pwd=%s"
cursor.execute(sql,(username,password))
"""
ps:
若是想一次性傳入多個數據,可使用executemany語法,用元組傳入多個數據
	executemany(sql,[(),(),()...])
"""

檢視

檢視就是通過查詢得到一張虛擬表,儲存下來下次可直接使用。
create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;

1.檢視的表只能用來查詢,不能做增刪改操作
2.儘量少用,使用檢視的表容易與真正的表產生混淆,干擾操作者

觸發器

觸發器:滿足達成某條件自動觸發,針對表繼續增刪改的操作能夠自動觸發
主要包含六種情況:增前、增後、刪前、刪後、改前、改後
create trigger 觸發器名字 before\after insert\update\delete on 表名 
for each row
begin
	sql語句
end
1.觸發器命名規律
	tri_before_insert_t1
    tri_after_delete_t2
    tri_after_update_t2

2.臨時修改SQL語句的結束符
	因為有些操作需要使用分號,為避免衝突可修改SQL語句的結束符。如($/&/@)

觸發器實際應用
CREATE TABLE cmd(
id INT PRIMARY KEY auto_increment,
USER CHAR(32),
priv CHAR(10),
cmd CHAR(64),
sub_time datetime,
# 提交時間
success enum('yes','no')
# 0代表執行失敗
);

CREATE TABLE errlog(
	id INT PRIMARY KEY auto_increment,
    err_cmd CHAR(64),
    err_time datetime
);

delimiter $$
# 換預設的結束符為$$,是因為程式碼中需大量使用分號,以避免衝突
create trigger tri_after_insert_cmd after insert on cmd for each row 
begin
	if NEW.success='no' then
    # 新紀錄都會被MySQL封裝成NEW物件
    	insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
	end if;
end $$
delimiter;
# 結束之後要改回來,否則後面結束符會都變成$$

INSERT INTO cmd (
    USER,
    priv,
    cmd,
    sub_time,
    success
)
VALUES
    ('kevin','0755','ls -l /etc',NOW(),'yes'),
    ('kevin','0755','cat /etc/passwd',NOW(),'no'),
    ('kevin','0755','useradd xxx',NOW(),'no'),
    ('kevin','0755','ps aux',NOW(),'yes');
#往表cmd中插入記錄,觸發觸發器,根據IF的條件決定是否插入錯誤日誌

select * from errlog;
# 查詢errlog表記錄

show triggers;
# 檢視所有的觸發器

drop trigger tri_after_insert_cmd;
# 刪除觸發器

事務

多個操作同時進行,同時成功或失敗,這就是事務。

  • 事務是一個最小的不可再分的工作單元,通常一個事務對應一個完整的業務。
  • 一個完整的業務需要批量的DML(insert、update、delete)語句共同聯合完成。
  • 事務只和DML語句有關,業務邏輯不同,DML語句的個數不同。
事務四個特性:
	一致性、永續性、原子性、隔離性

一致性:事務執行接收之後,資料庫完整性不被破壞
永續性:事務一旦提交,對資料庫的改變是永久的。只要提交了事務,將會對資料庫的資料進行        永久性重新整理
原子性:事務是一個不可分割的工作單位,同時成功或失敗。
	例:當兩個人發起轉賬業務時,如果A轉賬發起,而B因為一些原因不能成功接受,事務最         終將不會提交,則A和B的請求最終不會成功。
隔離性:多個事務之間相互隔離,互不干擾
"""
注意:只有當後三條性質都滿足了,才能保證事務的一致性
"""
# 事務程式碼應用
create table user(
id int primary key auto_increment,
name char(32),
balance int
);

insert into user(name,balance)
values
('jason',1000),
('kevin',1000),
('tank',1000);

# 修改資料之前先開啟事務操作
start transaction;

# 修改操作
update 表名 set 修改後的欄位 where 約束條件;
update user set balance=900 where name='jason';
update user set balance=1010 where name='kevin';
update user set balance=1090 where name='tank';

rollback;
# 回滾到上一個狀態,返回修改前的資料

commit;
# 開啟事務之後,只要沒有執行commit操作,資料其實都沒有真正重新整理到硬碟;但是執行	       commit後,資料便無法使用rollback返回之前的狀態了

"""
事務關鍵字
	start transaction;
	rollback
	commit
	savepoint:
		給rollback設定一個回滾的點,執行rollback時使用該點可返回到該點對應		的位置。
	savepoint應用語法結構:
         savepoint 點名
         rollback to 點名
"""

SQL四種隔離級別:
	每一種級別都規定了一個事務中所做的修改
InnoDB支援所有隔離級別
	set transaction isolation level 級別

1. read uncommitted(未提交讀)
	事務中已修改但沒有提交的資料,對其他事務可見,事務可以讀取未提交的資料,這一現象也稱之為"髒讀"
2.read committed(提交讀)
	大多數資料庫系統預設的隔離級別。一個事務從開始直到提交之前所作的任何修改對其他事務都是不可見的,這種級別也叫做"不可重複讀"
3.repeatable read(可重複讀)		
# MySQL預設隔離級別
 	能夠解決"髒讀"問題,但是無法解決"幻讀"
    所謂幻讀指的是當某個事務在讀取某個範圍內的記錄時另外一個事務又在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍的記錄會產生幻行,InnoDB和XtraDB通過多版本併發控制(MVCC)及間隙鎖策略解決該問題
4.serializable(可序列讀)
	強制事務序列執行,很少使用該級別

	MVCC只能在read committed(提交讀)、repeatable read(可重複讀)兩種隔離級別下工作,其他兩個不相容(read uncommitted:總是讀取最新;serializable:所有的行都加鎖)
    InnoDB的MVCC通過在每行記錄後面儲存兩個隱藏的列來實現MVCC
	一個列儲存了行的建立時間
    一個列儲存了行的過期時間(或刪除時間)  # 本質是系統版本號
	每開始一個新的事務版本號都會自動遞增,事務開始時刻的系統版本號會作為事務的版本號用來和查詢到的每行記錄版本號進行比較
    
例如:
# 剛插入第一條資料的時候,我們預設事務id為1,實際是這樣儲存的
    username		create_version		delete_version
    jason					1					
	可以看到,我們在content列插入了kobe這條資料,在create_version這列儲存了1,1是這次插入操作的事務id。
# 然後我們將jason修改為jason01,實際儲存是這樣的
    username		create_version		delete_version
    jason					1				2
    jason01					2
	可以看到,update的時候,會先將之前的資料delete_version標記為當前新的事務id,也就是2,然後將新資料寫入,將新資料的create_version標記為新的事務id
# 當我們刪除資料的時候,實際儲存是這樣的
	username		create_version		delete_version
    jason01					2				3
"""
由此可見,當我們查詢一條記錄的時候,只有滿足以下兩個條件的記錄才會被顯示出來:
   1.當前事務id要大於或者等於當前行的create_version值,這表示在事務開始前這行資料已經存在了。
   2.當前事務id要小於delete_version值,這表示在事務開始之後這行記錄才被刪除。
"""

MySQL提供兩種事務型儲存引擎InnoDB和NDB cluster及第三方XtraDB、PBXT
事務處理中有幾個關鍵詞彙會反覆出現
  事務(transaction)
  回退(rollback)
  提交(commit)
  保留點(savepoint)
	為了支援回退部分事務處理,必須能在事務處理塊中合適的位置放置佔位符,這樣如果需要回退可以回退到某個佔位符(保留點)
    建立佔位符可以使用savepoint
    	savepoint sp01;
    回退到佔位符地址
    	rollback to sp01;
# 保留點在執行rollback或者commit之後自動釋放

"""
事務日誌:事務日誌可以幫助提高事務的效率 
	儲存引擎在修改表的資料時只需要修改其記憶體拷貝再把該修改記錄到持久在硬碟上的事務日誌中,而不用每次都將修改的資料本身持久到磁碟
  事務日誌採用的是追加方式因此寫日誌操作是磁碟上一小塊區域內的順序IO而不像隨機IO需要次哦按的多個地方移動磁頭所以採用事務日誌的方式相對來說要快的多
  事務日誌持久之後記憶體中被修改的資料再後臺可以慢慢刷回磁碟,目前大多數儲存引擎都是這樣實現的,通常稱之為"預寫式日誌"修改資料需要寫兩次磁碟
"""

儲存過程

儲存過程可看成是python中的自定義函式,它包含了一系列可執行的SQL語句,儲存過程存放於MySQL中,通過呼叫它的名字可以執行其內部的一堆SQL。
# 無參函式
delimiter $$
create procedure p1()
begin
	select * from cmd;
    # 真正執行的程式碼
end $$
delimiter;

call p1()
# 呼叫


# 有參函式
delimiter $$
create procedure p3(
	in m int,in n int,out res int)
 # in表示這個引數必須只能是傳入不能被返回出去
 # out表示這個引數可以被返回出去,還有一個inout表示既可以傳入也可以被返回出去
begin
	select * from emp where id >m and id < n;
	set res=0;
    # 用來標誌儲存過程是否執行
end$$
delimiter;

# 針對res需要先提前定義
set @res=10;
select @res;
call p3(1,5,@res)
select @res 

"""
檢視儲存過程具體資訊
	show create procedure pro1;
檢視所有儲存過程
	show procedure status;
刪除儲存過程
	drop procedure pro1;
ps:
儲存過程在哪個庫下面建立的只能在對應的庫下面才能使用!
"""


1、直接在mysql中呼叫
set @res=10  
# res的值是用來判斷儲存過程是否被執行成功的依據,所以需要先定義一個變數@res儲存10
call p1(2,4,10);  # 報錯
call p1(2,4,@res);  

# 檢視結果
select @res;  
# 執行成功,@res變數值發生了變化

2、在python程式中呼叫
pymysql連結mysql
產生的遊表cursor.callproc('p1',(2,4,10)) 
# 內部原理:@_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute('select @_p1_2;')

函式

可看成python中的內建函式,與儲存過程的區別,mysql的內建函式只能在SQL語句中使用。
"可以通過help 函式名,檢視幫助資訊"
# 移除指定字元
Trim、LTrim、RTrim

# 大小寫轉換
Lower、Upper

# 獲取左右起始指定個數字元
Left、Right

# 返回讀音相似值(對英文效果)
Soundex
"""
eg:客戶表中有一個顧客登記的使用者名稱為J.Lee
		但如果這是輸入錯誤真名其實叫J.Lie,可以使用soundex匹配發音類似的
		where Soundex(name)=Soundex('J.Lie')
"""

# 日期格式:date_format
'''在MySQL中表示時間格式儘量採用2022-11-11形式'''
CREATE TABLE blog (
    id INT PRIMARY KEY auto_increment,
    NAME CHAR (32),
    sub_time datetime
);
INSERT INTO blog (NAME, sub_time)
VALUES
    ('第1篇','2015-03-01 11:31:21'),
    ('第2篇','2015-03-11 16:31:21'),
    ('第3篇','2016-07-01 10:21:31'),
    ('第4篇','2016-07-22 09:23:21'),
    ('第5篇','2016-07-23 10:11:11'),
    ('第6篇','2016-07-25 11:21:31'),
    ('第7篇','2017-03-01 15:33:21'),
    ('第8篇','2017-03-01 17:32:21'),
    ('第9篇','2017-03-01 18:31:21');
select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');

1.where Date(sub_time) = '2015-03-01'
2.where Year(sub_time)=2016 AND Month(sub_time)=07;
# 更多日期處理相關函式 
	adddate	增加一個日期 
	addtime	增加一個時間
	datediff計算兩個日期差值

流程控制

# 分支結構
declare i int default 0;
IF i = 1 THEN
	SELECT 1;
ELSEIF i = 2 THEN
	SELECT 2;
ELSE
	SELECT 7;
END IF;

# 迴圈結構
DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
	SELECT num ;
	SET num = num + 1 ;
END WHILE ;

索引相關概念

1)索引就好比一本書的目錄,它能讓你更快的找到自己想要的內容
2)讓獲取的資料更有目的性,從而提高資料庫檢索資料的效能

索引在MySQL中也叫做“鍵”,是儲存引擎用於快速找到記錄的一種資料結構
	* primary key
	* unique key
	* index key
1.上述的三個key都可以加快資料查詢
2.primary key和unique key除了可以加快查詢本身還自帶限制條件而index key很單一就是用來加快資料查詢
3.外來鍵不屬於索引鍵的範圍 是用來建立關係的 與加快查詢無關

索引加快查詢的本質
	id int primary key auto_increment,
 	name varchar(32) unique,
  	province varchar(32)
 	age int
 	phone bigint
 	
	select name from userinfo where phone=18818888888;  # 一頁頁的翻
	select name from userinfo where id=99999;  # 按照目錄確定頁數找

索引可以加快資料查詢 但是會降低增刪的速度
通常情況下我們頻繁使用某些欄位查詢資料
	為了提升查詢的速度可以將該欄位建立索引
    
聚集索引(primary key)
	主鍵、主鍵索引
輔助索引(unique,index)
	除主鍵意外的都是輔助索引
覆蓋索引
	select name from user where name='jason';
非覆蓋索引
	select age from user where name='jason';

索引資料結構

索引底層其實是樹結構>>>:樹是計算機底層的資料結構
 
樹有很多中型別
	二叉樹、b樹、b+樹、B*樹......

二叉樹
	二叉樹裡面還可以細分成很多領域 我們簡單的瞭解即可 
  	二叉意味著每個節點最大隻能分兩個子節點
B樹
	所有的節點都可以存放完整的資料
B+\*樹
	只有葉子節點才會存放真正的資料 其他節點只存放索引資料
 	B+葉子節點增加了指向其他葉子節點的指標
  	B*葉子節點和枝節點都有指向其他節點的指標

輔助索引在查詢資料的時候最會還是需要藉助於聚集索引
	輔助索引葉子節點存放的是資料的主鍵值

有時候就算採用索引欄位查詢資料 也可能不會走索引!!!
	最好能記三個左右的特殊情況

慢查詢優化

explain

1)index		儘量避免
2)range		
3)ref
4)eq_ref
5)const
6)system
7)null

測試索引

#1. 準備表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);

#2. 建立儲存過程,實現批量插入記錄
delimiter $$ #宣告儲存過程的結束符號為$$
create procedure auto_insert1()
BEGIN
    declare i int default 1;
    while(i<3000000)do
        insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
        set i=i+1;
    end while;
END$$ #$$結束
delimiter ; #重新宣告分號為結束符號

#3. 檢視儲存過程
show create procedure auto_insert1\G 

#4. 呼叫儲存過程
call auto_insert1();

# 表沒有任何索引的情況下
select * from s1 where id=30000;
# 避免列印帶來的時間損耗
select count(id) from s1 where id = 30000;
select count(id) from s1 where id = 1;

# 給id做一個主鍵
alter table s1 add primary key(id);  # 速度很慢

select count(id) from s1 where id = 1;  # 速度相較於未建索引之前兩者差著數量級
select count(id) from s1 where name = 'jason'  # 速度仍然很慢


"""
範圍問題
"""
# 並不是加了索引,以後查詢的時候按照這個欄位速度就一定快   
select count(id) from s1 where id > 1;  # 速度相較於id = 1慢了很多
select count(id) from s1 where id >1 and id < 3;
select count(id) from s1 where id > 1 and id < 10000;
select count(id) from s1 where id != 3;

alter table s1 drop primary key;  # 刪除主鍵 單獨再來研究name欄位
select count(id) from s1 where name = 'jason';  # 又慢了

create index idx_name on s1(name);  # 給s1表的name欄位建立索引
select count(id) from s1 where name = 'jason'  # 仍然很慢!!!
"""
再來看b+樹的原理,資料需要區分度比較高,而我們這張表全是jason,根本無法區分
那這個樹其實就建成了“一根棍子”
"""
select count(id) from s1 where name = 'xxx';  
# 這個會很快,我就是一根棍,第一個不匹配直接不需要再往下走了
select count(id) from s1 where name like 'xxx';
select count(id) from s1 where name like 'xxx%';
select count(id) from s1 where name like '%xxx';  # 慢 最左匹配特性

# 區分度低的欄位不能建索引
drop index idx_name on s1;

# 給id欄位建普通的索引
create index idx_id on s1(id);
select count(id) from s1 where id = 3;  # 快了
select count(id) from s1 where id*12 = 3;  # 慢了  索引的欄位一定不要參與計算

drop index idx_id on s1;
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';
# 針對上面這種連續多個and的操作,mysql會從左到右先找區分度比較高的索引欄位,先將整體範圍降下來再去比較其他條件
create index idx_name on s1(name);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';  # 並沒有加速

drop index idx_name on s1;
# 給name,gender這種區分度不高的欄位加上索引並不難加快查詢速度

create index idx_id on s1(id);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';  # 快了  先通過id已經講資料快速鎖定成了一條了
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 慢了  基於id查出來的資料仍然很多,然後還要去比較其他欄位

drop index idx_id on s1

create index idx_email on s1(email);
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 快 通過email欄位一劍封喉 

聯合索引

select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  
# 如果上述四個欄位區分度都很高,那給誰建都能加速查詢
# 給email加然而不用email欄位
select count(id) from s1 where name='jason' and gender = 'male' and id > 3; 
# 給name加然而不用name欄位
select count(id) from s1 where gender = 'male' and id > 3; 
# 給gender加然而不用gender欄位
select count(id) from s1 where id > 3; 

# 帶來的問題是所有的欄位都建了索引然而都沒有用到,還需要花費四次建立的時間
create index idx_all on s1(email,name,gender,id);  # 最左匹配原則,區分度高的往左放
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';  # 速度變快

全文檢索

MySQL的全文檢索功能MYISAM儲存引擎支援而InnoDB儲存引擎不支援
一般在建立表的時候啟用全文檢索功能
create table t1(
	id int primary key auto_increment,
  content text
	fulltext(content)
)engine=MyISAM;

# match括號內的值必須是fulltext括號中定義的(單個或者多個)
select content from t1 where match(content) against('jason')
'''上述語句可以用like實現但是查詢出來的結果順序不同 全文檢索會以文字匹配的良好程度排序資料再返回效果更佳'''

# 查詢擴充套件
select note_text from productnotes where Math(note_text) Against('jason' with query expansion);
"""
返回除jason外以及其他jason所在行相關文字內容行資料
eg:
	jason is handsome and cool,every one want to be cool,tony want to be more handsome;
	二三句雖然沒有jason關鍵字 但是含有jason所在行的cool和handsome
"""

# 布林文字搜尋
即使沒有定義fulltext也可以使用,但是這種方式非常緩慢效能低下
select note_text from productnotes where Match(note_text) Against('jason' in boolean mode);

# 注意事項
1.三個及三個以下字元的詞視為短詞,全文檢索直接忽略且從索引中排除
2.MySQL自身自帶一個非用詞列表,表內詞預設均被忽略(可以修改該列表)
3.出現頻率高於50%的詞自動作為非用詞忽略,該規則不適用於布林搜尋
4.針對待搜尋的文字內容不能少於三行,否則檢索不返回任何結果
5.單引號預設忽略

# 插入資料
	資料庫經常被多個使用者訪問,insert操作可能會很耗時
    (特別是有很多索引需要更新的時候)而且還可能降低等待處理的select語句效能
	如果資料檢索是最重要的(一般都是),則可以通過在insert與into之間新增關鍵字		low_priority指示MySQL降低insert語句優先順序
		# insert low_priority  into 
  	insert還可以將一條select語句的結果插入表中即資料匯入:
        # insert select
eg:想從custnew表中合併資料到customers表中
  insert into customers(contact,email) select contact,email from custnew;

# 更新資料
	如果使用update語句更新多列值,並且在更新這些列中的一列或者多列出現一個錯誤會導致整個update操作被取消,如果想發生錯誤也能繼續執行沒有錯誤的更新操作可以採用
	update ignore custmoers ...
  """
  update ignore  set name='jason1',id='a' where id=1;
  	name欄位正常修改
  update set name='jason2',id='h' where id=1;
  	全部更新失敗
  """

# 刪除資料
	delete語句從表中刪除資料,甚至可以是所有資料但是不會刪除表本身。
    並且如果想從表中刪除所有的行不要使用delete,可以使用truncate。
    速度更快並且會重置主鍵值
    (實際是刪除原來的表並重新建立一個表而不是逐行刪除表中的資料)
    

主鍵

檢視當前表主鍵自增到的值(表當前主鍵值減一)
	select last_insert_id();

外來鍵

MySQL儲存引擎可以混用,但是外來鍵不能跨引擎。
即使用一個引擎的表不能引用具有使用不同引擎表的外來鍵

重命名錶

rename關鍵字可以修改一個或者多個表名
	rename table customer1 to customer2;
  rename table back_cust to b_cust,
  						 back_cust1 to b_cust1,
   						 back_cust2 to b_cust2;

安全管理

1.建立使用者
	create user 使用者名稱 identified by '密碼';
 	"""修改密碼"""
  	set password for 使用者名稱 = Password('新密碼');
    set password = Password('新密碼');  # 針對當前登入使用者
2.重新命名
	rename user 新使用者名稱 to 舊使用者名稱; 
3.刪除使用者
	drop user 使用者名稱;
4.檢視使用者訪問許可權
	show grants for 使用者名稱;
5.授予訪問許可權
	grant select on db1.* to 使用者名稱;
  # 授予使用者對db1資料庫下所有表使用select許可權
6.撤銷許可權
	revoke select on db1.* from 使用者名稱;
"""
整個伺服器
	grant all/revoke all
整個資料庫
	on db.*
特定的表
	on db.t1
"""

讀鎖(共享鎖)
	多個使用者同一時刻可以同時讀取同一個資源互不干擾
寫鎖(排他鎖)
	一個寫鎖會阻塞其他的寫鎖和讀鎖
死鎖
	1.多個事務試圖以不同的順序鎖定資源時就可能會產生死鎖
  2.多個事務同時鎖定同一個資源時也會產生死鎖
	# Innodb通過將持有最少行級排他鎖的事務回滾

MVCC多版本控制

MVCC只能在read committed(提交讀)、repeatable read(可重複讀)兩種隔離級別下工作,其他兩個不相容(read uncommitted:總是讀取最新  serializable:所有的行都加鎖)

InnoDB的MVCC通過在每行記錄後面儲存兩個隱藏的列來實現MVCC
	一個列儲存了行的建立時間
  一個列儲存了行的過期時間(或刪除時間)  # 本質是系統版本號
每開始一個新的事務版本號都會自動遞增,事務開始時刻的系統版本號會作為事務的版本號用來和查詢到的每行記錄版本號進行比較

例如
剛插入第一條資料的時候,我們預設事務id為1,實際是這樣儲存的
    username		create_version		delete_version
    jason						1					
可以看到,我們在content列插入了kobe這條資料,在create_version這列儲存了1,1是這次插入操作的事務id。
然後我們將jason修改為jason01,實際儲存是這樣的
    username		create_version		delete_version
    jason						1									2
    jason01					2
可以看到,update的時候,會先將之前的資料delete_version標記為當前新的事務id,也就是2,然後將新資料寫入,將新資料的create_version標記為新的事務id
當我們刪除資料的時候,實際儲存是這樣的
		username		create_version		delete_version
    jason01					2									 3
"""
由此當我們查詢一條記錄的時候,只有滿足以下兩個條件的記錄才會被顯示出來:
   1.當前事務id要大於或者等於當前行的create_version值,這表示在事務開始前這行資料已經存在了。
   2.當前事務id要小於delete_version值,這表示在事務開始之後這行記錄才被刪除。
"""

轉換表的引擎

主要有三種方式,並各有優缺點!
# 1.alter table
	alter table t1 engine=InnoDB;
  """
  	適用於任何儲存引擎 但是需要執行很長時間 MySQL會按行將資料從原表賦值到一張新的表中,在複製期間可能會消耗系統所有的IO能力,同時原表會加讀鎖
  """
# 2.匯入匯出
	"""
	使用mysqldump工具將資料匯出到檔案,然後修改檔案中相應的SQL語句
		1.引擎選項
		2.表名
	""" 	
# 3.insert ... select
	"""
	綜合了第一種方案的高效和第二種方案的安全
		1.先建立一張新的表
		2.利用insert ... select語法導資料
	資料量不大這樣做非常合適 資料量大可以考慮分批處理 針對每一段資料執行事務提交操作避免產生過多的undo
	"""
  ps:上述操作可以使用pt-online-schema-change(基於facebook的線上schema變更技術)工具,簡單方便的執行上述過程