データベースの読み取りと書き込みを含むビジネス ロジックを並列化できるように、マルチスレッド ソリューションを実装しようとしています。
テクノロジースタック: Spring 4.0.2、Hibernate 4.3.8
議論するコードは次のとおりです。
構成
@Configuration
public class PartitionersConfig {
@Bean
public ForkJoinPoolFactoryBean forkJoinPoolFactoryBean() {
final ForkJoinPoolFactoryBean poolFactory = new ForkJoinPoolFactoryBean();
return poolFactory;
}
}
サービス
@Service
@Transactional
public class MyService {
@Autowired
private OtherService otherService;
@Autowired
private ForkJoinPool forkJoinPool;
@Autowired
private MyDao myDao;
public void performPartitionedActionOnIds() {
final ArrayList<UUID> ids = otherService.getIds();
MyIdPartitioner task = new MyIdsPartitioner(ids, myDao, 0, ids.size() - 1);
forkJoinPool.invoke(task);
}
}
リポジトリ/DAO
@Repository
@Transactional(propagation = Propagation.MANDATORY)
public class IdsDao {
public MyData getData(List<UUID> list) {
// ...
}
}
再帰アクション
public class MyIdsPartitioner extends RecursiveAction {
private static final long serialVersionUID = 1L;
private static final int THRESHOLD = 100;
private ArrayList<UUID> ids;
private int fromIndex;
private int toIndex;
private MyDao myDao;
public MyIdsPartitioner(ArrayList<UUID> ids, MyDao myDao, int fromIndex, int toIndex) {
this.ids = ids;
this.fromIndex = fromIndex;
this.toIndex = toIndex;
this.myDao = myDao;
}
@Override
protected void compute() {
if (computationSetIsSamllEnough()) {
computeDirectly();
} else {
int leftToIndex = fromIndex + (toIndex - fromIndex) / 2;
MyIdsPartitioner leftPartitioner = new MyIdsPartitioner(ids, myDao, fromIndex, leftToIndex);
MyIdsPartitioner rightPartitioner = new MyIdsPartitioner(ids, myDao, leftToIndex + 1, toIndex);
invokeAll(leftPartitioner, rightPartitioner);
}
}
private boolean computationSetIsSamllEnough() {
return (toIndex - fromIndex) < THRESHOLD;
}
private void computeDirectly() {
final List<UUID> subList = ids.subList(fromIndex, toIndex);
final MyData myData = myDao.getData(sublist);
modifyTheData(myData);
}
private void modifyTheData(MyData myData) {
// ...
// write to DB
}
}
これを実行すると、次のようになります。
伝播「必須」でマークされたトランザクションの既存のトランザクションが見つかりません
トランザクションは異なるスレッドを介して伝播しないため、これは完全に正常であることを理解しました。したがって、1 つの解決策は、別の同様の質問で提案されているように、すべてのスレッドでトランザクションを手動で作成することです。しかし、これは私にとって十分に満足できるものではなかったので、探し続けました。
Spring のフォーラムで、トピックに関するディスカッションを見つけました。私が非常に興味深いと思うパラグラフ:
「トランザクションコンテキストを手動で別のスレッドに伝播できると想像できますが、実際に試してみるべきではないと思います.トランザクションは、理由で単一のスレッドにバインドされています-基本的な基礎となるリソース-jdbc接続-はスレッドセーフではありません.複数のスレッドでの単一の接続は、基本的な jdbc 要求/応答契約を破り、些細な例で機能するかどうかは少し不思議です."
したがって、最初の疑問が生じます。データベースへの読み取り/書き込みを並列化する価値はありますか?これは DB の一貫性を本当に損なう可能性がありますか?
上記の引用が当てはまらない場合、私は疑問に思いますが、次のことを達成する方法はあります
か?トランザクション管理はSpringに任せますか?