0

我有一个在 Jboss AS 7 容器中运行的 Web 软件,它通过 JPA 将我们的数据保存在 PostgreSQL 9.1 数据库中,它的配置委托给 JTA。

去年它被改编为在 AWS EC2 云上运行。随着用户需求的增长,我们的数据库使用量也在增长。正如预期的那样,我们的数据库服务器在高峰时间变得忙碌,这影响了我们用户的使用体验。

在对 PostgreSQL 进行一些复制研究之后,我们意识到 PGPool2 可能是我们案例的一个很好的复制解决方案:它为 SELECT 查询提供负载平衡,并为 CUD 操作(更新、插入和删除)提供复制。

到目前为止一切都很好,只是它会使软件变慢。事实上,正如 PGPool2 文档中明确指出的那样,如果 SELECT 查询是在显式 BEGIN/END 事务中定义的,它将不会被负载平衡。

对于要进行负载平衡的查询,必须满足以下所有要求:
  - PostgreSQL 版本 7.4 或更高版本
  - 查询不能在显式声明的事务中(即不在 BEGIN ~ END 块中)
  - 这不是 SELECT nextval 或 SELECT setval
  - 这不是 SELECT INTO
  - 这不是 SELECT FOR UPDATE 也不是 FOR SHARE
  - 它以“SELECT”或 COPY TO STDOUT、EXPLAIN、EXPLAIN ANALYZE SELECT 之一开头...
  - ignore_leading_white_space = true 将忽略前导空白。

两个问题:

  • 我如何才能找出在显式事务中运行的 SELECT 查询?
  • _javax.ejb.TransactionAttributeType.NOT_SUPPORTED_ 是否修复了事务范围,允许我的 SELECT 方法将作为“无事务”运行?
4

2 回答 2

1

我如何才能找出在显式事务中运行的 SELECT 查询?

  1. 打开 pgpool2 记录 SQL 和连接:

    将以下语句放入 pgpool.conf(您可以通过 设置cp $prefix/etc/pgpool.conf.sample $prefix/etc/pgpool.conf):

    log_per_node_statement
    log_connections
    
  2. 或者,打开 JPA 的日志跟踪:

    这需要不同的方法,具体取决于您的 JPA 实现(如何查看 JPA 发出的 SQL 查询?使用 Glassfish 3.0.1 和 NetBeans 6.9.1 的 JPA 2.0(记录和跟踪):)

    这将记录 SQL,但不会记录事务启动/提交/回滚。

    此外,将您自己的调试日志代码放入开始和结束事务的方法中,以便您可以看到事务何时开始/提交/回滚。

Does _javax.ejb.TransactionAttributeType.NOT_SUPPORTED_ fix the transaction scopes, granting that my SELECT method will be running as "transaction-free"?

如果您使用的是容器管理事务(注解@TransactionManagement(CONTAINER)@TransactionAttribute),那么NOT_SUPPORTED将暂时解除 JTA 事务与当前线程的关联。然后该方法将在没有事务上下文的情况下运行。

您的后续 JPA 查询将在 JTA 事务之外运行 - 因为 JTA 事务不可供它使用。

  1. 如果您已经使用了Transaction-Scoped EntityManager

    在您的无状态会话 Bean 中,您有一个带EntityManager注释 的@PersistenceContext(type=PersistenceContextType.TRANSACTION),或带注释的@PersistenceContext没有type属性(因为 TRANSACTION是默认值):

    • 那么该 EM 将在 NOT_SUPPORTED 方法中丢失它的持久性上下文,因为 PC 与当前事务相关联,该事务不再可访问
    • 所以你不能在方法中使用这样的 EM(例如运行查询或查找缓存的对象)
    • 因此您必须在 NOT_SUPPORTED 方法中使用额外的应用程序管理的 EM
    • 您必须在没有 JTA 事务处于活动状态的地方(例如在 NOT_SUPPORTED 方法中)从 EntityManagerFactory 创建应用程序管理的 EM,因为应用程序管理的 EM 将在创建期间自动将自身与当前线程的 JTA 事务关联
    • 新的应用管理的 EM 从查询返回的任何对象都将位于与原始 EM 不同的持久性上下文中,因此您需要非常小心地从 PC 中彻底分离这些对象(例如 appMgdEM.clear() 或 appMgdEM.close()或 appMgdEM.detach(someEntity)) 如果您要将它们与原始 EM 修改/合并。
  2. 如果您已经使用了扩展范围的 EntityManager

    在您的有状态会话 Bean 中,您有一个带EntityManager注释的@PersistenceContext(type=PersistenceContextType.EXTENDED).

    • 那么该 EM 仍将在 NOT_SUPPORTED 方法中具有其持久性上下文,因为 PC 与有状态会话 bean 相关联
    • 但是 EM 正在使用一个已经处于“实时”事务中间的连接
    • 所以如果你想在事务之外运行查询,你不能在方法中使用这样的 EM
    • 同样,您必须在 NOT_SUPPORTED 方法中使用附加的应用程序管理的 EM(与上述相同的点适用)。
  3. 例子

    @Stateless
    public class DepartmentManagerBean implements DepartmentManager {
    
        @PersistenceUnit(unitName="EmployeeService")
        EntityManager txScopedEM;
    
        @PersistenceUnit(unitName="EmployeeService")
        EntityManagerFactory emf;
    
        @TranactionAttribute(REQUIRED)
        public void modifyDepartment(int deptId) {
            Department dept = txScopedEM.find(Department.class, deptId);
            dept.setName("New Dept Name");
            List<Employee> empList = getEmpList();
            for(Employee emp : empList) {
                txScopedEM.merge(emp);
                dept.addEmployee(emp);
            }
            dept.setEmployeeCount(empList.size()); 
        }
    
        @TranactionAttribute(NOT_SUPPORTED)
        public void getEmpList() {
            EntityManager appManagedEM = emf.createEntityManager();
            TypedQuery<Employee> empQuery = appManagedEM.createQuery("...", Employee.class);
            List<Employee> empList = empQuery.getResultList();
            // ...
            appManagedEM.clear();
            return empList;
        }
    }
    

    替代/调整方法

    以上对您如何查询以及如何使用结果对象有一些限制。如果您使用无状态会话 bean,它需要“动态”创建一个 EM,并且还需要entityManager.merge()被调用。它可能不适合你。

    一个强有力的替代方法是重新设计您的应用程序,以便您在事务开始之前运行所有查询。然后应该可以使用单个扩展范围的 EntityManager。使用扩展范围 EM 在“NOT_SUPPORTED”方法 1(无事务)中运行查询。然后使用相同的扩展范围 EM 在“REQUIRED”方法 2(带有事务)中运行修改。Transaction-Scoped EntityManaged 不起作用(它会从一开始就尝试进行事务处理,并且在 NOT_SUPPORTED 方法中没有 PC)。

干杯:)

于 2013-06-24T08:37:05.087 回答
1

您可能需要考虑使用 EclipseLink 数据分区在 JPA 中进行分区,

http://java-persistence-performance.blogspot.com/2011/05/data-partitioning-scaling-database.html

于 2013-06-25T12:54:20.723 回答