以前做一个CS系统,有很多终端同时更新数据,例如有50个终端同时更新大量财务数据,每笔数据都是很小的,客户端提交到数据库时,会发现出现数据莫名丢失的现象,例如客户端明明在一个小时内共计更新了6000条记录,结果从客户端只能查询到5992条记录,另外8条记录却不见了,但如果全部客户端全部退出登录,则可以查询到全部记录,真要命!
后来才知道,客户端数据提交时只是保存在数据库服务器的缓存中,并不会立即更新,只会在系统设置的一个固定时间片只(例如30秒)提交给数据库磁盘文件内,而从客户端查询的数据,只是从数据库磁盘文件中取出数据查询,并不会把缓存中的记录算在内,所以,一边在大量更新提交数据库,一边要及时查询了解更新情况,如果不知道还有许多记录在缓存中而去查询,你将得到根本不靠谱的结果,特别是金额计算,根本就是错误的!
我是这样解决的:强行让数据库提交,每次提交后都CHECKPOINT一下。
为何数据库设计者这么不小心,导致数据这样不可靠,难道我的理解有误吗,不知数据库高手还有什么别的办法?
------解决思路----------------------
数据库端不会这样的,其它会话的更新不递交会有锁存在,查询会话会等待的。
很可能你客户端的数据对象存在缓存,代码修改数据后没有马上上传到服务器上,而正常退出前肯定会把最后的更新上传的。
------解决思路----------------------
可以修改系统参数,来缩短 checkpoint的间隔:
EXEC sys.sp_configure N'recovery interval (min)', N'1'
GO
RECONFIGURE WITH OVERRIDE
GO
------解决思路----------------------
数据库端假如最新的数据在缓存里,查询会直接取缓存不会特地载入磁盘的就数据,不会是这个原因。
用的是纯SQL Server数据库吧?没有用外部数据?
------解决思路----------------------
数据库的缓存机制只是从物理层面提高数据库性能的设计,不会影响逻辑层面的结果。一般的数据库操作,增删改查,都要先将页面读取到缓存中。我不知道是否有方法能让程序绕过缓存,直接从文件读取数据,但正常情况下是不会发生的。
出现你所说的情况,有可能是部分客户端的更新操作没有完成造成的。如果更新操作只是一条INSERT或者UPDATE语句,那么就是程序没有把SQL语句发送到数据库;如果更新操作是多条DML语句,并且用BEGIN TRANSACTION开启了事务,那么必须要COMMIT TRANSACTION进行提交。这可以解释所有客户端退出后数据就正确了的现象,应该是程序退出时自动发送了语句或者提交了事务。
至于你说用CHECKPOINT解决了问题,我不认为是这样,这条命令的作用是把缓存中被修改过的页面写入磁盘。如之前所说,程序正常情况下不会绕开缓存读取数据。缓存中的数据是否与磁盘上一致,不会影响查询语句读取到的结果。只要你的更新语句发送到了数据库,并且提交了事务,那么从其它会话中就一定能够读取到更新的数据。而且,频繁地执行CHECKPOINT对数据库性能有很大影响。
------解决思路----------------------
如果你执行了 commit 语句,那肯定会写到数据库中的,当然了,会先写 log ,其他的会话再读取,也会得新最新数据,如果你提交了 6000 行,而只得到 5992 行,应该你的程序有这种机制,点了确定后,并没有发出 commit 动作,只是在本机暂时标记了一下。
------解决思路----------------------
不是哪个组件的BUG,是你们的设计问题,应该是嵌套事务控制上出了漏洞
如果熟悉数据库原理、机制,这个疑问就根本不会出现
------解决思路----------------------
不要用 Requery(),从头开始新建对象查询。
ADO只要不是操作微软自己的数据库,不止一次碰到执行结果和文档说明不一致的情况了。
MainDataMd看起来象所有ADO对象的缓存,不是微软自己的数据库真不应该这样设计,没法保证行为和文档说明一致。
------解决思路----------------------
后来才知道,客户端数据提交时只是保存在数据库服务器的缓存中,并不会立即更新,只会在系统设置的一个固定时间片只(例如30秒)提交给数据库磁盘文件内,而从客户端查询的数据,只是从数据库磁盘文件中取出数据查询,并不会把缓存中的记录算在内,所以,一边在大量更新提交数据库,一边要及时查询了解更新情况,如果不知道还有许多记录在缓存中而去查询,你将得到根本不靠谱的结果,特别是金额计算,根本就是错误的!
不可能是checkpoint的原因,事务提交之后,数据库会保证数据是一致的,最终是写入磁盘,但是究竟什么时候写入磁盘,是数据库自身的机制决定的。但是,不管新提交的数据有没有写入磁盘,读取数据的时候一定是读取提交后的数据,也就是说,读取数据的时候,你也没办法决定是去内存(缓存)中读取还是从磁盘中读取,但是一定会读取到的是提交后的数据,你说的这个情况,我觉得更多的问题出在应用端,而不是数据库端,事务的一致性是关系数据库最最基本的一个特征吧,后来你又说你用的sqlanywhere,没听说过这个数据库,不太清楚
------解决思路----------------------
如果是数据库的话,可能是读写分离,或者查询使用 nolock、readpast 等。否则事务是一致性的,不会出错。
------解决思路----------------------
手动checkpoint不应该出现在用户操作中,这个一般在维护操作中比较常见.数据及时性更多的应该是如何考虑把你的SQL处理得更快.
------解决思路----------------------
sqlanywhere数据库是Sybase公司的,我在10年前用过,非主流数据库平台,powerbuild+sybase11,也经常遇到了一些莫名其妙的问题,后来公司终于08年淘汰了这个系统,转向了Oracle,楼主还在用intraWeb吗,我是看了你的帖子,果断放弃javaEE,在今年2月转向intraWeb,对Delphi程序员来说,确实开发效率很高,我现在的开发环境为DelphiXE7+intraWeb14.0.37+tms5.6.0.2,几乎是最新的,后台数据库为Oracle11G,数据库引擎为dbexpress,目前项目进展顺利,没发现什么问题,在此,对楼主表示感谢。