系统设计:设计要素、短网址系统的设计与实现

《Python服务端工程师面试宝典-PegasusWang》学习笔记,第八章:设计要素、短网址系统的设计与实现
275阅读 · 2020-6-6 18:14发布

8.1 系统设计

什么是系统设计?

  • 系统设计(System Design)是一个定义系统架构、模块、接口和数据满足特定需求的过程。
  • 比如设计一个短网址服务、评论服务、Feed流系统、抢红包系统。
  • 微服务架构很多系统被按照业务拆分,需要单独设计一个系统服务。

系统设计需要的知识点

  • 需要具备相关领域、算法的经验,有一定的架构设计能力。
  • 熟悉后端技术组件,比如消息队列、缓存、数据库、框架。
  • 具备文档撰写、流程图绘制、架构设计、编码实现等综合能力。

系统设计要素

  • 使用场景和限制条件
  • 数据存储设计
  • 算法模块设计

要素一:场景和限制

  • 系统是在什么地方使用?例如短网址系统提供给站内各种服务生成短网址。
  • 限制条件:用户估计多少?至少要能支撑多少用户(服务)?
  • 估算并发qps:峰值qps是多少?平均qps是多少?

要素二:数据存储设计

  • 按需设计数据表,需要哪些字段,使用什么类型?数据增长规模?
  • 数据库选型:是否需要持久化?使用关系型还是非关系型?
  • 如何优化?如何设计索引?是否可以使用缓存?

要素三:算法模块设计

  • 程序=算法+数据结构。系统=服务+存储。
  • 需要哪些接口?接口如何设计?
  • 使用什么算法或者模型?
  • 不同实现方式之间的优劣对比,如何取舍(时间换空间或空间换时间)?

8.2 系统设计:短网址系统的设计与实现

场景和限制

  • 使用场景:提供短网址服务为公司其他各个业务服务。
  • 功能:一个长网址转成短网址并存储;根据短网址还原长url。
  • 要求短网址的后缀不超过7位(大小写字母和数字)。
  • 预估峰值插入请求数量:数百;查询请求数量级:数千。

数据存储设计

  • 使用Mysql即可满足。
  • 数据库需要的字段:id、token、url(原网址)、created_at。
  • 设计索引:使用token作为索引(只有token有查询需求)。

算法实现设计

  • 需要两个API:long2short_url,stort2long_url。
  • 采取自增序列算法实现。

实现代码示例

  • 转换短网址的算法:

    CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
      def encode(num):
          if num == 0:
              return CHARS[0]
          res = []
          while num:
              num, rem = divmod(num,len(CHARS))  # 62位,divmod返回商和取模求余数
              res.append(CHARS[rem])
          return ''.join(reversed(res))
    
      print(encode(1))
      print(encode(61))
    
  • 创建sql表语句

    # demo数据库中执行
      CREATE TABLE short_url(
          id bigint unsigned NOT NULL AUTO_INCREMENT,
          token varchar(10),
          url varchar(2048),
          created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
          PRIMARY KEY (`id`),
          KEY `idx_token` (`token`)
      );
    
  • flask+mysql的具体实现:

    import os
      from flask import Flask, jsonify, render_template, request
      from flask_mysqldb import MySQL
      #from flask.ext.redis import FlaskRedis
    
      app = Flask(__name__)
      app.config['MYSQL_USER']='root'
      app.config['MYSQL_PASSWORD']='root'
      app.config['MYSQL_DB']='demo'
      app.config['MYSQL_CURSORCLASS']='DictCursor'
    
      mysql = MySQL(app)
      #redis_store = FlaskRedis(app)
    
      CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    
      def encode(num):
          if num == 0:
              return CHARS[0]
          res = []
          while num:
              num, rem = divmod(num,len(CHARS))  # 62位,divmod返回商和取模求余数
              res.append(CHARS[rem])
          return ''.join(reversed(res))
    
      @app.route('/shorten', methods=['POST'])
      def shorten_url():
          long_url = request.form['url']
          print(long_url)
          # index = int(redis_store.incr('SHORT_CNT')
          index = 1 # 可以通过redis实现获取自增
          token = encode(index)
          sql = "INSERT INTO short_url(token,url) VALUES(%s, %s)"
          cur = mysql.connection.cursor()
          cur.execute(sql,(token,long_url))
          mysql.connection.commit()
          short_url = 'https://datatest.org/'+token
          return jsonify(url=short_url)
    
      @app.route('/')
      def index():
          return render_template('index.html')
    
      if __name__ == '__main__':
          app.run(debug=1)
    
  • html代码

    <form action="/shorten" method="post">
        <p>long url: <input type="text" name="url" /></p>
        <input type="submit" value="Submit" />
      </form>
    
  • 最终效果图(页面直接通过form表单提交,没有做页面的处理,所以访问后直接看到json的结果)

第九章 练习题

9.1 Redis应用-分布式锁

  • 请你基于Redis编写代码实现一个简单的分布式锁。
  • 要求:支持超时时间参数。
  • 深入思考:如果Redis单个节点宕机了,如何处理?还有其他业界的方案实现分布式锁吗?

9.2 如何设计一个秒杀系统

  • 什么是秒杀系统?
  • 如何根据我们提到的三个要素来设计秒杀系统?
  • 秒杀系统涉及到哪些后端组件(参考网上资料思考如何设计)。