MySQL 基於TCP 協議之上的開發,可是網絡鏈接後,傳輸的數據必須遵循MySQL的協議,封裝好MySQL協議的包,就是驅動程序 python
MySQL 的驅動
MySQLDB 最有名的庫,對MySQL 的C Client 封裝實現,支持python2,不更新了,不支持python3
MySQL 官方的connector
pymysql 語法兼容MySQLdb,使用python寫的庫,支持python3mysql
本文使用的是mariadb數據庫,與MySQL類似sql
grant all(表示全部操做) on .(庫.表(全部的,也能夠指定)) to root@localhost(設置用戶名爲root,連接爲本地連接) identified by 'roiot123';(設置密碼)數據庫
flush privileges;(刷新權限)緩存
server.cnf 中的操做(前面必須有空格,不然不生效,不能寫入漢字)服務器
client.cnf 操做同上網絡
重啓加載字符集多線程
如上,則表示加載完畢!!!socket
2 查看安裝是否完成,若完成,則不會報錯ide
創建鏈接
獲取遊標
執行SQL
提交事務
釋放資源
導入名重命名重命名
參數含義以下
connection 初始化經常使用參數 | 說明 |
---|---|
host | 主機 |
user | 用戶名 |
password | 密碼 |
database | 數據庫 |
port | 端口 |
其中必選參數是user和passwd 其餘可選
其中user 表示數據庫的用戶名,就是上面初始化的用戶名和密碼,db 是上面初始化的數據庫,host 表示本地連接,可使用IP地址或域名進行遠程連接,charset 表示連接使用的字符集,若是和上面的utf8不對應,則可能出現亂碼現象
對於數據庫的操做有 增insert 刪 delete 改 update 等 查 select show 等
建立完成後須要提交,若是不提交則不生效,提交使用的是建立的連接的關鍵字。
須要關閉連接,首先須要關閉的是遊標,其次是連接。
在MySQL數據庫中進行查看:
顯示與數據庫查詢類似的結果show 和 select
數據庫中的顯示
再次建立一個數據表以備查詢所用
使用遊標.fetchone()表示每次查看一行操做,兩個表示兩個操做一塊兒輸出
將其恢復遊標到起始位置,能夠進行屢次查看,若是沒有此配置,默認重上一次查詢的下一行開始查詢
查看
查看
查看
當%s 沒有雙引號時:
查看
查看生成結果:
查看是否生成:
pymysql 是第三方模塊庫,須要安裝
pip install pymysql
pymysql.connect() 方法返回的是connections模塊下的connection類實例,connect方法傳遞就是給connection類的__init__提供參數
源碼以下
def __init__(self, host=None, user=None, password="", database=None, port=0, unix_socket=None, charset='', sql_mode=None, read_default_file=None, conv=None, use_unicode=None, client_flag=0, cursorclass=Cursor, init_command=None, connect_timeout=10, ssl=None, read_default_group=None, compress=None, named_pipe=None, autocommit=False, db=None, passwd=None, local_infile=False, max_allowed_packet=16*1024*1024, defer_connect=False, auth_plugin_map=None, read_timeout=None, write_timeout=None, bind_address=None, binary_prefix=False, program_name=None, server_public_key=None):
上述初始化參數中autocommit=False,則指的是默認的事務提交是關閉的,及須要手動提交事務
Connection.ping() 方法,測試數據庫服務器是否活着,有一個參數熱connection表示斷開與服務器鏈接是否重連
def ping(self, reconnect=True): """ Check if the server is alive. :param reconnect: If the connection is closed, reconnect. :raise Error: If the connection is closed and reconnect=False. """ if self._sock is None: if reconnect: self.connect() reconnect = False else: raise err.Error("Already closed") try: self._execute_command(COMMAND.COM_PING, "") self._read_ok_packet() except Exception: if reconnect: self.connect() self.ping(False) else: raise
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) finally: if conn: print('關閉連接:',conn.ping(False)) conn.close()
結果以下
操做數據庫,必須使用遊標,須要先獲取一個遊標對象
Connection.cursor(cursor=None) 方法返回一個新的遊標對象
鏈接沒有關閉以前,遊標對象能夠反覆使用
cursor參數,能夠指定一個Cursor類,若是爲None,則使用默認Cursor類
數據庫操做須要使用Cursor 類的實例,提供的execute()方法,執行SQL語句,成功返回影響行數。
基本代碼以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 獲取遊標 cursor=conn.cursor() sql="insert into t(id,username,password) values(3,'mysql','mysql')" line=cursor.execute(sql) # 此處未提交事務。若關閉,則直接致使事務回滾 print (line) cursor.close() finally: if conn: print('關閉連接:',conn.ping(False)) conn.close()
結果以下
數據庫結果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 獲取遊標 cursor=conn.cursor() sql="insert into t(id,username,password) values(3,'mysql','mysql')" line=cursor.execute(sql) # 此處未提交事務。若關閉,則直接致使事務回滾 print (line) cursor.close() # 若沒有異常,則提交事務 conn.commit() except: # 若存在異常,則回滾事務 conn.rollback() finally: if conn: print('關閉連接:',conn.ping(False)) conn.close()
結果以下
數據庫結果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 獲取遊標 cursor=conn.cursor() for i in range(5): sql="insert into t(id,username,password) values(3,'mysql','mysql')" line=cursor.execute(sql) print (line) # 此處未提交事務。若關閉,則直接致使事務回滾 # 若沒有異常,則提交事務 conn.commit() except: # 若存在異常,則回滾事務 conn.rollback() finally: if cursor: cursor.close() if conn: conn.close()
數據庫結果以下
變量的方式插入多行數據
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 獲取遊標 cursor=conn.cursor() for i in range(10,15): sql="insert into t(id,username,password) values({},'mysql','mysql')".format(i) line=cursor.execute(sql) # 此處未提交事務。若關閉,則直接致使事務回滾 # 若沒有異常,則提交事務 conn.commit() except: # 若存在異常,則回滾事務 conn.rollback() finally: if cursor: cursor.close() if conn: conn.close()
結果以下
cursor類的獲取查詢結果集的方法有fetchone(),fetchmany(size=None),fetchall() 三種
fetchone()方法,獲取結果集的下一行
fetchmany(size=None) 方法,size指定返回的行數的行,None則返回用組
fetchall() 方法,獲取全部行
返回多行,若是走到末尾,就返回空元組,不然返回一個元組,其元素就是每一行的記錄,每一行的記錄都裝載在一個元組中。
注意:fetch操做的是結果集,結果集是保存在客戶端的,也就是說fetch的時候,查詢已經結束了。
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 獲取遊標 cursor=conn.cursor() sql="select * from t" line=cursor.execute(sql) print ('獲取一個',cursor.fetchone()) # 獲取一個 print ('獲取下面兩個',cursor.fetchmany(2)) # 獲取下面兩個 print ('獲取全部',cursor.fetchall()) # 獲取全部 cursor.rownumber=0 # 遊標初始化,支持負索引,當大於len的索引,則會不存在 # 此處未提交事務。若關閉,則直接致使事務回滾 # 若沒有異常,則提交事務 print ('獲取全部',cursor.fetchall()) # 獲取全部 print ('獲取一個',cursor.fetchone()) #此處沒法獲取到了 conn.commit() finally: if cursor: cursor.close() if conn: conn.close()
結果以下
Cursor類有一個Mixin的子類DictCursor
只須要cursor=conn.cursor(DictCursor)就好了
返回多行,放在列表中,元素是字典,表明行,返回是列表
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 獲取遊標 cursor=conn.cursor(DictCursor) sql="select * from t" line=cursor.execute(sql) print (cursor.fetchall()) # 獲取一個 finally: if cursor: cursor.close() if conn: conn.close()
結果以下
猜想後臺數據的查詢語句使用拼接字符串的方式,從而通過設計爲服務端傳遞參數,令其拼接處特殊的字符串,返回用戶想要的結果
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 獲取遊標 cursor=conn.cursor() sql="select * from t where id={}".format('5 or 1=1') print (sql) line=cursor.execute(sql) print (cursor.fetchall()) # 獲取一個 finally: if cursor: cursor.close() if conn: conn.close()
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') print (conn.ping(False)) # 獲取遊標 name="'test'" passwd="'test' or 1=1" cursor=conn.cursor() sql="select * from t where username={} and password ={}".format(name,passwd) print (sql) line=cursor.execute(sql) print (cursor.fetchall()) # 獲取一個 finally: if cursor: cursor.close() if conn: conn.close()
結果以下
參數化查詢,能夠有效防止SQL注入,並提升查詢效率
cursor.execute(query,args=None)
args, 必須是元祖,列表或者字典,若是查詢字符串使用%(name)s,就必須使用字典
參數化查詢爲何能提升效率
緣由就是SQL語句緩存
數據庫服務器通常都會對SQL語句編譯和緩存,編譯只是對SQL部分,因此參數化就算有SQL指令也不會被執行
編譯過程,須要詞法分析,語法分析,生成AST,優化,生成執行計劃等過程,比較耗資源,服務端會先查詢是否對同一條語句進行緩存,若是緩存未失效,則不須要再次編譯,從而下降了編譯的成本,減低了內存消耗。
能夠認爲SQL語句字符串就是一個key,若是使用拼接方案,每次發過去的SQL語句都不同,都須要從新編譯並緩存。
大量查詢的時候,首選使用參數化查詢,以節省資源。
開發時,應該使用參數化查詢
注意:這裏說的是查詢字符串的緩存,不是查詢結果的緩存
>
慢查詢通常用不上緩存。
字典參數化查詢
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') d={'name':"'test'",'passwd':"'test' or 1=1"} cursor=conn.cursor() sql="select * from t where username=%(name)s and password=%(passwd)s" line=cursor.execute(sql,d) print (cursor.fetchall()) # 獲取一個 finally: if cursor: cursor.close() if conn: conn.close()
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') d={'id': '1 or 1=1'} cursor=conn.cursor() sql="select * from t where id=%(id)s" line=cursor.execute(sql,d) print (cursor.fetchall()) # 獲取一個 finally: if cursor: cursor.close() if conn: conn.close()
元祖處理
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') cursor=conn.cursor() L1=[ (i,'admin','admin') for i in range(20,23)] for x in L1: sql="insert into t(id,username,password) values(%s,%s,%s)" line=cursor.execute(sql,x) print (line) conn.commit() finally: if cursor: cursor.close() if conn: conn.close()
結果以下
列表以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') cursor=conn.cursor() L1=[ [i,'admin','admin'] for i in range(24,26)] for x in L1: sql="insert into t(id,username,password) values(%s,%s,%s)" line=cursor.execute(sql,x) conn.commit() finally: if cursor: cursor.close() if conn: conn.close()
結果以下
def __enter__(self): """Context manager that returns a Cursor""" warnings.warn( "Context manager API of Connection object is deprecated; Use conn.begin()", DeprecationWarning) return self.cursor() def __exit__(self, exc, value, traceback): """On successful exit, commit. On exception, rollback""" if exc: self.rollback() else: self.commit()
有上述代碼可得,連接在使用上下文時會自動返回cursor遊標,並在連接關閉時會自動判斷執行語句是否出錯,若出錯,則直接回滾,不然提交,但未定義相關的關閉連接的操做
def __enter__(self): return self def __exit__(self, *exc_info): del exc_info self.close()
有上述可知,遊標的上下文返回的是本身,關閉連接時遊標會自動關閉連接
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') with conn as cursor: #此處返回一個cursor連接 sql="select * from t" cursor.execute(sql) print (cursor.fetchmany(5)) finally: if cursor: #上述的代碼中未使用cursor的上下文,所以此處仍是須要的 cursor.close() if conn: conn.close()
結果以下
#!/usr/bin/poython3.6 #conding:utf-8 from pymysql.connections import Connection from pymysql.cursors import DictCursor conn=None cursor=None try: conn=Connection('192.168.1.200','test','Admin@Root123','test') with conn as cursor: #此處返回一個cursor連接 with cursor: #此處使用cursor的上下文,默認會關閉其遊標 sql="select * from t" cursor.execute(sql) print (cursor.fetchmany(5)) finally: if conn: conn.close()
結果以下
鏈接不該該隨隨便便銷燬,應該造成重複使用的習慣,應該是多個cursor共享一個鏈接 。
這裏的鏈接池,指的是數據庫的鏈接池
鏈接池,是一個容器,裏面存放着已經鏈接到數據庫的鏈接,若是須要鏈接,使用者能夠直接從池中獲取一個鏈接,使用完後歸還便可
從而減小頻繁的建立,銷燬數據庫鏈接的過程,提升了性能。
一個鏈接池,應該是一個能夠設置大小的容器,裏面存放着數據庫的鏈接。
使用者須要鏈接,從池中獲取一個鏈接,用完則須要歸還。
面向對象的設計思路,構建一個鏈接池
構建時,傳入鏈接的服務器相關參數(主機,端口,用戶名,密碼,庫名稱),還有提供一個池的容量
考慮多線程的使用,使用者從池中get一個鏈接,用完後歸還該鏈接便可。
#!/usr/local/bin/python3.6 #coding:utf-8 from pymysql.connections import Connection from threading import local import queue class ConnPool: def __init__(self,size,*args,**kwargs): self.__size=size self.__pool=queue.Queue(size) # 此處用於保存鏈接 self.local=local() # 此處用於隔離線程間數據,用於對不一樣的conn進行區分,具體在線程基礎一章全部介紹 for i in range(size): # 此處用於生成鏈接池,此處是建立鏈接 conn=Connection(*args,**kwargs) # 初始化鏈接 self.__pool.put(conn) @property def maxsize(self): return self.__size @property def size(self): return self.__pool.qsize() def __getconn(self): # 此處用於返回conn鏈接 return self.__pool.get() def __returnconn(self,conn:Connection): # 此處用於歸還鏈接 if isinstance(conn,Connection): # 此處如果一個鏈接,則可用於返回 self.__pool.put(conn) def __enter__(self): # 此處用於返回一個遊標,固然能夠返回一個鏈接 if getattr(self.local,'conn',None) is None: # 若不存在,則進行添加,若存在,則返回 self.local.conn=self.__getconn() return self.local.conn.cursor() def __exit__(self, exc_type, exc_val, exc_tb): if exc_type: self.local.conn.rollback() else: self.local.conn.commit() self.__returnconn(self.local.conn) self.local.conn=None # 此處若重置此連接,則不會致使cursor的不可用,由於cursor中保存着conn的相關信息,但若被刪除,則會致使所有出錯,del # 語句不可輕易執行 pool=ConnPool(3,'192.168.1.120','root','666666','test') with pool as cursor: with cursor: sql="select * from login" cursor.execute(sql) print (cursor.fetchall()) sql="SHOW PROCESSLIST;" #此處是SQL 自己自帶的用於查看進程的命令 cursor.execute(sql) for x in cursor: print (x)
上述的cursor可以被遍歷的緣由是其中有iter方法,具體源碼以下
def __iter__(self): return iter(self.fetchone, None)
結果以下