Python 數據庫的Connection、Cursor兩大對象
pymysql是Python中操作MySQL的模塊,其使用方法和py2的MySQLdb幾乎相同。
Python 數據庫圖解流程
Connection、Cursor比喻
Connection()的參數列表
host,連接的數據庫服務器主機名,默認為本地主機(localhost)。
user,連接數據庫的用戶名,默認為當前用戶。
passwd,連接密碼,沒有默認值。
db,連接的數據庫名,沒有默認值。
conv,將文字映射到Python類型的字典。
MySQLdb.converters.conversions
cursorclass,cursor()使用的種類,默認值為MySQLdb.cursors.Cursor。
compress,啟用協議壓縮功能。
named_pipe,在windows中,與一個命名管道相連接。
init_command,一旦連接建立,就為數據庫服務器指定一條語句來運行。
read_default_file,使用指定的MySQL配置文件。
read_default_group,讀取的默認組。
unix_socket,在unix中,連接使用的套接字,默認使用TCP。
port,指定數據庫服務器的連接端口,默認是3306。
連接對象的db.close()方法可關閉數據庫連接,並釋放相關資源。
連接對象的db.cursor([cursorClass])方法返回一個指針對象,用於訪問和操作數據庫中的數據。
連接對象的db.begin()方法用於開始一個事務,如果數據庫的AUTOCOMMIT已經開啟就關閉它,直到事務調用commit()和rollback()結束。
連接對象的db.commit()和db.rollback()方法分別表示事務提交和回退。
指針對象的cursor.close()方法關閉指針並釋放相關資源。
指針對象的cursor.execute(query[,parameters])方法執行數據庫查詢。
指針對象的cursor.fetchall()可取出指針結果集中的所有行,返回的結果集一個元組(tuples)。
指針對象的cursor.fetchmany([size=cursor.arraysize])從查詢結果集中取出多行,我們可利用可選的參數指定取出的行數。
指針對象的cursor.fetchone()從查詢結果集中返回下一行。
指針對象的cursor.arraysize屬性指定由cursor.fetchmany()方法返回行的數目,影響fetchall()的性能,默認值為1。
指針對象的cursor.rowcount屬性指出上次查詢或更新所發生行數。-1表示還沒開始查詢或沒有查詢到數據。
Cursor
常用方法:
close():關閉此游標對象
fetchone():得到結果集的下一行
fetchmany([size = cursor.arraysize]):得到結果集的下幾行
fetchall():得到結果集中剩下的所有行
excute(sql[, args]):執行一個數據庫查詢或命令
excutemany(sql, args):執行多個數據庫查詢或命令
常用屬性:
connection:創建此游標對象的數據庫連接
arraysize:使用fetchmany()方法一次取出多少條記錄,默認為1
事務
事務命令
事務指邏輯上的一組操作,組成這組操作的各個單元,要不全部成功,要不全部不成功。
數據庫開啟事務命令
start transaction 開啟事務 Rollback 回滾事務,即撤銷指定的sql語句(只能回退insert delete update語句),回滾到上一次commit的位置 Commit 提交事務,提交未存儲的事務 savepoint 保留點 ,事務處理中設置的臨時占位符 你可以對它發布回退(與整個事務回退不同)
轉賬實例:
UPDATE account set balance=balance-5000 WHERE name=”yuan”; UPDATE account set balance=balance+5000 WHERE name=”xialv”;
-- 創建表 create table test2(id int PRIMARY KEY auto_increment,name VARCHAR(20)) engine=innodb; -- 插入數據 INSERT INTO test2(name) VALUE ("alvin"), ("yuan"), ("xialv"); start transaction; -- 開啟事務 insert into test2 (name)values('silv'); select * from test2; commit; -- 提交事務 -- 保留點 start transaction; insert into test2 (name)values('wu'); savepoint insert_wu; -- 給上面剛才insert的命令起了個名字叫insert_wu,並設置一個保留點,對重要的sql,要緊挨着設置保留點 select * from test2; delete from test2 where id=4; savepoint delete1; select * from test2; delete from test2 where id=1; savepoint delete2; select * from test2; rollback to delete1; -- 回滾到某個設置的節點 select * from test2; savepoint
事務特性
<1> 原子性(Atomicity):原子性是指事務是一個不可分割的工作單位,事務中的操作要么都發生,要么都不發生。
<2> 一致性(Consistency):事務前后數據的完整性必須保持一致。在事務執行之前數據庫是符合數據完整性約束的,無論事務是否執行成功,事務結束后的數據庫中的數據也應該是符合完整性約束的。在某一時間點,如果數據庫中的所有記錄都能保證滿足當前數據庫中的所有約束,則可以說當前的數據庫是符合數據完整性約束的。
比如刪部門表前應該刪掉關聯員工(已經建立外鍵),如果數據庫服務器發生錯誤,有一個員工沒刪掉,那么此時員工的部門表已經刪除,那么就不符合完整性約束了,所以這樣的數據庫也就性能太差啦!
<3>隔離性(Isolation):事務的隔離性是指多個用戶並發訪問數據庫時,一個用戶的事務不能被其它用戶的事務所干擾,多個並發事務之間數據要相互隔離。
<4>持久性(Durability):持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響。
三、隔離性:
將數據庫設計為串行化程的數據庫,讓一張表在同一時間內只能有一個線程來操作。如果將數據庫設計為這樣,那數據庫的效率太低了。所以數據庫的設計這沒有直接將數據庫設計為串行化,而是為數據庫提供多個隔離級別選項,使數據庫的使用者可以根據使用情況自己定義到底需要什么樣的隔離級別。
不考慮隔離性可能出現的問題:
臟讀
--一個事務讀取到了另一個事務未提交的數據,這是特別危險的,要盡力防止。 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;(把某一個賬戶的資金置為0) 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(b+c) ------------------- d:start transaction; insert into account values(d,3000); commit; ------------------- select count(*)from account;---3 3(算上插入的d總共三個人) 3000/3 = 1000 1000 (但是總金額還是按照b和c兩個人算的,所以平均金額不對)
四個隔離級別:
Serializable:可避免臟讀、不可重復讀、虛讀情況的發生。(串行化)
Repeatable read:可避免臟讀、不可重復讀情況的發生。(可重復讀)不可以避免虛讀
Read committed:可避免臟讀情況發生(讀已提交)
Read uncommitted:最低級別,以上情況均無法保證。(讀未提交)
安全性考慮:Serializable>Repeatable read>Read committed>Read uncommitted
數據庫效率:Read uncommitted>Read committed>Repeatable read>Serializable
一般情況下,我們會使用Repeatable read、Read committed mysql數據庫默認的數據庫隔離級別Repeatable read
mysql中設置數據庫的隔離級別語句:
set [global/session] transaction isolation level xxxx;
如果使用global則修改的是數據庫的默認隔離級別,所有新開的窗口的隔離級別繼承自這個默認隔離級別如果使用session修改,則修改的是當前客戶端的隔離級別,和數據庫默認隔離級別無關。當前的客戶端是什么隔離級別,就能防止什么隔離級別問題,和其他客戶端是什么隔離級別無關。
mysql中設置數據庫的隔離級別語句:
select @@tx_isolation;
事務
事務指邏輯上的一組操作,組成這組操作的各個單元,要不全部成功,要不全部不成功。
數據庫開啟事務命令
-- start transaction 開啟事務
-- Rollback 回滾事務,即撤銷指定的sql語句(只能回退insert delete update語句),回滾到上一次commit的位置
-- Commit 提交事務,提交未存儲的事務
--
-- savepoint 保留點 ,事務處理中設置的臨時占位符 你可以對它發布回退(與整個事務回退不同)
實例
--創建表
create table account(
id int primary key auto_increment,
name varchar (25),
balance double
);
insert into account values (1,'alex',8000),(2,'ego',8000);
-- +----+------+---------+
-- | id | name | balance |
-- +----+------+---------+
-- | 1 | alex | 8000 |
-- | 2 | ego | 8000 |
-- +----+------+---------+
start transaction ;--開始事務
update account set balance=balance-5000 where name='alex';
select * from account;
-- +----+------+---------+ --此時數據並沒有寫入數據庫,只是顯示命令的結果,除非在操作下面寫commit
-- | id | name | balance |
-- +----+------+---------+
-- | 1 | alex | 3000 |
-- | 2 | ego | 8000 |
-- +----+------+---------+
savepoint update1; --設置保留點
update account set balance=balance+5000 where name='ego';
select * from account;
-- +----+------+---------+ --一樣數據沒有寫入數據庫
-- | id | name | balance |
-- +----+------+---------+
-- | 1 | alex | 3000 |
-- | 2 | ego | 13000 |
-- +----+------+---------+
savepoint update2;
rollback to update1; --回滾至操作update1處,update1以上的操作任然存在,update1下的操作將全被取消
select * from account;
-- +----+------+---------+
-- | id | name | balance |
-- +----+------+---------+
-- | 1 | alex | 3000 |
-- | 2 | ego | 8000 |
-- +----+------+---------+
rollback ; --直接回滾,則會回滾自前面的commit處,如果沒有commit就一直回滾至開頭
-- +----+------+---------+
-- | id | name | balance |
-- +----+------+---------+
-- | 1 | alex | 8000 |
-- | 2 | ego | 8000 |
-- +----+------+---------+
commit ; --提交數據,此時數據才真正寫入數據庫
select * from account;
-- +----+------+---------+
-- | id | name | balance |
-- +----+------+---------+
-- | 1 | alex | 8000 |
-- | 2 | ego | 8000 |
-- +----+------+---------+
Python3 pymysql事務處理,執行多條sql語句后提交
# -*- coding: utf-8 -*- import pymysql.cursors # 連接數據庫 connect = pymysql.Connect( host='localhost', port=3310, user='user', passwd='123', db='test', charset='utf8' ) # 事務處理 sql_1 = "UPDATE staff SET saving = saving + 1000 WHERE user_id = '1001' " sql_2 = "UPDATE staff SET expend = expend + 1000 WHERE user_id = '1001' " sql_3 = "UPDATE staff SET income = income + 2000 WHERE user_id = '1001' " try: cursor.execute(sql_1) # 儲蓄增加1000 cursor.execute(sql_2) # 支出增加1000 cursor.execute(sql_3) # 收入增加2000 except Exception as e: connect.rollback() # 事務回滾 print('事務處理失敗', e) else: connect.commit() # 事務提交 print('事務處理成功', cursor.rowcount) # 關閉連接 cursor.close() connect.close()
python pymysql cursor的問題
使用python操作mysql的時候一般都會使用如下語句:
官方示例
import pymysql.cursors # 連接到數據庫后實際上TCP的連接狀態是ESTABLISHED connection = pymysql.connect(host='localhost', user='user', password='passwd', db='db', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) try: with connection.cursor() as cursor: sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)" cursor.execute(sql, ('webmaster@python.org', 'very-secret')) #默認不自動提交事務,所以需要手動提交 connection.commit() with connection.cursor() as cursor: sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s" cursor.execute(sql, ('webmaster@python.org',)) result = cursor.fetchone() print(result) finally: connection.close()
在這段代碼里,有一個疑惑的點是,我們現在創建了一個連接,但是實例化了多個cursor,我們可不可以使用同一個連接的同一個cursor來重復使用,代碼如下
with connect.cursor() as cursor: cursor.execute("select * from person limit 1") print(id(cursor)) data = cursor.fetchone() print(data) print("=============") cursor.execute("select * from person limit 1") print(id(cursor)) data = cursor.fetchone() print(data)
上面的代碼,我們執行完了之后發現查詢操作是可以直接使用的,並且不會產生沖突,我們通過打印cursor的地址發現是同一個cursor。
插入操作
with connect.cursor() as cursor: for id in range(1, 100, 2): cursor.execute("insert into test(id)values(%d)"%id) print(id) id += 1 cursor.execute("insert into test(id)values(%d)"%id) time.sleep(2) print(id)
在單進程單線程環境下依然沒有問題
刪除
with connect.cursor() as cursor: for id in range(100): # cursor.execute("insert into test(id)values(%d)" % id) cursor.execute("delete from test where id=%s"%id) time.sleep(5) time.sleep(10)
也沒有問題
但是有博客說多進程環境下會出現問題,我一直想重現,但是沒有成功,等以后重現了再來更新。
但是
- 創建了一個 cursor 以后,建議完成一個事務就 commit 一下,不要一直用它,這樣一直使用,並不會和數據庫完成數據同步,如果操作太多,提交的時候會超時,造成部分數據更新,部分數據丟失,數據不一致且效率低。
並且握着 cursor 期間,數據庫端發生改變,也沒法和客戶端同步。
參考鏈接:
Python 數據庫的Connection、Cursor兩大對象
PyMySQL 0.9.2
python - 在 python MySQLdb模塊中,是否應該重用游標
你看一下MySQLdb下面的cursors模塊,DictCursor
的這個功能是繼承於CursorDictRowsMixIn
,這個MixIn提供了3個額外的方法: fetchoneDict、fetchmanyDict、fetchallDict。
>>> import MySQLdb >>> c = MySQLdb.connect('127.0.0.1', 'root', 'password', 'test') >>> x = c.cursor(MySQLdb.cursors.DictCursor) >>> x.execute('select * from user') 2L >>> x.fetchoneDict() {'age': 26L, 'id': 1L, 'name': 'test'}
二、execute()之sql注入
1
2
3
解決方法:
pymysql .cursor屬性方法(tcy)
cursor.excutemany(query, args)#執行多個數據庫查詢或命令
參數:
query - 要在服務器上執行的查詢
args - 序列或映射的序列。它用作參數。
返回:
受影響的行數(如果有)
說明:
提高多行INSERT和REPLACE的性能。否則它等同於使用execute()循環遍歷args
executemany()生成的最大語句大小為max_allowed_packet - packet_header_size
cursor.max_stmt_length #1024000
con.max_allowed_packet#16777216
實例:
str_insert = "INSERT INTO Cu_table (id) VALUES (%s)"
cursor.executemany(str_insert,['A','B','C','D']) #一次插入A B C D 四個值
方法:調用函數
cursor.callproc(procname, args=()) # 調用數據庫中的存儲過程
參數:
procname表示數據庫中存儲過程的名字,args表示為存儲過程傳入的參數。
返回:
返回原始args
調用的結果作為輸入序列的修改副本返回。
該過程還可以提供結果集作為輸出。然后必須通過標准.fetch * ()
方法使其可用
調用無參數存儲過程:cursor.callproc('p2') # 等價cursor.execute("call p2()")
調用有參數存儲過程:cursor.callproc('p1', args=(1, 22, 3, 4))
class pymysql.cursors.Cursor(connection) # 游標結果作為元祖的元祖返回
說明:
這是您用於與數據庫交互的對象。
不要自己創建Cursor實例。調用connections.Connection.cursor()
class pymysql.cursors.SSCursor(connection) # 無緩沖游標結果作為元祖的元祖返回,
用途:
用於返回大量數據查詢,或慢速網絡連接到遠程服務器
不將每行數據復制到緩沖區,根據需要獲取行。客戶端內存使用少
在慢速網絡上或結果集非常大時行返回速度快
限制:
MySQL協議不支持返回總行數,判斷有多少行唯一方法是迭代返回的每一行。
目前無法向后滾動,因為只有當前行保存在內存中。
class pymysql.cursors.DictCursor(connection) # 將結果作為字典返回游標
class pymysql.cursors.SSDictCursor(connection) # 無緩沖游標結果作為字典返回
游標屬性:
cursor.max_stmt_length # 1024000
cursor.rownumber # 5 #當前結果集中游標所在行的索引(起始行號為 0)
cursor.arraysize # 1 #此讀/寫屬性指定用.fetchmany()一次獲取的行數。
# 默認1表示一次獲取一行;也可以用於執行.executemany()
cursor.lastrowid # None #只讀屬性提供上次修改行的rowid
# DB僅在執行單個INSERT 操作時返回rowid 。
# 如未設rowid或DB不支持rowid應將此屬性設置為None
# 如最后執行語句修改了多行,例如用INSERT和.executemany()時lastrowid語義是未定義
cursor.rowcount # 5 #最近一次 execute() 創建或影響的行數
# 如無cursor.execute()或接口無法確定最后一個操作的rowcount則該屬性為-1
# 該行數屬性可以在動態更新其值的方式來編碼。
# 這對於僅在第一次調用.fetch()方法后返回可用rowcount值的 數據庫非常有用。
commit()方法:在數據庫里增,刪,改的時候。必須要進行提交,否則插入的時候數據不生效