Python:提高性能-以独立线程写入数据库

Python:提高性能-以独立线程写入数据库,python,mysql,python-multithreading,mysql-python,Python,Mysql,Python Multithreading,Mysql Python,我正在运行一个python应用程序,出于各种原因,我必须在世界某个地方的服务器上运行我的程序,然后在另一个地方运行我的数据库 我通过一个简单的脚本进行了测试,从我的家(位于邻国)到数据库服务器,从数据库中写入和检索一行的时间约为0.035秒(这在我看来是一个不错的速度),而在世界另一端的python服务器执行相同操作时,这一时间为0.16秒。 这是一个问题,因为我正试图保持我的python应用程序尽可能快,所以我想知道是否有一个聪明的方法来做到这一点 当我同步运行我的代码时,我的程序每次必须写入

我正在运行一个python应用程序,出于各种原因,我必须在世界某个地方的服务器上运行我的程序,然后在另一个地方运行我的数据库

我通过一个简单的脚本进行了测试,从我的家(位于邻国)到数据库服务器,从数据库中写入和检索一行的时间约为0.035秒(这在我看来是一个不错的速度),而在世界另一端的python服务器执行相同操作时,这一时间为0.16秒。 这是一个问题,因为我正试图保持我的python应用程序尽可能快,所以我想知道是否有一个聪明的方法来做到这一点

当我同步运行我的代码时,我的程序每次必须写入数据库时都在等待,大约每秒3次,所以时间加起来。是否可以在一个单独的线程中运行与数据库的连接,这样在它尝试向数据库发送数据时不会停止整个程序?或者可以使用asyncio(我没有使用异步代码的经验)来完成吗

我真的很难找到解决这个问题的好办法。
提前,非常感谢

是的,您可以创建一个在后台执行写操作的线程。在您的例子中,使用一个队列似乎是合理的,其中主线程放置要写入的内容,而db线程获取并写入这些内容。队列可以具有最大深度,以便在挂起太多内容时,主线程等待。你也可以做一些不同的事情,比如放弃发生得太快的事情。或者,使用具有同步功能的数据库并写入本地副本。您还可能有机会通过一次提交多个文件来加快写入速度

这是工作线程的草图

import threading
import queue

class SqlWriterThread(threading.Thread):

    def __init__(self, db_connect_info, maxsize=8):
        super().__init__()
        self.db_connect_info = db_connect_info
        self.q = queue.Queue(maxsize)
        # TODO: Can expose q.put directly if you don't need to
        # intercept the call
        # self.put = q.put
        self.start()

    def put(self, statement):
        print(f"DEBUG: Putting\n{statement}")
        self.q.put(statement)

    def run(self):
        db_conn = None
        while True:
            # get all the statements you can, waiting on first
            statements = [self.q.get()]
            try:
                while True:
                    statements.append(self.q.get(), block=False)
            except queue.Empty:
                pass
            try:
                # early exit before connecting if channel is closed.
                if statements[0] is None:
                    return
                if not db_conn:
                    db_conn = do_my_sql_connect()
                try:
                    print("Debug: Executing\n", "--------\n".join(f"{id(s)} {s}" for s in statements))
                    # todo: need to detect closed connection, then reconnect and resart loop
                    cursor = db_conn.cursor()
                    for statement in statements:
                        if statement is None:
                            return
                        cursor.execute(*statement)
                finally:
                    cursor.commit()       
            finally:
                for _ in statements:
                    self.q.task_done()

sql_writer = SqlWriterThread(('user', 'host', 'credentials'))
sql_writer.put(('execute some stuff',))

是的,您可以创建一个在后台执行写操作的线程。在您的例子中,使用一个队列似乎是合理的,其中主线程放置要写入的内容,而db线程获取并写入这些内容。队列可以具有最大深度,以便在挂起太多内容时,主线程等待。你也可以做一些不同的事情,比如放弃发生得太快的事情。或者,使用具有同步功能的数据库并写入本地副本。您还可能有机会通过一次提交多个文件来加快写入速度

这是工作线程的草图

import threading
import queue

class SqlWriterThread(threading.Thread):

    def __init__(self, db_connect_info, maxsize=8):
        super().__init__()
        self.db_connect_info = db_connect_info
        self.q = queue.Queue(maxsize)
        # TODO: Can expose q.put directly if you don't need to
        # intercept the call
        # self.put = q.put
        self.start()

    def put(self, statement):
        print(f"DEBUG: Putting\n{statement}")
        self.q.put(statement)

    def run(self):
        db_conn = None
        while True:
            # get all the statements you can, waiting on first
            statements = [self.q.get()]
            try:
                while True:
                    statements.append(self.q.get(), block=False)
            except queue.Empty:
                pass
            try:
                # early exit before connecting if channel is closed.
                if statements[0] is None:
                    return
                if not db_conn:
                    db_conn = do_my_sql_connect()
                try:
                    print("Debug: Executing\n", "--------\n".join(f"{id(s)} {s}" for s in statements))
                    # todo: need to detect closed connection, then reconnect and resart loop
                    cursor = db_conn.cursor()
                    for statement in statements:
                        if statement is None:
                            return
                        cursor.execute(*statement)
                finally:
                    cursor.commit()       
            finally:
                for _ in statements:
                    self.q.task_done()

sql_writer = SqlWriterThread(('user', 'host', 'credentials'))
sql_writer.put(('execute some stuff',))

所以我尝试在我的代码中实现它,但我遇到了一个奇怪的问题,我对你的代码有一个问题。在上面的示例中,您有一个sql_q变量,但从未分配或引用它。这是从哪里来的?或者它本应该是self.q而不是self.q,这是一个错误吗?我尝试使用self.q实现代码,但似乎每次调用sql_writer.put并向que添加新语句时,所有语句都是彼此的副本,虽然我确实输入了不同的语句,但我只是做了一些测试,发现这是由于我的程序在2秒内加入了队列。如果我使用time.sleep(2),队列项目将保持真实状态,但是如果我在2秒钟的间隔内放置,队列中的所有项目都将成为彼此的副本。我怎样才能解决这个问题,因为等待2秒就不能解决拥有这个队列系统的全部问题。是的,应该是
self.q
。我最初将其作为函数编写,然后将其更改为类,并在过程中注入了bug。就像一对一的报价。看第二个问题。我找不到重复放置的原因,所以在混合中添加了一些调试。很好,我已经解决了我的问题,所以我多次感谢您提供了一个很好的示例。我以前从来没有听说过python队列,但它似乎是个天才,将来我肯定会大量使用它。有一个愉快的周末!所以我尝试在我的代码中实现它,但我遇到了一个奇怪的问题,我对你的代码有一个问题。在上面的示例中,您有一个sql_q变量,但从未分配或引用它。这是从哪里来的?或者它本应该是self.q而不是self.q,这是一个错误吗?我尝试使用self.q实现代码,但似乎每次调用sql_writer.put并向que添加新语句时,所有语句都是彼此的副本,虽然我确实输入了不同的语句,但我只是做了一些测试,发现这是由于我的程序在2秒内加入了队列。如果我使用time.sleep(2),队列项目将保持真实状态,但是如果我在2秒钟的间隔内放置,队列中的所有项目都将成为彼此的副本。我怎样才能解决这个问题,因为等待2秒就不能解决拥有这个队列系统的全部问题。是的,应该是
self.q
。我最初将其作为函数编写,然后将其更改为类,并在过程中注入了bug。就像一对一的报价。看第二个问题。我找不到重复放置的原因,所以在混合中添加了一些调试。很好,我已经解决了我的问题,所以我多次感谢您提供了一个很好的示例。我以前从来没有听说过python队列,但它似乎是个天才,将来我肯定会大量使用它。有一个愉快的周末!