1. 程式人生 > 其它 >SQL注入問題、檢視、索引等

SQL注入問題、檢視、索引等

  • SQL注入問題

  • 檢視

  • 觸發器

  • 事務

  • 儲存過程

  • 函式

  • 流程控制

  • 索引相關概念

  • 索引資料結構

  • 慢查詢優化

  • 測試索引

  • 聯合索引

  • 全文檢索

  • 插入資料

  • 更新資料

  • 刪除資料

  • 主鍵

  • 外來鍵

  • 重命名錶

  • 事務

  • 安全管理

  • 隔離級別

  • 事務日誌

  • MVCC多版本控制

  • 轉換表引擎

SQL注入問題

# 使用者登陸
import pymysql

conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    passwd='123',
    database='db3',
    charset='utf8',
    autocommit=True
)
cursor = conn.cursor(
    cursor=pymysql.cursors.DictCursor
)
# 1.獲取使用者名稱密碼
name = input('請輸入使用者名稱>>>:').strip()
password = input('請輸入密碼>>>:').strip()
# 2.構造查詢SQL語句
sql = "select * from userinfo where name = '%s' and pwd='%s';" % (name, password)
# 3.傳送給服務端執行SQL語句
cursor.execute(sql)
# 4.獲取執行結果
res = cursor.fetchall()
if res:
    print('使用者登陸成功')
else:
    print('使用者名稱或密碼錯誤')
怪像1:輸對使用者名稱就可以登陸成功
select * from userinfo where name='jason' -- asdasdasdfdc' and pwd=''
怪像2:不需要對的使用者名稱和密碼也可以成功
select * from userinfo where name='qwe' or 1=1 -- wqdadadfsa' and pwd=''
這兩個現象的出現是因為SQL注入:
    利用特殊符號符合組合產生特殊的含義 從而避開正常的業務邏輯
    
針對上述的SQL注入問題 核心在於手動拼接了關鍵資料 
解決SQL注入問題也很簡單 就是想辦法過濾掉特殊符號
# execute 方法自帶校驗SQL注入的問題,自動處理特殊符號
# 交給execute處理即可
sql = "select * from userinfo where name = %s and pwd=%s;"
cursor.execute(sql, (name, password))
"""
execute方法補充(瞭解)
	批量插入資料
sql = 'insert into userinfo(name,password) values(%s,%s)'
cursor.executemany(sql,[('tom',123),('lavin',321),('pony',333)])
"""

檢視

檢視就是通過查詢得到一張虛擬表,然後儲存下來,下次直接使用
# 為什麼要用檢視
如果要頻繁使用一張虛擬表,可以不用重複查詢
# 如何用檢視
create view 檢視名 as 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

# 觸發器名字
一般採用下列的形式
tri_after_insert_t1  給表t1增加之後觸發
tri_after_update_t2  給表t2修改之後觸發
tri_after_delete_t3  給表t3刪除資料之後

# 臨時修改SQL語句的結束符
	因為有些操作中需要使用分號 
# 觸發器實際應用
'''1.先建立兩張表'''
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
);


'''2.需求:cmd表插入資料的sucess如果值為no,則去errlog表中插入一條記錄'''
delimiter $$  # 將mysql預設的結束符由;換成$$
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 ;  # 結束之後記得再改回來,不然後面結束符就都是$$了


'''3.僅僅往cmd表中插入資料'''
# 往表cmd中插入記錄,觸發觸發器,根據IF的條件決定是否插入錯誤日誌
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');
    

'''4.觸發器其他補充'''
# 查詢errlog表記錄
select * from errlog;
# 檢視所有的觸發器
show triggers;
# 刪除觸發器
drop trigger tri_after_insert_cmd;

事務(重要)

# 事務的概念
    事務可以包含諸多SQL語句並且這些SQL語句
    要麼同時執行成功、要麼執行失敗,這是事務的原子性特點 
    
# 事務的作用 
   eg:A欠了B一筆錢
      還錢時,A拿著交行的銀行卡去招商銀行的ATM給B的建設銀行轉錢
        1、朝交行的伺服器傳送請求,修改A的賬戶
        2、朝建行的伺服器傳送請求,修改B的賬戶
        
# 事務的四大特性(重要)
事務的四大特性(ACID)
A:原子性
      一個事務是一個不可分割的整體,裡面的操作要麼都成立要麼都不成立
C:一致性
      事務必須使資料庫從一個一致性狀態變到另外一個一致性 狀態
I:隔離性
      併發程式設計中,多個事務之間是相互隔離的,不會彼此干擾
D:永續性
      事務一旦提交,產生的結果應該是永久性的,不可逆的
        
#具體操作
'''建立表及錄入資料'''
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;

# 修改操作 編寫SQL語句(同屬於一個事務)
update user set balance=900 where name='jason'; #買支付100元
update user set balance=1010 where name='kevin'; #中介拿走10元
update user set balance=1090 where name='tank'; #賣家拿到90元

'''事務回滾(返回執行事務操作之前的資料庫狀態)'''
rollback;  
# 執行完回滾之後 事務自動結束

commit;
# 開啟事務之後,只要沒有執行commit操作,資料其實都沒有真正重新整理到硬碟
"""
事務相關關鍵字
	start transaction;
	rollback
	commit
	savepoint
"""
在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值,這表示在事務開始之後這行記錄才被刪除。
"""

儲存過程

可以看成是python中的自定義函式

# 無參函式
delimiter $$
create procedure p1()
begin
	select * from cmd;
end $$
delimiter ;

# 呼叫
call p1()


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

# 針對res需要先提前定義
set @res=10;  定義
select @res;  檢視
call p1(1,5,@res)  呼叫
select @res  檢視

"""
檢視儲存過程具體資訊
	show create procedure pro1;
檢視所有儲存過程
	show procedure status;
刪除儲存過程
	drop procedure pro1;
"""

# 大前提:儲存過程在哪個庫下面建立的只能在對應的庫下面才能使用!!!

# 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中的內建函式

"ps:可以通過help 函式名    檢視幫助資訊!"
# 1.移除指定字元
Trim、LTrim、RTrim

# 2.大小寫轉換
Lower、Upper

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

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

# 5.日期格式: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)
	主鍵、主鍵索引
    
聚集索引是將主鍵與行記錄儲存在一起,當根據主鍵進行查詢時,可直接在表中獲取到資料,不用回表查詢。InnonDB的所有的表都是索引組織表,主鍵與資料存放在一起。InnoDB選擇聚集索引遵循以下原則:

在建立表時,如果指定了主鍵,則將其作為聚集索引。
如果沒有指定主鍵,則選擇第一個NOT NULL(非空)的唯一索引作為聚集索引。
如果沒有唯一索引,則內部會產生一個6位元組的rowID(主鍵值)作為主鍵
  • 輔助索引
輔助索引(unique,index)
	除主鍵意外的都是輔助索引
    查詢資料的時候不可能都是用id作為篩選條件,也可能會用name,password等欄位資訊,那麼這個時候就無法利用到聚集索引的加速查詢效果。就需要給其他欄位建立索引,這些索引就叫輔助索引

'''
  葉子結點存放的是輔助索引欄位對應的那條記錄的主鍵的值(比如:按照name欄位建立索引,那麼葉子節點存放的是:{name對應的值:name所在的那條記錄的主鍵值})
資料查詢 如果一開始使用的是輔助索引 那麼還需要使用聚焦索引才可以獲取到真實資料
  '''
  • 覆蓋索引
只在輔助索引的葉子節點中就已經找到了所有我們想要的資料
	select name from user where name='gavin';
非覆蓋索引:雖然查詢的時候命中了索引欄位name,但是要查的是age欄位,所以還需要利用主鍵才去查詢
	select age from user where name='gavin';

索引資料結構

索引底層其實是樹結構>>>:樹是計算機底層的資料結構
 
樹有很多中型別
	二叉樹、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';  # 速度變快

慢查詢日誌

設定一個時間檢測所有超出該時間的sql語句,然後針對性的進行優化!

全文檢索

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;

事務擴充套件

MySQL提供兩種事務型儲存引擎InnoDB和NDB cluster及第三方XtraDB、PBXT

事務處理中有幾個關鍵詞彙會反覆出現
  事務(transaction)
  回退(rollback)
  提交(commit)
  保留點(savepoint)
		為了支援回退部分事務處理,必須能在事務處理塊中合適的位置放置佔位符,這樣如果需要回退可以回退到某個佔位符(保留點)
    建立佔位符可以使用savepoint
    	savepoint sp01;
    回退到佔位符地址
    	rollback to sp01;
    # 保留點在執行rollback或者commit之後自動釋放

安全管理

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
"""

隔離級別

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

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

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

事務日誌

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

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變更技術)工具,簡單方便的執行上述過程