python-如何在数据库中使龙卷风请求成为原子

我有一个在Tornado异步框架中编写的python应用程序.当HTTP请求进入时,此方法将被调用:

@classmethod
def my_method(cls, my_arg1):

    # Do some Database Transaction #1
    x = get_val_from_db_table1(id=1, 'x')
    y = get_val_from_db_table2(id=7, 'y')
    x += x + (2 * y) 

    # Do some Database Transaction #2
    set_val_in_db_table1(id=1, 'x', x)

    return True

这三个数据库操作是相互关联的.这是一个并发应用程序,因此可以同时发生多个这样的HTTP调用并命中同一数据库.

出于数据完整性的目的,重要的一点是,必须调用此方法中的三个数据库操作,而无需其他进程对其之间的数据库行进行读写操作.

如何确保此方法具有数据库原子性?龙卷风有这个装饰器吗?

最佳答案

同步数据库访问

您尚未说明如何访问数据库.如果(可能是)您在get_val_from_db_table1和朋友(例如,使用pymysql)中具有同步数据库访问权限,而my_method正在阻止(不会将控制返回到IO循环),则您将阻止服务器(这会影响服务器的性能和响应能力) ),但有效地序列化了您的客户端,一次只能执行一个my_method.因此,就数据一致性而言,您无需执行任何操作,但是通常这是一个糟糕的设计.您可以使用@xyres的解决方案在短期内解决这两个问题(代价是要牢记线程安全问题,因为Tornado的大部分功能都是isn’t thread-safe).

异步数据库访问

如果您在get_val_from_db_table1和朋友中拥有异步DB访问权限(例如,使用tornado-mysql),则可以使用tornado.locks.Lock.这是一个示例:

from tornado import web, gen, locks, ioloop


_lock = locks.Lock()

def synchronised(coro):
    async def wrapper(*args, **kwargs):  
        async with _lock:
            return await coro(*args, **kwargs)

    return wrapper


class MainHandler(web.RequestHandler):

    async def get(self):
        result = await self.my_method('foo')
        self.write(result)

    @classmethod
    @synchronised
    async def my_method(cls, arg):
        # db access
        await gen.sleep(0.5)
        return 'data set for {}'.format(arg)


if __name__ == '__main__':
    app = web.Application([('/', MainHandler)])
    app.listen(8080)
    ioloop.IOLoop.current().start()

注意,以上是关于正常的单进程龙卷风应用的说法.如果使用tornado.process.fork_processes,则只能使用multiprocessing.Lock.