MySQL實戰-5

目錄

Memory引擎

自增主鍵

insert...select的鎖問題

快速複製表

grant的權限問題

分區表

自增id


Memory引擎

InnoDB引擎的索引組織方式

Memory使用的是hash索引,索引的key不是有序的

Memory使用的是hash索引,索引的key不是有序的

InnoDB引擎把數據放在主鍵索引上,其他索引上保存的是主鍵id,這種方式成爲 索引組織表
Memory引擎採用的是把數據單獨存放,索引上保存數據位置的數據組織形式,成爲 堆組織表
區別

  1. InnoDB表的數據總是有序存放的,而內存表的數據就是按照寫入順序存放的
  2. 當數據文件有空洞的時候,InnoDB表在插入新數據的時候,爲了保證數據有序性,只能在固定位置寫入新值,而內存表找到空位就可以插入新值
  3. 數位置發生變化的時候,InnoDB表只需要修改主鍵索引,而內存表需要修改所有索引
  4. InnoDB表用主鍵索引查詢的時候走一次索引查找,普通索引走兩次,內存表沒有這個區別
  5. InnoDB支持變長數據類型,內存表不支持

內存表也支持B-Tree索引,可以在id列上創建一個索引
alter table t1 add index a_btree_index using btree (id);

表t1的數據組織形式如下


內存表的鎖是表級別的,比起InnoDB併發度差了很多
內存表也不支持持久化,在高可用架構下就不能用了
另外對於大量讀的場景,InnoDB有Buffer Pool,其性能也不差
建議普通的內存表都用InnoDB來替代
但有一個場景可以用內存表,即臨時表的情況

  1. 臨時表不會被其他線程訪問,沒有併發問題
  2. 臨時表重啓後也是需要刪除的,清空數據這個問題不存在
  3. 備庫的臨時表也不會影響主庫的用戶線程
     

自增主鍵

自增的id未必是連續的
默認是放到內存中,mysql5.7之後開始有了持久化
新的自增算法是
auto_increment_offset開始,以auto_increment_increment爲步長,持續疊加,直到找到第一個大於x的值,作爲新的自增值
其中auto_increment_offset和auto_increment_increment是兩個系統參數,分別用來標識自增的初始化和步長,默認值都是1
雙M的時候,可以讓步長設置爲1,初始值分別爲1和2,這樣每次都是奇數和偶數保證不衝突

如果使用了自定義的值

  1. 插入的值>=當前自增值,新的自增值就是 準備插入的值+1
  2. 否則,自增值不變

自增id不連續的原因

  1. 唯一鍵衝突後自增值不會回滾,導致不連續
  2. 事務回滾也會產生類型的現象
  3. 自增鎖優化時候,批量返回自增id

批量申請自增id策略

  1. 語句執行過程中,第一次申請自增id,會分配1
  2. 一個用完後,第二次申請會分配2個
  3. 第三次分配四個
  4. 用同一個語句去申請自增id,每次都會得到自增id個數據都是上一次的兩倍

如果第一次插入1條,第二次插入2條(2,3),第三次插入2條(4,5),第四次的自增id就是8
但是第三次只插入兩條,所以6,7這兩個id就沒有,導致了不連續

 

insert...select的鎖問題

。。。

 

快速複製表

用mysqldump方式

mysqldump -h$host -P$port -u$user --add-locks=0 --no-create-info --single-transaction  --set-gtid-purged=OFF db1 t --where="a>900" --result-file=/client_tmp/t.sql

如果希望生產的文件中一條insert語句只插入一行數據的話,加上-skip-extended-insert

mysql -h127.0.0.1 -P13000  -uroot db2 -e "source /client_tmp/t.sql"

導出CSV文件

select * from db1.t where a>900 into outfile '/server_tmp/t.csv';

注意這個導出的文件會保存在服務端
導出後,使用下面命令將將數據導入到目標表db2.t中

load data infile '/server_tmp/t.csv' into table db2.t;

這個語句執行流程如下
1.主庫執行完後,將csv文件內容寫到binlog中
2.往binlog文件中寫入語句load data local infile '/tmp/SQL_LOAD_MB-1-0' INTO TABLE ‘db2.t’
3.把這個binlog日誌傳到備庫
4.備庫的apply線程在執行這個事務日誌時
  先將binlog中t.csv文件中的內容讀出來,寫入到本地臨時目錄/tmp/SQL_LOAD_MB-1-0中
  再執行load data語句,往備庫的db2.t表中插入跟主庫相同的數據
執行圖如下

不加local讀的是服務端,加了local讀的是客戶端
selelct... into outfile 方法不會生產表結構文件
mysqldump 提供了一個方式,可以同時導出表結構定義文件和csv數據文件
 

mysqldump -h$host -P$port -u$user ---single-transaction  --set-gtid-purged=OFF db1 t --where="a>900" --tab=$secure_file_priv


物理拷貝
直接拷貝db1.t表的.frm文件,以及.ibd文件是不行的
InnoDB表除了包含這兩個物理文件,還需要在數據字典中註冊,mysql5.6之後引入了 可傳輸表空間
通過導出 導入表空間的方式,實現物理拷貝表
具體操作步驟

  1. 執行create table r like t,創建一個相同的空表
  2. 執行alter table r discard tablespace,這時候r.ibd文件會被刪除
  3. 執行flush table t for export,這時候db1目錄下會生成一個t.cfg文件
  4. 在db1目錄下還行cp t.cfg r.cfg, cpt.idb r.idb
  5. 執行unlock tables,這時候t.cfg文件會被刪除
  6. 執行alter table r import tablespace,將r.ibd文件作爲表r的新表空間,由於這個文件的數據內容和t.ibd是相同的,所以表r中就有了和表t相同的數據

整個過程如下圖

物理拷貝速度最快,但必須是全表拷貝,需要導服務器上拷貝數據
select...into outfile最靈活,但每次只能導出一張表的數據

 

grant的權限問題

。。。

 

分區表

創建分區表的語句

CREATE TABLE `t` (
  `ftime` datetime NOT NULL,
  `c` int(11) DEFAULT NULL,
  KEY (`ftime`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
PARTITION BY RANGE (YEAR(ftime))
(PARTITION p_2017 VALUES LESS THAN (2017) ENGINE = InnoDB,
 PARTITION p_2018 VALUES LESS THAN (2018) ENGINE = InnoDB,
 PARTITION p_2019 VALUES LESS THAN (2019) ENGINE = InnoDB,
PARTITION p_others VALUES LESS THAN MAXVALUE ENGINE = InnoDB);
insert into t values('2017-4-1',1),('2018-4-1',1);

這個表包含了一個.frm文件和4個.ibd文件,每個分區對應一個.ibd文件
對於引擎來說是4個表
對於server層來說是1個表

普通的表如果插入兩個數據'2017-4-1'和'2018-4-1',會觸發間歇鎖,如下

使用分區表後,間歇鎖的狀態如下

手工分區和表分區,一個是由server層決定使用哪個分區,一個是由應用層來決定使用哪個分區
從引擎層看是沒有差別的
這兩個方案的區別,主要在server層

當第一次訪問一個分區表時,mysql需要把所有的分區都訪問一遍
MyISAM分區策略是由server層控制的,存在嚴重性能問題
InnoDB由引擎層控制,到mysql8.0之後就不允許MyISAM創建分區
只有InnoDB和NDB允許

另一個性能問題是MDL鎖,對於手工分區來說,MDL鎖對於不同的分區是沒影響的,但是對於分區表就是一個全局鎖
分區表的優勢是對業務透明,分區表可以很方便的清理歷史數據

 

自增id

表定義自增id
主鍵的自增id如果達到了最大值後,就不會改變了,於是會報主鍵衝突
4字節的主鍵可能會出現這個問題,8字節無符號基本上就不會出現了

row_id
如果沒有主鍵,mysql會生成一個默認主鍵,這個是2^48範圍大小,理論上也是可能會到上限的
而且達到上限之後就回0了,會把之前的數據覆蓋掉

xid
server層維護的事務id,是一個純內存變量
mysql重啓之後會重新生成binlog文件,所以再同一個binlog中xid是唯一的
這個變量是8字節,達到上限後會回0,如果一個binlog裏面有2^64個查詢會讓xid回0

trx_id
這個是InnoDB內部維護的事務id,這個id會持久化的,當mysql實例運行足夠久之後就會回0
這樣老事務對比id大小,認爲0比當前的id小就會讀到這個id=0的內容,這就屬於髒讀了

thread_id
4個字節的變量,到達上限後會回0,mysql保證了新生成的線程id不會跟老的衝突

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章