事务与并发(1)
SQL Server中的数据库都是由一或多个数据文件以及一或多个事务日志文件组成的。顾名思意,数据文件主要存储数据库的数据,包括数据库内容结构,数据页,索引页等等。而事务文件主要是用来保存数据库修改记录的.
SQL Server为什么要存在事务文件呢?为什么不把数据立刻写入数据文件呢,而要经过一个事务文件?原因很简单:为了得到更高的效率和性能。数据文件为了适应新的数据可能会扩展,可能会重新分配页,分配新空间等等。而日志都是连续被记录的,所以记录事务日志要快得多。这也就是为什么推荐把物理磁盘单独划分一区用来存储事务日志的原因了,这样可以使磁盘在读写上最大程序的保持自然连续。数据文件的读写有很大的随机性。
那么事务日志到底都存些什么呢?在事务日志中,数据变化被记录在一个连续的日志记录中,且每一个记录都有一个编号,叫做日志序列编号(Log Sequence Number, LSN)。在事务日志中,每一个日志记录都被存储在一个虚拟日志文件中。事务日志可以有任意多个虚拟日志文件,数量的多少取决于数据库引擎,而且每个虚拟日志文件的大小也不是固定的
事务的分类:
分布式事务:是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式的不同节点之上。
本地事务:在单个 EIS 或数据库的本地并且限制在单个进程内的事务。本地事务不涉及多个数据来源。
本地事物处理:
定义一个数组,并将数组中的数据插入到具有两字段的数据表中,其中一字段为自增长,另一个字段中数据为char(15)
建立控制台应用程序。
代码如下:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data
using System.Data.SqlClient
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string [] names=new string []{"1adfa","2sdfadsf","3adsdfaadfadfasdfadsfadf","4adfadf"};
//其中有一项的数据长度大于15,将插入失败
string strcon="链接字符串";//自定义链接字符串
using (SqlConnection con=new SqlConnection(strcon ))//using语句使connection链接不用时自动关闭
{
using (SqlCommand cmd=con.CreateCommand())//using语句使command不用时自动关闭
{
con .Open() ;//执行事物之前必须先打开数据库链接
SqlTransaction trans= con.BeginTransaction();
//创建sqltransaction用于管理事物,由connection对象创建。
cmd.Transaction =trans;
//与command命令结合在一起。
try
{
for (int i=0;i <names .Length ;i ++)
{
string ins="插入语句插入项为(name[i])";
cmd .CommandText =ins ;
cmd.ExecuteNonQuery ();//执行不包含返回结果的sql语句
}
trans.Commit() ;//事物的结束
}
catch (Exception ex)//捕捉错误信息
{
Console .WriteLine(ex.Message );//显示错误信息
trans .Rollback ();//在事物执行的外部设置事物回滚
}
}
}
}
}
}
分布式事物处理:
由于本地事物是由connection对象创建的,当一个事物中存在多个connection对象时就不能用本地事物的transaction对象。需要用到分布式事物及transactionscope
需要实现这样一个业务,程序系统运行有一个数据库,记录程序运行现阶段需要的实时数据,但还有一个数据库是需要记录此程序运行过程中基本数据的变更情况,用于账目的核算,通常为了安全起见两个数据库在两个不同的服务器上,但是当基本数据库更改时,日志数据库也要添加相应的日志记录。
建立控制台应用程序:
引入命名空间system.Transactions
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Transactions;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
string strConn1 = "第一个连接数据库字符串";//定义一个连接字符串。
string strConn2 = "第二个连接数据库字符串";//定义另一个链接字符串。
string olddata;//定义一个变量用于保存就的记录值
string newdata = "需要更新的最新值";//定义一个变量保存新的纪录值。
try
{
using (TransactionScope scope = new TransactionScope())
//定义一个Transactionscope 分布式事物对象。
{
using (SqlConnection Conn1 = new SqlConnection(strConn1))
{
using (SqlCommand Cmd1 = Conn1.CreateCommand())
{
Cmd1.CommandText = "sql语句select...";//用于查出旧记录。
Conn1.Open();
using (SqlDataReader sdr1 = Cmd1.ExecuteReader())
{
while (sdr1.Read())
{
olddata = sdr1.GetString(0);//保存旧记录.
}
}
Cmd1.CommandText = "sql语句update..." + "'" + newdata + "'";//用于更新记录,把newdata更新到数据库
Cmd1.ExecuteNonQuery();
}
}
using (SqlConnection Conn2 = new SqlConnection(strConn2))
{
using (SqlCommand Cmd2 = Conn2.CreateCommand())
{
Cmd2.CommandText = "sql语句insert...+ olddata +newdata +";//插入记录到日志数据库。
Conn2.Open();
Cmd2.ExecuteNonQuery();
}
}
scope.Complete();//事物结束。
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);//显示错误信息。
}
}
}
}