一个服务器上有数据库,两台电脑各有一个单机版软件(可联网,可远程连接服务器上的数据库,两个软件不相同),服务器和两台电脑在同一个局域网。
服务器操作系统是windows server2003,数据库是sqlserver2008,其中一台电脑的软件是c#写的winform程序,另一台电脑软件不知何时、不定期地会对服务器上数据库里的一个数据表添加记录。
winform程序启动后开启一个异步线程并做一个定时器,每隔一秒执行一次查询数据的SQL语句(发现添加新记录了会弹出一个窗口),只查询一个固定的表(只是带有where条件和order排序的普通查询),这个表的数据量也不是很大(大概几万条),这两台电脑还会同时做一些其他的sql操作。
这里不使用Socket通信,也不在服务器上增加服务器端程序。这种情况下,这个“每隔一秒查询一次”的操作会对数据库性能有多大影响,会对局域网网络资源有多大影响?
可以用缓存的,缓存依赖数据库 当数据库更新时 缓存自动更新 然后你程序里面只有监控缓存就行了 不需要这样一次次的去查。。。。。
你这个能否说详细点儿?缓存是winform程序实现,还是在winform那台电脑上再写一个windows服务程序实现,还是在数据库所在的服务器实现?数据库更新时,缓存如何自动更新,需要在数据库中写存储过程还是触发器(数据库可以在insert触发器中给局域网中的某台电脑的程序发消息或命令吗)?程序监控缓存是用监听事件吗?小弟是菜鸟,求不吝赐教!
@守护晴天: 不定期地会对服务器上数据库里的一个数据表添加记录!所以虽然使用了缓存,但是实质没有起到神马大的作用!
@Beyond-bit: 恩 这个需要看你更新频率。。
没有特别清晰和明了的回答,可能是因为我掌握的知识还不够多吧。我自己找到了一个解决方案:使用SqlDependency监听SqlServer数据库变更通知。你的回答看起来比较像,就选你的回答做最佳答案。
如果有朋友有这样的需求:对于数据库中某表某字段的变更(如insert或update),需要在程序中发出响应,比如说某个表一旦增加新记录,应用程序就需要弹出对话框。可以不用我在问题中采用的方法(轮询数据库),而在数据库启用监听服务,并在程序中使用SqlDependency类。具体原理和使用方法我还没太搞懂,有需要了解的朋友百度SqlDependency就能搜出文章。
最后感谢参与和关注回答的朋友们!
1s一次?个人认为频率太高了,3-5秒一次就可以了嘛!不过这种解决方案貌似内耗很高,对winform性能有影响,还会对sqlserver DB有影响!当然如果建有连接池一定程度上能缓解性能问题!
我在想你1s,select 一次,那么你的网络要是中断?不就坏了!
多谢回复,我也很担心频率太高,所以才请教一下大家的。3-5秒不敢说,因为本身就是网络远程连接数据库,怕有网络延迟,另外从业务角度考虑,最好是一有新记录,就能立刻响应,所以采用1S一次,内耗问题想靠异步线程,不过实际效果也没测试过,心里也没底,至于sqlserverDB,因为不存在大数据量并发处理,所以想冒险试一下。至于连接池,我还需要额外处理吗?因为我用的ADO.NET,记得里面好像已经有了连接池的概念。网络中断肯定要跳出定时器循环,谢谢你的提醒,十分感谢!
@守护晴天: 还可以给你一种方案,如果是sqlserver那么你可以本地建一个sqlserverDB, 通过网络分发订阅功能,实时监控远程sqlserver,这样数据同步了,你就可以通过本地连接完成了!何必远程?
我是搞java滴,所以你说的ado.net知道,但是具体我想你应该比我懂!
以前的经验是使用sqlserver的扩展存储过程,通过发送UDP广播来通知其他程序进行刷新。2台机器应该是无压力的。
发送UDP广播来通知其他程序如何实现?其实我一开始一直想在数据库的insert触发器中实现这样一个功能:当这个表一插入新记录时,数据库就给服务器的某台电脑的程序发消息或命令。另外说明一下,生产环境中不是两台机器,分为两类机器,A类机器可能会有30、40台,里面运行的程序会不定期地往服务器数据库某个固定表中增加新记录;B类机器就只有1台,里面运行winform程序,当那个表一插入新记录,就要立刻弹出对话框提示操作者。A类机器程序不能修改,B类机器的winform程序是我编写。
如果你有关于sqlserver发送UDP广播来通知其他程序的扩展存储过程的相关教程资料,把网页链接发给我也行,如果能亲自给我讲一下,更感谢!
using System; using System.Runtime.InteropServices; using System.Reflection; using System.Runtime.CompilerServices; using System.Net; using System.Net.Sockets; using System.Text; [assembly: AssemblyTitle("Shining Res Msg Server")] [assembly: AssemblyDescription("Msg Server")] [assembly: AssemblyVersion("1.0.0.1")] [assembly: AssemblyDelaySign(false)] [assembly: AssemblyKeyFile("MsgSendKey.snk")] namespace SQLInterop { public interface IResMsg { string SendMsgToClient(); } [ClassInterface(ClassInterfaceType.AutoDual)] public class RMSMessageSendCOM : IResMsg { public string MsgContext = ""; public string SendMsgToClient() { //初始化一个Scoket实习,采用UDP传输 Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //初始化一个发送广播和指定端口的网络端口实例 IPEndPoint iep = new IPEndPoint(IPAddress.Broadcast, 6888); //设置该scoket实例的发送形式 sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); IPHostEntry ipHost = Dns.Resolve(Dns.GetHostName()); IPAddress ipAddr = ipHost.AddressList[0]; //string msg = Encoding.GetEncoding("GBK").GetString(buffer,0,buffer.Length); byte[] buffer = Encoding.GetEncoding("GBK").GetBytes(MsgContext); sock.SendTo(buffer, iep); sock.Close(); return "Send Msg OK!"; } } }
你的想法是对的。我当时也是使用触发器,在更新数据时调用扩展存储过程,调COM+组件给其他程序发消息。等有空整理一篇文章出来吧,你先自己试试。
@守护晴天:
USE [rmsdb] GO /****** Object: StoredProcedure [dbo].[proc_SendMsgToClient] Script Date: 04/27/2013 08:57:39 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER proc [dbo].[proc_SendMsgToClient](@MsgContext varchar(4000)) /* 使用COM+发送局域网广播消息 @MsgContext varchar(4000) 消息内容 返回值: Send Message OK.表示发送成功 */ as DECLARE @object int DECLARE @hr int DECLARE @property varchar(255) DECLARE @return varchar(255) DECLARE @src varchar(255), @desc varchar(255) EXEC @hr = sp_OACreate 'SQLInterop.RMSMessageSendCOM', @object OUT IF @hr <> 0 BEGIN EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT SELECT hr=convert(varbinary(4),@hr), Source=@src, Description=@desc RETURN END EXEC @hr = sp_OASetProperty @Object, 'MsgContext', @MsgContext IF @hr <> 0 BEGIN EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT SELECT hr=convert(varbinary(4),@hr), Source=@src, Description=@desc RETURN END EXEC @hr = sp_OAMethod @object, 'SendMsgToClient', @return OUT IF @hr <> 0 BEGIN EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT SELECT hr=convert(varbinary(4),@hr), Source=@src, Description=@desc RETURN END PRINT @return EXEC @hr = sp_OADestroy @object IF @hr <> 0 BEGIN EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT SELECT hr=convert(varbinary(4),@hr), Source=@src, Description=@desc RETURN END
@DataCool:
按照你目前的情况,1秒1次没有什么问题。除非你的服务器已经老掉牙了 :)。放心大胆的用吧。
我也遇到了这个问题,需要每秒查询一次数据库,不过winform客户端和数据库在一台服务器上,但我每次查询出120条数据,然后还要开120个线程去处理这120条数据(PS,120个数据是用硬件api处理,拨打出120个手机号码,并发送语音内容)。。最近老报内存溢出错误。。最近考虑是否用Quartz.Net来替换每秒查询的问题;题主有没什么好方案?
这个我后来采用的是SqlDependency,但是实际使用中还是比较耗资源。
关于你说的这个业务需求,我觉得跟我稍微有点区别,因为我的不涉及线程,只是单纯考虑1秒执行一次服务器数据库中的查询,因为我的winform客户端所在电脑有30台左右,每台都有一个winform程序,对于服务器/数据库来说就是1秒有30个数据库查询的请求。
你的内存溢出错误,建议你查一下是轮询数据库造成的还是开多线程造成的,比如把数据库分到另一台机器上,因为我没有过你这个业务场景的处理经验,也没用过Quart.Net,所以没法给你更好建议,十分抱歉!