sql server 大数据量并发插入问题

时间:2023-01-12 08:58:56
开发了一个设备采集监控系统。其中设备数据项有2000多个,保存在sql server2008中。存储方案是针对每个设备创建一个单独的数据库,里面建立多个数据表,把数据项分表存储。在主数据库中提供插入数据存储过程,将传入的数据插入到对应数据库中。
测试中发现,使用ado连接数据库,当有60个线程同时插入60个设备的数据,同时开30个线程进行查询。每个线程都采用独立的连接,查询的时候将数据表设置为nolock。但是还是会出现查询超时已过期问题。想问一下这是由于sql server本身处理不过来导致的,还是ado的问题。
同时单独运行一个线程插入时速度还可以,但60个线程同时插入的时候,单个线程的插入速度就降到原来的1/20。所有线程调用的是同一个存储过程,但是插入数据库不同。

31 个解决方案

#1


当有60个线程同时插入60个设备的数据,同时开30个线程进行查询。每个线程都采用独立的连接,查询的时候将数据表设置为nolock。但是还是会出现查询超时已过期问题。

你看下这个时候数据库资源吃紧不?
插入慢,应该是插入的时候有阻塞的,同表的insert相互之间阻塞。

#2


我觉得这个同时有60个线程插入,而你的每个设备一个数据库,你的数据库都是放到一个实例中的吗

#3


另外,看看系统的等待情况:



--等待类型统计
select top 10
wait_type,            --等待类型
waiting_tasks_count,  --

wait_time_ms,
max_wait_time_ms,
signal_wait_time_ms
from sys.dm_os_wait_stats
order by wait_time_ms desc

#4


60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

#5


如果一个服务器上面建的数据库太多,并且同时插数据,相互间应该会竞争资源的,
不知道有没有那位大牛试过,同一台服务器上面一台数据库和n台数据库,针对单库来说性能会差多少

#6


这个如果你钱不缺的话,多级汇总,避免过于集中插入

#7


1.如果业务允许,建议想办法读写分离。
2.insert能不能想办法做出批量的。就是一次插入多条数据,而不是多次插入每次插入一条数据。
3.这么多线程一起插入,即使内存cpu都能吃得消,估计IO不一定能扛得住

#8


1.用BULK INSERT插入
2..NET使用长连接
3.数据库模式设置为简单模式
4.删除表上的所有索引,只剩主键聚集索引,该主键最好是自增长列
5.服务器阵列是RAID10吗?
6.如果是SQL2014可以用最新的延迟写入机制就是DELAY或使用内存表,IO能提高10倍没问题。

#9


扯,分为多个数据库,应该是在一台SVR上吧,那么就是多个日志文件,你让磁盘磁头乱跳,效率会高么??
ADO默认执行时间是30秒,什么任务执行要超过30秒?
这个超时属客户端行为,非SQL SERVER服务行为
正确设计是正道

#10


为什么要用同一个存储过程呢?
既然是不同的连接就调用该连接的数据库中的那个存储过程了
同一台机器上哪怕用存储过程了但跨数据库了效率还是有影响的

#11


都分数据库了,为什么还要使用同一个存储过程来往不同数据库里写数据?

插入数据的话会在page的级别产生IX锁,这个是不影响查询的。

如果表有聚集索引,会在key级别产生X锁,所谓X锁就是排他锁。
也就是说如果事务T在某个页面或者表上加了X锁,那么别的事务是不能够访问这个页面或者表的。

#12


目前的数据库设计是这样的,有一个主数据库,里面有设备类型描述表,提供一个创建设备的存储过程,在主数据库的设备表中增加一条记录,同时根据设备类型描述创建一个新的设备数据库,里面创建设备数据表。设备数据库就只有数据表,没有存储过程。同时主数据库提供一个存储过程用于插入数据,这个存储过程用于构建sql语句,use到对应的设备数据库,执行插入语句。主数据库和设备数据库都在一个实例里面。
之前这样设计是考虑兼容扩展多种不同的设备,如果添加新的设备,只需要在主数据库的设备类型描述表中增加对应设备数据的描述就行。而且应用程序调用也比较简单,不用考虑数据库结构,只需要调用存储过程接口就行。
之前一直提示查询超时的问题找到了,是由于查询数据的范围包含正在插入的数据,所以导致等待锁超时。

#13


引用 4 楼 jwwyqs 的回复:
60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。

#14


引用 13 楼 imlmy 的回复:
Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

#15


楼上的都牛X的一塌糊涂。。。学习下。。。

#16


引用 14 楼 yupeigu 的回复:
Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。

#17


引用 16 楼 imlmy 的回复:
Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


是的,游标不仅慢,而且可能锁住表的所有数据,造成严重的阻塞问题。

#18


引用 16 楼 imlmy 的回复:
Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end

#19


引用 18 楼 yupeigu 的回复:
Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。

#20


引用 19 楼 imlmy 的回复:
Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

#21


引用 20 楼 yupeigu 的回复:
Quote: 引用 19 楼 imlmy 的回复:

Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

多谢。用临时表row_number代替游标进行遍历,减少了打开游标的阻塞问题。另外使用临时表会对系统表sysprocesses等进行加锁,所以有时候也可能导致阻塞,但是问题不大。目前我是使用了临时表变量。不过测试的时候发现还是存在一些超时已过期的问题。主要是在执行存储过程的时候,这个存储过程实现的功能就是构建插入语句插入到对应数据库中。但是在出现超时问题的时候查询阻塞情况,发现基本没有阻塞操作,或者阻塞的时间很短。出现超时的时候就会连续出现多次。还会有什么问题会导致这个存储过程超时呢。

#22


会不会是设备数据库文件大小增长问题,不过我目前已经设置成1M的增长

#23


引用 21 楼 imlmy 的回复:
Quote: 引用 20 楼 yupeigu 的回复:

Quote: 引用 19 楼 imlmy 的回复:

Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

多谢。用临时表row_number代替游标进行遍历,减少了打开游标的阻塞问题。另外使用临时表会对系统表sysprocesses等进行加锁,所以有时候也可能导致阻塞,但是问题不大。目前我是使用了临时表变量。不过测试的时候发现还是存在一些超时已过期的问题。主要是在执行存储过程的时候,这个存储过程实现的功能就是构建插入语句插入到对应数据库中。但是在出现超时问题的时候查询阻塞情况,发现基本没有阻塞操作,或者阻塞的时间很短。出现超时的时候就会连续出现多次。还会有什么问题会导致这个存储过程超时呢。


其实出现超时,一个是因为阻塞问题,另一个就是你语句的执行速度,由于你的语句大部分是插入操作,那么在插入大量数据的时候,是把数据插入到某一个数据页中的,一个页大小时8K,当这个页中的数据已经很多的时候,比如8K中,已经占用了7k了,那么这个时候再次插入多条记录,那么这个数据页可能就放不下这些记录了,于是sql server需要进行页分裂, 也就是page split,就是新增一个数据页,然后把原来的一页的内容,一般是平均分到这2页中,然后再把数据插入进去。

所以,这个时候可以考虑,先在数据页中,预留一部分空间,这个可以通过重建索引,然后指定fillfactor = 50,这样就可以让页中有一半是空闲空间,于是下次再次插入时,就会减少页的分裂。

随着插入的数据越来越多,这些页的填充程度,也越来越高,可能上升到80,那么这个时候,我们可以再次重建聚集索引,指定这个fillfactor = 50,于是数据再次重新分布。

#24


1. 去除游标的使用。
2. 新装个SSD,把数据库文件安到SSD上,再挂在server上,不需要更改其他任何设置。

#25


引用 23 楼 yupeigu 的回复:
Quote: 引用 21 楼 imlmy 的回复:

Quote: 引用 20 楼 yupeigu 的回复:

Quote: 引用 19 楼 imlmy 的回复:

Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

多谢。用临时表row_number代替游标进行遍历,减少了打开游标的阻塞问题。另外使用临时表会对系统表sysprocesses等进行加锁,所以有时候也可能导致阻塞,但是问题不大。目前我是使用了临时表变量。不过测试的时候发现还是存在一些超时已过期的问题。主要是在执行存储过程的时候,这个存储过程实现的功能就是构建插入语句插入到对应数据库中。但是在出现超时问题的时候查询阻塞情况,发现基本没有阻塞操作,或者阻塞的时间很短。出现超时的时候就会连续出现多次。还会有什么问题会导致这个存储过程超时呢。


其实出现超时,一个是因为阻塞问题,另一个就是你语句的执行速度,由于你的语句大部分是插入操作,那么在插入大量数据的时候,是把数据插入到某一个数据页中的,一个页大小时8K,当这个页中的数据已经很多的时候,比如8K中,已经占用了7k了,那么这个时候再次插入多条记录,那么这个数据页可能就放不下这些记录了,于是sql server需要进行页分裂, 也就是page split,就是新增一个数据页,然后把原来的一页的内容,一般是平均分到这2页中,然后再把数据插入进去。

所以,这个时候可以考虑,先在数据页中,预留一部分空间,这个可以通过重建索引,然后指定fillfactor = 50,这样就可以让页中有一半是空闲空间,于是下次再次插入时,就会减少页的分裂。

随着插入的数据越来越多,这些页的填充程度,也越来越高,可能上升到80,那么这个时候,我们可以再次重建聚集索引,指定这个fillfactor = 50,于是数据再次重新分布。


之前只是查看阻塞情况,忽视了对比效率。今天测试发现使用游标的效率比使用row_number高。使用游标的方式是open cursor for select ....... from table1,table2 where.......
而使用row_number的方式是将table1,table2的查询结果以及row_number存入临时表#temptable。然后通过
select @max = count(*) from #temptable
set @i = 1
while @i < @max
begin
    select ....... from #temptable where rownum=@i
    set @i = @i+1
end


通过这种方式进行遍历,发现效率比游标低很多,后来将#temptable的rownum设为主键,效率提高不少,但是还是比游标遍历低。
是不是我使用的方式有问题。
网上有人提到说row_number主要用产生顺序指令, http://social.msdn.microsoft.com/Forums/en-US/bb30f8ed-8ca3-448e-8f46-f55a7246b865/cursor-rownumber

#26


引用 25 楼 imlmy 的回复:
Quote: 引用 23 楼 yupeigu 的回复:

Quote: 引用 21 楼 imlmy 的回复:

Quote: 引用 20 楼 yupeigu 的回复:

Quote: 引用 19 楼 imlmy 的回复:

Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

多谢。用临时表row_number代替游标进行遍历,减少了打开游标的阻塞问题。另外使用临时表会对系统表sysprocesses等进行加锁,所以有时候也可能导致阻塞,但是问题不大。目前我是使用了临时表变量。不过测试的时候发现还是存在一些超时已过期的问题。主要是在执行存储过程的时候,这个存储过程实现的功能就是构建插入语句插入到对应数据库中。但是在出现超时问题的时候查询阻塞情况,发现基本没有阻塞操作,或者阻塞的时间很短。出现超时的时候就会连续出现多次。还会有什么问题会导致这个存储过程超时呢。


其实出现超时,一个是因为阻塞问题,另一个就是你语句的执行速度,由于你的语句大部分是插入操作,那么在插入大量数据的时候,是把数据插入到某一个数据页中的,一个页大小时8K,当这个页中的数据已经很多的时候,比如8K中,已经占用了7k了,那么这个时候再次插入多条记录,那么这个数据页可能就放不下这些记录了,于是sql server需要进行页分裂, 也就是page split,就是新增一个数据页,然后把原来的一页的内容,一般是平均分到这2页中,然后再把数据插入进去。

所以,这个时候可以考虑,先在数据页中,预留一部分空间,这个可以通过重建索引,然后指定fillfactor = 50,这样就可以让页中有一半是空闲空间,于是下次再次插入时,就会减少页的分裂。

随着插入的数据越来越多,这些页的填充程度,也越来越高,可能上升到80,那么这个时候,我们可以再次重建聚集索引,指定这个fillfactor = 50,于是数据再次重新分布。


之前只是查看阻塞情况,忽视了对比效率。今天测试发现使用游标的效率比使用row_number高。使用游标的方式是open cursor for select ....... from table1,table2 where.......
而使用row_number的方式是将table1,table2的查询结果以及row_number存入临时表#temptable。然后通过
select @max = count(*) from #temptable
set @i = 1
while @i < @max
begin
    select ....... from #temptable where rownum=@i
    set @i = @i+1
end


通过这种方式进行遍历,发现效率比游标低很多,后来将#temptable的rownum设为主键,效率提高不少,但是还是比游标遍历低。
是不是我使用的方式有问题。
网上有人提到说row_number主要用产生顺序指令, http://social.msdn.microsoft.com/Forums/en-US/bb30f8ed-8ca3-448e-8f46-f55a7246b865/cursor-rownumber


看了文章的连接,结合你的实际测试情况,看来用row_number的效率也不是太好。

要不帮你看看,你的存储过程到底是怎么写的,能不能优化吧,我加你关注了,可以发私信。

#27


存储过程中使用游标导致的阻塞主要是由于对tempdb的并发读写,waitresource为2:1:1或2:1:3。
http://support.microsoft.com/kb/328551/zh-cn

#28


多线程并发查询,select语句被自身给阻塞了,这个不太好理解。查询sysprocesses,发现spid和blocked相同,cmd为select,waitresource为ACCESS_METHODS_DATASET_PARENT。waittime达到5763ms。select的表有增加nolock标志,不过这个是针对插入数据的,应该没有关系。

#29


怎么觉得数据库的设计显得怪怪的……
设备数据项有2000多个
如果达到这个级别,硬件设备才是最大的瓶颈吧?
如果就是一台服务器,拖那么多库,还是并发,怎么都快不起来吧……

也许你可以看看这个大神问的。也是搞监控的。
猛戳这个传送门: 海量数据,每天5T,数据至少保存1年,使用啥数据库来解决呢?

#30


引用 28 楼 imlmy 的回复:
多线程并发查询,select语句被自身给阻塞了,这个不太好理解。查询sysprocesses,发现spid和blocked相同,cmd为select,waitresource为ACCESS_METHODS_DATASET_PARENT。waittime达到5763ms。select的表有增加nolock标志,不过这个是针对插入数据的,应该没有关系。


这个是latch,也就是闩锁等待:ACCESS_METHODS_DATASET_PARENT 用于同步在并行操作期间对父数据集进行的子数据集访问。

也就是你的select语句,采用了并行的执行计划,在执行时,需要同步这些并行操作,所以就出现了等待。

比如,一共处理n条数据,给2个线程处理,每个n/2条,但是有一个线程处理的快,一个慢,所以快的那个就得等着慢的那个,所以就出现了这种等待。

#31


楼主的方案有很大问题,60个设备+一个主数据库+60个设备数据库,你服务器再牛,存储过程也只能一个个的将60个设备数据依先后次序写入相应的设备数据库,主数据库是写入的性能瓶颈,现在60个设备以内写入可能感觉不到慢,是因为设备数据库数据量还不大,一但大起来,不用到60个设备就能感觉到慢了,所以我说你的方案设计是有问题的,如何解决:架构改为N设备对N数据库,如体做法是:
修改你的数据采集程序,由程序线程自动通过设备号来判断应该连接哪个数据库(ado连接字符串可以确定),跳过数据写入瓶颈,调用的存储过程自然就是相应的设备数据库的存储过程,这样就能真正的做到并发写入,磁盘阵列IO性能得到发挥.

#1


当有60个线程同时插入60个设备的数据,同时开30个线程进行查询。每个线程都采用独立的连接,查询的时候将数据表设置为nolock。但是还是会出现查询超时已过期问题。

你看下这个时候数据库资源吃紧不?
插入慢,应该是插入的时候有阻塞的,同表的insert相互之间阻塞。

#2


我觉得这个同时有60个线程插入,而你的每个设备一个数据库,你的数据库都是放到一个实例中的吗

#3


另外,看看系统的等待情况:



--等待类型统计
select top 10
wait_type,            --等待类型
waiting_tasks_count,  --

wait_time_ms,
max_wait_time_ms,
signal_wait_time_ms
from sys.dm_os_wait_stats
order by wait_time_ms desc

#4


60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

#5


如果一个服务器上面建的数据库太多,并且同时插数据,相互间应该会竞争资源的,
不知道有没有那位大牛试过,同一台服务器上面一台数据库和n台数据库,针对单库来说性能会差多少

#6


这个如果你钱不缺的话,多级汇总,避免过于集中插入

#7


1.如果业务允许,建议想办法读写分离。
2.insert能不能想办法做出批量的。就是一次插入多条数据,而不是多次插入每次插入一条数据。
3.这么多线程一起插入,即使内存cpu都能吃得消,估计IO不一定能扛得住

#8


1.用BULK INSERT插入
2..NET使用长连接
3.数据库模式设置为简单模式
4.删除表上的所有索引,只剩主键聚集索引,该主键最好是自增长列
5.服务器阵列是RAID10吗?
6.如果是SQL2014可以用最新的延迟写入机制就是DELAY或使用内存表,IO能提高10倍没问题。

#9


扯,分为多个数据库,应该是在一台SVR上吧,那么就是多个日志文件,你让磁盘磁头乱跳,效率会高么??
ADO默认执行时间是30秒,什么任务执行要超过30秒?
这个超时属客户端行为,非SQL SERVER服务行为
正确设计是正道

#10


为什么要用同一个存储过程呢?
既然是不同的连接就调用该连接的数据库中的那个存储过程了
同一台机器上哪怕用存储过程了但跨数据库了效率还是有影响的

#11


都分数据库了,为什么还要使用同一个存储过程来往不同数据库里写数据?

插入数据的话会在page的级别产生IX锁,这个是不影响查询的。

如果表有聚集索引,会在key级别产生X锁,所谓X锁就是排他锁。
也就是说如果事务T在某个页面或者表上加了X锁,那么别的事务是不能够访问这个页面或者表的。

#12


目前的数据库设计是这样的,有一个主数据库,里面有设备类型描述表,提供一个创建设备的存储过程,在主数据库的设备表中增加一条记录,同时根据设备类型描述创建一个新的设备数据库,里面创建设备数据表。设备数据库就只有数据表,没有存储过程。同时主数据库提供一个存储过程用于插入数据,这个存储过程用于构建sql语句,use到对应的设备数据库,执行插入语句。主数据库和设备数据库都在一个实例里面。
之前这样设计是考虑兼容扩展多种不同的设备,如果添加新的设备,只需要在主数据库的设备类型描述表中增加对应设备数据的描述就行。而且应用程序调用也比较简单,不用考虑数据库结构,只需要调用存储过程接口就行。
之前一直提示查询超时的问题找到了,是由于查询数据的范围包含正在插入的数据,所以导致等待锁超时。

#13


引用 4 楼 jwwyqs 的回复:
60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。

#14


引用 13 楼 imlmy 的回复:
Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

#15


楼上的都牛X的一塌糊涂。。。学习下。。。

#16


引用 14 楼 yupeigu 的回复:
Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。

#17


引用 16 楼 imlmy 的回复:
Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


是的,游标不仅慢,而且可能锁住表的所有数据,造成严重的阻塞问题。

#18


引用 16 楼 imlmy 的回复:
Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end

#19


引用 18 楼 yupeigu 的回复:
Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。

#20


引用 19 楼 imlmy 的回复:
Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

#21


引用 20 楼 yupeigu 的回复:
Quote: 引用 19 楼 imlmy 的回复:

Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

多谢。用临时表row_number代替游标进行遍历,减少了打开游标的阻塞问题。另外使用临时表会对系统表sysprocesses等进行加锁,所以有时候也可能导致阻塞,但是问题不大。目前我是使用了临时表变量。不过测试的时候发现还是存在一些超时已过期的问题。主要是在执行存储过程的时候,这个存储过程实现的功能就是构建插入语句插入到对应数据库中。但是在出现超时问题的时候查询阻塞情况,发现基本没有阻塞操作,或者阻塞的时间很短。出现超时的时候就会连续出现多次。还会有什么问题会导致这个存储过程超时呢。

#22


会不会是设备数据库文件大小增长问题,不过我目前已经设置成1M的增长

#23


引用 21 楼 imlmy 的回复:
Quote: 引用 20 楼 yupeigu 的回复:

Quote: 引用 19 楼 imlmy 的回复:

Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

多谢。用临时表row_number代替游标进行遍历,减少了打开游标的阻塞问题。另外使用临时表会对系统表sysprocesses等进行加锁,所以有时候也可能导致阻塞,但是问题不大。目前我是使用了临时表变量。不过测试的时候发现还是存在一些超时已过期的问题。主要是在执行存储过程的时候,这个存储过程实现的功能就是构建插入语句插入到对应数据库中。但是在出现超时问题的时候查询阻塞情况,发现基本没有阻塞操作,或者阻塞的时间很短。出现超时的时候就会连续出现多次。还会有什么问题会导致这个存储过程超时呢。


其实出现超时,一个是因为阻塞问题,另一个就是你语句的执行速度,由于你的语句大部分是插入操作,那么在插入大量数据的时候,是把数据插入到某一个数据页中的,一个页大小时8K,当这个页中的数据已经很多的时候,比如8K中,已经占用了7k了,那么这个时候再次插入多条记录,那么这个数据页可能就放不下这些记录了,于是sql server需要进行页分裂, 也就是page split,就是新增一个数据页,然后把原来的一页的内容,一般是平均分到这2页中,然后再把数据插入进去。

所以,这个时候可以考虑,先在数据页中,预留一部分空间,这个可以通过重建索引,然后指定fillfactor = 50,这样就可以让页中有一半是空闲空间,于是下次再次插入时,就会减少页的分裂。

随着插入的数据越来越多,这些页的填充程度,也越来越高,可能上升到80,那么这个时候,我们可以再次重建聚集索引,指定这个fillfactor = 50,于是数据再次重新分布。

#24


1. 去除游标的使用。
2. 新装个SSD,把数据库文件安到SSD上,再挂在server上,不需要更改其他任何设置。

#25


引用 23 楼 yupeigu 的回复:
Quote: 引用 21 楼 imlmy 的回复:

Quote: 引用 20 楼 yupeigu 的回复:

Quote: 引用 19 楼 imlmy 的回复:

Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

多谢。用临时表row_number代替游标进行遍历,减少了打开游标的阻塞问题。另外使用临时表会对系统表sysprocesses等进行加锁,所以有时候也可能导致阻塞,但是问题不大。目前我是使用了临时表变量。不过测试的时候发现还是存在一些超时已过期的问题。主要是在执行存储过程的时候,这个存储过程实现的功能就是构建插入语句插入到对应数据库中。但是在出现超时问题的时候查询阻塞情况,发现基本没有阻塞操作,或者阻塞的时间很短。出现超时的时候就会连续出现多次。还会有什么问题会导致这个存储过程超时呢。


其实出现超时,一个是因为阻塞问题,另一个就是你语句的执行速度,由于你的语句大部分是插入操作,那么在插入大量数据的时候,是把数据插入到某一个数据页中的,一个页大小时8K,当这个页中的数据已经很多的时候,比如8K中,已经占用了7k了,那么这个时候再次插入多条记录,那么这个数据页可能就放不下这些记录了,于是sql server需要进行页分裂, 也就是page split,就是新增一个数据页,然后把原来的一页的内容,一般是平均分到这2页中,然后再把数据插入进去。

所以,这个时候可以考虑,先在数据页中,预留一部分空间,这个可以通过重建索引,然后指定fillfactor = 50,这样就可以让页中有一半是空闲空间,于是下次再次插入时,就会减少页的分裂。

随着插入的数据越来越多,这些页的填充程度,也越来越高,可能上升到80,那么这个时候,我们可以再次重建聚集索引,指定这个fillfactor = 50,于是数据再次重新分布。


之前只是查看阻塞情况,忽视了对比效率。今天测试发现使用游标的效率比使用row_number高。使用游标的方式是open cursor for select ....... from table1,table2 where.......
而使用row_number的方式是将table1,table2的查询结果以及row_number存入临时表#temptable。然后通过
select @max = count(*) from #temptable
set @i = 1
while @i < @max
begin
    select ....... from #temptable where rownum=@i
    set @i = @i+1
end


通过这种方式进行遍历,发现效率比游标低很多,后来将#temptable的rownum设为主键,效率提高不少,但是还是比游标遍历低。
是不是我使用的方式有问题。
网上有人提到说row_number主要用产生顺序指令, http://social.msdn.microsoft.com/Forums/en-US/bb30f8ed-8ca3-448e-8f46-f55a7246b865/cursor-rownumber

#26


引用 25 楼 imlmy 的回复:
Quote: 引用 23 楼 yupeigu 的回复:

Quote: 引用 21 楼 imlmy 的回复:

Quote: 引用 20 楼 yupeigu 的回复:

Quote: 引用 19 楼 imlmy 的回复:

Quote: 引用 18 楼 yupeigu 的回复:

Quote: 引用 16 楼 imlmy 的回复:

Quote: 引用 14 楼 yupeigu 的回复:

Quote: 引用 13 楼 imlmy 的回复:

Quote: 引用 4 楼 jwwyqs 的回复:

60个设备也就是60个数据库通过一个存储过程同时插入数据啊,太恐怖了,估计内存资源也会很吃紧的 sql server 大数据量并发插入问题

我觉得单个存储过程被多个连接并发调用,只要使用的资源不存在排他锁问题,应该可以。不过cpu却是占用很高,几乎满了。我主要是在想是由于cpu处理不过来导致的,还是由于并发插入存在锁的问题。因为我并发插入的数据是在不同的数据库中,只是调用的存储过程是同一个。


运行下面的语句,看看有没有阻塞的问题:
select *
from sys.sysprocesses
where blocked <> 0

查到了,大部分是打开游标的阻塞。因为存储过程里面有一个操作是用游标遍历一个数据表的数据,构建sql语句。是不是游标会自动加锁。


你可以考虑不用游标,反正是2008,可以考虑用row_number函数,来变量表中的每一行数据,这样至少不会导致严重的阻塞问题,而且速度更快。

可以这样:


declare @i int;  
declare @count int;  
  
set @i = 1  
set @count = (select count(*) from 表);  
         
while @i <= @count  
begin  
   ;with t  
   as  
   (  
   select *,  
          row_number() over(order by @@servername) as rownum  
   from 表  
   )  
      
   select 你的变量1 = (select 字段 from 表 where rownum = @i),
          你的变量2 = ...
   
   --用变量构造动态语句
           
   set @i = @i + 1  
end


多谢大神。不过我看网上通过rownumber来遍历数据表很多采用了临时表,就是把查询数据放到临时表,再遍历临时表。这样是不是为了避免锁的问题。但是将数据插入临时表,再删除临时表又会减低一定的效率。


哦 不会,临时表的开销很小的,如果数据较少,是直接放到内存中,的只有数据比较大的时候,才会放到硬盘上。
另外,临时表还支持创建索引。

多谢。用临时表row_number代替游标进行遍历,减少了打开游标的阻塞问题。另外使用临时表会对系统表sysprocesses等进行加锁,所以有时候也可能导致阻塞,但是问题不大。目前我是使用了临时表变量。不过测试的时候发现还是存在一些超时已过期的问题。主要是在执行存储过程的时候,这个存储过程实现的功能就是构建插入语句插入到对应数据库中。但是在出现超时问题的时候查询阻塞情况,发现基本没有阻塞操作,或者阻塞的时间很短。出现超时的时候就会连续出现多次。还会有什么问题会导致这个存储过程超时呢。


其实出现超时,一个是因为阻塞问题,另一个就是你语句的执行速度,由于你的语句大部分是插入操作,那么在插入大量数据的时候,是把数据插入到某一个数据页中的,一个页大小时8K,当这个页中的数据已经很多的时候,比如8K中,已经占用了7k了,那么这个时候再次插入多条记录,那么这个数据页可能就放不下这些记录了,于是sql server需要进行页分裂, 也就是page split,就是新增一个数据页,然后把原来的一页的内容,一般是平均分到这2页中,然后再把数据插入进去。

所以,这个时候可以考虑,先在数据页中,预留一部分空间,这个可以通过重建索引,然后指定fillfactor = 50,这样就可以让页中有一半是空闲空间,于是下次再次插入时,就会减少页的分裂。

随着插入的数据越来越多,这些页的填充程度,也越来越高,可能上升到80,那么这个时候,我们可以再次重建聚集索引,指定这个fillfactor = 50,于是数据再次重新分布。


之前只是查看阻塞情况,忽视了对比效率。今天测试发现使用游标的效率比使用row_number高。使用游标的方式是open cursor for select ....... from table1,table2 where.......
而使用row_number的方式是将table1,table2的查询结果以及row_number存入临时表#temptable。然后通过
select @max = count(*) from #temptable
set @i = 1
while @i < @max
begin
    select ....... from #temptable where rownum=@i
    set @i = @i+1
end


通过这种方式进行遍历,发现效率比游标低很多,后来将#temptable的rownum设为主键,效率提高不少,但是还是比游标遍历低。
是不是我使用的方式有问题。
网上有人提到说row_number主要用产生顺序指令, http://social.msdn.microsoft.com/Forums/en-US/bb30f8ed-8ca3-448e-8f46-f55a7246b865/cursor-rownumber


看了文章的连接,结合你的实际测试情况,看来用row_number的效率也不是太好。

要不帮你看看,你的存储过程到底是怎么写的,能不能优化吧,我加你关注了,可以发私信。

#27


存储过程中使用游标导致的阻塞主要是由于对tempdb的并发读写,waitresource为2:1:1或2:1:3。
http://support.microsoft.com/kb/328551/zh-cn

#28


多线程并发查询,select语句被自身给阻塞了,这个不太好理解。查询sysprocesses,发现spid和blocked相同,cmd为select,waitresource为ACCESS_METHODS_DATASET_PARENT。waittime达到5763ms。select的表有增加nolock标志,不过这个是针对插入数据的,应该没有关系。

#29


怎么觉得数据库的设计显得怪怪的……
设备数据项有2000多个
如果达到这个级别,硬件设备才是最大的瓶颈吧?
如果就是一台服务器,拖那么多库,还是并发,怎么都快不起来吧……

也许你可以看看这个大神问的。也是搞监控的。
猛戳这个传送门: 海量数据,每天5T,数据至少保存1年,使用啥数据库来解决呢?

#30


引用 28 楼 imlmy 的回复:
多线程并发查询,select语句被自身给阻塞了,这个不太好理解。查询sysprocesses,发现spid和blocked相同,cmd为select,waitresource为ACCESS_METHODS_DATASET_PARENT。waittime达到5763ms。select的表有增加nolock标志,不过这个是针对插入数据的,应该没有关系。


这个是latch,也就是闩锁等待:ACCESS_METHODS_DATASET_PARENT 用于同步在并行操作期间对父数据集进行的子数据集访问。

也就是你的select语句,采用了并行的执行计划,在执行时,需要同步这些并行操作,所以就出现了等待。

比如,一共处理n条数据,给2个线程处理,每个n/2条,但是有一个线程处理的快,一个慢,所以快的那个就得等着慢的那个,所以就出现了这种等待。

#31


楼主的方案有很大问题,60个设备+一个主数据库+60个设备数据库,你服务器再牛,存储过程也只能一个个的将60个设备数据依先后次序写入相应的设备数据库,主数据库是写入的性能瓶颈,现在60个设备以内写入可能感觉不到慢,是因为设备数据库数据量还不大,一但大起来,不用到60个设备就能感觉到慢了,所以我说你的方案设计是有问题的,如何解决:架构改为N设备对N数据库,如体做法是:
修改你的数据采集程序,由程序线程自动通过设备号来判断应该连接哪个数据库(ado连接字符串可以确定),跳过数据写入瓶颈,调用的存储过程自然就是相应的设备数据库的存储过程,这样就能真正的做到并发写入,磁盘阵列IO性能得到发挥.