Apache 负载均衡器数据在多个应用程序之间同步
Apache load balancer data syncing across multiple applications
我有一个设置,其中同一应用程序的两个实例部署在负载平衡器后面,并且这两个应用程序都与同一个数据库通信。
我遇到的问题是,当通过其中一个应用程序将数据插入数据库时,完全相同的数据不会 accessible/visible 到另一个应用程序。
这两个应用程序在数据库抽象框架、spring框架等方面具有完全相同的配置。此外,这两个应用程序运行在不同的tomcat服务器上,在不同的物理上服务器。
问题的原因可能是什么?
编辑
Hibernate 配置如下:
编辑 2
这是ehcache.xml
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxElementsInMemory="10000"
eternal="false"
timeToLiveSeconds="86400"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToLiveSeconds="86400"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
导致此问题的可能性有多种。
1) 检查数据库访问权限。还要检查您的应用程序是否能够将数据提交到数据库中。
您需要调试不同的场景
- 从 cluster_1 插入数据并尝试从 cluster_2
读取相同的数据
- 以不同的顺序重复步骤 1
- 检查 SQL 数据库在插入数据(所有者信息、对象版本)时是否有任何差异。
2) 您可以在 (Clustering/Session Replication How-To)
中创建 tomcat 个服务器的集群
您的应用程序中是否有与数据库通信的 ORM?
我想是的,所以这是我的答案:
每个应用程序都通过 ORM(如 Hibernate)拥有自己的缓存机制。如果其中一个更改数据,更改将反映在其缓存中,但另一个对数据库中的更改一无所知。您可以执行以下选项之一:
- 在应用程序中禁用 ORM 缓存(最快的解决方案)
- 用另一个充当模型层+缓存服务器的应用程序包装数据库
- 创建消息系统以在实例之间广播更改事件并在触发时更新缓存。 (虽然不是个好主意)
您很可能遇到缓存复制问题,可能是由于 missing/incorrect 缓存协调配置。
假设
您在 Hibernate 中同时启用了二级缓存和查询缓存,这当然很好,并且您获得了性能提升。问题是您使用了 2 个节点,每个节点都有自己的缓存,或者它们没有正确地使缓存无效。
例如,节点A对所有实体Z执行查询(即产品类别列表:Z1、Z2、...)并缓存结果。随后,节点 B 添加了一个新的产品类别实体 Zn。节点 A 在其自己的缓存失效之前不会使用该新实体;到那时它将使用不包括 Zn 的缓存结果集。
如果禁用查询缓存(即设置 <property name="hibernate.cache.use_query_cache">false</property>
)后问题消失,您可以验证此假设。
解决方案
选项A
优化缓存失效。这在很大程度上取决于您的业务需求,但一些建议是:
- 减少某些查询的缓存 TTL,直到找到最佳点(您可以容忍 missing/extra 值的时间跨度)。您可以为此使用 EhCache 区域。
- 完全禁用某些实体的缓存
示例:
为 ehcache.xml
中的短期(即 5 分钟)查询结果创建区域:
<cache name = "short-lived"
maxElementsInMemory = "500"
eternal = "false"
timeToIdleSeconds = "600"
timeToLiveSeconds = "600"
overflowToDisk = "false"
/>
对于需要经常刷新的特定查询:
Query query = session.createQuery("FROM Z");
query.setCacheable(true);
query.setCacheRegion("short-lived");
有关该主题的更多信息 here。
选项B
这是真正的解决方案。您可以为所有节点提供 shared 缓存。虽然设置和配置肯定更难,但它可以为您提供最佳性能。例如,缓存查询将 运行 每个集群一次,而不是现在每个节点一次。
看看Terracotta。
我有一个设置,其中同一应用程序的两个实例部署在负载平衡器后面,并且这两个应用程序都与同一个数据库通信。
我遇到的问题是,当通过其中一个应用程序将数据插入数据库时,完全相同的数据不会 accessible/visible 到另一个应用程序。
这两个应用程序在数据库抽象框架、spring框架等方面具有完全相同的配置。此外,这两个应用程序运行在不同的tomcat服务器上,在不同的物理上服务器。
问题的原因可能是什么?
编辑
Hibernate 配置如下:
编辑 2
这是ehcache.xml
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxElementsInMemory="10000"
eternal="false"
timeToLiveSeconds="86400"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToLiveSeconds="86400"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
导致此问题的可能性有多种。
1) 检查数据库访问权限。还要检查您的应用程序是否能够将数据提交到数据库中。 您需要调试不同的场景
- 从 cluster_1 插入数据并尝试从 cluster_2 读取相同的数据
- 以不同的顺序重复步骤 1
- 检查 SQL 数据库在插入数据(所有者信息、对象版本)时是否有任何差异。
2) 您可以在 (Clustering/Session Replication How-To)
中创建 tomcat 个服务器的集群您的应用程序中是否有与数据库通信的 ORM? 我想是的,所以这是我的答案: 每个应用程序都通过 ORM(如 Hibernate)拥有自己的缓存机制。如果其中一个更改数据,更改将反映在其缓存中,但另一个对数据库中的更改一无所知。您可以执行以下选项之一:
- 在应用程序中禁用 ORM 缓存(最快的解决方案)
- 用另一个充当模型层+缓存服务器的应用程序包装数据库
- 创建消息系统以在实例之间广播更改事件并在触发时更新缓存。 (虽然不是个好主意)
您很可能遇到缓存复制问题,可能是由于 missing/incorrect 缓存协调配置。
假设
您在 Hibernate 中同时启用了二级缓存和查询缓存,这当然很好,并且您获得了性能提升。问题是您使用了 2 个节点,每个节点都有自己的缓存,或者它们没有正确地使缓存无效。
例如,节点A对所有实体Z执行查询(即产品类别列表:Z1、Z2、...)并缓存结果。随后,节点 B 添加了一个新的产品类别实体 Zn。节点 A 在其自己的缓存失效之前不会使用该新实体;到那时它将使用不包括 Zn 的缓存结果集。
如果禁用查询缓存(即设置 <property name="hibernate.cache.use_query_cache">false</property>
)后问题消失,您可以验证此假设。
解决方案
选项A优化缓存失效。这在很大程度上取决于您的业务需求,但一些建议是:
- 减少某些查询的缓存 TTL,直到找到最佳点(您可以容忍 missing/extra 值的时间跨度)。您可以为此使用 EhCache 区域。
- 完全禁用某些实体的缓存
示例:
为 ehcache.xml
中的短期(即 5 分钟)查询结果创建区域:
<cache name = "short-lived"
maxElementsInMemory = "500"
eternal = "false"
timeToIdleSeconds = "600"
timeToLiveSeconds = "600"
overflowToDisk = "false"
/>
对于需要经常刷新的特定查询:
Query query = session.createQuery("FROM Z");
query.setCacheable(true);
query.setCacheRegion("short-lived");
有关该主题的更多信息 here。
选项B这是真正的解决方案。您可以为所有节点提供 shared 缓存。虽然设置和配置肯定更难,但它可以为您提供最佳性能。例如,缓存查询将 运行 每个集群一次,而不是现在每个节点一次。
看看Terracotta。