数据库之所以叫数据库,因为它不仅仅是序列化对象然后替换文件内容的简单文件操作,数据库有机制保护数据一致性,应用程序一般没必要为一致性担心。下面以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 详细地址