Как эффективно зафиксировать изменения БД в приложении Flask или Bottle? - PullRequest
0 голосов
/ 04 мая 2020

Я заметил, что SQLite db.commit() (сохранение на диск) занимает от 50 до 100 мсек с моей небольшой базой данных. Это нормально и задокументировано здесь , но это слишком много, чтобы делать это после каждого запроса клиента / после каждого INSERT вот так:

import bottle, sqlite3, random

@bottle.route('/')
def index():
    c = db.cursor()
    c.execute('INSERT INTO test VALUES (?)', (random.randint(0, 10000)))
    c.close()
    db.commit()    # should we do it here? (100ms is too much after *each* request!)
    return 'hello'

def worker():      
    db.commit()    # or here? how to call this worker once every 10 seconds? 

db = sqlite3.connect('test.db')
db.execute("CREATE TABLE IF NOT EXISTS test (a int)")
bottle.run(port=80)

Точнее Я не хочу терять 100 мс после каждого запроса: я оптимизировал свой сервер для очень быстрого обслуживания страниц (10 мс), и было бы стыдно потерять 100 мс из-за фиксации БД.

Конечно, я мог запускать новый поток для вызова worker() только раз в 10 секунд (в худшем случае, если приложение выйдет из строя, будут потеряны только 10 последних секунд БД). Но я читал, что в этом контексте не рекомендуется использовать потоков .

Вопрос: как выполнять фиксацию SQLite DB только раз в несколько секунд (вместо одного раза после каждого INSERT) in a Bottle / Flask контекст веб-сервера?

Ответы [ 2 ]

0 голосов
/ 05 мая 2020

Вот попытка. Он фиксируется только раз в 10 секунд, если в этот период времени есть INSERT.

NB: требуется check_same_thread=False, как описано в ProgrammingError: объекты SQLite, созданные в потоке, могут использоваться только в том же потоке .

import bottle, sqlite3, random, threading, time

@bottle.route('/')
def index():
    global committhread
    c = db.cursor()
    c.execute('INSERT INTO test VALUES (?)', (random.randint(0, 10000),))
    c.close()
    if not committhread: 
        print('Calling commit()...')
        committhread = threading.Thread(target=commit)
        committhread.start()
    else:
        print('A commit is already planned.')
    return 'hello'

def commit():
    global committhread
    print("We'll commit in 10 seconds.")
    time.sleep(10)  # I hope this doesn't block/waste CPU here?
    db.commit()
    print('Committed.')
    committhread = None    

db = sqlite3.connect('test.db', check_same_thread=False)
db.execute("CREATE TABLE IF NOT EXISTS test (a int)")
committhread = None
bottle.run(port=80)

Но, как обсуждается в SQLite и Python: совершайте фиксацию максимум каждые 10 секунд, а не после каждого запроса клиента , есть некоторые другие варианты, которые стоит попробовать перед выполнением предыдущего кода, например:

c.execute('PRAGMA synchronous = OFF')
c.execute('PRAGMA journal_mode = OFF')

Это улучшает производительность как минимум на порядок.

Полезные ресурсы:

0 голосов
/ 04 мая 2020

Вы можете написать обработчик Flask after_request () и зафиксировать там базу данных. Вы даже можете ограничить это действие только в том случае, если с момента последнего коммита прошло определенное время.

Это не заблокирует ваши запросы и не сохранит файл слишком часто.

...