我们正在开发供小型企业使用的多租户系统,并且需要为其客户生成发票。基本上,每个租户出于明显的原因都希望他们自己的发票编号序列彼此独立生成。因此,第一租户可以拥有发票1、2、3,而第二租户可以具有相同的发票,因为它们是独立的业务,彼此之间一无所知。
我们将实体框架7用作ORM,将SQL 2014用作数据库。我们需要一种方法来生成这些发票编号,而不会在繁重的并行负载下意外复制同一租户。最初,在EF中,我们只是要获取租户=当前租户ID的发票列的最大值,然后在其中加1,但是在进行压力测试后,它为该租户创建了很多重复的发票编号。触发器对此是否更好?顺序?我不确定下一步该怎么做。
这是描述我们情况的简化表格布局。请注意最后一张表,每个租户如何重新启动发票编号。这就是我们要实现的目标
+----------+-------------+
| TenantID | TenantName |
+----------+-------------+
| 5 | ABC Company |
| 6 | XYZ Corp |
+----------+-------------+
+----------+------------+----------------+
| TenantID | CustomerID | CustomerName |
+----------+------------+----------------+
| 5 | 2 | Alpa Customer |
| 5 | 5 | Beta Customer |
| 6 | 3 | Delta Customer |
| 6 | 4 | Omega Customer |
+----------+------------+----------------+
+----------+------------+-----------+-------------------------------------------------------+
| TenantID | CustomerID | InvoiceID | InvoiceNumber(this one needs to restart per tenant) |
+----------+------------+-----------+-------------------------------------------------------+
| 5 | 2 | 1 | 1 |
| 5 | 2 | 7 | 2 |
| 5 | 5 | 2 | 3 |
| 5 | 5 | 4 | 4 |
| 5 | 5 | 5 | 5 |
| 6 | 3 | 8 | 1 |
| 6 | 4 | 3 | 2 |
| 6 | 4 | 6 | 3 |
+----------+------------+-----------+-------------------------------------------------------+
基于@ERIKE的答案我最终在TenantID和InvoiceNumber周围添加了唯一约束,然后我将发票编号的最大值添加到其中,并尝试添加一个。这被包裹在C#中的do while循环中。每当出现唯一性约束错误时,它将再次重试
bool retryInsert;
do
{
try
{
retryInsert = false;
var invNo = (db.tbl_Invoice
.Where(t => t.TenantID == invoice.TenantID)
.Max(t => t == null ? 0 : t.InvoiceNumber)
) + 1;
invoice.InvoiceNumber = invNo;
db.tbl_Invoice.Add(invoice);
db.SaveChanges();
}
catch (DbUpdateException ex)
{
retryInsert = false;
var sqlexception = ex.InnerException as SqlException;
if (sqlexception != null)
{
if (sqlexception.Errors.OfType<SqlError>()
.Any(se => se.Number == 2627))
{
retryInsert = true;
}
else throw ex;
}
}
} while (retryInsert);
return invoice;
为了支持每秒最高的交易,我相信您最好的答案是在发票表中放置一个唯一索引(租户ID,发票ID)并进行并发插入,然后在发生唯一密钥冲突的情况下重试。
我基于几年前读过的一篇文章介绍有关在这种情况下实现最高吞吐量的文章。
无论如何,我强烈建议不要每个租户有一张桌子。您可能会研究顺序,但是我不确定每个租户有一个顺序是否合理。
每个租户一行的“最后发票”表可以工作:
UPDATE dbo.TenantLastInvoice
SET
LastInvoiceID = LastInvoiceID + 1,
@InvoiceID = InvoiceID
WHERE TenantID = 123;
我不确定100%是否需要锁定提示,可以考虑ROWLOCK
,,UPDATELOCK
如果其他所有操作都失败,READPAST
(如果没有更新,请重试)。
不要指望能够完全避免偶尔跳过ID。一个很难的事实是,要么容忍冲突的机会(完全不可接受),要么容忍在某些极端情况下跳过ID(烦人,但世界末日)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句