关于预算系统存在小部分模块超时的问题,我一直都认为是通过VPN访问服务器速度太慢所致。但是,我在.16测试服务器和我自己本地的部署的服务器进行测试的时候,超时情况仍然存在。查阅网上相关资料修改Web.config之类,延长所谓的数据库服务器会话时间,没有什么效果。
下面我以“科目明细导入”超时为例,讲解预算系统出现数据访问超时的主要原因。
之所以把问题给拿出来,是因为我想得出几个结论。首先,基于目前预算系统的架构,在一个的事务范围之内,对同一个表进行多次操作,如果存在某一个操作语句没有包括在事务当中,则会造成等待超时现象。(之所以这样,是因为事务对某一个表已经进行的锁定,事务之外的动作无法访问该表)
其实,等待超时,也算是死锁的一种表现。回顾预算系统的多用户并发操作所造成的死锁现象,其实就是事务之间对表的交叉访问(这里的事务是不严谨的),造成线程之间的相互等待,都获取不了需要的资源,而造成的死锁现象。并发操作造成的死锁现象,这个问题的解决存在一定的复杂性,尚未有非常好的解决方法,只能从细节去抓。
这种事务机制,危害巨大。在引入数据库缓存依赖的时候,也同样因为这个问题报错
之所以报这种错误,是因为事务嵌套所致。在一个大的事务范围之类,子方法使用了引用了事务,但是在整个大的事务范围之内尚没有执行完成,而在方法中有把事务给Commit和Rollback了,所以事务已经执行完。而我们在另外一个方法中提交,将会导致事务的无法使用。
/// <summary>
/// 2013-01-18添加
/// 封装DataBase的ExecuteDataSet函数,返回DataTable
/// </summary>
/// <param name="sqlCmd">字符型存储过程</param>
/// <param name="pReadUncommitted">是否脏读</param>
/// <param name="Params">参数列表</param>
/// <returns></returns>
protected virtual DataTable ExecuteDataTable(string sqlCmd, bool pReadUncommitted, System.Data.Common.DbParameter[] Params,DbTransaction tran)
{
DataSet ds = null;
DataTable dataTable = new DataTable();
Database db = CreateDatabase();
if (pReadUncommitted)
{
DbConnection conn = CreateDatabase().CreateConnection();
conn.Open();
try
{
DbCommand dbcommand = db.GetSqlStringCommand(sqlCmd);
for (int i = 0; i < Params.Length; i++)
{
db.AddInParameter(dbcommand, Params[i].ParameterName, Params[i].DbType, Params[i].Value);
}
ds = db.ExecuteDataSet(dbcommand, tran);
//tran.Commit(); //在事务范围之内,不应该提交事务,所以注释了
if (ds.Tables.Count > 0)
dataTable = ds.Tables[0];
else
dataTable = null;
if (conn.State == ConnectionState.Open)
conn.Close();
}
catch (Exception ex)
{
tran.Rollback();
if (conn.State == ConnectionState.Open)
conn.Close();
throw ex;
}
}
else
{
DbCommand dbcommand = db.GetSqlStringCommand(sqlCmd);
for (int i = 0; i < Params.Length; i++)
{
db.AddInParameter(dbcommand, Params[i].ParameterName, Params[i].DbType, Params[i].Value);
}
ds = db.ExecuteDataSet(dbcommand);
if (ds.Tables.Count > 0)
dataTable = ds.Tables[0];
else
dataTable = null;
}
return dataTable;
}