当前位置:  开发笔记 > 前端 > 正文

数据库设计 - 谷歌应用引擎

如何解决《数据库设计-谷歌应用引擎》经验,为你挑选了2个好方法。

我正在使用谷歌应用引擎并使用低leval java api访问Big Table.我正在构建一个包含4层的SAAS应用程序:

客户端Web浏览器

RESTful资源层

业务层

数据访问层

我正在构建一个应用程序来帮助管理我的移动汽车细节公司(以及其他类似的公司).我必须代表这四个独立的概念,但不确定我目前的计划是否合适:

约会

订单项

发票

支付

预约: "预约"是指员工需要提供服务的地点和时间.

订单项: "订单项"是服务,费用或折扣及其相关信息.可能进入约会的订单项示例:

Name:                          Price: Commission: Time estimate   
Full Detail, Regular Size:        160       75       3.5 hours 
$10 Off Full Detail Coupon:       -10        0         0 hours 
Premium Detail:                   220      110       4.5 hours 
Derived totals(not a line item): $370     $185       8.0 hours

发票: "发票"是客户承诺支付的一个或多个订单项的记录.

付款: "付款"是付款的记录.

在本应用程序的先前实现中,生活更简单,我将所有这四个概念视为SQL数据库中的一个表:"约会".一个"约会"可以有多个订单项,多笔付款和一张发票.发票只是通过订单项和客户记录生成的电子邮件或打印件.

十分之九,这很好.当一个客户预约一辆或几辆车并自己付款时,一切都很棒.但是这个系统在很多条件下都不起作用.例如:

当一个客户预约了一个,但约会中途下雨导致细节设计者必须在第二天回来时,我需要两个约会,但只有一个订单项,一个发票和一个付款.

当一组办公室的客户都决定在同一天完成他们的汽车以获得折扣时,我需要一个预约,但需要多个发票和多个付款.

当一个客户通过一张支票支付两张约会时,我需要两次约会,但只需要一张发票和一张付款.

通过稍微捏造一些东西,我能够处理所有这些异常值.例如,如果细节设计者必须在第二天回来,我会在第二天再次预约,其中的一个项目表示"完成",费用为0美元.或者,如果我有一个客户通过一张支票支付两次预约的费用,我会在每次预约中分配支付记录.这样做的问题在于它为数据一致性创造了巨大的机会.数据一致性可能是一个严重的问题,尤其是涉及财务信息的案例,例如客户通过一张支票支付两次约会的第三个例子.付款必须与提供的商品和服务直接匹配,以便正确跟踪应收账款.

拟议结构:

下面是用于组织和存储该数据的标准化结构.也许是因为我缺乏经验,我非常重视数据规范化,因为它似乎是避免数据不一致错误的好方法.使用这种结构,可以通过一次操作完成对数据的更改,而无需担心更新其他表.但是,读取可能需要多次读取以及内存中的数据组织.我稍后会想到,如果存在性能问题,我可以在"约会"中添加一些非规范化字段,以便更快地查询,同时保持"安全"规范化结构的完整性.非规范化可能会减慢写入速度,

表:

Appointment
 start_time
 etc...

Invoice
 due_date
 etc...

Payment
 invoice_Key_List
 amount_paid
 etc...

Line_Item
 appointment_Key_List
 invoice_Key
 name
 price
 etc...

以下是将给定的约会列表将所有四个实体(表)绑定在一起所需的一系列查询和操作.这将包括有关每次预约安排了哪些服务的信息,每次预约的总费用以及每次预约收到的天气或不付款.当加载日历以进行约会安排或管理者获得操作的整体视图时,这将是一个常见的查询.

查询"约会"列表,其中"start_time"字段位于给定范围之间.

将返回约会中的每个键添加到List中.

查询所有"line_Items",其中的appointment_key_List字段包含任何退货约会

将所有订单项中的每个invoice_key添加到Set集合中.

发票组中所有"发票"的查询(这可以使用app引擎在一个异步操作中完成)

将返回的发票中的每个密钥添加到List中

查询所有"付款",其invoice_key_list字段包含与任何已退回发票匹配的密钥

在内存中重新组织,以便每个约会反映为其安排的line_items,总价格,总估计时间和天气是否已经支付.

...正如您所看到的,此操作需要4个数据存储区查询以及一些内存组织(希望内存中的速度非常快)

任何人都可以评论这个设计吗?这是我能想到的最好的,但我怀疑可能有更好的选择或完全不同的设计,我没想到它可能在一般情况下更好或者特别是在GAE(谷歌应用程序引擎)的优势,劣势和能​​力下.

谢谢!

用法说明

大多数应用程序更加读取密集型,有些应用程序更加密集.下面,我将描述用户想要执行的典型用例和细分操作:

经理接到客户的电话:

读取 - 管理器加载日历并查找可用的时间

- 管理员向客户查询他们的信息,我想这是一系列异步读取,因为经理输入了每一条信息,如电话号码,姓名,电子邮件,地址等......或者如有必要,可能是一个在客户端应用程序收集了所有信息之后写到最后,然后提交.

写入 - 管理员记下客户的信用卡信息,并将其作为单独的操作添加到他们的记录中

- 经理收取信用卡并验证付款是否通过

经理拨打电话:

读取管理器加载日历

阅读管理器为他想要呼叫的客户加载预约

写入管理器单击"呼叫"按钮,启动呼叫并写入新的CallReacord实体

读取呼叫服务器响应呼叫请求并读取CallRecord以了解如何处理呼叫

呼叫服务器将更新的信息写入CallRecord

在呼叫关闭时写入,呼叫服务器向服务器发出另一个请求以更新CallRecord资源(注意:此请求不是时间关键的)

接受的答案:: 前两个答案都非常周到和赞赏.为了尽可能不完美地平衡他们的曝光率,我接受了投票少的人.



1> David Underh..:

您指定了网站需要提供的两个特定"视图":

    安排预约.您当前的方案应该可以正常工作 - 您只需要执行您提到的第一个查询.

    整体运营观点.我不确定这会带来什么,但如果您需要执行上面提到的四个查询字符串来获得此信息,那么您的设计可能会使用一些改进.详情如下.

四个数据存储区查询本身并不一定是过分的.在你的情况下,问题是两个查询是昂贵的,甚至可能是不可能的.我将通过每个查询:

    获取约会列表 - 没问题.此查询将能够扫描索引以有效地检索您指定的日期范围内的约会.

    获取#1中每个约会的所有订单项 - 这是一个问题.此查询要求您执行IN查询. IN查询会N在幕后转换为子查询 - 因此您最终会从#1的每个约会密钥中获得一个查询!这些将并行执行,因此不是那么糟糕.主要问题是IN查询仅限于一小部分值(最多只有30个值).如果#1返回的约会密钥超过30个,则此查询将无法执行!

    获取订单项引用的所有发票 - 没问题.你这个查询很便宜是正确的,因为你可以直接通过密钥获取所有相关的发票.(注意:此查询仍然是同步的 - 我不认为异步是您正在寻找的单词).

    获取#3返回的所有发票的所有付款 - 这是一个问题.与#2一样,此查询将是一个IN查询,如果#3返回您需要获取付款的中等数量的发票,则该查询将失败.

如果#1和#3返回的项目数量足够小,那么GAE几乎肯定能够在允许的限制范围内执行此操作.这应该足以满足您的个人需求 - 听起来您最需要它才能工作,并且不需要扩展到大量用户(它不会).

改进建议:

非规范化!尝试存储密钥Line_Item,Invoice以及Payment相关的名单上任命本身就是一个给定的约会实体.然后,您可以消除您的IN查询.确保这些新ListProperty没有索引,以避免问题的爆发指数

其他不太具体的改进想法:

根据您的"整体操作视图"将要显示的内容,您可以拆分检索所有这些信息.例如,您可能首先显示约会列表,然后当经理想要了解有关特定约会的更多信息时,您可以继续获取与该约会相关的信息.如果您在单个页面上进行此交互,您甚至可以通过AJAX执行此操作.

Memcache是​​您的朋友 - 使用它来缓存数据存储区查询的结果(甚至更高级别的结果),这样您就不必在每次访问时从头开始重新计算它.



2> Jason Hall..:

正如您所注意到的,此设计无法扩展.它需要4(!!!)DB查询来呈现页面.这太多了:)

使用App Engine数据存储区的主流概念是,​​您希望在编写内容时尽可能多地完成工作,这样在检索和呈现内容时几乎不需要执行任何操作.大概数次写入数据,与渲染的次数相比.

规范化同样是你似乎正在努力的事情.数据存储区在归一化中没有任何价值 - 它可能意味着数据不一致,但这也意味着读取数据的速度会慢得多(4次读取?!!).由于您的数据读取的频率比写入的频率高得多,因此优化读取,即使这意味着您的数据偶尔会被复制或不同步.

不要考虑数据在存储时的外观,而是考虑数据在向用户显示时的外观.尽可能接近该格式存储,即使这意味着在数据存储区中存储预呈现的HTML.读取将是闪电般的,这是一件好事.

因此,既然您应该优化读取,那么您的写入通常会增长到巨大的比例.如此巨大,以至于您无法在30秒的时间限制内完成请求.那就是任务队列的用途.将您认为模型的"必需品"存储在数据存储区中,然后触发任务队列将其拉出,生成要呈现的HTML,并将其放在后台.这可能意味着您的模型可以立即显示,直到任务完成,因此在这种情况下您需要优雅降级,即使这意味着在数据完全填充之前将其呈现为"缓慢的方式".任何进一步的读取都会很快.

总之,我没有任何与您的数据库直接相关的具体建议 - 这取决于您希望数据在用户看到时的样子.

可以给你一些链接到有关数据存储的一些超级有用的视频:

Brett Slatkin在2008年和2009年关于在App Engine上构建可扩展的复杂应用程序的讨论,以及今年关于数据管道的一个很好的应用程序(我认为这不是直接适用的,但在一般情况下非常有用)

封面下的App Engine:App Engine如何在幕后完成它的工作

AppStats:查看您正在执行的数据存储读取数量的好方法,以及减少该数量的一些提示

推荐阅读
依然-狠幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有