随着网站浏览人数的增多,页面浏览日志的记录成了数据库TCP/IP连接数飙升的祸首,初步想法就是先把日志记录到内存,累计到一定数量时,一次性入库。故此,就想到了sqlbulkcopy,它可以使数据批量入库。Msdn对它的描述:使用 SqlBulkCopy 类只能向 SQL Server 表写入数据。但是,数据源不限于 SQL Server;可以使用任何数据源,只要数据可加载到DataTable或者IDataReader实例读取数据。
写个控制台程序测试了一下,生成了100000条数据,入库时间大概1秒,不过,只用了两个字段,实际中不会有这么少的字段。
先建一测试表,
CREATE TABLE [dbo].[Person](
[PersonId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
[DateOfBirth] [datetime] NULL,
CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED ([PersonId] ASC)
)
初步构想就是把进来的数据首先放入一个队列中,达到一定数量时把数据放到DataTable中,当DataTable中的数据累计到一定数量时,调用SqlBulkCopy进行入库。
先写一工具类,
public static class Util
{
public static Queue<Person> persons = new Queue<Person>();
private static DataTable dataPerson = null;
private static readonly object obj = new object();
public static DataTable DtPerson
{
get
{
if (dataPerson == null)
{
lock (obj)
{
dataPerson = CreateTable();
}
return dataPerson;
}
return dataPerson;
}
}
public static DataTable CreateTable()
{
DataTable dt = new DataTable();
DataColumn dc = null;
dc = new DataColumn();
dc.ColumnName = "Name";
dt.Columns.Add(dc);
dc = new DataColumn();
dc.ColumnName = "DateOfBirth";
dt.Columns.Add(dc);
return dt;
}
}
实体类的代码,
public class Person
{
public string Name { get; set; }
public DateTime? DateOfBirth { get; set; }
}
下面是Program类实现代码
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 100000; i++)
{
Util.persons.Enqueue(CreatePerson(i));
Console.WriteLine(" enqueue:" + i.ToString());
DataImportToDataTable();
}
Console.ReadLine();
}
public static void DataImportToDataTable()
{
if (Util.persons.Count >= 100)
{
for (int i = 0; i < 100; i++)
{
//出对数据
Person p = Util.persons.Dequeue();
Console.WriteLine("Import Data To DataTable:" + p.Name.ToString());
//写入表中
DataRow dr = Util.DtPerson.NewRow();
dr["Name"] = p.Name;
dr["DateOfBirth"] = p.DateOfBirth;
Util.DtPerson.Rows.Add(dr);
}
Console.WriteLine("Dequeue count:" + Util.persons.Count);
}
InsertTable(Util.DtPerson);
}
public static void InsertTable(DataTable dt)
{
//达到一定数量,进行入库
if (dt.Rows.Count >= 1000)
{
using (SqlConnection cnn = new SqlConnection(ConfigurationManager.ConnectionStrings["Conn"].ConnectionString))
{
//打开连接
cnn.Open();
using (SqlBulkCopy sbc = new SqlBulkCopy(cnn))
{
//Person为要插入数据的表
sbc.DestinationTableName = "Person";
//字段映射。如果dt中的字段数和数据库表中的字段数不一样多 一定要进行映射
sbc.ColumnMappings.Add("Name", "Name");
sbc.ColumnMappings.Add("DateOfBirth", "DateOfBirth");
//每次加载数据的行数。不设置默认为全部,在本例中就可以不设置
sbc.BatchSize = dt.Rows.Count;
Console.WriteLine("sqlbulkcopy begin:" + DateTime.Now);
try
{
sbc.WriteToServer(dt);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("sqlbulkcopy end:" + DateTime.Now);
}
}
lock (Util.DtPerson)
{
Util.DtPerson.Clear();
Console.WriteLine("after clear:" + Util.DtPerson.Rows.Count);
}
}
}
public static Person CreatePerson(int id)
{
Person p = new Person
{
Name="Person"+id.ToString(),
DateOfBirth = new DateTime(1950 + (id % 50), ((id * 3) % 12) + 1, ((id * 7) % 29) + 1)
};
return p;
}
}