系统设计:设计要素、短网址系统的设计与实现
《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 如何设计一个秒杀系统
- 什么是秒杀系统?
- 如何根据我们提到的三个要素来设计秒杀系统?
- 秒杀系统涉及到哪些后端组件(参考网上资料思考如何设计)。