最近进行了一次系统性能测试,处理并发操作时出现了许多问题,下面就这些问题及解决途径进行一下记录。
以下问题解决大部分来自于百度、CSDN、博客园等文章及社区,发现真的是好强大。
并发500条,测试。
- 连接数与连接池问题
最开始,并发失败loadrunner报错,服务器连接失败。
猜测可能是数据库连接数出了问题,打开SQL Server,查看数据库最大连接数已经为0,即默认最大。
看了一下IIS最大并发连接数,也是最大了。
百度一下,发现有可能是程序的连接数做了限制,经过查找,了解了SQL的连接字符串的相关属性。
发现应该是这个Max Pool Size参数导致的,默认100个,所以第101个就连接不上数据库了,测了一下
public static void TestSQLServerConnectionCount()
{
try
{
int maxCount = 200;
string connectionString =
"Data Source=localhost;Initial Catalog=EveryDayTest;User Id=sa;Password=sa123;Max Pool Size=100";
for (int i = 1; i < maxCount; i++)
{
//connectionString += " ";
var db = new SqlConnection(connectionString);
db.Open();
Console.WriteLine("已创建连接对象" + i);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
果然是这么回事,而且此时只能连接100次,而连接字符串变化的情况,即每次的连接字符串都不一样,哪怕多一个空格,最后发现是可以“打破最大连接数的限制”的(是不是因为每种连接字符串都会新建一个连接池?)
先看看MSDN是怎么说的吧
括号的答案是
正确的。Max Pool Size是指连接池的最大连接数,而修改了连接字符串会建立不同的池。
- 死锁
解决了这个问题再次测试,一个更大的问题来了——死锁!!!
通过使用
SQL Server Profiler监测,查到了这个个死锁。
简单说说这个插件的使用:
进入这个页面,选择一个空白模板,在事件选择中,点击Locks节点,勾选Deadlock graph、Lock:Deadlock、Lock:Deadlock Chain
点击列筛选器,选中DataBaseName填写类似于“你要操作的数据库名”进行过滤
然后运行即可。
还是先了解一下锁的相关机制吧。
锁的种类:共享锁(Shared Lock)、更新锁(Update Lock)、排他锁(独占锁、Exclusivel Lock)、意向锁(Intent Lock)、计划锁(Schema Locks)
这篇文章讲解的很是细致,可以参考:
回头我们来看
,
上面这个死锁错误是进程59(select)请求非聚集索引下的S锁被进程54持有(update,排他锁),同时54请求非聚集索引下的S锁也被59持有(update,排他锁)。
说白了,都准备执行查询,没想到对方都在更新,即排他。
问题大致找到了,由于本系统原因,调整代码相关SQL执行顺序、部分问题在SQL中使用乐观并发的模式,即加入NoLock,问题基本解决了,死锁问题也OK了。不过找的过程还是比较困难的,用log记录跟踪死锁发生位置,比较费时。(求大神推荐更简单的方法)
为避免或最小化死锁,参考以下建议:
1.确保事务范围足够小
2.使用较低隔离级别的事务
3.对于可能的查询操作,使用NoLock查询提示
4.规范化数据库设计
5.在需要的列上创建索引,这样不需要经常进行整表扫描
6.控制数据库对象访问的顺序是相同的顺序(顺序真的很重要!!!)
- 最后的问题
万万没想到,死锁的问题都解决了,loadrunner仍然报错,测试100个,总有几个或1个不通过,错误为服务器连接错误,此时说明并不是程序的问题。连接数的问题之前都已经搞定了,为什么?
百度了一下,说可能是负载生成器的性能太好,发数据包特别快,服务器也响应特别快,从而导致负载生成器的机器的端口在没有timeout之前就全部占满了。
在负载生成器的注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters里,有如下两个键值:
TcpTimedWaitDelay
MaxUserPort
1,这里的TcpTimedWaitDelay默认值应该中是30s,所以这里,把这个值调小为5s(按需要调整)。
2,也可以把MaxUserPort调大(如果这个值不是最大值的话)
试了一下,不行啊!
测了50个竟然通过了,与测试人员反复沟通,最后发现,妹的,测试脚本有点小问题,删掉后全通过了。
yes!终于搞定了,真是复杂呀