C#从mysql数据库获取锁定代码的关键部分

时间:2018-01-04 17:16:05

标签: c# mysql multithreading transactions

我正在使用Asp.NET和MySql数据库。

申请流程:

  1. 在Woocommerce中创建的订单并发送到我的应用
  2. 我的应用程序将woo订单对象转换为要添加到外部ERP系统的对象
  3. 在外部ERP系统中创建的订单,我们使用该订单信息更新本地数据库,以了解创建是否成功
  4. 我有一个代码的关键部分,可以在外部ERP资源上创建订单。对同一订单的多个请求可以同时运行,因为它们是从我无法控制的外部应用程序(woocommerce)创建的。因此,代码的关键部分必须只允许其中一个请求一次输入,否则可以创建重复的订单。

    重要说明:应用程序托管在Elastic Beanstalk上,后者具有负载均衡器,因此应用程序可以跨多个服务器进行扩展,这使得标准C#锁定对象无法正常工作。

    我想创建一个可以在多个服务器/应用程序实例之间共享的锁,这样只有一个服务器可以获取锁并一次输入代码的关键部分。我无法使用MySql和C#找到如何做到这一点,所以如果有人有一个很棒的例子。

    下面是我如何进行单实例线程安全锁定。如何在多个实例中将其转换为安全:

    SalesOrder newOrder = new SalesOrder();         //the external order object
    var databaseOrder = new SalesOrderEntity();     //local MySql database object
    
    /*
     * Make this section thread safe so multiple threads can't try to create
     * orders at the same time 
     */
    lock (orderLock)
    {
        //check if the order is already locked or created.
        //wooOrder comes from external order creation application (WooCommerce)
        databaseOrder = GetSalesOrderMySqlDatabase(wooOrder.id.ToString(), originStore);
    
        if (databaseOrder.OrderNbr != null)
        {
            //the order is already created externally because it has an order number
            return 1;
        }
        if (databaseOrder.Locked)
        {
            //the order is currently locked and being created
            return 2;
        }
    
        //the order is not locked so lock it before we attempt to create externally
        databaseOrder.Locked = true;
        UpdateSalesOrderDatabase(databaseOrder);
    
        //Create a sales order in external system with the specified values
        newOrder = (SalesOrder) client.Put(orderToBeCreated);
    
    
        //Update the order in our own database so we know it's created in external ERP system
        UpdateExternalSalesOrderToDatabase(newOrder);
    
    }
    

    如果需要进一步的详细信息,请告诉我。

4 个答案:

答案 0 :(得分:1)

您需要使用MySQL DBMS事务锁。

您不能直接显示您的DBMS查询,因此我无法猜测它们。你还需要这类查询。

 START TRANSACTION;
 SELECT col, col, col FROM wooTable WHERE id = <<<wooOrderId>>> FOR UPDATE;

 /* do whatever you need to do */

 COMMIT;

如果同一<<<wooOrderID>>>行被另一个ELB服务器上运行的另一个Web服务器实例的同一序列查询命中,那个SELECT ... FOR UPDATE查询将等待,直到第一个提交。

请注意,服务器内多线程和关键部分锁定既不必要也不足以解决您的问题。为什么呢?

这是不必要的,因为数据库连接首先不是线程安全的。

这是不够的,因为您需要一个数据库级事务,而不是进程级锁。

答案 1 :(得分:1)

您可以使用MySQL命名的咨询锁定功能GET_LOCK(name)

这在事务范围之外工作,因此您在释放锁之前更改了提交或回滚数据库。在此处阅读更多相关信息:https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock

您还可以使用其他一些专门的锁定服务。例如,您可以使用共享消息队列服务执行此操作。见https://softwareengineering.stackexchange.com/questions/127065/looking-for-a-distributed-locking-pattern

答案 2 :(得分:1)

您应该使用Transaction,这是数据库中的一个工作单元。它使你的代码不仅是原子的,而且它也是线程安全的。以下是mysql official website

采用的示例

您需要的代码:

START TRANSACTION


COMMIT // if your transaction worked
ROLLBACK // in case of failure 

另外,我强烈建议您阅读有关事务隔离级别的信息: Mysql Transaction Isolation Levels

如果你按照我上面的说法使用Transaction,那么你就可以锁定你的桌面,这会阻止其他查询,例如选择查询,执行,他们将等待事务结束。它被称为“服务器阻塞”,以防止只是密集地读取链接。

答案 3 :(得分:0)

我认为使用数据库没有任何好的解决方案,除非一切都可以在存储过程中整齐地完成,如另一个建议的答案。对于其他任何事情,我会看一个带有多个编写器和一个读取器的消息队列解决方案。