欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

MyFirstDay_答案_1.**猫(自己整理)

程序员文章站 2022-06-13 15:26:00
1>***猫: python基础类: 技能类: 业务类: 你对电商的业务了解吗? 我问你一个具体的电商的业务啊:电商在大多数领域中都是有库存的,我在下订单的时候,我的库存的变化,你知道吗?(我问的是你怎么去处理库存的变化?)因为客户下单的时候,有些是去减库存了,有些不减对吧?你的理解呢? 你事务的时 ......

1>***猫:

  1. python基础类:

    1. 字符串反转的常用处理方式:
      # 方法一:使用字符串切片
      s = "hello python"
      result = s[::-1]
      print(result)
      # [out]nohtyp olleh
      
      
      # 方法二:使用列表的reverse方法
      l = list(s)
      l.reverse()
      result = "".join(l)
      print(result)
      
      # 方法三:使用reduce
      from functools import reduce
      
      s = "hello python"
      
      result = reduce(lambda x, y: y + x, s)
      
      print(result)
      
      
      # 方法四:使用递归函数
      def func(s):
          if len(s) < 1:
              return s
          return func(s[1:]) + s[0]
      
      
      result = func(s)
      print(result)
      
      
      # 方法五:使用栈
      def func(s):
          l = list(s)  # 模拟全部入栈
          result = ""
          while len(l) > 0:
              result += l.pop()  # 模拟出栈
          return result
      
      
      result = func(s)
      print(result)
      
      
      # 方法六:for循环
      def func(s):
          result = ""
          max_index = len(s) - 1
          for index, value in enumerate(s):
              result += s[max_index - index]
          return result
      
      
      result = func(s)
      print(result)

       

      1. 你说一下python中的迭代器、生成器、装饰器

         

        # 可以被next()函数调用并不断返回下一个值的对象称为迭代器:iterator
          
        # 在python中,这种一边循环一边计算的机制,称为生成器:generator
        
        # 在代码运行期间动态增加功能的方式,称之为“装饰器”: decorator
        
        # 生成器和迭代器的区别:
        #     1、语法方面来讲:
        #         生成器是用函数中yield语句来创建的。迭代器的创建首先跟函数无关,可以用iter([1,2])来创建。
        #     2、使用方面来讲:
        #         由于生成器是使用函数的方式创建的,所以生成器里面的所有过程都会被执行,但请注意生成器里面的过程只有在被next()调用或者for循环调用时,里面的过程才会被执行

         

    2. 如果让你实现一个迭代器,你会怎么做
      # 迭代器有两个基本的方法:iter() 和 next()。
      # 字符串,列表或元组对象都可用于创建迭代器:
      list = [1, 2, 3, 4]
      it = iter(list)
      print(next(it))
      print(next(it))
      # 迭代器对象可以使用常规for语句进行遍历:
      list = [1, 2, 3, 4]
      it = iter(list)
      for x in it:
          print(x, end=" ")
      
      # 也可以使用 next() 函数:
      import sys
      
      list = [1, 2, 3, 4]
      it = iter(list)
      while true:
          try:
              print(next(it))
          except stopiteration:
              sys.exit()
      
      # 创建一个迭代器
      # 把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__() 与 __next__() 。
      # # 如果你已经了解的面向对象编程,就知道类都有一个构造函数,python 的构造函数为 __init__(), 它会在对象初始化的时候执行。
      # __iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 stopiteration 异常标识迭代的完成.
      # __next__() 方法(python 2 里是 next())会返回下一个迭代器对象。
      # 创建一个返回数字的迭代器,初始值为 1,逐步递增 1:
      # stopiteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 stopiteration 异常来结束迭代。
      # 在 20 次迭代后停止执行:
      
      
      class mynumbers:
          def __iter__(self):
              self.a = 1
              return self
      
          def __next__(self):
              if self.a <= 20:
                  x = self.a
                  self.a += 1
                  return x
              else:
                  raise stopiteration
      
      
      myclass = mynumbers()
      myiter = iter(myclass)
      
      for x in myiter:
          print(x)

       

    3. 怎么样获取一个生成器?
      # 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

       

    4. 你有用过yield对象吗?得到的是一个什么样的对象?
      # 调用一个生成器函数,返回的是一个迭代器对象。
      # 以下实例使用 yield 实现斐波那契数列:
      import sys
      
      
      def fibonacci(n):
          a, b, counter = 0, 1, 0
          while true:
              if(counter > n):
                  return
              yield a
              a, b = b, a + b
              counter += 1
      f = fibonacci(10)   # f 是一个迭代器,由生成器返回生成
      
      while true:
          try:
              print(next(f), end=" ")
          except stopiteration:
              sys.exit()

       

    5. 你有了解过python中的协程吗?
      # 协程,又称微线程,纤程。英文名coroutine。
      # 协程的概念很早就提出来了,但直到最近几年才在某些语言(如lua)中得到广泛应用。
      # 子程序,或者称为函数,在所有语言中都是层级调用,比如a调用b,b在执行过程中又调用了c,c执行完毕返回,b执行完毕返回,最后是a执行完毕。
      # 所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。
      # 子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。
      # 协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。
      def consumer():
          r = ''
          while true:
              n = yield r
              if not n:
                  return
              print('[consumer] consuming %s...' % n)
              r = '200 ok'
      
      def produce(c):
          c.send(none)
          n = 0
          while n < 5:
              n = n + 1
              print('[producer] producing %s...' % n)
              r = c.send(n)
              print('[producer] consumer return: %s' % r)
          c.close()
      
      c = consumer()
      produce(c)

       

  2. 技能类:

    1. 你有用过git吗?
      git是分布式版本控制系统
      集中式vs分布式:
      集中式版本控制系统,版本库集中存放在*服务器,必须要联网才能工作,没有历史版本库。
      分布式版本控制系统,没有“*服务器”,每个开发人员电脑上都有一个完整的版本库。
      分布式优势:安全性更高,无需联网,若“*服务器”故障,任何一个其他开发者本地都有最新的带历史记录的版本库。
      主要区别在于历史版本库的存放,集中式历史版本只存在于*服务器,而分布式中每个本地库都有历史记录存放。

       

    2. 你有合过代码吗?
      git status  
      查看下当前代码状态,有需要提交的就提交,没有需要提交的就保持原样
      git pull
      拉取远程代码,使得本地代码保持最新
      git branch -a 
      查看最新代码所在分支
      remotes/origin/head -> origin/master
      最新的分支会有remotes/origin/head ->指明
      git merge origin/master
      执行合并命令把最新分支代码合并到本地当前分支
      git diff
      查看冲突信息
      git status 
      查看下状态看看那些文件需要手工调整
      git add .
      把修改好的文件添加到索引
      git commit -m '合并xxx分支代码'
      提交代码
      git push
      把合并好的代码推送到远程
      
      如果合并过程中出现问题,可以使用以下命令回退到日志的合并之前的位置
      git reset --hard commit_id 

       

    3. 你有布署过代码吗?
    4. 布署的步骤是怎样的?你是怎么样把你的代码放到服务器中的?
      # 1、提交代码当在本地开发&测试完毕后,将代码合并到 master 分支,并 push 到远程的 git 仓库。
      # 通常在开发中使用 git 作为代码版本管理工具,通过创建一个 dev 分支来进行开发工作,在发布的时候再将代码合并到 master 分支,这样可以保证 master 分支永远都是稳定的版本
      git push origin master
      # 2、拉取并部署代码到预发机通过以下命令从 git 仓库获取到最新的代码
      git pull
      # 因为开发运行环境和线上运行环境的数据库、缓存等配置的差异,拉取的代码一般无法直接在预发机上直接运行。通常的做法是,在预发机上执行一个 shell 脚本,将线上的配置覆盖开发环境的配置。
      cp code/path/to/config-dist code/path/to/config
      # 预发机的主要作用是留出缓冲的空间,检验代码是否在线上环境可以正常工作。对于一个 web 项目,我们可以设置域名的 host 配置,直接访问预发机。
      # 3、同步代码到线上一般情况下,一个 web 项目都会有多个业务机,通过负载均衡将请求流量平均分配的 n 台机器,以提高服务的承载能力和可用性。
      # 因此,这里面临着一个发布代码到 n 台机器的问题。显然,我们不能一台台的发布,这样效率太低了。通常,我们通过在预发机上执行 shell 脚本,将代码 rsync 到 n 台机器上。
      rsync /path/to/code user@127.0.0.1::path/to/code --exclude-list=exclude.list
      # 4、快速回滚发布完代码后,我们会在预发机的 git 仓库上执行 :
      git tag v20160522
      # 记录此次发布的版本。如果在发布后发现出了问题,可以在预发机的 git 仓库执行如下命令:
      git tag -l
      # 找出上一次发布的版本,并回滚代码:
      git reset --hard v20160521
      # 然后,再通过步骤 3 的方式,将回滚的代码同步到 n 台业务机上。
      #
      # 作者:ceelog
      # 链接:https://www.jianshu.com/p/79dc6e0278e2
      # 来源:简书
      # 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

       

    5. docker你有使用过吗?谈一谈
      # docker镜像可以完全看作一台全新的电脑使用,无论什么镜像都是对某一东西进行了配置,然后打包后可以快速移植到需要的地方直接使用
      # 省去复杂的配置工作
      # 比如python web项目部署,如果是新部署,需要装系统,配置各类环境,费时费力还容易出错,
      # 而docker就可以省去配置环境的麻烦,直接把所需的包丢进去,实现分分钟部署一个项目
      # 给ubuntu安装docker,我安装的是docker ee:
      wget -qo- https://get.docker.com/ | sh
      # 安装好之后,便是启动docker
      sudo service docker start
      # 启动后,是没有任何镜像的,不过可以通过pull命令获取相关镜像
      sudo docker images
      sudo docker pull nginx
      sudo docker pull nginx 默认获取最新版本,即tag为latest的,如果要获取其他版本,则需要使用 sudo docker pull nginx:xxxx
      # 获取镜像后,通过docker run使其运行起来
      sudo docker ps -a 列出所有容器, 不加 -a 仅列出正在运行的,像退出了的或者仅仅只是创建了的就不列出来
      sudo docker run -d -p 8800:80 --name nginx_xiao  nginx  #运行指定的镜像
      dudo docker run -d --privileged=true -p 83:80 --name nginx83 nginx   #提升权限
      #宿主主机端口:容器内部端口
      #   -d  后台运行
      #   -p 8800:80 是指定对外暴露的端口  容器内部用80 对应外部宿主主机的的8800  代理一样
      #   --name指定容器的名字  最后的nginx 代码要运行的镜像名字  有tag的加上tag 如 nginx:xxx  默认为latest
      # 然后访问宿主主机地址+8800端口
      # pull到的镜像肯定有很多需要修改的地方,比如配置文件等或者要自己增加些什么玩意儿进去
      sudo docker exec -it 54d26bbce3d6 /bin/bash
      # 通过exec命令进入到容器内部进行操作, 其中字符串部分可以是容器id或容器名字
      # 进入之后就和操作新的系统一样,操作完成之后输入exit退出
      # 那么问题又来了, 进入容器内部并修改了东西后,怎么生成新的镜像供下次直接使用
      sudo docker commit nginx_huang huang/nginx:v1.0
        nginx_huang 表示我们刚修改的容器名字或者id
          huang/nginx:v1.0 为保存的镜像名字 :后面为tag
      
      # 刚刚commit的镜像仅仅是保存在本地的,如果要提交到网络上供其他人pull 使用呢? 如 https://cloud.docker.com/
      # 1.在https://cloud.docker.com/上注册一个账号
      # 2.提交本地镜像到https://cloud.docker.com/上去
      # 这样别人就可以通过docker pull xiaochangwei/nginx:v1.0 来获取并使用这个镜像了
      sudo docker commit nginx_huang huang/nginx:v1.0 镜像名里面包含了 我注册的账户名,这里需要一致,否则无法push
      
      # 到这里镜像怎么获取,查看,启动,编辑,保存,提交 容器查看 都知道了,但是怎么停止、启动、删除容器呢
      # 1.通过 sudo docker ps -a查看存在的容器信息
      # 2.通过 sudo docker start/stop/restart xxx 来启动、停止、重启指定的容器
      # 2.通过 sudo docker rm xxx 指定容器名或者容器id来删除,删除前请先停止容器,保证在非运行状态
      # 同样镜像的删除按如下操作
      # 1.通过sudo docker images 列出所有镜像
      # 2.通过sudo docker rmi xxx 来删除指定的镜像,镜像存在依赖关系,先删除最下层,最后删除顶层,建议根据镜像名字来删除

       

    6. 你平时是怎么去debug你的代码的?比如说有些代码是第一次写,你不知道你写的是不是对的,一但出现了问题,你要怎么去定位它,检测它?
      # 1.操作步骤:
      # 1-1.添加断点:直接在标记处点击鼠标左键即可。(删除断点只需再点击断点处即可)
      def add(x, y):
          z = x + y
          return z
      
      def sub(x, y):
          z = x - y
          return z
      
      def debug_test():           # 点击左侧,添加断点
          a = 10
          b = 5
          sum = add(a, b)
          sub = sub(a, b)
          print(sum)
          print(sub)
      
      if __name__ == "__main__":
          debug_test()
      
      # 1-2.debug下运行代码:七星瓢虫shift + f9
      # 1-3.按照所需调试进行代码调试。debug的调试方式如下所示:
      # 分别为:
      # 1.show execution point (f10)  显示当前所有断点
      # 2.step over(f8)  单步调试(若函数a内存在子函数a时,不会进入子函数a内执行单步调试,而是把子函数a当作一个整体,一步执行。)
      def add(x, y):
          z = x + y
          return z
      
      def sub(x, y):
          z = x - y
          return z
      
      def debug_test():           # 3 # 断点处
          a = 10                  # 4
          b = 5                   # 5
          sum = add(a, b)         # 6
          sub = sub(a, b)         # 7
          print(sum)              # 8
          print(sub)              # 9
      
      if __name__ == "__main__":  # 1
          debug_test()            # 2 # 10
      
      # 4.step into my code(alt + shift +f7) 执行下一行但忽略libraries(导入库的语句)(目前感觉没什么用)
      def add(x, y):
          z = x + y               # 7
          return z                # 8
      
      def sub(x, y):
          z = x - y               # 11
          return z                # 12
      
      def debug_test():           # 3 # 断点处
          a = 10                  # 4
          b = 5                   # 5
          sum = add(a, b)         # 9
          sub = sub(a, b)         # 13
          print(sum)              # 14
          print(sub)              # 15
      
      if __name__ == "__main__":  # 1
          debug_test()            # 2 # 16
      # 5.force step into(alt + shift +f7) 执行下一行忽略lib和构造对象等  (目前感觉没什么用)
      # 6.step out(shift+f8)当目前执行在子函数a中时,选择该调试操作可以直接跳出子函数a,而不用继续执行子函数a中的剩余代码。并返回上一层函数。
      # 7.run to cursor(alt +f9) 直接跳到下一个断点
      # ---------------------
      # 作者:放下扳手&拿起键盘
      # 来源:csdn
      # 原文:https://blog.csdn.net/william_hehe/article/details/80898031

       

    7. 你有多长时间的软件开发经验?
  3. 业务类:

    1. 你对电商的业务了解吗?

      # 电商项目用户部分:主要分为三大类——1、用户浏览商品 ,2、购买商品 ,3、管理订单
      # 电商项目管理部分:主要也为三大类——1、商品数据整合网站 ,2、接受用户信息数据存入后台(注册和管理),3、处理用户订单问题

       

    2. 我问你一个具体的电商的业务啊:电商在大多数领域中都是有库存的,我在下订单的时候,我的库存的变化,你知道吗?(我问的是你怎么去处理库存的变化?)因为客户下单的时候,有些是去减库存了,有些不减对吧?你的理解呢?

      # 电商项目用户部分:主要分为三大类——1、用户浏览商品 ,2、购买商品 ,3、管理订单
      # 电商项目管理部分:主要也为三大类——1、商品数据整合网站 ,2、接受用户信息数据存入后台(注册和管理),3、处理用户订单问题
      
      #  一个简单的使用场景:一件商品的库存只有5件,同时a用户买了5个,b用户买了5个,都提交数据,照成库存不足的问题。
      #         逻辑:根据一般电商商品的模型类,生成订单一般包括订单类(order)和订单详情类(detailorder),这两张表根据外键order_id 进行关联,所以是同生共死的关系,所以我们在这里用事务来控制。那么python如何解决库存问题呢?
      #         python 提供了2种方法解决该问题的问题:1,悲观锁;2,乐观锁
      #         悲观锁:在查询商品储存的时候加锁 select_for_update()  在发生事务的commit或者是事务的rollback时,自动释放该锁,这样其他用户就可以接着查询该商品。
      #         乐观锁:乐观锁不是真正的锁,在创建订单之前查询商品的库存,在创建订单详情表前,update更新查询数据,如果两次查询的库存量一样就创建详情表,并减去库存,否则,循环三次,如果都不一样,就发生rollback。
      #         使用场景:并发量高的时候使用悲观锁,缺点:加锁消耗资源
      #              并发量低的时候使用乐观锁,缺点:乐观锁循环耗费时间。
      # ---------------------
      # 作者:huangyali_python
      # 来源:csdn
      # 原文:https://blog.csdn.net/huangyali_python/article/details/79511654

       

       

    3. 你事务的时候有没有出现死锁的情况?

      # 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁
      # 解决方法,递归锁,在python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁rlock。
      from threading import thread,lock
      import time
      mutexa=lock()
      mutexb=lock()
      
      class mythread(thread):
          def run(self):
              self.func1()
              self.func2()
          def func1(self):
              mutexa.acquire()
              print('\033[41m%s 拿到a锁\033[0m' %self.name)
      
              mutexb.acquire()
              print('\033[42m%s 拿到b锁\033[0m' %self.name)
              mutexb.release()
      
              mutexa.release()
      
          def func2(self):
              mutexb.acquire()
              print('\033[43m%s 拿到b锁\033[0m' %self.name)
              time.sleep(2)
      
              mutexa.acquire()
              print('\033[44m%s 拿到a锁\033[0m' %self.name)
              mutexa.release()
      
              mutexb.release()
      
      if __name__ == '__main__':
          for i in range(10):
              t=mythread()
              t.start()
      # 这个rlock内部维护着一个lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用rlock代替lock,则不会发生死锁,二者的区别是:递归锁可以连续acquire多次,而互斥锁只能acquire一次
      from threading import thread,rlock
      import time
      
      mutexa=mutexb=rlock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的情况,则counter继续加1,这期间所有其他线程都只能等待,等待该线程释放所有锁,即counter递减到0为止
      
      class mythread(thread):
          def run(self):
              self.func1()
              self.func2()
          def func1(self):
              mutexa.acquire()
              print('\033[41m%s 拿到a锁\033[0m' %self.name)
      
              mutexb.acquire()
              print('\033[42m%s 拿到b锁\033[0m' %self.name)
              mutexb.release()
      
              mutexa.release()
      
          def func2(self):
              mutexb.acquire()
              print('\033[43m%s 拿到b锁\033[0m' %self.name)
              time.sleep(2)
      
              mutexa.acquire()
              print('\033[44m%s 拿到a锁\033[0m' %self.name)
              mutexa.release()
      
              mutexb.release()
      
      if __name__ == '__main__':
          for i in range(10):
              t=mythread()
              t.start()

       

    4. 你刚刚说到的下单之后的库存锁的问题?你是在下单的时候去扣库存,还是在支付完成了以后去扣?

      # 一、扣减库存的三种方案
      # (1)下单减库存
      #   用户下单时减库存
      #   优点:实时减库存,避免付款时因库存不足减库存的问题
      #   缺点:恶意买家大量下单,将库存用完,但是不付款,真正想买的人买不到
      # (2)付款减库存
      #   下单页面显示最新的库存,下单时不会立即减库存,而是等到支付时才会减库存。
      #   优点:防止恶意买家大量下单用光库存,避免下单减库存的缺点
      #   缺点:下单页面显示的库存数可能不是最新的库存数,而库存数用完后,下单页面的库存数没有刷新,出现下单数超过库存数,若支付的订单数超过库存数,则会出现支付失败。
      # (3)预扣库存
      #   下单页面显示最新的库存,下单后保留这个库存一段时间(比如10分钟),超过保留时间后,库存释放。若保留时间过后再支付,如果没有库存,则支付失败。例如:要求30分钟内支付订单。
      #   优点:结合下单减库存的优点,实时减库存,且缓解恶意买家大量下单的问题,保留时间内未支付,则释放库存。
      #   缺点:保留时间内,恶意买家大量下单将库存用完。并发量很高的时候,依然会出现下单数超过库存数。
      # 二、如何解决恶意买家下单的问题
      # 这里的恶意买家指短时间内大量下单,将库存用完的买家。
      # (1)限制用户下单数量
      #   优点:限制恶意买家下单
      #   缺点:用户想要多买几件,被限制了,会降低销售量
      # (2)标识恶意买家
      #   优点:卖家设定一个备用库存,当支付时,库存已用完,扣减备用库存数,这就是常见的补货场景
      #   缺点:因高并发场景下,数据可能存在不一致性的问题
      # 三、如何解决下单成功而支付失败(库存不足)的问题
      # (1)备用库存
      #   商品库存用完后,如果还有用户支付,直接扣减备用库存。
      #   优点:缓解部分用户支付失败的问题
      #   缺点:备用库存只能缓解问题,不能从根本上解决问题。另外备用库存针对普通商品可以,针对特殊商品这种库存少的,备用库存量也不会很大,还是会出现大量用户下单成功却因库存不足而支付失败的问题。
      # 四、如何解决高并发下库存超卖的场景
      # 库存超卖最简单的解释就是多成交了订单而发不了货。
      # 场景:
      # 用户a和b成功下单,在支付时扣减库存,当前库存数为10。因a和b查询库存时,都还有库存数,所以a和b都可以付款。
      # a和b同时支付,a和b支付完成后,可以看做两个请求回调后台系统扣减库存,有两个线程处理请求,两个线程查询出来的库存数 inventory=10,
      # 然后a线程更新最终库存数 lastinventory=inventory - 1 = 9,
      # b线程更新库存数 lastinventory=inventory - 1 = 9。
      # 而实际最终的库存应是8才对,这样就出现库存超卖的情况,而发不出货。
      # 那如何解决库存超卖的情况呢?
      # 1.sql语句更新库存时,如果扣减库存后,库存数为负数,直接抛异常,利用事务的原子性进行自动回滚。
      # 2.利用sql语句更新库存,防止库存为负数
      #  update [库存表] set 库存数 - 1 where 库存数 - 1 > 0
      #  如果影响条数大于1,则表示扣减库存成功,否则订单失败,并退款。
      # 五、秒杀场景下如何扣减库存
      # (1)下单减库存
      # 因秒杀场景下,大部分用户都是想直接购买商品的,可以直接用下单减库存。
      # 大量用户和恶意用户都是同时进行的,区别是正常用户会直接购买商品,恶意用户虽然在竞争抢购的名额,但是获取到的资格和普通用户一样,所以下单减库存在秒杀场景下,恶意用户下单并不能造成之前说的缺点。
      # 而且下单直接扣减库存,这个方案更简单,在第一步就扣减库存了。
      # (2)将库存放到redis缓存中
      #   查询缓存要比查询数据库快,所以将库存数放在缓存中,直接在缓存中扣减库存。然后在通过mq异步完成数据库处理。
      # (3)使用量自增方式
      # 可以先增加已使用量,然后与设定的库存进行比较,如果超出,则将使用量减回去。
      #
      # 项目中用到了很多机制,但是没有总结出来,学习架构需要不断地总结。
      #
      # 六 第三方支付
      # 1 支付成功多次回调:把减库存放在微信支付的成功回调url的方法里面。但是微信支付成功之后微信支付平台会发送8次请求到回调地址。这样的做法就会导致库存减少,一定要验证返回的编号是否已经完成扣减。

       

    5. 如果用户不支付,你的库存怎么办?(我还是没有听到你说,处理库存的点)

      # 设置支付时间,下单应该是锁定库存但是不减库存。如果你整个下单支付是队列方式的话,就支付完成之后立即减库存。超时就释放掉锁

       

    6. 如果支付失败了,你对库存有一个什么样的动作?

      # 支付完成后对库存进行更新,更新库存在事务中进行同时加锁,加的锁要避免堵塞,支付失败或者支付异常回滚,重新更新库存
      
      # 方案1:在下单就锁定库存
      #     优点:可以解决库存减扣问题
      #     缺点:体验差,如果只下单未付款,库存被锁定,让有意愿购买的用户无从下单,对销售业务有很大影响;
      # 
      # 方案2:支付后减扣库存
      #     优点:防止恶意下单,只要有足够的实际库存,随便多少意向客户下单
      #     缺点:下单页面显示的库存数可能不是最新的库存数,其他用户可能提示库存不足,可能出现超卖问题。
      # 
      # 方案3:调起支付界面前锁定库存
      # 
      #     优点:防止恶意下单,只要有足够的实际库存,随便多少意向客户下单
      #     缺点:体验差,有可能在支付时提示库存被其他用户锁定,提示库存不足
      # 
      # 方案4:下单占库存根据库存大小决定库存锁定时间
      # 
      #      库存充足下单占库存,设定库存最大占用时间,按库存大小限购数量按策略缩减库存占用时间
      # 
      #  
      # 
      #  
      # 
      # 具体以哪种方案为主还是要看公司的业务做决定
      # 
      # 
      #  
      # 
      # 下面主要讲解方案3 支付前锁定库存的的实现步骤,以下是使用到的关键表
      # 
      # -------------------------------------------------------------------------------------
      # 订单表
      # 订单唯一编号     库存锁定状态(1库存已锁定 2库存已释放 3库存已确认,库存减扣成功)
      # -------------------------------------------------------------------------------------
      # 订单详细表
      # 订单编号      商品id     购买数量
      # -------------------------------------------------------------------------------------
      # 商品库存表
      # 商品id     库存数量     实际锁定库存数量  预锁定库存数量   限购数量
      # -------------------------------------------------------------------------------------
      # 
      # a商品库存 1个
      # 
      # 
      # 用户1  下单1个
      # 用户2  下单1个
      # 
      # 
      # 业务场景及解决方式:
      # 1.支付前预占库存 :用户1和用户2可能同时点击支付按钮,此时对于商品锁定库存来说只能有一个用户会锁定a商品的库存,剩下一个肯定锁定失败,此时应该提示其中一个用户(库存可能不足,请稍后再试),这里更新库存减扣应该都有前置条件的或者说版本号
      # 
      # 2.限制支付时间,需要设置一个支付时间
      # 
      # (设置三方支付过期时间为30分钟)调起支付页面锁定库存30分钟,30分钟后还未支付则还原预减库存
      # 
      # 3.检测恶意下单用户加入到店铺黑名单
      # 
      # 4.加入限购
      # 
      # 5.优化方式
      # 另一个用户跳转到三方支付界面,
      # 如果此用户取消支付,客户端应该发送一条消息告诉服务端恢复库存锁定,
      # 如果此用户支付成功,客户端应该发送一条消息告诉服务端确认减扣库存,
      # 客户端可能因某种情况发送失败,此时要考虑使用定时任务恢复超过多久库存还处于(根据锁定状态)锁定中的订单商品库存,应该先调用支付系统关闭原有未完成支付的订单,然后再恢复商品锁定库存
      # 支付系统异步通知支付成功修改库存,此时根据库存锁定状态进行不同的业务处理(1库存已锁定 2库存已释放 3库存已确认,库存减扣成功)
      # --------------------- 
      # 作者:www.weixuehu.com 
      # 来源:csdn 
      # 原文:https://blog.csdn.net/t_332741160/article/details/80340188 
      # 版权声明:本文为博主原创文章,转载请附上博文链接!