读/写分裂休眠

时间:2014-09-18 11:23:08

标签: java postgresql hibernate jpa replication

我有一个非常繁重的java webapp,它服务于数千个请求/秒,它使用一个主Postgresql数据库,它使用流(异步)复制将自身复制到一个辅助(只读)数据库。

因此,我使用URL将请求从主要服务器分离为辅助服务器(只读服务器),以避免对错误主数据库进行只读调用,因为复制时间很短。

注意我使用一个sessionFactory和一个由spring提供的RoutingDataSource,它根据一个键查找db。我对多租户感兴趣,因为我正在使用支持它的hibernate 4.3.4。

我有两个问题:

  1. 我不认为基于URL的分割是有效的 只移动10%的流量意味着没有多少只读 网址。我应该考虑什么方法?
  2. 可能是,不知何故,在我达到某种程度的URL的基础上 在两个节点之间分配,但我的石英怎么办?     工作(甚至有单独的JVM)?我应该采取何种务实的方法     取?
  3. 我知道我可能在这里得不到完美的答案,因为这确实很广泛,但我只是想要你的意见。

    我的团队中有些人:

    • Spring4
    • Hibernate4
    • Quartz2.2
    • Java7 / Tomcat7

    请关注。提前谢谢。

3 个答案:

答案 0 :(得分:6)

你应该:

  1. 配置为连接到主节点的DataSource
  2. 配置为连接到从属节点的数据源
  3. 路由DataSource位于这两个前面,是SessionFactory使用的路由。
  4. 您可以使用@Transactional(readOnly = true)标志来确保将只读事务路由到从属DataSource。
  5. 主数据源和从属数据源都需要连接池机制,最快的机制绝对是HikariCP。 HikariCP速度非常快,在one test of mine上我获得了100us的平均连接获取时间。
  6. 您需要确保为连接池设置正确的大小,因为这可以产生巨大的差异。为此,我建议使用flexy-pool。您可以在herehere找到更多相关信息。
  7. 您需要非常勤奋,并确保相应地标记所有只读交易。不寻常的是,只有10%的交易是只读的。可能是您拥有这样一个最多的应用程序,或者您正在使用只发出查询语句的写入事务?
  8. 使用SQL logging framework监控所有查询执行情况。查询执行时间越短,锁定获取时间越短,系统容纳的每秒事务就越多。
  9. 对于batch processing,您肯定需要写入最多的事务,但一般来说OLTP和特别是Hibernate不是最适合OLAP的。如果您仍然决定将Hibernate用于您的石英作业,请确保enable JDBC batching并且您应该设置这些Hibernate属性:

    <property name="hibernate.order_updates" value="true"/>
    <property name="hibernate.order_inserts" value="true"/>
    <property name="hibernate.jdbc.batch_versioned_data" value="true"/>
    <property name="hibernate.jdbc.fetch_size" value="25"/>
    <property name="hibernate.jdbc.batch_size" value="25"/>
    
  10. 对于批处理,您可以使用使用不同连接池的单独数据源(并且因为您已经说过您拥有与您已有的不同的JVM)。只需确保所有连接池的总连接大小小于PostgreSQL配置的连接数。

    因此批处理器使用连接到master的单独HikariCPDataSource。每个批处理作业必须使用专用事务,因此请确保使用合理的批处理大小。您希望保持锁定并尽快完成交易。如果批处理器使用并发处理工作程序,请确保关联的连接池大小等于工作者数量,因此他们不会等待其他人释放连接。

答案 1 :(得分:1)

您说您的应用程序URL只有10%只读,因此其他90%至少有某种形式的数据库写入。

10%阅读

您可以考虑使用可能会提高数据库读取性能的CQRS design。它当然可以从辅助数据库中读取,并且可以通过专门为读/视图层设计查询和域模型来提高效率。

您尚未说明10%的请求是否昂贵(例如运行报告)

如果您要遵循CQRS设计,我宁愿使用单独的sessionFactory,因为正在加载/缓存的对象很可能与正在编写的对象不同。

90%写作

至于其他90%的用户,在一些写入逻辑期间,您不希望从辅助数据库读取(在写入主数据库时),因为您不希望涉及可能过时的数据。

其中一些读取可能会查找“静态”数据。如果Hibernate的缓存没有减少读取的数据库命中,我会考虑像Memcached或Redis这样的内存缓存来处理这种类型的数据。 10%-Read和90%-write进程都可以使用相同的缓存。

对于非静态读取(即读取您最近编写的数据),如果Hibernate的大小合适,Hibernate应该将数据保存在其对象缓存中。你能确定你的缓存命中/未命中性能吗?

<强> QUARTZ

如果您确定预定作业不会影响与另一个作​​业相同的数据集,则可以针对不同的数据库运行它们,但如果有疑问,则始终对一个(主)服务器执行批量更新并复制更改出。在逻辑上更正确,而不是引入复制问题。

DB PARTITIONING

如果您每秒1000个请求正在编写大量数据,请查看partitioning您的数据库。你可能会发现你有成长表。分区是在不归档数据的情况下解决此问题的一种方法。

有时您只需要很少或不需要更改应用程序代码。

存档显然是另一种选择

免责声明:任何类似的问题始终都是针对特定应用的。始终尽量使您的架构尽可能简单。

答案 2 :(得分:0)

如果我正确理解,90%的对您的webapp的HTTP请求至少涉及一次写入,并且必须在master数据库上运行。您可以将只读事务定向到副本数据库,但改进只会影响全局数据库操作的10%,甚至那些只读操作也会影响数据库。

这里的通用架构是使用一个好的数据库缓存(Infinispan或Ehcache)。如果你可以提供足够大的缓存,你可以希望数据库读取的很大一部分只访问缓存并成为仅内存操作,或者是只读事务的一部分。高速缓存调整是一项微妙的操作,但IMHO是实现高性能增益所必需的。即使在这种情况下配置有点困难,这些缓存甚至允许分布式前端(如果你想使用Ehcache,你可能需要寻找Terracotta集群。)

目前,数据库复制主要用于保护数据,并且只有当信息系统的高部分只读取数据时才会用作并发性改进机制 - 而且它不是您所描述的。