提升应用健壮性与可扩展性:事务、Memcache与数据存储设计
立即解锁
发布时间: 2025-08-18 01:09:35 阅读量: 1 订阅数: 4 

### 提升应用健壮性与可扩展性:事务、Memcache与数据存储设计
在开发应用时,如何提升应用的健壮性、速度和可扩展性是至关重要的。本文将深入探讨与数据存储相关的方法,以帮助提高应用的性能,并将这些技术应用到示例应用 Connectr 中。
#### 1. 数据建模与可扩展性
在设计应用的数据模型时,有多种方法可以提高应用的可扩展性和响应能力。下面将详细介绍几种方法及其在 Connectr 应用中的应用。
##### 1.1 减少延迟:读取一致性和数据存储访问截止时间
- **读取一致性**:
- **强一致性**:默认情况下,当数据存储中的实体更新时,后续对该实体的所有读取都会同时看到更新,这称为强一致性。为实现强一致性,每个实体都有一个主存储位置,强一致性读取会等待该位置的机器可用。
- **最终一致性**:App Engine 允许更改默认设置,对给定的数据存储读取使用最终一致性。在最终一致性下,如果主位置暂时不可用,查询可以从次要位置访问数据副本。数据更改会很快传播到次要位置,但最终一致性读取可能在更改合并之前访问次要位置。不过,最终一致性读取平均速度更快,它以一致性换取可用性。在许多情况下,如 Connectr 这样显示“活动流”信息的 Web 应用,这是可以接受的权衡。
- **代码示例**:以下代码展示了如何为查询设置最终读取一致性,以 `server.servlets.FeedUpdateFriendServlet` 为例。
```java
Query q = pm.newQuery("select from " + FeedInfo.class.getName() +
"where urlstring == :keys");
//Use eventual read consistency for this query
q.addExtension("datanucleus.appengine.datastoreReadConsistency",
"EVENTUAL");
```
- **数据存储访问截止时间**:App Engine 还允许更改默认的数据存储访问截止时间。默认情况下,数据存储会自动重试访问,最长约 30 秒。可以将此截止时间设置为更短的时间。如果关注响应延迟,并且愿意使用超时数据的缓存版本或放弃该数据,设置较短的截止时间通常是合适的。
- **代码示例**:以下代码展示了如何为给定的 JDO 查询设置访问超时间隔(以毫秒为单位)。
```java
Query q = pm.newQuery("…");
// Set a Datastore access timeout
q.setTimeoutMillis(10000);
```
##### 1.2 将大数据模型拆分为多个实体以提高访问效率
数据模型中的字段通常可以分为两组:经常或首先需要的主要和/或摘要信息,以及可能不需要或不立即需要的详细信息。在这种情况下,将数据模型拆分为多个实体,并将详细信息实体设置为摘要实体的子实体是很有成效的,例如使用 JDO 拥有的关系。子字段将被延迟获取,因此除非需要,否则不会从数据存储中提取子实体。
- **示例**:在 Connectr 应用中,Friend 模型可以这样看待。最初,只有每个 Friend 的一定数量的摘要信息(Friend 的姓名)通过 RPC 发送到应用的前端。只有当请求查看或编辑特定 Friend 的详细信息时,才需要更多信息。
- **设计**:我们通过将“摘要”信息保留在 Friend 中,并将“详细信息”放在 FriendDetails 对象中,通过 JDO 双向一对一拥有关系将 FriendDetails 设置为 Friend 的子实体。这样,当我们构建应用加载时显示的初始 'FriendSummaries' 列表并通过 RPC 发送时,只需要访问摘要对象。
```mermaid
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
Friend -->|拥有关系| FriendDetails
Friend(("Friend\n姓名")):::process
FriendDetails(("FriendDetails\n电子邮件地址, 关联 URL 列表")):::process
```
- **讨论**:在拆分数据模型时,需要考虑应用将执行的查询以及数据对象的设计如何支持这些查询。对于经常访问和更新的持久类,还需要考虑其字段是否需要索引。索引字段越少,该类对象的写入速度就越快。
##### 1.3 通过创建“索引”和“数据”实体拆分模型
如果识别出仅在执行查询时访问,但在实际检索对象后不需要的字段,可以考虑拆分模型,这在多值属性中很常见。例如,在 Connectr 应用中,`server.domain.FeedIndex` 类的 `friendKeys` 列表用于查找相关的提要对象,但在显示提要内容信息时不需要。
- **设计**:为避免检索不需要的属性,我们将模型拆分为索引实体和数据实体。索引实体仅包含用于查询的多值属性(或其他数据),数据实体包含实际需要使用的信息。数据实体键被定义为索引实体键的父键。
```mermaid
graph LR
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
FeedInfo -->|父键关系| FeedIndex
FeedInfo(("FeedInfo\n提要内容")):::process
FeedIndex(("FeedIndex\nfriendKeys 列表")):::process
```
- **步骤**:
1. 对索引类型进行仅键查询,以获取相关索引实体的键列表。
2. 从索引实体键派生父数据实体键。
3. 使用相关父键列表进行批量获取,以一次性获取所有数据实体。
- **代码示例**:以下代码展示了如何使用这种设计高效检索与给定 Friend 关联的 `FeedInfo` 对象。
```java
… imports…
@SuppressWarnings("serial")
public class FeedUpdateFriendServlet extends HttpServlet{
private static Logger logger =
Logger.getLogger(FeedUpdateFriendServlet.class.getName());
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOExcept
```
0
0
复制全文
相关推荐









