繁体   English   中英

如何在并发事务中实现高性能的对象隔离解决方案

[英]How do I achieve a performant object isolation solution in concurrent transactions

我正在创建此方法issueTransfer以实现从一个帐户到另一个帐户的转移。

我的解决方案是:

public void issueTransfer(final int amount, final Account src,
        final Account dst) {
    /*
     * TODO implement issueTransfer using object-based isolation instead of
     * global isolation, based on the reference code provided in
     * BankTransactionsUsingGlobalIsolation. Keep in mind that isolation
     * must be applied to both src and dst.
     */

    isolated(src, dst, () -> {
        if (src.withdraw(amount)) {
            dst.deposit(amount);
        }
    });
}

全局隔离的解决方案是:

public void issueTransfer(final int amount, final Account src,
        final Account dst) {
    isolated(() -> {
        src.performTransfer(amount, dst);
    });
}

全局和对象隔离方法的定义如下:

public static void isolated(Runnable runnable) {
    isolatedManager.acquireAllLocks();

    try {
        runnable.run();
    } finally {
        isolatedManager.releaseAllLocks();
    }
}

public static void isolated(Object obj1, Object obj2, Runnable runnable) {
    Object[] objArr = new Object[]{obj1, obj2};
    isolatedManager.acquireLocksFor(objArr);

    try {
        runnable.run();
    } finally {
        isolatedManager.releaseLocksFor(objArr);
    }
}

辅助方法(获取和释放)是:

public void acquireAllLocks() {
    for(int i = 0; i < this.locks.length; ++i) {
        this.locks[i].lock();
    }
}

public void releaseAllLocks() {
    for(int i = this.locks.length - 1; i >= 0; --i) {
        this.locks[i].unlock();
    }
}

public void acquireLocksFor(Object[] objects) {
    TreeSet<Object> sorted = this.createSortedObjects(objects);
    Iterator var3 = sorted.iterator();

    while(var3.hasNext()) {
        Object obj = var3.next();
        int lockIndex = this.lockIndexFor(obj);
        this.locks[lockIndex].lock();
    }
}

public void releaseLocksFor(Object[] objects) {
    TreeSet<Object> sorted = this.createSortedObjects(objects);
    Iterator var3 = sorted.iterator();

    while(var3.hasNext()) {
        Object obj = var3.next();
        int lockIndex = this.lockIndexFor(obj);
        this.locks[lockIndex].unlock();
    }
}

private int lockIndexFor(Object obj) {
    return Math.abs(obj.hashCode()) % 64;
}

private TreeSet<Object> createSortedObjects(Object[] objects) {
    TreeSet<Object> sorted = new TreeSet(new Comparator<Object>() {
        public int compare(Object o1, Object o2) {
            return IsolatedManager.this.lockIndexFor(o1) - IsolatedManager.this.lockIndexFor(o2);
        }
    });
    Object[] var3 = objects;
    int var4 = objects.length;

    for(int var5 = 0; var5 < var4; ++var5) {
        Object obj = var3[var5];
        sorted.add(obj);
    }

    return sorted;
}

如您所见,我应该按照文档中的要求应用第二种方法(对象隔离)。 测试顺利通过:

public void testObjectIsolation() {
    testDriver(new BankTransactionsUsingGlobalIsolation());
    final long globalTime = testDriver(
            new BankTransactionsUsingGlobalIsolation());

    testDriver(new BankTransactionsUsingObjectIsolation());
    final long objectTime = testDriver(
            new BankTransactionsUsingObjectIsolation());
    final double improvement = (double)globalTime / (double)objectTime;

    final int ncores = getNCores();
    final double expected = (double)ncores * 0.75;
    final String msg = String.format("Expected an improvement of at " +
            "least %fx with object-based isolation, but saw %fx", expected,
            improvement);
    assertTrue(msg, improvement >= expected);
}

但是,用于评估的平台说我没有通过2核或4核的测试。 根据执行时间的不同,有时我会通过其中一项测试(我假设是2核心测试。

从我通过的测试中可以看出,我的对象隔离解决方案比全局隔离要快(每个内核的比率为1:0.75)。 是平台故障还是我的代码可以改进? 我曾尝试使用锁定,解锁和trylock,但是我的解决方案似乎运行得更快,但还不够。

您可以尝试使用Brian Goetz和Tim Pierels的“ Java Concurrency in Practice”一书中的这种方法。

http://jcip.net/listings/InduceLockOrder.java

您可以将Taooka分布式锁管理器与某些SQL数据库一起使用,以编写如下代码:

if taooka.Lock(srcUser.ID, time.Minute) {

    if srcUser.Balance < transferSum {
        taooka.Unlock(srcUser.ID)
        return "Not enough money"
    }

    if taooka.Lock(dstUser.ID, time.Minute) {

        srcSQLServer = GetSQLServerFor(srcUser)
        dstSQLServer = GetSQLServerFor(dstUser)

        transferID = journalSQLServer.do("INSERT INTO money_transfer SET start_time=NOW()")

        srcSQLServer.do("INSERT INTO money_log SET transfer_id=", transferID, ", user_id=", srcUser.ID, ", sum=", -transferSum)
        dstSQLServer.do("INSERT INTO money_log SET transfer_id=", transferID, ", user_id=", dstUser.ID, ", sum=", transferSum)

        journalSQLServer.do("DELETE FROM money_transfer WHERE id=", transferID)

        taooka.Unlock(dstUser.ID)
        taooka.Unlock(srcUser.ID)

        return "Ok"
    }

    taooka.Unlock(srcUser.ID)
    return "wait please..."

}
else {
    return "wait please..."
}

并在后台运行此代码:

for {
    partialTransferID = journalSQLServer.do("SELECT id FROM money_transfer WHERE start_time < DATE_SUB(NOW(), INTERVAL 1 MINUTE) LIMIT 1")

    if partialTransferID > 0 {
        foreach sql in SQLServers {
            sql.do("DELETE FROM money_log WHERE transfer_id=", partialTransferID)
        }
        journalSQLServer.do("DELETE FROM money_transfer WHERE id=", partialTransferID)
        continue
    }

    Sleep(time.Second)
}

但是不幸的是Taooka仍然没有Java绑定。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM