Python 數據庫連接方法和數據庫連接池

Python連接數據庫

Python官方制定的數據庫接口標準中,主要包含了頂層connect函數、部分常量、數據庫操作異常、用於管理連接的Connection類以及執行查詢的Cursor類。
DBUtils是python用於管理數據庫連接池的包,爲高頻度高併發的數據庫訪問提供更好的性能,可以自動管理連接對象的創建和釋放。最常用的兩個外部接口是PersistentDB和PooledDB,前者提供了單個線程專用的數據庫連接池,後者則是進程內所有線程共享的數據庫連接池。

在Python中操作數據庫,基本步驟如下:
1)導入相應的Python模塊
2)使用connect函數連接數據庫,並返回一個Connection對象
3)通過Connection對象的cursor方法,返回一個Cursor對象
4)通過Cursor對象的execute方法執行SQL語句
5)如果執行的是查詢語句,通過Cursor對象的fetchall語句返回結果
6)調用Cursor對象的close方法關閉Cursor
7)調用Connection對象的close方法關閉數據庫連接

**python訪問MySQL數據庫需要安裝第三方模塊,使用最廣泛的是mysqldb和pymysql.
python訪問SqlServer數據庫需要安裝第三方模塊pymssql
實現數據庫連接池 pip install dbutils
**

完整示例如下

import pymysql
db = pymysql.connect(host="10.133.0.46", port=3306, user='rw_aps', password='rw_aps.aac',
                         db='aac_lens_analysis',connect_timeout=100)

cur = db.cursor()

sql = f"select * from aac_lens_analysis.t_mysql_status where pair_date='2020-06-03' and pair_shift='day' "

cur.execute(sql)

results = cur.fetchall()

print(cur.rowcount)

cur.close()
db.close()

連接數據庫

模塊中包含了connect函數、常量和異常。調用PyMySQL的Connect函數,將創建一個數據庫連接得到一個Connection對象。Connect函數的部分參數如下:

db = pymysql.connect(host="10.133.0.46", port=3306, user='rw_aps', password='rw_aps.aac',db='aac_lens_analysis',connect_timeout=100)

host:數據庫服務的地址
port:連接數據庫的端口號,默認mysql端口號3306
user:登錄數據庫的用戶名
passwd:登錄數據庫的密碼
connect_timeout:連接超時時間
read_default_file:讀取MySQL的配置文件中的配置進行連接

Connection類的成員

通過正確的參數調用PyMySQL的Connect函數,將會返回Connection類的對象。

db = pymysql.connect(host="10.133.0.46", port=3306, user='rw_aps', password='rw_aps.aac',db='aac_lens_analysis',connect_timeout=100)
cursor = db.cursor()
#以字典的形式返回操作結果
#cursor = db.cursor(pymysql.cursors.DictCursor)

常用的方法如下:
begin:開始事務
commit;提交事務
rollback:回滾事務
cursor:返回一個Cursor對象
在實際編程過程中,一般不會直接調用begin、commit、rollback函數,而是通過上下文管理器實現事務的提交與回滾操作。
DictCursor:在默認情況下cursor方法返回的是BaseCursor類型對象,BaseCursor類型對象在執行查詢後每條記錄的結果以列表(list)表示返回,要返回字典(dict)表示的記錄,就要設置cursor參數爲pymysql.cursors.DictCursor類

Cursor類的成員

Cursor對象表示數據庫遊標,用於執行SQL語句並獲取SQL語句的執行結果。Cursor包含了一些異常、常量和函數。常用的常量和函數如下所示:

cursor = db.cursor()
sql = f"select * from aac_lens_analysis.t_mysql_status where pair_date='2020-06-03' and pair_shift='day' "
cursor.execute(sql)
results = cursor.fetchall()
print(cur.rowcount)
cur.close()
db.close()

execute:執行SQL語句
close:關閉遊標
fetchall:獲取SQL語句的所有記錄
fetchmany:獲取SQL語句的多條記錄
fetchone:獲取SQL語句的一條記錄
rowcount:常量,表示SQL語句的結果集中返回了多少條記錄

使用上下文管理器對數據庫連接進行管理

with語句可以實現任何try/finally語句實現的功能,而且代碼更加清晰簡潔。可以對數據庫的操作進行封裝,封裝完成以後,可以使用with語句保證數據庫連接無論在什麼情況下都會關閉。
還可以使用contextlib模塊中的contextmanager進行上下文管理。

from contextlib import contextmanager
import pymysql

@contextmanager
def get_conn(**kwargs):
    conn = pymysql.connect(host=kwargs.get('host','localhost'),
                           user=kwargs.get('user'),
                           passwd=kwargs.get('password'),
                           port=kwargs.get('port',3306),
                           db=kwargs.get('db')
                           )
    try:
        yield conn
    finally:
        if conn:
            conn.close()

conn_args = dict(host="10.133.0.46", port=3306, user='rw_aps', password='rw_aps.aac',
                 db='aac_lens_analysis',connect_timeout=100)

# 對創建的數據庫連接進行封裝以後,可以使用with語句管理數據庫連接,使得代碼更加清晰,且不容易出錯
with get_conn(**conn_args) as conn:
    with conn as cur:
        sql = f"select * from aac_lens_analysis.t_mysql_status where pair_date='2020-06-03' and pair_shift='day' "
        cur.execute(sql)
        results = cur.fetchall()
        print(cur.rowcount)

使用數據庫連接池訪問數據庫

使用pymysql、pymssql連接數據庫查詢的弊端是:每次都要連接數據庫浪費資源,所有必須用數據庫連接池。

參數 說明
creater 數據庫接口
mincached 啓動時開啓的空連接數量
maxcached 連接池最大可用連接數量
maxshared 連接池最大可共享連接數量
maxconnections 最大允許連接數
blocking 達到最大數量時是否阻塞
maxusage 單個連接最大複用次數
setsession 用於傳遞到數據庫的準備會話
from DBUtils.PooledDB import PooledDB
import pymysql

config = {
    'creator': pymysql,
    'host': '10.133.0.46',
    'port': 3306,
    'user': 'rw_aps',
    'password': 'rw_aps.aac',
    'db': 'aac_lens_analysis',
    'charset': 'utf8',
    'maxconnections': 10,  # 連接池最大連接數量
    'cursorclass': pymysql.cursors.DictCursor  #以字典的形式返回操作結果
}

pool = PooledDB(**config)
db = pool.connection()
cur = db.cursor()
sql = "select * from aac_lens_analysis.t_mysql_status where pair_date='2020-06-03' and pair_shift='day' "
cur.execute(sql)
result = cur.fetchall()
for i in result:
    print(i)
cur.close()
db.close()

輸出結果如下所示:

{'id': 48, 'pair_date': '2020-06-03', 'pair_shift': 'day', 'mysql_status': 'Success', 'site': '(2080, 2090, 5060)', 'created_time': datetime.datetime(2020, 6, 4, 2, 0)}

案例一: 從csv文件導入數據到MySQL數據庫

csv文件內容如下:
在這裏插入圖片描述

首先創建數據庫連接,在for循環中遍歷get_data函數的結果。get_data函數打開csv文件,並將csv文件的每一行轉換成一個命名元組。由於get_data使用yield語句返回結果,可以直接在for循環中遍歷get_data函數的返回值。得到命名元組以後,拼接SQL語句,然後通過Cursor對象執行SQL語句

import pymysql
import csv
from collections import namedtuple
from contextlib import contextmanager

# 使用上下文管理器對數據庫連接進行管理
@contextmanager
def get_conn(**kwargs):
    conn = pymysql.connect(host=kwargs.get('host','localhost'),
                           user=kwargs.get('user'),
                           passwd=kwargs.get('password'),
                           port=kwargs.get('port',3306),
                           db=kwargs.get('db')
                           )
    try:
        yield conn
    finally:
        if conn:
            conn.close()

#使用命名元組以後,允許使用列名代替下標訪問元組中的內容
def get_data(file_name):
    with open(file_name) as f:
        f_csv = csv.reader(f)
        #['id', 'name', 'sex', 'age', 'love']
        headings = next(f_csv)
        #命名元組
        Row = namedtuple('Row',headings)
        for r in f_csv:
        #['1', '劉備', '男', '20', 'book']
            yield Row(*r)

def execute_sql(conn,sql):
    with conn as cur:
        cur.execute(sql)



def main():
    conn_args = dict(host="10.177.33.45", port=3306, user='root', password='root',
                 db='dw',connect_timeout=100)

    with get_conn(**conn_args) as conn:
        for i in get_data(r"D:\tmp\test_csv.csv"):
            sql = f"insert into student values({i.id},'{i.name}','{i.sex}',{i.age},'{i.love}') "
            print(sql)
            execute_sql(conn,sql)

if __name__ == '__main__':
    main()

數據庫結果如下所示:
在這裏插入圖片描述
案例二:查詢數據庫數據往釘釘羣推送

import requests
import json
import datetime
import pymysql
from collections import namedtuple



api_url = 'https://oapi.dingtalk.com/robot/send?access_token=98574912ab4cde67a19fbb875df93671088e6de87337bc1fedea10e1d2637461'
headers={'Content-Type':'application/json;charset=utf-8'}


retry = 26
# retry = 120
# 重試時間間隔:秒
retry_sec = 60
# retry_sec = 60

stop_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
start_time = datetime.datetime.now() + datetime.timedelta(hours=-1)
start_time = start_time.strftime("%Y-%m-%d %H:%M:%S")


# 往釘釘發送審計日誌
def msg(text):
    json_text={
        "msgtype":"markdown",
        "markdown":{
            "title":"編組狀態通知",
            "text":text
        }
    }
    print(requests.post(api_url,json.dumps(json_text),headers=headers).content)


# 獲取數倉開始狀態
def get_monitor_audit_data():
    try:
        db = pymysql.connect(host='10.133.0.19', port=30334, user='rw_etl', password='rw_etl.aac', db='lens_olap')
        cursor = db.cursor()
        sql = "select time as '命令執行時間',regexp_replace(user, 'default_cluster:', '') as 用戶,cast(query_time / 1000 / 60  as int) as '查詢時長(分鐘)',cast(scan_bytes/1024/1024 as int) as '查詢數據量(M)',scan_rows as 查詢行數,return_rows as 返回行數,stmt as 查詢語句 from doris_audit_db__.doris_audit_tbl__ where query_time > 300000 and time > date_sub(now(), INTERVAL 1 hour) order by  time  desc"
        print("---ddddddddd---------")
        print(sql)
        cursor.execute(sql)
        results = cursor.fetchall()
        a=cursor.rowcount
        print(a)
        list=[]
        if len(results) >= 1:
            msg(f"{start_time}到{stop_time}時間範圍內,palo數據庫大的sql查詢情況如下:")
            headings = ['命令執行時間', '用戶', '查詢時長', '查詢數據量', '查詢行數', '返回行數', '查詢語句']
            Row = namedtuple('Row', headings)
            for i in results:
                a=[*i]
                row_data=Row(*a)
                text = f"""
            用戶:{row_data.用戶}                             
            命令執行時間:{row_data.命令執行時間}               
            查詢時長(分鐘):{row_data.查詢時長}                 
            查詢數據量(M):{row_data.查詢數據量}                
            查詢行數:{row_data.查詢行數}                      
            返回行數:{row_data.返回行數}                      
            查詢語句:{row_data.查詢語句}
            """
            msg(text)
            cursor.close()
            db.close()
        else:
            print(f"{start_time}到{stop_time}時間範圍內,palo數據庫無大的sql查詢")

    except Exception as e:
        print(e)
        msg(f"{start_time}到{stop_time},連接palo數據庫異常")


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