1. 程式人生 > >python從資料庫獲取全量資料的方法

python從資料庫獲取全量資料的方法

資料庫:postgresql

(1)第一種方法:使用分頁查詢的方式,不推薦使用特別是在資料量大的時候

       首先計算總資料量,然後根據每次查詢的大小batch_size計算總共有多少頁,再一頁一頁的去獲取資料。

       由於分頁查詢類似ES中的深度分頁,頁數越大效率越低,因此在資料量小的時候用用還湊合。

import psycopg2.pool
from datetime import datetime

# 資料庫連線
conn = psycopg2.connect(database="dbname", user="username", password="123456", host="172.0.0.0", port="5432")
# 獲取遊標
cursor = conn.cursor()
# 批量查詢大小
batch_size = 1000

def limit_offset_query():
    """
    分頁查詢
    :return:
    """
    # 資料總量
    total_count = get_total_count()
    if total_count <= 0:
        return
    # 總頁數
    total_page = total_count / batch_size
    # 開始時間
    start_time = datetime.now()
    for i in range(int(total_page) + 1):# 遍歷每一頁
        # 起始位置
        start = batch_size * i
        # 查詢資料
        result_list = get_tweet_by_page(start, batch_size)
        if (len(result_list) > 0):
            print('獲取%s到%s資料成功' % (start, start + batch_size))
    print('limit offset獲取全量資料所用時間:', (datetime.now() - start_time).seconds)


def get_total_count():
    """
    分頁查詢獲取總數量
    :return:
    """
    sql = "select count(*) from tablename"
    cursor.execute(sql)
    rst = cursor.fetchone()
    if rst is None:
        return 0
    return rst[0]


def get_tweet_by_page(start, pagesize):
    """
    分頁查詢
    :param start:
    :param pagesize:
    :return:
    """
    sql = "select * from tablename limit %s offset %s "
    cursor.execute(sql, [pagesize, start])
    rst = cursor.fetchall()
    return rst

limit_offset_query()

(2)第二種方式,使用遊標fetchmany方法,獲取全量資料,在大資料量下推薦使用

fetchmany方法接受一個size引數,fetchmany每次呼叫的時候從上次的位置向後移動遊標返回size條資料。

參考文件中的例子:

從test表中獲取全部資料,一共有3條資料:

第一次呼叫fetchmany引數返回兩條資料,遊標移動兩個位置。

第二次呼叫fetchmany雖然size傳入為2,但是隻剩一條資料,因此返回一條資料,遊標向後移動一個位置。

第三次呼叫fetchmany方法時由於資料已經全部返回,因此返回空資料。

>>> cur.execute("SELECT * FROM test;")
>>> cur.fetchmany(2)
[(1, 100, "abc'def"), (2, None, 'dada')]
>>> cur.fetchmany(2)
[(3, 42, 'bar')]
>>> cur.fetchmany(2)
[]

關於服務端遊標server side curosr和客戶端遊標client side cursor:

根據文件的描述,通過資料庫連線建立遊標的時候,如果傳入name引數,就會返回一個服務端遊標,如果name引數為空,返回的則是一個客戶端遊標,如果返回的結果集數量比較大,應該使用服務端遊標。

測試資料大概有100萬,剛開始name引數為空執行程式的時候直接記憶體飆升,pycharm卡頓,可能就是因為返回的是客戶端遊標,客戶端遊標只能處理小量資料。

import psycopg2.pool
from datetime import datetime


# 批量查詢大小
batch_size = 1000


def cursor_query():
    # 使用資料庫連線池,使用普通的連線方法執行貌似也會記憶體飆升,因此改為了連線池
    simple_conn_pool = psycopg2.pool.SimpleConnectionPool(minconn=1, maxconn=5, database="dbname", user="username",
                                                          password="123456", host="172.0.0.1", port="5432")
    # 從資料庫連線池獲取連線
    conn = simple_conn_pool.getconn()
    # 自動提交事務設為false
    conn.autocommit = False
    # 建立遊標,這裡傳入name引數,會返回一個服務端遊標否則返回的是客戶端遊標
    cursor = conn.cursor('cursorname')
    # 首先查詢全量資料
    cursor.execute('select * from tablename')
    count = 0
    # 開始時間
    start_time = datetime.now()
    while True:
        count = count + 1
        # 每次獲取時會從上次遊標的位置開始移動size個位置,返回size條資料
        data = cursor.fetchmany(batch_size)
        # 資料為空的時候中斷迴圈
        if not data:
            break
        print('獲取%s到%s資料成功' % ((count - 1) * batch_size, count * batch_size))
    print('fetchmany獲取全量資料所用時間:', (datetime.now() - start_time).seconds)

cursor_query()

資料表大概有100萬資料,分別用兩種方法測試了一下時間:

使用limit offset分頁查詢耗時321s:

limit offset獲取全量資料所用時間:321

使用fetchmany方法耗時122s:

fetchmany獲取全量資料所用時間: 122