我正在尝试了解如何安全地增加计数器列,许多用户可能会同时增加计数器列(这是移动应用程序的Web API)。
我已经阅读了SO中针对该问题的战略中的常见问题,但是我似乎无法弄清楚使用简单方法有什么问题:
UPDATE Table SET Counter = Counter + 1
我构建了以下代码示例,以尝试获取不一致的值,并向自己证明,仅使用此简单的update语句不是一种好习惯:
class Program { static void Main(string[] args) { List<Task> tasks = new List<Task>(); for (int i = 0; i < 100; i++) { Task t = Task.Factory.StartNew(() => { WriteToCounter(); }); tasks.Add(t); } Task.WaitAll(tasks.ToArray()); } static void WriteToCounter() { string connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString; using (SqlConnection connection = new SqlConnection(connString)) { connection.Open(); Random rnd = new Random(); for (int i = 1; i <= 100; i++) { int wait = rnd.Next(1, 3); Thread.Sleep(wait); string sql = "UPDATE Table SET Counter = Counter + 1"; SqlCommand command = new SqlCommand(sql, connection); command.ExecuteNonQuery(); } } } }
在示例中,我试图模拟一个场景,其中许多用户同时访问API并更新计数器。代码运行时,计数器 始终为10000 ,这意味着它是一致的。
测试是否正确模拟了我描述的场景? 如果是这样,我怎么能在没有任何特殊的锁定/事务策略的情况下使用update语句并仍然获得一致的结果?
如果您只用过这种简单的方法,那很好。
问题从以下时间开始出现:
Counter
TransactionScope
select
update
output
当然,如果事务隔离级别发生变化,情况可能会大不相同。这实际上是造成错误的合理原因,因为SQL连接池不会重置事务隔离级别,因此,如果您更改了它,则需要确保它不会影响在SqlConnection取出数据库时执行的任何其他SQL 。游泳池。
SqlConnection