# 每个程序员都应该知道的数字

这组数据同样来自演进: Building Scalable Web Applications with Google App Engine,

highscalability 网站上有整理这个演讲主要内容: http://highscalability.com/blog/2009/2/18/numbers-everyone-should-know.html

读完之后,感觉对于理解架构和系统设计有一定帮助,我把它部分内容翻译为中文,如下:


# 一、写操作成本很高

  1. 数据存储是事务性的:写入操作需要访问磁盘
  2. 访问磁盘意味着磁盘寻道
  3. 经验法则:一个磁盘寻道大约需要10毫秒
  4. 简单的数学计算:1秒 / 10毫秒 = 每秒最多100次寻道
  5. 这取决于:
  • 你的数据的大小和形状
  • 批量操作(批量插入和获取)

# 二、读取成本很低

  1. 读取操作不需要是事务性的,只需要保持一致性
  2. 数据从磁盘读取一次后,就可以很容易地被缓存
  3. 所有随后的读取操作都直接从内存中完成
  4. 经验法则:从内存中读取1MB的数据大约需要250微秒
  5. 简单的数学计算:1秒 / 250微秒 = 每秒最多4GB
  6. 对于一个1MB的实体,这意味着每秒可以进行4000次获取操作

# 三、各项延迟数据

详情请参考阅读这篇文章: 计算机各种访问操作的耗时对比数据 (opens new window)

# 四、主要经验

  • 写入操作的代价是读取操作的40倍。
  • 全局共享数据非常昂贵。这是分布式系统的一个基本限制。在大量写入的共享对象中,锁竞争会导致事务序列化并降低性能。
  • 为扩展写入操作进行架构设计。
  • 尽量减少写入竞争。
  • 进行广度优化。尽可能使写入操作并行。

# 五、分片计数器设计思想

我们似乎总是想要记录存储的事务的数量。

但是BigTable并不会记录实体的数量,因为它是一个键值存储。它非常擅长通过键获取数据,但对拥有多少并不感兴趣。

因此,记录数量的任务就转嫁给了你。

最初级的计数器实现方式是锁定-读取-增加-写入。

如果写入操作较少,这种方式就没问题。

但是,如果经常有更新,就会产生高度的竞争。

鉴于每秒可进行的写入操作数量如此有限,高写入负载会使整个过程序列化并变慢。

解决方案是使用分片计数器。这意味着:

  • 并行创建N个计数器。
  • 对每个计数的条目随机选择一个分片进行事务性增加。
  • 要获取真正的当前计数,只需将所有分片计数器的总和相加即可。
  • 通过1/N减少了竞争。因为它们已经被分布在不同的分片上,所以写入操作已经被优化。已经移除了围绕共享状态的瓶颈。

这种方法似乎违反直觉,因为我们习惯于将计数器视为一个可以递增的单一变量。

读取操作很便宜,因此我们用需要多次读取来恢复实际计数的方法,替换了拥有一个容易读取的单一计数器。

频繁更新的共享变量非常昂贵,所以我们对这些写入操作进行分片和并行化。

对于集中式数据库,让数据库成为序列号的来源是可行的。

但要扩展写入操作,你需要进行分区,一旦你进行了分区,保持任何共享状态,如计数器,就变得困难了。

# 六、评论系统设计

如何存储评论,使得我们能按照大致的输入顺序浏览它们?

在高写入负载的情况下,这个问题的答案出奇地难找。

显然,我们想要的只是一个计数器。当有新的评论发表时,获取一个序列号,这就是显示评论的顺序。

但是,正如在上一节中所看到的,像单一计数器这样的共享状态在高写入环境中是无法扩展的。

在这种情况下,分片计数器也不能工作,因为对共享计数器求和并不是事务性的。

没有办法保证每个评论都能获取它分配的序列号,所以我们可能会有重复。

在BigTable中,搜索返回的数据是按字母顺序排列的。

所以,我们需要的键是唯一的,且按字母顺序排列,这样在浏览评论时,你可以只使用键来前后移动。

很多分页算法使用计数。

给我1-20条记录,21-30条记录,等等。

SQL使这变得简单,但它对BigTable并不适用,BigTable知道如何通过键获取事物,所以你必须创建能按正确顺序返回数据的键。

在制作唯一键的古老传统中,我们只是不断添加东西,直到它变得唯一。

对于GAE(Google App Engine),建议的键是:时间戳 + 用户ID + 用户评论ID。

按日期排序是显然的,好处是,获取时间戳是一个本地决策,它不依赖于写入操作,且可扩展。

问题是时间戳并不唯一,尤其是在有很多用户的情况下。

所以,我们可以将用户名添加到键中,以便将其与在同一时间发表的所有其他评论区分开,我们已经有了用户名,所以这也是一个便宜的调用。

理论上,即使是单个用户的时间戳也不够,所以,我们需要的是每个用户评论的序列号。

这就是GAE解决方案变成某种完全出乎意料的东西的地方。

我们的目标是消除写入竞争,以实现并行化写入,我们有很多可用的存储空间,所以我们不用担心这个。

考虑到这些因素,想法是为每个用户创建一个计数器。当用户添加评论时,它将添加到用户的评论列表中,并分配一个序列号。在Entity Groups的事务性上下文中,按用户为单位添加评论。

所以,每次添加评论都保证是唯一的,因为在Entity Group中的更新是序列化的。

结果键保证是唯一的,并在字母顺序中正确排序。当浏览时,可以使用ID索引跨实体组进行查询。结果将按正确的顺序排列。分页就是在当前页面的查询中获取前一个和后一个键。这些键可以用来在索引中移动。

我肯定是想不到这种方法的。保持每个用户评论索引的想法是出乎意料的。

但它巧妙地遵循了在分布式系统中扩展的规则。

写入和读取是并行的,这就是目标。

写入竞争被移除了。

# 参考

最新原创的文章都先发布在公众号,欢迎关注哦~,
扫描下方二维码回复「CS」可以获得我汇总整理的计算机学习资料~

编程指北图片
@2021-2024 编程指北 版权所有 粤ICP备2021169086号-2