1>***猫:
python基础类:
- 字符串反转的常用处理方式:
# 方法一:使用字符串切片 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)
- 你说一下python中的迭代器、生成器、装饰器
# 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator # 在Python中,这种一边循环一边计算的机制,称为生成器:generator # 在代码运行期间动态增加功能的方式,称之为“装饰器”: Decorator # 生成器和迭代器的区别: # 1、语法方面来讲: # 生成器是用函数中yield语句来创建的。迭代器的创建首先跟函数无关,可以用iter([1,2])来创建。 # 2、使用方面来讲: # 由于生成器是使用函数的方式创建的,所以生成器里面的所有过程都会被执行,但请注意生成器里面的过程只有在被next()调用或者for循环调用时,里面的过程才会被执行
- 你说一下python中的迭代器、生成器、装饰器
- 如果让你实现一个迭代器,你会怎么做
# 迭代器有两个基本的方法: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)
- 怎么样获取一个生成器?
# 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
- 你有用过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()
- 你有了解过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)
技能类:
- 你有用过git吗?
Git是分布式版本控制系统 集中式VS分布式: 集中式版本控制系统,版本库集中存放在中央服务器,必须要联网才能工作,没有历史版本库。 分布式版本控制系统,没有“中央服务器”,每个开发人员电脑上都有一个完整的版本库。 分布式优势:安全性更高,无需联网,若“中央服务器”故障,任何一个其他开发者本地都有最新的带历史记录的版本库。 主要区别在于历史版本库的存放,集中式历史版本只存在于中央服务器,而分布式中每个本地库都有历史记录存放。
- 你有合过代码吗?
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
- 你有布署过代码吗?
- 布署的步骤是怎样的?你是怎么样把你的代码放到服务器中的?
# 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 # 来源:简书 # 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
- 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 来删除指定的镜像,镜像存在依赖关系,先删除最下层,最后删除顶层,建议根据镜像名字来删除
- 你平时是怎么去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
- 你有多长时间的软件开发经验?
- 你有用过git吗?
业务类:
你对电商的业务了解吗?
# 电商项目用户部分:主要分为三大类——1、用户浏览商品 ,2、购买商品 ,3、管理订单 # 电商项目管理部分:主要也为三大类——1、商品数据整合网站 ,2、接受用户信息数据存入后台(注册和管理),3、处理用户订单问题
我问你一个具体的电商的业务啊:电商在大多数领域中都是有库存的,我在下订单的时候,我的库存的变化,你知道吗?(我问的是你怎么去处理库存的变化?)因为客户下单的时候,有些是去减库存了,有些不减对吧?你的理解呢?
# 电商项目用户部分:主要分为三大类——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
你事务的时候有没有出现死锁的情况?
# 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁 # 解决方法,递归锁,在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()
你刚刚说到的下单之后的库存锁的问题?你是在下单的时候去扣库存,还是在支付完成了以后去扣?
# 一、扣减库存的三种方案 # (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次请求到回调地址。这样的做法就会导致库存减少,一定要验证返回的编号是否已经完成扣减。
如果用户不支付,你的库存怎么办?(我还是没有听到你说,处理库存的点)
# 设置支付时间,下单应该是锁定库存但是不减库存。如果你整个下单支付是队列方式的话,就支付完成之后立即减库存。超时就释放掉锁
如果支付失败了,你对库存有一个什么样的动作?
# 支付完成后对库存进行更新,更新库存在事务中进行同时加锁,加的锁要避免堵塞,支付失败或者支付异常回滚,重新更新库存 # 方案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 # 版权声明:本文为博主原创文章,转载请附上博文链接!