身为开发人员,这些数据库合知识不掌握不合适( 四 )


还有复杂的情况 。应用程序和数据库通常位于不同的计算机中(甚至可能位于不同的数据中心) 。不仅分布在几台计算机上的数据库节点无法在时间上达成共识,应用服务器时钟和数据库节点时钟也无法达成共识 。
Google的TrueTime在这里采用了不同的方法 。大多数人认为Google在时钟方面的进步可以归因于对原子钟和GPS时钟的使用,但这只是一部分原因 。TrueTime的实际工作原理如下:

  • TrueTime使用两个不同的源:GPS和原子钟 。这些钟表的失败模式不同,因此同时使用两种可以提高可靠性 。
  • TrueTime使用了非传统的API 。它返回一个时间范围,实际的时间位于下界和上界之间的任意地方 。Google的分布式数据库Spanner会一直等待,直到它能确信当前的时间超过了某个特定时间 。该方法给系统引入了一些延迟,特别是当主服务器广播的不确定较高时,但即使在全球分布式的情况下依然能够提供正确性 。

身为开发人员,这些数据库合知识不掌握不合适

文章插图
Spanner的组件使用TrueTime,其中TT.now返回一个时间范围,这样Spanner可以插入sleep来确保当前时间超过某个特定时间 。
对当前时间的信心下降,意味着Spanner操作可能需要更多时间 。因此,尽管不可能拥有精确的时钟,但高可信度对于性能依然很重要 。
 
延迟有很多含义对于“延迟”一词每个人的理解都不同 。在数据库中,延迟通常称为“数据库延迟”,而不是客户端感知的延迟 。客户端看到的延迟是数据库延迟和网络延迟之和 。在调试不断升级的问题时,能够识别客户端和数据库延迟至关重要 。收集和显示指标时,请始终考虑同时使用两者 。
 
评测性能需要针对每个事务进行有时,数据库在宣传其性能和限制时会使用读写吞吐量和延迟作为指标 。而实际上,在评估新数据库的性能时,更全面的方法是分别评估关键操作(每个查询和/或每个事务) 。例如:
  • 在表X(包含五千万行数据,并带有约束)中插入一行并填充关联数据的写吞吐量和延迟 。
  • 当每个用户的平均朋友数为500时,查询给定用户的朋友的朋友的延迟 。
  • 当每个用户订阅了500个账号,每个账号每小时有X条消息时,获取时间线的最新100条记录的延迟 。
评测和实验应当包含这种极端用例,才能确信数据库是否能够满足你的性能要求 。类似的经验法则是,在收集延迟数据和设定服务水平目标时也要用这种细致的用例 。
 
嵌套事务的弊大于利并非每个数据库都支持嵌套事务,但是当嵌套数据库支持嵌套事务时,嵌套事务可能会导致出人意料的编程错误,这些错误通常很难被发现,直到你发现数据异常 。
如果想避免嵌套事务,客户端库通常可以帮你检测并避免嵌套事务 。如果无法避免,则必须注意避免出现子事务会导致已提交的事务意外中止的意外情况 。
将事务封装在不同的层中可能会导致令人惊讶的嵌套事务案例,并且从可读性的角度来看,其意图会很难理解 。看一看以下程序:
身为开发人员,这些数据库合知识不掌握不合适

文章插图
上述代码的结果是什么?它会回滚两个事务,还是只会回滚内层事务?如果这段代码依赖于库中的多个层,每个层都封装了事务处理?我们怎样才能识别并改善这种情况?
想像一个包含多个操作(如newAccount)的数据层,该数据层已经实现了自己的事务处理 。数据层无需自己创建事务就能实现高层操作 。于是,业务逻辑可以启动事务、在事务中执行操作,然后提交或放弃 。
身为开发人员,这些数据库合知识不掌握不合适

文章插图
 14  事务不应该维持应用程序状态应用程序开发者也许想在事务中使用应用程序状态,以更新特定的值,或修改查询参数 。需要考虑的一个关键因素就是要使用正确的作用域 。客户端通常会在发生网络问题时重试整个事务 。如果事务依赖于某个状态,而该状态可能在其他地方被修改,那么事务就可能会在数据竞争发生时读取错误的值 。事务应当注意应用程序内的数据竞争 。
身为开发人员,这些数据库合知识不掌握不合适

文章插图
上述事务每次运行时会增加序列数字,而不管实际执行结果如何 。如果网络故障导致提交失败,那么第二次尝试时就会使用不同的序列数字进行查询 。


推荐阅读