如何有效地在 Flask 或 Bottle 应用程序中提交数据库更改?
How to commit DB changes in a Flask or Bottle app efficiently?
我注意到对于我的小型数据库,SQLite db.commit()
(保存到磁盘)需要 50 毫秒到 100 毫秒。这是正常的 documented here,但是 在客户端的每个请求之后/在每次 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 毫秒将是一种耻辱。
当然我可以启动一个新线程每10秒调用一次worker()
(在最坏的情况下,如果应用程序崩溃,只有最后10数秒的 DB 将丢失)。但我读到不建议使用 threads in this context.
问题:如何在 Bottle/Flask 网络服务器上下文中每隔几秒提交一次 SQLite 数据库(而不是每次 INSERT 后提交一次)?
您可以编写一个 Flask after_request() 处理程序并在那里提交数据库。您甚至可以限制仅在自上次提交后经过一定时间后才执行此操作。
这不会阻止您的请求,也不会过于频繁地保存文件。
这是一次尝试。如果在此时间范围内有 INSERT,它只会每 10 秒提交一次。
注意:它需要 check_same_thread=False
,如 中所述。
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 and Python: commit once every 10 seconds maximum, and not after every client request 中所讨论的,在执行前面的代码之前还有一些其他选项值得尝试,例如:
c.execute('PRAGMA synchronous = OFF')
c.execute('PRAGMA journal_mode = OFF')
这至少提高了一个数量级的性能。
有用的资源:
How to improve SQLite insert performance in Python 3.6?
我注意到对于我的小型数据库,SQLite db.commit()
(保存到磁盘)需要 50 毫秒到 100 毫秒。这是正常的 documented here,但是 在客户端的每个请求之后/在每次 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 毫秒将是一种耻辱。
当然我可以启动一个新线程每10秒调用一次worker()
(在最坏的情况下,如果应用程序崩溃,只有最后10数秒的 DB 将丢失)。但我读到不建议使用 threads in this context.
问题:如何在 Bottle/Flask 网络服务器上下文中每隔几秒提交一次 SQLite 数据库(而不是每次 INSERT 后提交一次)?
您可以编写一个 Flask after_request() 处理程序并在那里提交数据库。您甚至可以限制仅在自上次提交后经过一定时间后才执行此操作。
这不会阻止您的请求,也不会过于频繁地保存文件。
这是一次尝试。如果在此时间范围内有 INSERT,它只会每 10 秒提交一次。
注意:它需要 check_same_thread=False
,如
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 and Python: commit once every 10 seconds maximum, and not after every client request 中所讨论的,在执行前面的代码之前还有一些其他选项值得尝试,例如:
c.execute('PRAGMA synchronous = OFF')
c.execute('PRAGMA journal_mode = OFF')
这至少提高了一个数量级的性能。
有用的资源:
How to improve SQLite insert performance in Python 3.6?