博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.Net 环境下比较各种数据库插入操作的性能
阅读量:6863 次
发布时间:2019-06-26

本文共 6584 字,大约阅读时间需要 21 分钟。

1、简介

再说Windows的异步I/O操作前,先聊聊一些题外话,能帮助我们更好的理解异步I/O操作,常规的Web程序,当用户发起一次请求,当请求通过管道到达客户端的这个过程,会唤起一个线程池线程(后台线程),处理我们的业务代码,即所有的用户请求是通过异步的方式发起的,这个过程,.Net Framework会自动进行,即使我们没有显示的通过代码来实现这个过程.所以这个过程明显是存在性能瓶颈的,假设现在有一个4核服务器,意味这该服务器同时只能处理4个用户请求(超理想情况下,一般不可能),但是这个时候来了10000个用户请求(并发执行)的情况下,那么意味者大量线程会堆积起来,等待着前面的线程执行完毕,同时进行频繁的上下文切换,这个时候你会发现CPU会爆表.

上面只是一个例子,再说一个数据库的例子,现在需要向数据库插入20000条记录,分为三个版本去实现,第一个版本是单个线程同步插入,第二个版本多线程同步插入(Parallel),第三个版本多线程异步插入,来比较下性能和CPU利用零及使用情况.

 

(1)、单线程同步版本

这个场景是只有一个用户请求进来,进行20000次的数据库插入操作,这个版本不会产生线程堆积,因为所有的插入操作都只由主线程完成.

private static readonly string ConnectionStrings;        static Program()        {            //配置数据库连接             ConnectionStrings = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;        }        static void Main(string[] args)        {            var stop = Stopwatch.StartNew();            InstertSync();            stop.Stop();            Console.WriteLine($"同步执行20000次插入操作,耗时:{stop.ElapsedMilliseconds/1000}秒");            Console.ReadKey();        }        private static void InstertSync()        {            var totalCount = 0;            var failCount = 0;            //这里以同步方式执行数据库操作,注这里只有一个线程执行所有的数据库插入操作            for (int i = 0; i <= 20000; i++)            {                var conn = new SqlConnection(ConnectionStrings);                conn.Open();                try                {                    //模拟数据库耗时操作                    var sql = "insert into [dbo].[User]([Amount]) values (@Amount)";                    var command = new SqlCommand(sql, conn);                    command.Parameters.Add(                        new SqlParameter("@Amount", i)                    );                    //这里线程会等待这一段时间,等待数据库返回结果,并继续执行下面的代码                    var result = command.ExecuteNonQuery();                    if (result == 1)                    {                        totalCount+=1;                    }                    else                    {                        failCount+=1;                    }                    Console.WriteLine($"成功插入{totalCount}条记录,插入失败{failCount}条记录");                }                catch (Exception ex)                {                    throw ex;                }                finally                {                    conn.Close();                    conn.Dispose();                }            }        }

再看看数据库的批请求数数据

大概稳定在300次左右每秒

 

 

(2)、多线程同步

这个场景是大多数没有使用Async Await模型的Web应用程序(Parallel代表同时有多个用户请求进来),同时数据库也使用的是同步Api,这个时候以同步的方式发起数据库请求,每个线程会等待不确定的时间,等待数据库返回结果,同时另一个线程开启,也会等待数据库返回结果,这样用户请求一多,就会产生大量的线程堆积,造成大量的内存浪费,而且当数据库开始响应线程时,线程会被唤醒,全部开始执行,这时候CPU又会开始繁忙的执行.

private static readonly string ConnectionStrings;        static Program()        {            //配置数据库连接             ConnectionStrings = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;        }        static void Main(string[] args)        {            InsertAsync();            Console.ReadKey();        }        private static void InsertAsync()        {            var stop = Stopwatch.StartNew();            var totalCount = 0;            var failCount = 0;            var res = Parallel.For(0, 20000, i =>            {                var conn = new SqlConnection(ConnectionStrings);                conn.Open();                try                {                    //模拟数据库耗时操作                    var sql = "insert into [dbo].[User]([Amount]) values (@Amount)";                    var command = new SqlCommand(sql, conn);                    command.Parameters.Add(                        new SqlParameter("@Amount", i)                    );                    var result = command.ExecuteNonQuery();                    if (result == 1)                    {                        Interlocked.Add(ref totalCount, 1);                    }                    else                    {                        Interlocked.Add(ref failCount, 1);                    }                    Console.WriteLine($"成功插入{totalCount}条记录,插入失败{failCount}条记录");                }                catch (Exception ex)                {                    throw ex;                }                finally                {                    conn.Close();                    conn.Dispose();                }            });            if (res.IsCompleted)            {                stop.Stop();                Console.WriteLine($"同步执行20000次插入操作,耗时:{stop.ElapsedMilliseconds / 1000}秒");            }        }

去除Interlocked稍稍快一些.明显可以发现在多线程环境下,使用同步的数据库操作api,效率显著下降.CPU的利用率也很低,同时跑了很多操作线程,但数据库使用同步Api,只能响应一个线程,其余的都需要排队.

 再看看数据库批请求数

只能稳定在130次左右,说明多线程环境下,使用同步数据库操作,阻碍了请求的提交速度.个人理解.

 

(3)、多线程异步

这个场景用户使用基于Async Await模型的Web程序,且使用数据库的异步Api

private static readonly string ConnectionStrings;        static Program()        {            //配置数据库连接             ConnectionStrings = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;        }        static void Main(string[] args)        {            InsertAsync();            Console.ReadKey();        }        private static void InsertAsync()        {            var stop = Stopwatch.StartNew();            var totalCount = 0;            var failCount = 0;            var res = Parallel.For(0, 20000,async i =>            {                var conn = new SqlConnection(ConnectionStrings);                conn.Open();                try                {                    //模拟数据库耗时操作                    var sql = "insert into [dbo].[User]([Amount]) values (@Amount)";                    var command = new SqlCommand(sql, conn);                    command.Parameters.Add(                        new SqlParameter("@Amount", i)                    );                    var result =await command.ExecuteNonQueryAsync();                    if (result == 1)                    {                        Interlocked.Add(ref totalCount, 1);                    }                    else                    {                        Interlocked.Add(ref failCount, 1);                    }                    Console.WriteLine($"成功插入");                }                catch (Exception ex)                {                    throw ex;                }                finally                {                    conn.Close();                    conn.Dispose();                }            });            if (res.IsCompleted)            {                stop.Stop();                Console.WriteLine($"同步执行20000次插入操作,耗时:{stop.ElapsedMilliseconds / 1000}秒");            }        }

可以发现这个模式插入效率非常之高.但是它的插入是无序的,因为Parallel执行线程的顺序是无序的.CPU的利用率也是极高的.

再看看数据库批请求数

直线飙升>1000次的请求提交,说明使用异步Api数据库每秒接收的请求数,远大于同步方式,也是使用异步Api如此之快的原因.

 

转载于:https://www.cnblogs.com/GreenLeaves/p/10604988.html

你可能感兴趣的文章
最长回文子序列
查看>>
我的友情链接
查看>>
Java Timer定时器 使用
查看>>
事务的应用demo1-------采用JDBC硬编码方式模拟"银行转账"。
查看>>
我的友情链接
查看>>
初始 lucene
查看>>
Cent6.5 64位yum安装mysql5.5
查看>>
我的友情链接
查看>>
Gson解析json数据 亲自测试可用
查看>>
我与监控宝之间的点点滴滴
查看>>
delphi 数据库显示的TDBGrid配置
查看>>
jquery选择器 之 获取父级元素、同级元素、子元素
查看>>
找不到命令报错bash:command not found解决方案
查看>>
对51CTO的看法
查看>>
userenv和sys_context函数
查看>>
是否会回到起点.回忆只能是回忆
查看>>
原创数据结构算法Flash动画演示课件-Action Script(AS)脚本实现
查看>>
基于Mysql主从同步的读写分离
查看>>
北漂这两年
查看>>
tomcat 日志分割脚本
查看>>