黑马点评项目相关思路

个人Bolg:dykang.top

黑马点评项目

登录和注册:

1.输入手机号->校验手机号是否正确->正确,然后生成验证码->将验证码保存到redis中以String保存(手机号code,验证码)->打印到控制台 2.前端输入验证码->后端根据手机号在redis中拿去验证码->验证两个密码是否正确->正确,根据手机号查询登录用户信息->生成一个随机字符串作为token,然后以 token作为key并设置过期时间,value是用户信息用hash结构存储->token返回前端 3.拦截器 :第一个拦截器让其拦截所有路径(目的是为了在用户不登录的前提下就可以刷新redis中存的用户信息的过期时间) 分析前端是否携带token->拿着token去redis查询用户信息 ->然后将信息保存到threadlocal中 没有就放行 第二个拦截器 :第一个拦截器放行之后->在threadlocal中去获取登录信息->获取成功就放行代表登录成功 访问完成之后退出,ThreadLocal中的map key是虚引用 value是强引用 value不会被回收,如果不回收就会造成内存泄漏。所以要访问完成后移除用户

查询商户:

(三大缓存问题 以及缓存一致性问题)

先从redis中查,redis中为null就去数据库中读取,并写入redis中。缓存容易出现的三大问题及解决方案

  1. 缓存穿透 (用户对redis中和mysql中不存在的数据进行大量查找,都会打到数据库,造成数据库崩溃)

  • 缓存null值 在数据库中未查找到就返回null,并将null设置过期时间缓存到redis中,下一次请求直接到redis中 大量null占用内存空间

  • 布隆过滤器 将数据库中的数据 id 作为key通过hash函数进行3次hash,然后放到对应的数组下标位置,然后访问的时候先经过布隆过滤器中经过3次hash看看是否和之前做的hash位置一样,位置一样代表有,然后可以去查询后面操作。没有直接返回提示信息。容易出现误判,但节约内存空间

  • 增强id的复杂度,避免被猜测id规律

  • 做好数据的基础格式校验

  • 加强用户权限校验

  • 做好热点参数的限流

  1. 缓存击穿(热点数据在Redis中的缓存失效并且重新缓存需要很长的时间,大量请求同时访问MySQL造成崩溃) 这里采用给热点数据在Redis中的缓存设置逻辑过期+互斥锁

  • 互斥锁(setnx)(数据一致性好,但是容易发生死锁,性能低)相当于分布式锁,判断没有key就返回1代表获取锁成功,有则相反 缓存中未查到,然后去获取锁,若获取锁失败就sleep一段时间再去获取锁 ,获取锁成功就去查询数据并缓存数据(也可防止缓存穿透缓存null值,到数据库)

  • 逻辑过期 (数据可能不一致,性能高) 给热点key设计逻辑过期时间存到redis中,首先判断是否逻辑过期,逻辑过期然后去获取锁,然后开启一个线程去查数据库,缓存数据完后释放锁,主线程直接返回旧数据

  1. 缓存雪崩(Redis中大量缓存同时失效或Redis宕机,大量请求同时访问数据库,造成数据库崩溃)

  • 给不同的Key的TTL添加随机值

  • 利用Redis集群提高服务的可用性

  • 给缓存业务添加降级限流策略

  • 给业务添加多级缓存

秒杀业务:

(首先是一人一单)

采用异步下单的方式,先运行Lua脚本,判断库存是否充足,判断是否下过单,若未下过单,则扣减Redis库存,脚本运行成功,有购买资格,则生成一个全局Id作为订单id,生成订单信息,把订单保存到一个阻塞队列,阻塞队列收到订单后,获取分布式锁后再把订单信息和库存信息同步到MySQL,然后释放锁。

获取用户id,获取商品id,传到lua脚本中。在lua脚本中完成商品库存是否充足、一人一单、扣减库存和下订单操作,然后将订单id和优惠券id和用户id放到redis消息队列中,执行lua脚本,放一个线程池,然后从线程池中拿线程去消息队列中获取订单,然后再判断一次一人一单、商品库存是否充足和下订单操作避免丢失(在这里利用分布式锁来解决并发问题),然后下订单

全局唯一ID:

订单id使用redis全局唯一id生成,订单id不能有太明显规则,另外分布式场景下mysql拆库拆表然后他们的id不能是一样的。所以要保证唯一id 该项目如何使用redis实现唯一的全局id?

用户在对优惠券抢购时一个订单就对应一个订单ID在高并发情况下需要保证ID的以下特点

唯一性ID必须保证唯一 高可用可以以极快的速度生成唯一ID 递增型递增的ID有利于在数据库中建立索引 在本项目中自己实现了一个ID生成器。生成的ID结构如下

第一位符号位永远为0 时间戳位31bit以秒为单位可以使用69年。当前时间戳–自定义的起始时间戳 32bit秒内的计数器支持每秒产生2^32个不同ID。这个使用Redis中的incr函数实现 Key的设计incr业务名日期

如果一个业务只建立一个key那么随着时间的推移redis中的value会达到上限此时ID生成器就不可用了。

超卖问题:

用乐观锁解决超卖问题:扣减库存时,一开始是在,想要在更新库存时判断是否查询库存的时候一致,一致说明没有被修改过就可以扣减。但是 不一样的话会回滚 但是如果100个线程同时拿了第100个库存,一个线程改了之后其他线程都不会去更改,也会造成问题 然后直接条件为库存>0就可以解决

创建订单:

插入并不适合用乐观锁了,乐观锁适合更新数据。 所以我们首先想到给整个下订单的方法加syn锁,但是锁的粒度太大,我们就减小锁的粒度,给扣减库存和创建订单加锁。锁用 userId.toString其实是new出来的对象,不相同。所以我们用userId.toString.intern()从常量池中拿一个唯一的锁标志。然后当前事务被Spring事务控制,锁释放但是事务还没提交也会造成不安全问题。所以我们将方法直接包裹起来用锁。因为你调用的方法,其实是this.的方式调用的,事务想要生效,还得利用代理来生效,所以这个地方,我们需要获得原始的事务对象, 来操作事务 但是在集群下,不同jvm环境下,锁监视器不相同,然后会出现问题。所以用分布式锁。保证可见性,不同服务器多个线程都能看见。互斥。高可用不崩溃安全

解决锁锁执行完误删问题? 在删除锁时判段是否是自己的锁 使用lua脚本来确保原子性 先判断后删除

redission锁

是可重入锁,超时续约

Lua脚本解决多条命令原子性问题

好友点赞:(一个用户只能赞一次)

Redis中的Zset集合,socre是用时间戳(用来后续点赞的排序) key是blogId value是用户的id 判断这个用户是否点过赞,点过赞就数据库-1,redis中删除这个用户id 没点过,数据库+1 redis中加入这个用户的id

点赞前五名 :就是redis根据时间戳进行排序 拿着blogId去redis中查对应点赞的用户id,然后拿出来,用id去查询数据库 需要利用手写sql 因为list是用in查,并不是按我们规定的去查,所以需要order by field(字段,ids)去查

好友共同关注:

想到交集 用set来实现 数据库表是 userId 登录用户id和 follow_user_id 关注的那个人的id set 的key:用userId value用follow_user_id来做 然后 两个用户的共同关注就是取这两个人的交集 interact

订阅模块:

(涉及拉(每个人都会存到发件箱,粉丝想要查的话就需要去对应的发件箱拿)、推(直接发送到粉丝的收件箱)、推拉结合模式(大V、活跃粉丝使用推,不活跃使用拉)) 这里用推模式实现的。用户发布博客之后会向自己的粉丝的收件箱(Zset实现,粉丝的id作为key,blogId作为value,时间戳作为分数)中发送blog,粉丝会动态分页查询blog 首先需要查找到粉丝,然后为每个粉丝创建一个收件箱,讲blogId保存进收件箱 粉丝动态查询收件箱:粉丝id查看redis中的收件箱 ->然后利用 zset revByscore(max,min,offset,page)按降序查询出数据 解析offset (与开始数据的相等的时间戳的个数)、blogid 和 max(哪条数据开始)minTime就是循环最后的那个时间戳,因为在redis中查询出的就是按降序排的,然后offset就用一个计数器来计数,相等就计数,不等就讲offset恢复成1 根据ids查询数据库也需要手写order by field 封装返回

签到模块

使用bitMap (二进制文件所占的内存小) 使用时间bitMap,打卡取1,为打卡取0,从第0位开始,n日的打卡数据在n-1位 获取签到数据,先当月签到数据和1做与运算得知今天是否打卡,为0直接返回,为1则将签到数据向右移一位,循环做与运算,直到为与运算结果0,循环次数为连续签到次数

  • 48
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值