1.电商平台
1.项目中遇到的问题以及解决方法
打造一个高可用的超高流量的并发读,并发写的秒杀系统关键点
- 实际上秒杀时的并发:绝大多数事务会被挡回去,返回个“您没有资格参与”完事
- 用户请求的数据尽量少
- 通过CDN动静分离,将静态数据缓存在离用户近的地方
- 请求数尽量少
- 前端和服务端的限流处理
- 前端限流:这个很简单,一般秒杀不会让你一直点的,一般都是点击一下或者两下然后几秒之后才可以继续点击,这也是保护服务器的一种手段。
- 请求路径尽量短
- 通过redis缓存,尽量减少访问数据库
- 不要有单点
- 冗余整个秒杀系统的各个部分,保证高可用
秒杀场景:(jmeter并发压测50000用户,10分钟)
- 秒杀开始,大量用户点击秒杀按钮,产生大量并发请求查询库存
- 查询有库存-库存扣减-系统生成实际订单-后续处理(订单支付、物流等)
- 查不到库存,继续查询
简单来说,主要分为三步:库存查验、库存扣减和订单处理。其中最大的并发压力在库存查验。
- 库存查验与库存扣减在redis中处理(本机QPS17万,小数据可抗10万并发)
- 额外的开销。redis中保存了库存量,而库存量的最新值在mysql维护,所以mysql更新后,还需要和redis同步,这个过程带来了额外的开销
- 出现超售。由于mysql处理速度较慢,不能及时更新库存余量,会导致大量库存查验请求读取到旧的库存值。出现下单量大于库存量
- 操作方法在redis中进行库存扣减。一旦库存有余量,立即在redis中扣减。为了避免查询到旧的库存值,库存查验和库存扣减需要保证原子性
- 订单处理在mysql(本机QPS4万)
- 订单处理会设计支付、物流等多个关联操作,设计数据库中多张表数据,要保证处理的事务性。并且,订单处理时的请求压力已经不大,mysql可以支撑
- 秒杀业务要独立
- 单独为秒杀分库分表
- 业务分离
- 超卖问题
- 产生原因:非原子性的库存校验导致在并发环境下,库存校验的结果不准确
- 解决办法:
- redis本身单线程原子性扣减库存,(分布式锁,mysql读写加锁)
- Mysql排他锁(效率低)
- CAS乐观锁,给数据加版本号,只要其中有一个线程修改了数据,其他线程放弃此次update
- 用户下单未付款
- 下单后锁定库存,支付成功后,扣减库存,超时未支付,则释放库存(12306购票)
缓存雪崩
- 定义:大量的应用请求无法在redis缓存进行处理,直接打到数据库
- 原因:缓存中大量数据同时过期
- 避免给大量数据设置相同的过期时间(可设置随机过期时间)
- 服务降级
- 当访问的是非核心数据时,暂停从缓存查询,直接返回错误信息或者预设值
- 访问核心数据时,缓存缺失可以从数据库查询
- 这样操作只有部分请求会发送到数据库,减小压力
缓存击穿
- 定义:某个访问频繁的热点数据过期失效,访问该数据的大量请求发送至数据库
- 策略:热点数据永不过期
缓存穿透
- 定义:需要查询的数据不在redis也不在mysql,应用无法从数据库中读取数据写入缓存。
- 发生原因:
- 业务层误操作:缓存和数据库中的数据被误删除
- 恶意攻击:专门访问数据库中没有的数据
- 策略:
- 缓存空值
- 使用布隆过滤器快速判断数据是否存在,避免无数据查询,降低数据库压力
- 在前端对请求进行检测,把参数非法的参数过滤掉
2.用户登录以及鉴权
用户登录
- 用户注册时,将用户密码密文放入数据库(密码明文不可以放入数据库)
- 用户通过访问微服务网关调用微服务,同时携带头文件信息
- 在微服务网关这里进行拦截,拦截后获取用户要访问的路径,识别用户访问的路径是否需要登录,如果需要,识别用户的身份是否能访问该路径
- 此处自定义全局过滤器,如访问微服务需要登录,则获取头文件中的令牌并解析
- 如未登录,进行登录验证
- 用common中的BCrypt工具类进行解密验证
- 登录成功,服务器生成令牌,放入cookie中,响应给用户
- 令牌中包括(role、username等信息)
- .用户下次访问,cookie令牌信息即可识别是否登录。JWT(JSON Web Token)鉴权
网关鉴权是采用全局过滤器
- 检查cookie或者http请求头中是否存在token
- 检验token是否正确
- 正确则放行
- jwt令牌组成:
- JWT是一个字符串
- JWT分为三部分:头部(令牌的类型,令牌的签名算法)、载荷(存放有效信息)和签证(用于身份验证和检验信息有没有被篡改)
- 使用方法:
- 1.引入相关依赖jwtutil,然后利用api对信息可以加密为token
- 2.服务器对信息加工生成token—>服务器将token发送给客户端—>客户端带着token访问服务器—>服务器接收到token并解析内容
- 过滤器实现原理
- 实现spring-cloud-gateway的GlobalFilter接口,
- 重写filter()方法(如果符合条件就放行filter)
3.购物车系统
购物车的种类
- 需要用户登录才能操作(天猫)
- 存在redis中(选取方案)
- 不需要登录即可操作(京东)
- 非登陆状态下存入cookie
- 登录状态下 购物车合并 存入redis
购物车数据类型:
- 每一个用户对应一个购物车, 一个用户可以添加多个商品,可以采用hash,将用户名字作为namesapce,商品id作为key,商品详情作为value(商品详情,包括商品名称、价格、数量等,将其封装为OrderItem对象转为string存储至redis)
cookie&&session
2.校园多功能平台
1.选用mongoDB的原因
-
mongodb存储数据格式是类似json的bson,存储评论数据较为复杂数据类型(图片url等)较为方便
-
数据库(database)—>集合(collection)—>文档(document)
-
mongodb的命令本身就是线程安全的,比如inc列值增长命令(点赞数增长),只有在上一个命令完成后下一个命令才会执行
-
mongoDB的写入性能优于mysql(mongoDB默认事务级别低于mysql)
-
MongoDB数据类型
-
常用命令
-
在springboot中的使用方法
- 添加依赖
- 配置文件(mongoDB不需要配置密码)
- 自动注入
- 添加依赖
@Autowired
private MongoTemplate mongoTemplate;
MongoDB增删改查接口的实现:
SpringDataMongo提供了一个MongoTemplate类似于Spring的JDBCTemplate,可实现mongo的增删改查
MongoDB索引的创建:
1.和mysql类似创建唯一索引可以大幅度提高查询速度
2.频繁查询适合建立索引,频繁写入不适合建立索引
3.json数据中都是key-value,推荐key的长度要小
4.建议每个集合的索引数量不要超过5个,索引会占用空间
MongoDB中的Object_id:
1.Object_id是文档的id,类似于唯一主键,包含12bytes,建议使用默认,不要自行设置,否则会降低查询速率
MongoDB评论id的创建:
1.采用雪花算法随机生成评论id
2.将id存入mongoDB中
- 在项目中实现的功能
- 查询、新增、删除评论
- 根据评论id增加点赞数
- 根据文章id分页查询评论数据
- 禁止重复点赞
- 将评论id+用户id绑定放入redis中,
- 每次点赞前先查询点赞信息
评论集合包含字段
3.数据库解耦
1.集中式架构缺点
- 单点特征明显,服务器稍微出点问题都会影响到整体业务。
- 使用同一个数据库,数据库压力过大(分库分表)
- 不利于分组开发维护
2.需求梳理
-
存在跨库访问其他数据库的微服务
- 请求该微服务开发人员提供数据访问接口
-
不存在跨库访问
- 满足独立部署,申请独立数据库(新旧字段的对应等)
- 梳理旧的代码逻辑
- 编写新的持久层代码(公共类,公共方法)
3.代码编写
-
公共方法的抽取
- 提高复用性
- 传参的类型(传入对象,还是对象的具体属性)
- 方便调用者
- 方便前端传参
- 查询数据量过大,返回数据过大,
- 分页查询
- 联表查询代码逻辑复杂
-
门禁系统
- 每个类都要有版权信息
- 减少中间参数的命名
- 所有传参的判空
- 频繁的日志打印
4.接口验证
- 构造查询场景验证接口功能是否正确