数据库中数值+1的问题
时间:2015-10-22 12:40:25
收藏:0
阅读:197
在网络上看到一篇文章,有回复认为在应用程序代码中执行+1操作是错误的,理由是并行两次对一个记录加一,结果只会加一次,丢失一次。数值+1应用是众多并发应用之一,如果数值+1这么基本都会有并发问题,所有由程序执行的更新代码都有并发问题,这样任何数值更新代码都不能写在应用层了,显然数据库不是这样工作的。
数据库之所以叫数据库,因为它不仅仅是序列化对象然后替换文件内容的简单文件操作,数据库有机制保护数据一致性,应用程序一般没必要为一致性担心。下面以iBoxDB为例,讲解一下在应用程序代码中并行执行+1会有什么情况,其它专业数据库有大同小异的处理方式。
首先是个简单的例子,创建两个Box,相当于两个事务,然后在每个事务中把数据加1,接着提交。
这个测试是先Box1提交 再Box2提交.
Java C# 数据库引擎 iBoxDB 详细地址
数据库之所以叫数据库,因为它不仅仅是序列化对象然后替换文件内容的简单文件操作,数据库有机制保护数据一致性,应用程序一般没必要为一致性担心。下面以iBoxDB为例,讲解一下在应用程序代码中并行执行+1会有什么情况,其它专业数据库有大同小异的处理方式。
首先是个简单的例子,创建两个Box,相当于两个事务,然后在每个事务中把数据加1,接着提交。
这个测试是先Box1提交 再Box2提交.
Box box1 = auto.cube(); Box box2 = auto.cube(); { Binder binder1 = box1.bind("Book", 1l); Book book1 = binder1.select(Book.class); book1.Total++; binder1.update(book1); } { Binder binder2 = box2.bind("Book", 1l); Book book2 = binder2.select(Book.class); book2.Total++; binder2.update(book2); } System.out.println("Box1 Commit " + box1.commit()); System.out.println("Box2 Commit " + box2.commit());这时可以看到输出
Box1 Commit OK Box2 Commit Conflict结果显示程序在并发执行时如果出现冲突,数据库是会感知并给出提示,并不会丢失一次,如果像不重要的+1,如步行记录,可以直接忽略反馈,或者提示最终用户系统忙,如果是重要的,把代码函数放到一个循环里面,只在反馈等于OK时退出, 下面放到多线程看并发情况, 启动32条线程,进行10万次+1操作,查看最后结果是否一致。
final Database db = auto.getDatabase(); final long total = 100000; ExecutorService es = Executors.newFixedThreadPool(32); for (int i = 0; i < total; i++) { es.submit(() -> { for (;;) { if (Inc1(db) == CommitResult.OK) { return; } } }); }最后可以看到屏幕显示 "100000 == 100000", 结果是一致的。测试显示数据库是有机制保护数据一致,并不受并发影响,这是个基本功能。虽然数据库能处理数据一致性,但在应用程序中就预测到冲突,并减少冲突发生会有更好的效果,在Java中也非常简单,加个 synchronized 关键字到函数中就行了,大量冲突的应用中简单加锁比一致性检测有更好的性能。
public static synchronized CommitResult Inc1(Database db)完整代码:
import java.util.concurrent.*; import iBoxDB.LocalServer.*; public class P1 { public static class Book { public long ID; public long Total; } public static void Main() throws InterruptedException { BoxSystem.DBDebug.DeleteDBFiles(1); DB server = new DB(); server.getConfig().ensureTable(Book.class, "Book", "ID"); DB.AutoBox auto = server.open(); Book b = new Book(); b.ID = 1; b.Total = 0; auto.insert("Book", b); Box box1 = auto.cube(); Box box2 = auto.cube(); { Binder binder1 = box1.bind("Book", 1l); Book book1 = binder1.select(Book.class); book1.Total++; binder1.update(book1); } { Binder binder2 = box2.bind("Book", 1l); Book book2 = binder2.select(Book.class); book2.Total++; binder2.update(book2); } System.out.println("Box1 Commit " + box1.commit()); System.out.println("Box2 Commit " + box2.commit()); System.out.println(auto.selectKey("Book", 1l)); server.close(); BoxSystem.DBDebug.DeleteDBFiles(1); server = new DB(); server.getConfig().ensureTable(Book.class, "Book", "ID"); auto = server.open(); b = new Book(); b.ID = 1; b.Total = 0; auto.insert("Book", b); final Database db = auto.getDatabase(); final long total = 100000; ExecutorService es = Executors.newFixedThreadPool(32); for (int i = 0; i < total; i++) { es.submit(() -> { for (;;) { if (Inc1(db) == CommitResult.OK) { return; } } }); } es.shutdown(); es.awaitTermination(1, TimeUnit.DAYS); System.out.println(total + " == " + auto.selectKey(Book.class, "Book", 1l).Total); } public static CommitResult Inc1(Database db) { try (Box box = db.cube()) { Binder binder = box.bind("Book", 1l); Book book = binder.select(Book.class); book.Total++; binder.update(book); return box.commit(); } } }
Java C# 数据库引擎 iBoxDB 详细地址
原文:http://my.oschina.net/iboxdb/blog/520503
评论(0)