1. 程式人生 > 其它 >013、事務介紹與字符集

013、事務介紹與字符集

1、事務的基本概念

MySQL的事務簡單理解為一組DML語句,包括:insert、delete、update,或者DDL語句。MySQL對事務是預設自動提交的,不需要手動寫commit或者rollback命令,檢視引數設定:
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)
若是修改該引數值為off,一般以begin; 作為事務的開始,commit或者rollback作為事務的結束。SAVEPOINT <savepoint_name> :事務的儲存點,允許一段事務回滾時,不回滾整個事務,而只回滾到該儲存點:ROLLBACK TO [SAVEPOINT] <savepoint_name>。

2、事務的隱式提交和回滾

檢視MySQL的自動提交

建立測試表,並插入資料:
mysql> use test;
Database changed
mysql> create table t(id int);
Query OK, 0 rows affected (0.24 sec)

mysql> insert into t select 1;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into t select 2;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into t select 3;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0
由於MySQL事務自動提交,所以另外開啟一個session可以查詢到這3條資料:
mysql> select * from test.t;
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
+------+
3 rows in set (0.00 sec)

會話斷開,自動回滾

使用begin開啟事務,向表中插入資料,直接退出session,檢視是否提交:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t select 4;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> exit
Bye

mysql> select * from test.t;
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
+------+
3 rows in set (0.00 sec)
可以看到事務並沒有提交,exit、斷開session、斷電:主動退出相當於回滾rollback。如果一個事務以begin; 開始,不commit提交,在寫的過程中有DDL(建立表,索引,修改表等)語句,事務自動提交。

事務的隱式提交

3、事務的特性

事務的四大特性:原子性、一致性、隔離性、永續性,簡稱ACID

原子性(Atomicity)

事務的原子性是指事務中包含的所有操作要麼都做,要麼都不做,保證資料庫是一致的。例如:A帳戶向B帳戶劃賬1000,則先將A減少1000,再將B增加1000,這兩個動作要麼都提交,要麼都回退,不可能發生一個有效、一個無效的情況。

一致性(Consistency)

一致性是指資料庫在事務操作前和事務處理後,其中的資料必須都滿足業務規則約束。
例如:A、B帳戶的總金額在轉賬前和轉帳後必須一致,其中的不一致必須是短暫的,在事務提交前才會出現的。再如:約定B帳戶不能多於1000元,A轉出1000成功,B轉入1000失敗,最終由原子性得到——整個事務回滾 。

隔離性(Isolation)

隔離性是資料庫允許多個併發事務同時對資料進行讀寫和修改的能力,隔離性可以防止多個事務併發執行時由於交叉執行而導致資料的不一致。例如:A、B之間轉帳,C同時向A轉帳,若同時進行,則A、B之間的一致性不能得到滿足。所以在A、B事務執行過程中,其他事務不能訪問(修改)當前相關的數值。

永續性(Durability)

永續性表示為:事務處理結束後,對資料的修改就是永久的,即便系統故障也不會丟失。
在提交之前如果系統故障,則所有資訊全部丟失。提交之後資料存放在磁碟中,是永久性的。再MySQL中,有兩個引數:innodb_flush_log_at_trx_commit = 1sync_binlog = 1這兩個引數值都設定為1,叫做雙1,用於保證資料的永續性(最安全的模式)。事務提交後,保證資料不會丟失。檢視資料庫中支援事務的儲存引擎:
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)

使用begin開啟事務

開啟一個讀寫事務:
mysql> start transaction read write;
Query OK, 0 rows affected (0.00 sec)
--start transaction [read write];開啟一個讀寫事務(預設)
或者:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
--begin=start transaction[沒有修飾詞]
也可以開啟一個只讀事務:
mysql> start transaction read only;
Query OK, 0 rows affected (0.00 sec)

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
--SET AUTOCOMMIT = 0/1 置事務是否自動提交
autocommit = 0 必要嗎(開發框架都是這樣)

優點:多個事務一起提交,可以提高tps;缺點:但某個事務一直不提交,也會導致行鎖等待。ibdata1表空間暴漲 ,併發事務可能存在的風險。用於隱式提交的 SQL 語句: – START TRANSACTION– SET AUTOCOMMIT = 1導致提交的非事務語句:1.資料定義語句(ALTER、CREATE 和 DROP)2.管理語句(GRANT、REVOKE 和 SET PASSWORD)3.鎖定語句(LOCK TABLES 和 UNLOCK TABLES)導致隱式提交的語句示例: – TRUNCATE TABLE– LOAD DATA INFILE隱式回滾:連線斷開,自動退出。

事務的隔離級別

可以用以下方法修改事務隔離級別:
set {global|session} transaction isolation level
{
READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE
}
若想永久生效,需要修改引數檔案,並重啟生效。檢視MySQL的預設事務隔離級別:
mysql> show variables like '%iso%';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)
檢視哪些事務沒有提交在information_schema庫下:
  1. INNODB_LOCKS
  2. INNODB_TRX
  3. INNODB_LOCK_WAITS
如果沒有事務控制的話,那麼併發讀寫資料庫會有什麼隱患?髒讀:一個事務按相同的查詢條件重新讀取以前檢索過的資料,發現其他事務更新後達到了滿足其查詢條件的舊資料(此時它還未被提交),這種現象就稱為“髒讀”。簡單講就是一個會話讀取到了另一個會話還未提交的事務。這種現象一定要避免發生,涉及到的事務隔離級別為RU。不可重複讀:一個事務按相同的查詢條件重新讀取以前檢索過的資料,發現其他事務更新後達到了滿足其查詢條件的舊資料(此時它已被提交),這種現象稱為“不可重複讀”。幻讀:一個事務按相同的查詢條件重新讀取以前檢索過的資料,發現其他事務插入了滿足其查詢條件的新資料(此時它已被提交),這種現象就稱為“幻讀”。
事務的隔離級別:read uncommitted,RU,讀未提交。一個事務中,可以讀取到其他事務未提交的變更。該級別屬於髒讀。read committed,RC,讀已提交。一個事務中,可以讀取到其他事務已經提交的變更。該級別允許幻讀,不可重複讀的發生。repetable read,RR,可重複讀。一個事務中,直到事務結束前,都可以反覆讀取到事務一開始看到底資料,不會發生變化。該級別可保證事務一致性。serializable,SR,序列。即每次讀都需要獲得表級共享鎖,每次寫都加表級排它鎖,兩個會話間讀寫會相互阻塞。該級別會導致innodb表的併發特性喪失,變成myisam一樣。MySQL與Oracle事務隔離級別的不同:Oracle只支援RU和RC。MySQL中,如果innodb_locks_unsafe_for_binlog=1,即便在RR下,也會發生幻讀(相當於退到RC),因為innodb_locks_unsafe_for_binlog=1時,取消了gap lock,就無法規避幻讀問題了。gap lock確實可以避免幻讀。

關於事務的其他資訊

在innodb儲存引擎中,事務日誌通過重做日誌檔案(redo log)和innddb儲存引擎的日誌快取來實現。
  • 當開始一個事務時,會記錄該事務的一個lsn號;
  • 當事務執行時,會往innodb儲存引擎的日誌快取裡插入事務日誌;
  • 當事務提交時,必須將innodb儲存引擎的日誌快取寫入磁碟,也就是寫資料前,需要先寫日誌,稱為預寫日誌方式(WAL).
innodb儲存引擎通過預寫日誌的方式,來保證事務的完整性,這意味著磁碟上儲存的資料頁和記憶體緩衝池中的頁是不同步的,對於記憶體緩衝池中的頁的修改,先是寫入重做日誌檔案,然後再寫入磁碟,是一種非同步方式。通過以下命令來檢視當前重做日誌的情況:
mysql> show engine innodb status \G;
*************************** 1. row ***************************
  Type: InnoDB
  Name: 
Status: 
=====================================
2021-04-16 17:13:37 7fb848b93700 INNODB MONITOR OUTPUT
……
Log sequence number 169893195
Log flushed up to   169893195
Pages flushed up to 169893195
Last checkpoint at  169893195
……
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Main thread process no. 2700, id 140429464028928, state: sleeping
Number of rows inserted 0, updated 0, deleted 0, read 0
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set (0.00 sec)
以上資訊中:

redo與undo

重做日誌記錄了事務的行為,可以很好地通過其進行重做,但是事務有時還需要撤銷,這時就需要undo。undo與redo正好相反,對於資料庫進行修改時,資料庫不但產生redo,而且還會產生一定量的undo,即使你執行的事務或語句由於某種原因失敗了,或者用rollback語句請求回滾,就可以利用這些undo資訊將資料回滾到修改之前的樣子。與redo不同的是,redo存放在重做日誌檔案中,undo存放在資料庫內部的一個特殊段中,undo segment,undo段位於共享表空間內。

事務控制語句

Mysql命令列的預設設定下,事務都是自動提交的,即執行sql語句後就會馬上執行commit操作。因此開始一個事務,必須使用begin,start transantion,或者執行set autocommit=0,以禁用當前會話的自動提交。start transaction|begin;顯示地開啟一個事務;commit:提交事務,並使得已對資料庫做的所有修改成為永久性。rollback:回滾事務,撤銷正在進行的所有未提交的修改。

對於事務操作的統計

因為innodb儲存引擎是支援事務的,因此對於innodb儲存引擎的應用,在考慮每秒請求(question per second,QPS)的同時,也許更應該關注每秒事務的處理能力(transaction per second,TPS)。計算TPS的方法是:(com_commit+com_rollback)/time,但是這種計算方式的前提是所有的事務必須是顯示提交的,隱式提交或者隱式回滾不會記錄到該變數中。

分散式事務

innodb儲存疫情支援XA事務,通過XA事務可以來支援分散式事務的實現。分散式事務指的是允許多個獨立的事務資源參與一個全域性的事務中,事務資源通常是關係型資料庫系統,但也可以是其他型別的資源。全域性事務要求在其中所有參與的事務要麼都提交,要麼都回滾。在使用分散式事務時,innodb儲存引擎的事務隔離級別必須設定為serialiable.
分散式事務使用兩段式提交的方式:
  • 在第一階段,所有參與全域性事務的節點都開始準備,告訴事務管理器它們準備好提交了;
  • 第二階段,事務管理器告訴資源管理器執行rollback還是commit。
如果任何一個節點顯示不能提交,則所有的節點都被告知需要回滾。注意:對於XA事務的支援,是在MySQL的儲存引擎層,因此即使不參與外部的XA事務,MySQL內部不同儲存引擎層也會使用XA事務。

MySQL字符集

MySQL資料庫預設字符集為utf8,常用的字符集有3種:utf8,GBK,latin1MySQL資料庫避免中文亂碼出現,原則叫做:三碼合一(資料庫、作業系統、連線到資料庫的終端工具)。1、連線到MySQL資料庫伺服器的終端與MySQL資料庫字符集一致。
mysql> show variables like '%char%';
+--------------------------+----------------------------------+
| Variable_name            | Value                            |
+--------------------------+----------------------------------+
| character_set_client     | utf8                             |  --MySQL客戶端字符集
| character_set_connection | utf8                             |  --MySQL返回結果字符集
| character_set_database   | utf8                             |  --MySQL資料庫字符集
| character_set_filesystem | binary                           |  
| character_set_results    | utf8                             |
| character_set_server     | utf8                             |
| character_set_system     | utf8                             |
| character_sets_dir       | /usr/local/mysql/share/charsets/ |
+--------------------------+----------------------------------+
8 rows in set (0.03 sec)
若以上字符集不一致,調整/etc/my.cnf引數檔案,新增以下引數,並重啟資料庫:
[root@localhost ~]# vi /etc/my.cnf
[client]
default-character-set=utf8

[mysql]
default-character-set=utf8

[mysqld]
character-set-server=utf8
2、作業系統字符集
[root@localhost ~]# env|grep LANG
LANG=en_US.UTF-8
[root@localhost ~]# cat /etc/sysconfig/i18n 
LANG="en_US.UTF-8"
SYSFONT="latarcyrheb-sun16"
3、臨時修改字符集
mysql> set names 'GBK';
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%char%';
+--------------------------+----------------------------------+
| Variable_name            | Value                            |
+--------------------------+----------------------------------+
| character_set_client     | gbk                              |
| character_set_connection | gbk                              |
| character_set_database   | utf8                             |
| character_set_filesystem | binary                           |
| character_set_results    | gbk                              |
| character_set_server     | utf8                             |
| character_set_system     | utf8                             |
| character_sets_dir       | /usr/local/mysql/share/charsets/ |
+--------------------------+----------------------------------+
4、模擬字符集不一致導致亂碼:檢視預設字符集:
mysql> show variables like '%char%';
+--------------------------+----------------------------------+
| Variable_name            | Value                            |
+--------------------------+----------------------------------+
| character_set_client     | utf8                             |
| character_set_connection | utf8                             |
| character_set_database   | utf8                             |
| character_set_filesystem | binary                           |
| character_set_results    | utf8                             |
| character_set_server     | utf8                             |
| character_set_system     | utf8                             |
| character_sets_dir       | /usr/local/mysql/share/charsets/ |
+--------------------------+----------------------------------+
建立表,插入含有漢字的資料:
mysql> use test;
Database changed
mysql> create table t(id int,name varchar(10));
Query OK, 0 rows affected (0.17 sec)
mysql> insert into t values(1,'張三努力學習五筆輸入法');
Query OK, 1 row affected, 1 warning (0.08 sec)

mysql> show warnings;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'name' at row 1 | --提示超出長度的欄位會被擷取
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)
mysql> select * from t;
+------+--------------------------------+
| id   | name                           |
+------+--------------------------------+
|    1 | 張三努力學習五筆輸入           |
+------+--------------------------------+
1 row in set (0.00 sec)
臨時修改字符集,再次查詢:
mysql> set names 'latin1';
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%char%';
+--------------------------+----------------------------------+
| Variable_name            | Value                            |
+--------------------------+----------------------------------+
| character_set_client     | latin1                           |
| character_set_connection | latin1                           |
| character_set_database   | utf8                             |
| character_set_filesystem | binary                           |
| character_set_results    | latin1                           |
| character_set_server     | utf8                             |
| character_set_system     | utf8                             |
| character_sets_dir       | /usr/local/mysql/share/charsets/ |
+--------------------------+----------------------------------+
8 rows in set (0.00 sec)
mysql> select * from t;
+------+------------+
| id   | name       |
+------+------------+
|    1 | ?????????? |
+------+------------+
1 row in set (0.00 sec)
出現亂碼,代表三碼不一致,針對這種問題,處理方法:臨時設定字符集,使三碼一致。匯出資料;刪除亂碼資料;再匯入資料。

來自為知筆記(Wiz)