欢迎光临
个人技术文档整理

.Net 高并发数据的重复写入问题

高并发数据的重复写入问题

一、情景

.Net Core部署为Web时,当多个线程(多个用户),同时调用后台接口,进行插入操作的话会导致数据库中插入重复的字段!下面把我的解决方法记录一下。

二、实例重现

  接口Test 是很简单的功能:对表Demo进行 先判断是否存在,然后插入数据。

 /// <summary>
        /// 高并发 会出现重复插入 的问题
        /// </summary>
        /// <returns></returns>
        [HttpGet, HttpPost]
        public IActionResult Test()
        {
            var name = Request.Query["name"].ToString();

            if (string.IsNullOrWhiteSpace(name))
            {
                return Json("name为空" + DateTime.Now);
            }
            using (TestDbContext db = new TestDbContext())
            {
                var entity = db.Demo.Where(u => u.Name == name).FirstOrDefault();
                if (entity == null)
                {
                    var model = new DemoModel();
                    model.Name = name;
                    model.CreateTime = DateTime.Now;
                    db.Demo.Add(model);
                    db.SaveChanges();
                    return Json("OK-录入成功");
                }
                else
                {
                    return Json("Exist--已经存在");
                }
            }


        }

当开启多个线程 同于并发的时候
(这里使用的 JMeter来测试的 :20线程 循环200次 重复数据插入)
结果如下:

 

三、问题解决方案 (锁)

既然问题是并发请求导致的,最简便的解决方法就是:通过lock()来处理。
互斥锁lock作用:将会锁住代码块的内容,并阻止其他线程进入该代码块,直到该代码块运行完成,释放该锁。

最终实现代码如下:

  [HttpGet, HttpPost]
        public IActionResult Test2()
        {
            var name = Request.Query["name"].ToString();

            if (string.IsNullOrWhiteSpace(name))
            {
                return Json("name为空" + DateTime.Now);
            }

            var str = DemoHelper.Add(name);
            return Json(str);
           

        }
public class DemoHelper
    {
        private static object syncObject = new object();
        public static string Add(string name)
        {
            lock (syncObject)
            {
                using (TestDbContext db = new TestDbContext())
                {
                    var entity = db.Demo.Where(u => u.Name == name).FirstOrDefault(); 
                    if (entity == null)
                    {
                        var model = new DemoModel();
                        model.Name = name;
                        model.CreateTime = DateTime.Now;
                        db.Demo.Add(model);
                        db.SaveChanges();

                        return "OK-录入成功==" + name;
                    }
                    else
                    {
                        return "Exist--已经存在==" + name;
                    }
                }
            }
        } 
    }

四、总结

针对这类请求并发问题,最简单的就是通过代码锁的方式,将特定功能的并发请求执行转化为队列请求执行,从而避免了问题的发生。
当然,处理并发还有其它方式,如通过数据库锁的方式,再如分布式部署情况下,我们用代码锁的方式也会失效。
加锁本身就有性能上的损耗,如果非必须 一般不加锁。

 

赞(1)