数据库中数值+1的问题

原创
2015/10/22 11:03
阅读数 1.7K
看到一篇评论认为在应用程序代码中执行+1操作是错误的,理由是并行两次对一个记录加一,结果只会加一次,丢失一次。数值+1是一个非常简单的数据操作,如果数值+1会有并发问题,所有由程序执行的更新代码都有并发问题,这样任何数据更新代码都不能写在应用层了,显然数据库不是这样工作的。
数据库之所以叫数据库,因为它不仅仅是序列化对象然后替换文件内容的简单文件操作,数据库有机制保护数据一致性,应用程序一般没必要为一致性担心。下面以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 详细地址
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
3 收藏
0
分享
返回顶部
顶部