#在網上查詢到的緣由爲: html
InnoDB 的默認隔離級別。它能夠防止任何被查詢的行被其餘事務更改,從而阻止不可重複的讀取,而不是 幻讀取。它使用中度嚴格的鎖定策略,以便事務內的全部查詢都會查看同一快照中的數據,即數據在事務開始時的數據。python
那麼此時問題就找到了,跟當前的事務級別有關係的;當建立查詢事務時,事務一直沒有進行更新,每次查詢到的數據都是以前查詢結果的快照。mysql
想要解決上述的問題,首先要明白mysql中事務這個概念,本地的mysql數據庫是默認安裝的,默認存儲引擎是(InnoDB),事務隔離級別是(REPEATABLE READ):web
查看存儲引擎:sql
mysql> show engines; # 查看數據庫支持的存儲引擎 +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | Engine | Support | Comment | Transactions | XA | Savepoints | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL | | MRG_MYISAM | YES | Collection of identical MyISAM tables | 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 | | CSV | YES | CSV storage engine | NO | NO | NO | | MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO | | ARCHIVE | YES | Archive storage engine | NO | NO | NO | | InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES | | PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ 9 rows in set (0.00 sec) mysql> show variables like '%storage_engine%'; # 當前的存儲引擎 +----------------------------+--------+ | Variable_name | Value | +----------------------------+--------+ | default_storage_engine | InnoDB | | default_tmp_storage_engine | InnoDB | | storage_engine | InnoDB | +----------------------------+--------+ 3 rows in set (0.00 sec)
查看當前的事務隔離級別:數據庫
mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set (0.00 sec)
重複讀(REPEATABLE READ):安全
InnoDB 的默認隔離級別。它能夠防止任何被查詢的行被其餘事務更改,從而阻止不可重複的讀取,而不是 幻讀取。它使用中度嚴格的鎖定策略,以便事務內的全部查詢都會查看同一快照中的數據,即數據在事務開始時的數據。服務器
REPEATABLE READ The default isolation level for InnoDB. It prevents any rows that are queried from being changed by other transactions, thus blocking non-repeatable reads but not phantom reads. It uses a moderately strict locking strategy so that all queries within a transaction see data from the same snapshot, that is, the data as it was at the time the transaction started.
重複讀session
那麼此時問題就找到了,跟當前的事務級別有關係的;當建立查詢事務時,事務一直沒有進行更新,每次查詢到的數據都是以前查詢結果的快照,下面會詳細介紹每種事務隔離級別的區別併發
知道了具體緣由是事務級別的問題,致使查詢事務並無更新,那麼針對事務隔離級別進行應對就能夠了,此類問題有三種解決方案,修改事務隔離級別、每次查詢後更新事務、關閉數據庫的事務(慎選)
① 每次查詢後更新事務:
# 第一種方案,每次查詢後進行commit操做,進行事務更新 import pymysql import time # Connect to the database connection = pymysql.connect(host='192.168.1.134', port=3306, user='remote', password='tx_1234abc', db='Jefrey', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor, ) while True: with connection.cursor() as cursor: # Create a new record sql = " select * from users WHERE email=%s" # sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)" rows_count = cursor.execute(sql, ('webmaster@python.org')) result = cursor.fetchall() print(result) connection.commit() # 新增 time.sleep(1) # 第二種方案,建立connect鏈接時,autocommit=True,自動進行commit提交 import pymysql import time # Connect to the database connection = pymysql.connect(host='192.168.1.134', port=3306, user='remote', password='tx_1234abc', db='Jefrey', charset='utf8mb4', autocommit = True, #新增 cursorclass=pymysql.cursors.DictCursor, ) while True: with connection.cursor() as cursor: # Create a new record sql = " select * from users WHERE email=%s" # sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)" rows_count = cursor.execute(sql, ('webmaster@python.org')) result = cursor.fetchall() print(result) # connection.commit() time.sleep(1)
打印輸出,能夠查到新更新的數據:
() () [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}] [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}] [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}, {u'password': u'test', u'id': 341, u'email': u'webmaster@python.org'}]
② 修改事務隔離級別(具體級別詳情下面介紹)
設置隔離級別命令 set [global/session] transaction isolation level xxxx; 若是使用global則修改的是數據庫的默認隔離級別,全部新開的窗口的隔離級別繼承自這個默認隔離級別若是使用session修改,則修改的是當前客戶端的隔離級別,和數據庫默認隔離級別無關。當前的客戶端是什麼隔離級別,就能防止什麼隔離級別問題,和其餘客戶端是什麼隔離級別無關。
# 設置事務隔離級別 mysql> set global transaction isolation level READ COMMITTED; Query OK, 0 rows affected (0.00 sec) mysql> exit Bye [root@localhost ~]# mysql Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 130 Server version: 5.6.36 MySQL Community Server (GPL) Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> select @@tx_isolation; +----------------+ | @@tx_isolation | +----------------+ | READ-COMMITTED | +----------------+
此時用最剛剛開始的代碼查詢,即便不更新事務依然能夠查的到新添加的數據,打印輸出,能夠查到新更新的數據:
() () [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}] [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}] [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}, {u'password': u'test', u'id': 341, u'email': u'webmaster@python.org'}]
③ 關閉數據庫的事務(修改存儲引擎)
# 查看存儲引擎 mysql> show create table users; +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | users | CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) COLLATE utf8_bin NOT NULL, `password` varchar(255) COLLATE utf8_bin NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=350 DEFAULT CHARSET=utf8 COLLATE=utf8_bin | +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) # 修改成MyISAM mysql> alter table users engine = MyISAM; Query OK, 5 rows affected (0.02 sec) Records: 5 Duplicates: 0 Warnings: 0 # 再次查看存儲引擎 mysql> show create table users; +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | users | CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) COLLATE utf8_bin NOT NULL, `password` varchar(255) COLLATE utf8_bin NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=350 DEFAULT CHARSET=utf8 COLLATE=utf8_bin | +-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
再次用最剛剛開始的代碼查詢,此時跟事務已經沒有什麼關係了,能夠查的到新添加的數據
() () [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}] [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}] [{u'password': u'test', u'id': 340, u'email': u'webmaster@python.org'}, {u'password': u'test', u'id': 341, u'email': u'webmaster@python.org'}]
事務指邏輯上的一組操做,組成這組操做的各個單元,要不所有成功,要不所有不成功
事務特性:
隔離性:
將數據庫設計爲串行化程的數據庫,讓一張表在同一時間內只能有一個線程來操做。若是將數據庫設計爲這樣,那數據庫的效率過低了。因此數據庫的設計這沒有直接將數據庫設計爲串行化,而是爲數據庫提供多個隔離級別選項,使數據庫的使用者能夠根據使用狀況本身定義到底須要什麼樣的隔離級別。
髒讀:
一個事務讀取到了另外一個事務未提交的數據,這是特別危險的,要盡力防止。 a 1000 b 1000 a: start transaction; update set money=money+100 where name=b; b: start transaction; select * from account where name=b;--1100 commit; a: rollback; b: start transaction; select * from account where name=b;--1000
不可重複讀(開題遇到的問題就是此項):
在一個事務內讀取表中的某一行數據,屢次讀取結果不一樣。(一個事務讀取到了另外一個事務已經提交的數據--增長記錄、刪除記錄、修改記錄),在某寫狀況下並非問題,在另外一些狀況下就是問題。 a: start transaction; select 活期帳戶 from account where name=b;--1000 活期帳戶:1000 select 按期帳戶 from account where name=b;--1000 按期帳戶:1000 select 固定資產 from account where name=b;--1000 固定資產:1000 ------------------------------ b: start transaction; update set money=0 where name=b; commit; ------------------------------ select 活期+按期+固定 from account where name=b; --2000 總資產: 2000
虛讀:
是指在一個事務內讀取到了別的事務插入的數據,致使先後讀取不一致。(一個事務讀取到了另外一個事務已經提交的數據---增長記錄、刪除記錄),在某寫狀況下並非問題,在另外一些狀況下就是問題。 b 1000 c 2000 d 3000 a: start transaction select sum(money) from account;---3000 3000 ------------------- d:start transaction; insert into account values(d,3000); commit; ------------------- select count(*)from account;---3 3 3000/3 = 1000 1000
Read Uncommitted(讀取未提交內容):
全部事務均可以看到其餘未提交事務的執行結果
本隔離級別不多用於實際應用,由於它的性能也不比其餘級別好多少
該級別引起的問題是——髒讀(Dirty Read):讀取到了未提交的數據
#首先,修改隔離級別 set tx_isolation='READ-UNCOMMITTED'; select @@tx_isolation; +------------------+ | @@tx_isolation | +------------------+ | READ-UNCOMMITTED | +------------------+ #事務A:啓動一個事務 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事務B:也啓動一個事務(那麼兩個事務交叉了) 在事務B中執行更新語句,且不提交 start transaction; update tx set num=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ #事務A:那麼這時候事務A能看到這個更新了的數據嗎? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | --->能夠看到!說明咱們讀到了事務B尚未提交的數據 | 2 | 2 | | 3 | 3 | +------+------+ #事務B:事務B回滾,仍然未提交 rollback; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事務A:在事務A裏面看到的也是B沒有提交的數據 select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | --->髒讀意味着我在這個事務中(A中),事務B雖然沒有提交,但它任何一條數據變化,我均可以看到! | 2 | 2 | | 3 | 3 | +------+------+
READ-UNCOMMITTED
Read Committed(讀取提交內容):
這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)
它知足了隔離的簡單定義:一個事務只能看見已經提交事務所作的改變
這種隔離級別出現的問題是——不可重複讀(Nonrepeatable Read):不可重複讀意味着咱們在同一個事務中執行徹底相同的select語句時可能看到不同的結果。
致使這種狀況的緣由可能有:(1)有一個交叉的事務有新的commit,致使了數據的改變;(2)一個數據庫被多個實例操做時,同一事務的其餘實例在該實例處理其間可能會有新的
#首先修改隔離級別 set tx_isolation='read-committed'; select @@tx_isolation; +----------------+ | @@tx_isolation | +----------------+ | READ-COMMITTED | +----------------+ #事務A:啓動一個事務 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事務B:也啓動一個事務(那麼兩個事務交叉了) 在這事務中更新數據,且未提交 start transaction; update tx set num=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ #事務A:這個時候咱們在事務A中能看到數據的變化嗎? select * from tx; ---------------> +------+------+ | | id | num | | +------+------+ | | 1 | 1 |--->並不能看到! | | 2 | 2 | | | 3 | 3 | | +------+------+ |——>相同的select語句,結果卻不同 | #事務B:若是提交了事務B呢? | commit; | | #事務A: | select * from tx; ---------------> +------+------+ | id | num | +------+------+ | 1 | 10 |--->由於事務B已經提交了,因此在A中咱們看到了數據變化 | 2 | 2 | | 3 | 3 | +------+------+
READ-COMMITTED
Repeatable Read(可重讀):
這是MySQL的默認事務隔離級別
它確保同一事務的多個實例在併發讀取數據時,會看到一樣的數據行
此級別可能出現的問題——幻讀(Phantom Read):當用戶讀取某一範圍的數據行時,另外一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的「幻影」 行
InnoDB和Falcon存儲引擎經過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題
#首先,更改隔離級別 set tx_isolation='repeatable-read'; select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ #事務A:啓動一個事務 start transaction; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ #事務B:開啓一個新事務(那麼這兩個事務交叉了) 在事務B中更新數據,並提交 start transaction; update tx set num=10 where id=1; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+ commit; #事務A:這時候即便事務B已經提交了,但A能不能看到數據變化? select * from tx; +------+------+ | id | num | +------+------+ | 1 | 1 | --->仍是看不到的!(這個級別2不同,也說明級別3解決了不可重複讀問題) | 2 | 2 | | 3 | 3 | +------+------+ #事務A:只有當事務A也提交了,它纔可以看到數據變化 commit; select * from tx; +------+------+ | id | num | +------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +------+------+
REPEATABLE-READ
Serializable(可串行化):
這是最高的隔離級別
它經過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每一個讀的數據行上加上共享鎖。
在這個級別,可能致使大量的超時現象和鎖競爭
#首先修改隔離界別 set tx_isolation='serializable'; select @@tx_isolation; +----------------+ | @@tx_isolation | +----------------+ | SERIALIZABLE | +----------------+ #事務A:開啓一個新事務 start transaction; #事務B:在A沒有commit以前,這個交叉事務是不能更改數據的 start transaction; insert tx values('4','4'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction update tx set num=10 where id=1; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
SERIALIZABLE
注:
安全性考慮:Serializable>Repeatable read>Read committed>Read uncommitted
數據庫效率:Read uncommitted>Read committed>Repeatable read>Serializable
通常狀況下,咱們會使用Repeatable read、Read committed mysql數據庫默認的數據庫隔離級別Repeatable read
轉自:https://www.bkjia.com/Pythonjc/1228355.html