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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#从mysql数据库获取锁定代码的关键部分相关的知识,希望对你有一定的参考价值。

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

申请流程:

  1. 在Woocommerce中创建的订单并发送到我的应用程序
  2. 我的应用程序将woo订单对象转换为要添加到外部ERP系统的对象
  3. 在外部ERP系统中创建的订单,我们使用该订单信息更新本地数据库,以了解创建是否成功

我有一个代码的关键部分,可以在外部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);

}

如果需要进一步的细节,请告诉我。

答案

您需要为此使用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查询将等到第一个查询执行提交。

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

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

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

另一答案

您可以使用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

另一答案

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

您需要的代码:

START TRANSACTION


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

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

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

另一答案

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

以上是关于C#从mysql数据库获取锁定代码的关键部分的主要内容,如果未能解决你的问题,请参考以下文章

C系统v信号量不锁定关键区域

代码片--实现一个简单的模版方法设计模式(获取一段程序运行的时间)

python从简介中获取行业分类

数据库分表之Mybatis+Mysql实践(含部分关键代码)

C#“lock”关键字:为啥语法需要对象?

如何获取基堆栈指针的地址