缓解API流量限制的系统设计方案

问题:
设计一个API A(100M QPS),已知这个A会call另一个API B,但是B有traffic限制只能Handle 10M QPS,该怎么办?(如何设计一个系统,使得一个高流量的API A(100M QPS)能够有效地调用另一个流量受限的API B(10M QPS),而不违反B的流量限制?)

在设计这样一个系统时,关键是要在API A和API B之间引入一层流量控制机制。以下是一些可能的解决方案:

  1. Rate Limiting: 在API A端实施速率限制,以确保对API B的调用不会超过10M QPS。这可以通过令牌桶或漏桶算法来实现。

    令牌桶:令牌桶算法允许一定程度的突发流量,因为如果桶内有令牌,请求就可以立即得到处理。这模拟了短时间内高流量的情况。
    漏桶:漏桶算法则以恒定的速率允许请求通过,平滑了流量的峰值。

  2. Caching: 对API B的响应进行缓存(cache),尤其是对于那些不经常变更的数据。这样,API A可以先查询缓存,只有在缓存未命中的情况下才调用API B。

    缓存的大小应该根据可用资源和数据的访问模式来确定。理想情况下,大小应足够存储最常访问的数据集。
    设置缓存通常涉及选择一个适当的缓存策略,如LRU(最近最少使用)或TTL(生存时间)来决定何时淘汰数据。

  3. Batch Processing: 如果API B的调用可以批量处理,那么API A可以收集多个请求并批量发送给API B,以减少调用的频率。

    一次性批处理的数量可以基于API B的处理能力和响应时间来决定,同时还需要考虑API A的请求特性。
    统计可以通过日志记录或者使用度量工具实时监控每个批次的大小和处理效率。

  4. Microservice Architecture: 将API A分解为多个微服务,每个服务独立调用API B,并且每个服务都有自己的流量限制控制。
  5. Asynchronous Processing: 通过队列和后台工作进程来异步处理对API B的调用。API A可以将请求放入队列中,并由后台服务以控制的速度处理这些请求。

    这个具体会用到什么服务 可以用到什么平台呢?
    异步处理可以利用如RabbitMQ、Kafka或AWS SQS这样的消息队列服务。
    平台选择可以是任何能够提供必要可靠性和可扩展性的云服务平台,如AWS、Azure或Google Cloud。

  6. Failover Strategy:当达到流量上限时,实施备用策略。例如,如果API B不可用,API A可以返回一个缓存的响应或一个错误代码,提醒用户稍后重试。
  7. Load Balancing:如果可能的话,可以使用多个API B实例,每个实例都有自己的流量限制,然后在它们之间进行负载均衡。

    我的实例可以架在哪里呢?有没有形象的例子
    实例可以部署在云服务提供商提供的虚拟机或容器服务中,如AWS EC2或EKS。
    形象的例子是Netflix的微服务架构,它使用Amazon AWS上的多个服务实例来处理巨大的流量。

  8. Priority Queue:实现一个优先级队列系统,确保高优先级的请求首先被处理,而低优先级的请求可以延迟或者在系统负载较低时处理。

    该怎么区别什么是优先高的 什么是优先低的呢
    优先级的判断可以基于业务需求,例如,付费用户的请求可能具有更高的优先级。
    也可以根据请求的性质,如实时性要求高的请求(如搜索请求)优先级更高,而数据同步等后台任务优先级较低。

数据库ID设计选项比较:UUID vs Serial Number

问题:设计数据库表格的ID有什么设计选项(UUIC,serial number),对于相应的选项做对比。

UUID(Universally Unique Identifier):

  • 优点:

    1. 全局唯一性:UUID几乎可以保证在任何时间任何地点都是唯一的。
    2. 无中心化生成:可以在应用程序端生成,无需数据库交互。
    3. 安全性:由于其随机性,UUID较难预测,更加安全。
    4. 可扩展性:在分布式系统中,UUID不依赖于中心数据库的序列生成器,避免了单点故障。
  • 缺点:

    1. 存储大小:UUID通常是128位长,比传统的整型序列号占用更多的存储空间。
    2. 性能影响:由于其大小和随机性,UUID可能会导致数据库索引效率降低。
    3. 可读性差:UUID很长,不易于人类阅读和记忆。

序列号(Serial Number):

  • 优点:

    1. 性能:整型ID在数据库中索引效率高,尤其是当作为主键时。
    2. 紧凑:序列号通常是32位或64位长,节省存储空间。
    3. 易于排序:序列号天然有序,对于需要按顺序检索记录的应用很有用。
    4. 可读性:数字ID更短,对人类更友好。
  • 缺点:

    1. 可预测性:连续的ID更容易预测,可能存在安全隐患。
    2. 中心化生成:依赖于数据库的序列生成器,可能成为分布式系统中的瓶颈或单点故障。
    3. 不适合分布式:在分布式数据库环境中,保证ID全局唯一性更加困难。

在选择时,应考虑以下因素:

  • 数据量:如果数据量巨大,UUID可能是更好的选择,因为它减少了ID冲突的风险。

  • 分布式系统:在分布式系统中,UUID提供了一种不依赖于中心数据库的解决方案。

  • 性能要求:如果系统对数据库索引性能要求极高,序列号可能更优。

  • 安全性考虑:如果应用程序的安全性是一个关键考虑因素,那么不可预测的UUID会是更安全的选项。

    综上所述,在系统设计中选择哪种ID生成策略取决于具体的应用场景和需求。对于需要高性能、易读性以及适用于不是分布式系统的场景,序列号是一个好的选择。而对于需要在全局范围内保证唯一性、在分布式系统中工作、或者关注安全性的应用,UUID则更加适合。在实际应用中,也可以根据需要将两者结合使用,例如,在数据库内部使用序列号作为主键,同时使用UUID来对外提供一个不可预测的标识符。

设计基于Elasticsearch的分布式日志关键词搜索系统

问题:如何设计一个系统,能够像CloudWatch那样,在多个服务器上接收请求并在本地写入日志,同时支持对这些分布式存储的日志进行关键词搜索?

解决方法: 构建一个分布式日志关键词搜索系统,可以借助Elasticsearch这样的全文搜索引擎来实现。
Elasticsearch是基于Lucene的搜索引擎,非常适合处理大规模日志数据的搜索和分析。以下是构建此系统的步骤和策略:

  1. 日志收集: 使用Logstash或Fluentd等日志收集工具,从多个服务器收集日志。这些工具能够监听服务器上的日志文件,一旦有新的日志生成,就自动将其收集起来。
  2. 数据传输: 配置Logstash或Fluentd将收集到的日志发送到Elasticsearch。这些工具支持多种输出插件,可以直接与Elasticsearch集成,实现日志数据的实时传输。
  3. Elasticsearch集群配置: 搭建一个Elasticsearch集群,以便存储和索引来自所有服务器的日志数据。根据数据量和查询负载,合理配置集群的大小和节点资源。
  4. 日志索引: 在Elasticsearch中为日志数据设计合适的索引结构。考虑到日志数据的特点,可以使用时间戳作为索引的一部分,这样可以更有效地查询特定时间范围内的日志。
  5. 关键词搜索: 利用Elasticsearch强大的搜索能力,实现对日志数据的关键词搜索。Elasticsearch提供了丰富的查询DSL(Domain Specific Language),支持模糊匹配、正则表达式、范围查询等多种搜索需求。
  6. 前端界面: 开发一个前端界面,为用户提供一个简单易用的搜索界面。可以使用Kibana或自定义的Web应用,让用户能够方便地输入搜索关键词,查看搜索结果和日志详情。
  7. 安全性和权限管理: 在Elasticsearch和前端界面上配置适当的安全措施,如HTTPS、用户认证和角色基于的访问控制,确保只有授权用户才能访问日志数据。
  8. 监控和维护: 对Elasticsearch集群进行监控,确保其健康稳定运行。可以利用Elasticsearch自带的监控工具,或者集成到现有的监控系统中。

通过上述设计,可以建立一个强大的分布式日志关键词搜索系统,类似于CloudWatch的功能,但是更加灵活和可扩展。这样的系统不仅可以提高日志数据的可查询性和可分析性,还可以帮助团队快速定位问题和分析系统性能。

设计TikTok视频观看历史记录系统

问题:
如何设计一个系统,用于记录用户在TikTok上的视频观看历史、查询历史记录,以及删除特定的历史记录?

tiktok video view history with following functions:

  1. record video view history
  2. check vidoe view history
  3. detele video view history

300M DAU,每个人看50个video,就是15billion records per day。

DAU: Daily Active Users(每日活跃用户数)

300M DAU意味着有3亿用户每天至少访问或使用一次应用程序。 针对每天15亿(15 billion)条视频观看记录的大访问量,以下是一些应对策略:

  1. 分布式数据库系统:
    • 使用分布式数据库来存储大量的观看历史记录。这种数据库可以水平扩展,通过增加更多的服务器来处理更大的数据量和访问请求。
  2. 数据分区(Sharding):
    • 对数据库进行分区,将数据分散存储在多个服务器上。可以根据用户ID、地理位置或观看时间等策略进行分区,以优化查询性能和提高数据管理效率。
  3. 缓存策略:
    • 实施缓存机制,如使用Redis或Memcached缓存频繁访问的数据。这可以大大减少对数据库的直接访问,降低延迟,提高系统响应速度。
  4. 读写分离:
    • 将数据库的读操作和写操作分离。通过设置一主多从的数据库架构,写操作只在主数据库进行,而读操作则可以在多个从数据库上进行,从而提高并发处理能力。
  5. 异步处理:
    • 将用户的观看记录异步写入数据库。例如,可以先将观看事件发送到消息队列(如Kafka或RabbitMQ),然后由后端服务批量异步处理这些事件,减少对即时数据库写入的压力。
  6. 数据压缩和归档:
    • 对旧的观看记录进行数据压缩和归档处理,将不常访问的数据转移到成本较低的存储系统中,如云存储或冷数据存储,以优化存储成本。
  7. 自动扩容:
    • 在云环境中,利用自动扩容服务来动态增减计算资源,根据实际的访问压力自动调整资源分配。
  8. 监控和报警:
    • 实施全面的系统监控和实时报警机制,及时发现和解决性能瓶颈或系统故障,确保服务的高可用性。

设计搜索自动补全功能

问题
搜索自动补全功能旨在用户输入搜索查询时实时提供相关的建议词或短语,以提高用户体验和搜索效率。这要求系统能够快速响应用户的每次键入,并从大量可能的选项中筛选出最相关的建议。

  1. 使用Trie(前缀树)存储:
    • 例子: 如果有词汇“Apple”,“App”,和“Application”,前缀树允许在用户输入“A”、“Ap”、“App”时快速找到这些词汇。
    • Trie树能够高效地根据当前输入的字符序列查找所有以这个序列为前缀的字符串。
  2. 实现智能排序
    • 例子: 如果“Apple”是一个更受欢迎的搜索词相比于“Application”,则在用户输入“App”时,“Apple”应该排在“Application”之前。
    • 使用用户历史搜索数据、词频等因素来动态调整自动补全建议的顺序。
  3. 利用缓存优化性能
    • 例子: 如果“App”是一个常见的查询前缀,可以将与“App”相关的自动补全结果存储在缓存中,以加速响应时间。
    • 使用内存缓存(如Redis)存储最常见的查询和它们的自动补全结果,减少对后端系统的查询压力。
  4. 分布式架构支持扩展性
    • 例子: 为了支持全球用户,可以在多个数据中心部署自动补全服务的实例,确保低延迟。
    • 使用分布式系统设计,如微服务架构,以便在用户量增加时通过添加更多服务实例来水平扩展。
  5. 更新和维护数据集
    • 例子: 新词汇“COVID-19”突然成为热门搜索,系统需要能够快速地将其纳入自动补全的建议中。
    • 定期更新前缀树中的数据,以反映最新的搜索趋势和热词。