专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

实现高并发秒杀的七种方式

  引言商品秒杀超卖解决商品超卖方式一(改进版加锁)方式二(AOP版加锁)方式三(悲观锁一)方式四(悲观锁二)方式五(乐观锁)方式六(阻塞队列)方式七(Disruptor队列)小结1。引言
  高并发场景在现场的日常工作中很常见,特别是在互联网公司中,这篇文章就来通过秒杀商品来模拟高并发的场景。文章末尾会附上文章的所有代码、脚本和测试用例。本文环境:SpringBoot2。5。7MySQL8。0XMybatisPlusSwagger2。9。2模拟工具:Jmeter模拟场景:减库存创建订单模拟支付2。商品秒杀超卖
  在开发中,对于下面的代码,可能很熟悉:在Service里面加上Transactional事务注解和Lock锁
  控制层:ControllerApiOperation(value秒杀实现方式Lock加锁)PostMapping(startlock)publicResultstartLock(longskgId){try{log。info(开始秒杀方式一。。。);finallonguserId(int)(newRandom()。nextDouble()(99999100001))10000;ResultresultsecondKillService。startSecondKillByLock(skgId,userId);if(result!null){log。info(用户:{}{},userId,result。get(msg));}else{log。info(用户:{}{},userId,哎呦喂,人也太多了,请稍后!);}}catch(Exceptione){e。printStackTrace();}finally{}returnResult。ok();}
  业务层:ServiceOverrideTransactional(rollbackForException。class)publicResultstartSecondKillByLock(longskgId,longuserId){lock。lock();try{校验库存SecondKillsecondKillsecondKillMapper。selectById(skgId);IntegernumbersecondKill。getNumber();if(number0){扣库存secondKill。setNumber(number1);secondKillMapper。updateById(secondKill);创建订单SuccessKilledkillednewSuccessKilled();killed。setSeckillId(skgId);killed。setUserId(userId);killed。setState((short)0);killed。setCreateTime(newTimestamp(System。currentTimeMillis()));successKilledMapper。insert(killed);模拟支付PaymentpaymentnewPayment();payment。setSeckillId(skgId);payment。setSeckillId(skgId);payment。setUserId(userId);payment。setMoney(40);payment。setState((short)1);payment。setCreateTime(newTimestamp(System。currentTimeMillis()));paymentMapper。insert(payment);}else{returnResult。error(SecondKillStateEnum。END);}}catch(Exceptione){thrownewScorpiosException(异常了个乖乖);}finally{lock。unlock();}returnResult。ok(SecondKillStateEnum。SUCCESS);}
  对于上面的代码应该没啥问题吧,业务方法上加事务,在处理业务的时候加锁。
  但上面这样写法是有问题的,会出现超卖的情况,看下测试结果:模拟1000个并发,抢100商品
  Jmeter不了解的,可以参考这篇文章:
  https:blog。csdn。netzxd1435513775articledetails106372446
  这里在业务方法开始加了锁,在业务方法结束后释放了锁。但这里的事务提交却不是这样的,有可能在事务提交之前,就已经把锁释放了,这样会导致商品超卖现象。所以加锁的时机很重要!3。解决商品超卖
  对于上面超卖现象,主要问题出现在事务中锁释放的时机,事务未提交之前,锁已经释放。(事务提交是在整个方法执行完)。如何解决这个问题呢,就是把加锁步骤提前可以在controller层进行加锁可以使用Aop在业务方法执行之前进行加锁3。1方式一(改进版加锁)ApiOperation(value秒杀实现方式Lock加锁)PostMapping(startlock)publicResultstartLock(longskgId){在此处加锁lock。lock();try{log。info(开始秒杀方式一。。。);finallonguserId(int)(newRandom()。nextDouble()(99999100001))10000;ResultresultsecondKillService。startSecondKillByLock(skgId,userId);if(result!null){log。info(用户:{}{},userId,result。get(msg));}else{log。info(用户:{}{},userId,哎呦喂,人也太多了,请稍后!);}}catch(Exceptione){e。printStackTrace();}finally{在此处释放锁lock。unlock();}returnResult。ok();}
  上面这样的加锁就可以解决事务未提交之前,锁释放的问题,可以分三种情况进行压力测试:并发数1000,商品100并发数1000,商品1000并发数2000,商品1000
  对于并发量大于商品数的情况,商品秒杀一般不会出现少卖的请况,但对于并发数小于等于商品数的时候可能会出现商品少卖情况,这也很好理解。
  对于没有问题的情况就不贴图了,因为有很多种方式,贴图会太多
  3。2方式二(AOP版加锁)
  对于上面在控制层进行加锁的方式,可能显得不优雅,那就还有另一种方式进行在事务之前加锁,那就是AOP
  自定义AOP注解Target({ElementType。PARAMETER,ElementType。METHOD})Retention(RetentionPolicy。RUNTIME)DocumentedpublicinterfaceServiceLock{Stringdescription()default;}
  定义切面类Slf4jComponentScopeAspectOrder(1)order越小越是最先执行,但更重要的是最先执行的最后结束publicclassLockAspect{思考:为什么不用synchronizedservice默认是单例的,并发下lock只有一个实例privatestaticLocklocknewReentrantLock(true);互斥锁参数默认false,不公平锁Service层切点用于记录错误日志Pointcut(annotation(com。scorpios。secondkill。aop。ServiceLock))publicvoidlockAspect(){}Around(lockAspect())publicObjectaround(ProceedingJoinPointjoinPoint){lock。lock();Objectobjnull;try{objjoinPoint。proceed();}catch(Throwablee){e。printStackTrace();thrownewRuntimeException();}finally{lock。unlock();}returnobj;}}
  在业务方法上添加AOP注解OverrideServiceLock使用Aop进行加锁Transactional(rollbackForException。class)publicResultstartSecondKillByAop(longskgId,longuserId){try{校验库存SecondKillsecondKillsecondKillMapper。selectById(skgId);IntegernumbersecondKill。getNumber();if(number0){扣库存secondKill。setNumber(number1);secondKillMapper。updateById(secondKill);创建订单SuccessKilledkillednewSuccessKilled();killed。setSeckillId(skgId);killed。setUserId(userId);killed。setState((short)0);killed。setCreateTime(newTimestamp(System。currentTimeMillis()));successKilledMapper。insert(killed);支付PaymentpaymentnewPayment();payment。setSeckillId(skgId);payment。setSeckillId(skgId);payment。setUserId(userId);payment。setMoney(40);payment。setState((short)1);payment。setCreateTime(newTimestamp(System。currentTimeMillis()));paymentMapper。insert(payment);}else{returnResult。error(SecondKillStateEnum。END);}}catch(Exceptione){thrownewScorpiosException(异常了个乖乖);}returnResult。ok(SecondKillStateEnum。SUCCESS);}
  控制层:ApiOperation(value秒杀实现方式二Aop加锁)PostMapping(startaop)publicResultstartAop(longskgId){try{log。info(开始秒杀方式二。。。);finallonguserId(int)(newRandom()。nextDouble()(99999100001))10000;ResultresultsecondKillService。startSecondKillByAop(skgId,userId);if(result!null){log。info(用户:{}{},userId,result。get(msg));}else{log。info(用户:{}{},userId,哎呦喂,人也太多了,请稍后!);}}catch(Exceptione){e。printStackTrace();}returnResult。ok();}
  这种方式在对锁的使用上,更高阶、更美观!3。3方式三(悲观锁一)
  除了上面在业务代码层面加锁外,还可以使用数据库自带的锁进行并发控制。
  悲观锁,什么是悲观锁呢?通俗的说,在做任何事情之前,都要进行加锁确认。这种数据库级加锁操作效率较低。
  使用forupdate一定要加上事务,当事务处理完后,forupdate才会将行级锁解除
  如果请求数和秒杀商品数量一致,会出现少卖ApiOperation(value秒杀实现方式三悲观锁)PostMapping(startpeslockone)publicResultstartPesLockOne(longskgId){try{log。info(开始秒杀方式三。。。);finallonguserId(int)(newRandom()。nextDouble()(99999100001))10000;ResultresultsecondKillService。startSecondKillByUpdate(skgId,userId);if(result!null){log。info(用户:{}{},userId,result。get(msg));}else{log。info(用户:{}{},userId,哎呦喂,人也太多了,请稍后!);}}catch(Exceptione){e。printStackTrace();}returnResult。ok();}
  业务逻辑OverrideTransactional(rollbackForException。class)publicResultstartSecondKillByUpdate(longskgId,longuserId){try{校验库存悲观锁SecondKillsecondKillsecondKillMapper。querySecondKillForUpdate(skgId);IntegernumbersecondKill。getNumber();if(number0){扣库存secondKill。setNumber(number1);secondKillMapper。updateById(secondKill);创建订单SuccessKilledkillednewSuccessKilled();killed。setSeckillId(skgId);killed。setUserId(userId);killed。setState((short)0);killed。setCreateTime(newTimestamp(System。currentTimeMillis()));successKilledMapper。insert(killed);支付PaymentpaymentnewPayment();payment。setSeckillId(skgId);payment。setSeckillId(skgId);payment。setUserId(userId);payment。setMoney(40);payment。setState((short)1);payment。setCreateTime(newTimestamp(System。currentTimeMillis()));paymentMapper。insert(payment);}else{returnResult。error(SecondKillStateEnum。END);}}catch(Exceptione){thrownewScorpiosException(异常了个乖乖);}finally{}returnResult。ok(SecondKillStateEnum。SUCCESS);}
  Dao层RepositorypublicinterfaceSecondKillMapperextendsBaseMapperSecondKill{将此行数据进行加锁,当整个方法将事务提交后,才会解锁paramskgIdreturnSelect(valueSELECTFROMseckillWHEREseckillid{skgId}FORUPDATE)SecondKillquerySecondKillForUpdate(Param(skgId)LongskgId);}
  上面是利用forupdate进行对查询数据加锁,加的是行锁3。4方式四(悲观锁二)
  悲观锁的第二种方式就是利用update更新命令来加表锁UPDATE锁表paramskgId商品idparamuserId用户idreturnOverrideTransactional(rollbackForException。class)publicResultstartSecondKillByUpdateTwo(longskgId,longuserId){try{不校验,直接扣库存更新intresultsecondKillMapper。updateSecondKillById(skgId);if(result0){创建订单SuccessKilledkillednewSuccessKilled();killed。setSeckillId(skgId);killed。setUserId(userId);killed。setState((short)0);killed。setCreateTime(newTimestamp(System。currentTimeMillis()));successKilledMapper。insert(killed);支付PaymentpaymentnewPayment();payment。setSeckillId(skgId);payment。setSeckillId(skgId);payment。setUserId(userId);payment。setMoney(40);payment。setState((short)1);payment。setCreateTime(newTimestamp(System。currentTimeMillis()));paymentMapper。insert(payment);}else{returnResult。error(SecondKillStateEnum。END);}}catch(Exceptione){thrownewScorpiosException(异常了个乖乖);}finally{}returnResult。ok(SecondKillStateEnum。SUCCESS);}
  Dao层RepositorypublicinterfaceSecondKillMapperextendsBaseMapperSecondKill{将此行数据进行加锁,当整个方法将事务提交后,才会解锁paramskgIdreturnSelect(valueSELECTFROMseckillWHEREseckillid{skgId}FORUPDATE)SecondKillquerySecondKillForUpdate(Param(skgId)LongskgId);Update(valueUPDATEseckillSETnumbernumber1WHEREseckillid{skgId}ANDnumber0)intupdateSecondKillById(Param(skgId)longskgId);}3。5方式五(乐观锁)
  乐观锁,顾名思义,就是对操作结果很乐观,通过利用version字段来判断数据是否被修改
  乐观锁,不进行库存数量的校验,直接做库存扣减
  这里使用的乐观锁会出现大量的数据更新异常(抛异常就会导致购买失败)、如果配置的抢购人数比较少、比如120:100(人数:商品)会出现少买的情况,不推荐使用乐观锁。ApiOperation(value秒杀实现方式五乐观锁)PostMapping(startoptlock)publicResultstartOptLock(longskgId){try{log。info(开始秒杀方式五。。。);finallonguserId(int)(newRandom()。nextDouble()(99999100001))10000;参数添加了购买数量ResultresultsecondKillService。startSecondKillByPesLock(skgId,userId,1);if(result!null){log。info(用户:{}{},userId,result。get(msg));}else{log。info(用户:{}{},userId,哎呦喂,人也太多了,请稍后!);}}catch(Exceptione){e。printStackTrace();}returnResult。ok();}OverrideTransactional(rollbackForException。class)publicResultstartSecondKillByPesLock(longskgId,longuserId,intnumber){乐观锁,不进行库存数量的校验,直接try{SecondKillkillsecondKillMapper。selectById(skgId);剩余的数量应该要大于等于秒杀的数量if(kill。getNumber()number){intresultsecondKillMapper。updateSecondKillByVersion(number,skgId,kill。getVersion());if(result0){创建订单SuccessKilledkillednewSuccessKilled();killed。setSeckillId(skgId);killed。setUserId(userId);killed。setState((short)0);killed。setCreateTime(newTimestamp(System。currentTimeMillis()));successKilledMapper。insert(killed);支付PaymentpaymentnewPayment();payment。setSeckillId(skgId);payment。setSeckillId(skgId);payment。setUserId(userId);payment。setMoney(40);payment。setState((short)1);payment。setCreateTime(newTimestamp(System。currentTimeMillis()));paymentMapper。insert(payment);}else{returnResult。error(SecondKillStateEnum。END);}}}catch(Exceptione){thrownewScorpiosException(异常了个乖乖);}finally{}returnResult。ok(SecondKillStateEnum。SUCCESS);}RepositorypublicinterfaceSecondKillMapperextendsBaseMapperSecondKill{将此行数据进行加锁,当整个方法将事务提交后,才会解锁paramskgIdreturnSelect(valueSELECTFROMseckillWHEREseckillid{skgId}FORUPDATE)SecondKillquerySecondKillForUpdate(Param(skgId)LongskgId);Update(valueUPDATEseckillSETnumbernumber1WHEREseckillid{skgId}ANDnumber0)intupdateSecondKillById(Param(skgId)longskgId);Update(valueUPDATEseckillSETnumbernumber{number},versionversion1WHEREseckillid{skgId}ANDversion{version})intupdateSecondKillByVersion(Param(number)intnumber,Param(skgId)longskgId,Param(version)intversion);}
  乐观锁会出现大量的数据更新异常(抛异常就会导致购买失败),会出现少买的情况,不推荐使用乐观锁3。6方式六(阻塞队列)
  利用阻塞队类,也可以解决高并发问题。其思想就是把接收到的请求按顺序存放到队列中,消费者线程逐一从队列里取数据进行处理,看下具体代码。
  阻塞队列:这里使用静态内部类的方式来实现单例模式,在并发条件下不会出现问题。秒杀队列(固定长度为100)publicclassSecondKillQueue{队列大小staticfinalintQUEUEMAXSIZE100;用于多线程间下单的队列staticBlockingQueueSuccessKilledblockingQueuenewLinkedBlockingQueueSuccessKilled(QUEUEMAXSIZE);使用静态内部类,实现单例模式privateSecondKillQueue(){};privatestaticclassSingletonHolder{静态初始化器,由JVM来保证线程安全privatestaticSecondKillQueuequeuenewSecondKillQueue();}单例队列returnpublicstaticSecondKillQueuegetSkillQueue(){returnSingletonHolder。queue;}生产入队paramkillthrowsInterruptedExceptionadd(e)队列未满时,返回true;队列满则抛出IllegalStateException(Queuefull)异常AbstractQueueput(e)队列未满时,直接插入没有返回值;队列满时会阻塞等待,一直等到队列未满时再插入。offer(e)队列未满时,返回true;队列满时返回false。非阻塞立即返回。offer(e,time,unit)设定等待的时间,如果在指定时间内还不能往队列中插入数据则返回false,插入成功返回true。publicBooleanproduce(SuccessKilledkill){returnblockingQueue。offer(kill);}消费出队poll()获取并移除队首元素,在指定的时间内去轮询队列看有没有首元素有则返回,否者超时后返回nulltake()与带超时时间的poll类似不同在于take时候如果当前队列空了它会一直等待其他线程调用notEmpty。signal()才会被唤醒publicSuccessKilledconsume()throwsInterruptedException{returnblockingQueue。take();}获取队列大小returnpublicintsize(){returnblockingQueue。size();}}
  消费秒杀队列:实现ApplicationRunner接口消费秒杀队列Slf4jComponentpublicclassTaskRunnerimplementsApplicationRunner{AutowiredprivateSecondKillServiceseckillService;Overridepublicvoidrun(ApplicationArgumentsvar){newThread((){log。info(队列启动成功);while(true){try{进程内队列SuccessKilledkillSecondKillQueue。getSkillQueue()。consume();if(kill!null){ResultresultseckillService。startSecondKillByAop(kill。getSeckillId(),kill。getUserId());if(result!nullresult。equals(Result。ok(SecondKillStateEnum。SUCCESS))){log。info(TaskRunner,result:{},result);log。info(TaskRunner从消息队列取出用户,用户:{}{},kill。getUserId(),秒杀成功);}}}catch(InterruptedExceptione){e。printStackTrace();}}})。start();}}ApiOperation(value秒杀实现方式六消息队列)PostMapping(startqueue)publicResultstartQueue(longskgId){try{log。info(开始秒杀方式六。。。);finallonguserId(int)(newRandom()。nextDouble()(99999100001))10000;SuccessKilledkillnewSuccessKilled();kill。setSeckillId(skgId);kill。setUserId(userId);BooleanflagSecondKillQueue。getSkillQueue()。produce(kill);虽然进入了队列,但是不一定能秒杀成功进队出队有时间间隙if(flag){log。info(用户:{}{},kill。getUserId(),秒杀成功);}else{log。info(用户:{}{},userId,秒杀失败);}}catch(Exceptione){e。printStackTrace();}returnResult。ok();}
  注意:在业务层和AOP方法中,不能抛出任何异常,thrownewRuntimeException()这些抛异常代码要注释掉。因为一旦程序抛出异常就会停止,导致消费秒杀队列进程终止!
  使用阻塞队列来实现秒杀,有几点要注意:消费秒杀队列中调用业务方法加锁与不加锁情况一样,也就是seckillService。startSecondKillByAop()、seckillService。startSecondKillByLock()方法结果一样,这也很好理解当队列长度与商品数量一致时,会出现少卖的现象,可以调大数值下面是队列长度1000,商品数量1000,并发数2000情况下出现的少卖
  3。7。方式七(Disruptor队列)
  Disruptor是个高性能队列,研发的初衷是解决内存队列的延迟问题,在性能测试中发现竟然与IO操作处于同样的数量级,基于Disruptor开发的系统单线程能支撑每秒600万订单。事件生成工厂(用来初始化预分配事件对象)publicclassSecondKillEventFactoryimplementsEventFactorySecondKillEvent{OverridepublicSecondKillEventnewInstance(){returnnewSecondKillEvent();}}事件对象(秒杀事件)publicclassSecondKillEventimplementsSerializable{privatestaticfinallongserialVersionUID1L;privatelongseckillId;privatelonguserId;setget方法略}使用translator方式生产者publicclassSecondKillEventProducer{privatefinalstaticEventTranslatorVarargSecondKillEventtranslator(seckillEvent,seq,objs){seckillEvent。setSeckillId((Long)objs〔0〕);seckillEvent。setUserId((Long)objs〔1〕);};privatefinalRingBufferSecondKillEventringBuffer;publicSecondKillEventProducer(RingBufferSecondKillEventringBuffer){this。ringBufferringBuffer;}publicvoidsecondKill(longseckillId,longuserId){this。ringBuffer。publishEvent(translator,seckillId,userId);}}消费者(秒杀处理器)Slf4jpublicclassSecondKillEventConsumerimplementsEventHandlerSecondKillEvent{privateSecondKillServicesecondKillService(SecondKillService)SpringUtil。getBean(secondKillService);OverridepublicvoidonEvent(SecondKillEventseckillEvent,longseq,booleanbool){ResultresultsecondKillService。startSecondKillByAop(seckillEvent。getSeckillId(),seckillEvent。getUserId());if(result。equals(Result。ok(SecondKillStateEnum。SUCCESS))){log。info(用户:{}{},seckillEvent。getUserId(),秒杀成功);}}}publicclassDisruptorUtil{staticDisruptorSecondKillEventdisruptor;static{SecondKillEventFactoryfactorynewSecondKillEventFactory();intringBufferSize1024;ThreadFactorythreadFactoryrunnablenewThread(runnable);disruptornewDisruptor(factory,ringBufferSize,threadFactory);disruptor。handleEventsWith(newSecondKillEventConsumer());disruptor。start();}publicstaticvoidproducer(SecondKillEventkill){RingBufferSecondKillEventringBufferdisruptor。getRingBuffer();SecondKillEventProducerproducernewSecondKillEventProducer(ringBuffer);producer。secondKill(kill。getSeckillId(),kill。getUserId());}}ApiOperation(value秒杀实现方式七Disruptor队列)PostMapping(startdisruptor)publicResultstartDisruptor(longskgId){try{log。info(开始秒杀方式七。。。);finallonguserId(int)(newRandom()。nextDouble()(99999100001))10000;SecondKillEventkillnewSecondKillEvent();kill。setSeckillId(skgId);kill。setUserId(userId);DisruptorUtil。producer(kill);}catch(Exceptione){e。printStackTrace();}returnResult。ok();}
  经过测试,发现使用Disruptor队列队列,与自定义队列有着同样的问题,也会出现超卖的情况,但效率有所提高。4。小结
  对于上面七种实现并发的方式,做一下总结:一、二方式是在代码中利用锁和事务的方式解决了并发问题,主要解决的是锁要加载事务之前三、四、五方式主要是数据库的锁来解决并发问题,方式三是利用forupate对表加行锁,方式四是利用update来对表加锁,方式五是通过增加version字段来控制数据库的更新操作,方式五的效果最差六、七方式是通过队列来解决并发问题,这里需要特别注意的是,在代码中不能通过throw抛异常,否则消费线程会终止,而且由于进队和出队存在时间间隙,会导致商品少卖
  上面所有的情况都经过代码测试,测试分一下三种情况:并发数1000,商品数100并发数1000,商品数1000并发数2000,商品数1000
  思考:分布式情况下如何解决并发问题呢?下次继续试验。
  源码地址:
  https:github。comHofankingspringbootsecondskillexample

妊娠期妇女该如何选择退烧药物?药师来解惑自国家疫情政策松绑后,老人和孩子首当其冲,受到新冠病毒的降维打击。然而,还有这么一群特殊人群,她们感染新冠后,用药问题可能更加麻烦。她就是那些准妈妈们。孕妇由于其特殊性,用药时既要湛江房价三连涨,湛江楼市今年还会出现返乡置业潮吗?湛江房价三连涨,2022年湛江楼市会有返乡置业潮吗?看来到了年底,湛江的那些房东还是期待湛江楼市能出现一波返乡置业潮。这段时间湛江的二手房挂牌价逆势上涨,也是比较神了。在全国楼市都颜值高寓意好!年宵花销售渐旺,蝴蝶兰玉兰发财树等受青睐视频加载中(央视财经经济信息联播)在各地的花卉市场里,各式各样的年宵花成为主角,销售越来越火爆。在西安秦岭花世界,一大早,工人们就忙着接货组盆,对各种鲜花进行浇水养护。花房里,盛开2022襄阳民营企业百强出炉骆驼集团民发集团蝉联前两位12月30日,第九届(2022)襄阳市民营企100强名单出炉。襄阳民企100强名单是按照企业2021年度主营业务收入和实际缴税额各占50的权重排序。排名前三位的企业是骆驼集团股份有创始人私挪1。4亿公款被罚百万,牙茅陷入至暗时刻素有牙茅之称的通策医疗,近两年来日子不太平。近日,通策医疗发布公告称,公司实际控制人吕建明收到浙江证监局的行政处罚决定书,被处罚原因涉及关联交易未披露事项。行政处罚决定书显示,吕建共享单车竞逐拐点时刻尾部出局,头部蓄势来源产业科技回看消费互联网风口之上的墓碑,共享出行行业的碑文最为凄惨。周期跌宕轮回,可清晰看到共享(电)单车万骨枯的坎坷往事。以六年为坐标,共享单车乘风口起飞,最终在资本枯竭盈利难苏醒被拍夜会美女,羽绒服却成重点,老婆回应很绅士,要是她在会送上楼近日,苏醒被爆料夜会美女。视频里,狗仔拍到苏醒在晚上约了美女一起吃饭,可能是看女方穿得太少,担心美女出来会冷,苏醒还非常贴心地送上了自己的白色羽绒服,女方也是不客气,直接套上了男方李玟VR演唱会看到的看不到的随着过去两年线上演唱会越来越受到关注,人们也越发期待,更沉浸的VR演唱会的到来。但是,当下VR产业发展仍然面临诸多瓶颈,以现有技术做一场VR演唱会究竟能达到什么样的效果,这个尚无答男孩咳嗽发烧硬抗一周后白肺!提醒有这4种症状,再麻烦也要就医来源科学家庭育儿(IDkexueyuer2012)咳咳咳咳很多家长和孩子阳康后咳嗽总不见好,本来就担心,最近的新闻上又出现一个白肺,看得大家更是慌张。白肺是啥?我家孩子也在咳嗽,不如何预防宝宝患上中耳炎?对他们的听力有影响吗?中耳炎是宝妈们照顾孩子遇到的一种常见疾病,如果没有及时预防或治疗会严重影响到宝宝的听力,这对孩子后期的成长会有不利影响。如何预防宝宝患上中耳炎?对他们的听力有影响吗?耳痛是婴幼儿患白肺高危孕妇在蚌医一附院产下新年宝宝大皖新闻讯2023年元旦当日,一名感染新冠双肺全白完全性前置胎盘的高危孕妇被紧急送入蚌医一附院,经过该院多学科医护团队的接力抢救,当晚,一名特殊的新年宝宝降生。大皖新闻记者5日前往
你吼过年迈的父母吗?吼过。那年母亲才62岁就因为脑干出血瘫痪在床上,由于几个兄弟姐妹都在外地,照顾妈妈的任务就落到了我的身上。我其实挺心疼妈妈的,可是真正照顾起来却经常是让人抓狂。具体的事情我不想去回产后便秘怎么办?产后便秘怎么办?产后便秘,首先可以通过饮食和生活方式调理,必要时可遵医嘱通过药物治疗,可用开塞露,肥皂水灌肠,或口服乳果糖等不吸收没有副作用的泻剂。饮食中要多吃水果和蔬菜,多食粗纤浙江东阳四好农村路带动沿线乡村产业发展东阳四好农村路生机盎然春意浓。张庆供图中新网浙江新闻3月28日电(董易鑫张庆李磊)我们这交通好风景好,最近来研学春游团建的人特别多。近日,在浙江金华东阳市湖溪镇八里湾共享田园投资创沈阳某酒店大张旗鼓的为日本天皇举办生日招待会,令人耻辱!2月23日。日本驻沈阳总领事馆发了这样一条微博。内容是在沈阳某大酒店里专门为日本天皇诞辰举办的生日招待会,同时还表示东北三省的相关领导中日两国企业代表注意它的篇微博写的是spans河南商丘宋国故城考古发现唐代墓志砖实证城摞城河南商丘宋国故城考古发现唐代墓志砖实证城摞城新华网这是宋国故城遗址内的唐代墓葬出土的墓志砖(3月25日摄)。新华社记者李安摄这是宋国故城遗址内出土墓志砖的唐代墓葬(3月25日摄)。头条故事会萨托二县出土匪。作者吴振平小时候总听长辈们说萨县托县(今内蒙古土默特右旗托克托县,简称萨托二县)出土匪。果真如此吗?其实,说萨托二县出土匪,一是指清末民国绥远省的匪患以萨县托县鄂尔多斯为多,如清末民初丰镇人为何唐朝是当时世界最中心?没有对比就没有伤害!2014年12月,一部讲述武则天(624705)生平的电视剧武媚娘传奇因为技术原因暂停播出。武则天是中国历史上唯一一个建立了自己的王朝(周朝,690705)并且登基成为皇帝的女性。如何看待广西师范学院更名为南宁师范大学?先跟广西师范学院的老师同学们说一句,既然你们不用这个校名了,那就把黄雲老先生写的那块校牌还给广西师大吧。1983年,桂林的广西师范学院改名广西师范大学,南宁师范学院就把广西师范学院安庆师范学院是一所什么档次的师范大学,哪些专业比较强?先来看一份大学排名(师范类)这里简单的谈谈安庆师范大学的情况安庆师范大学的办学历程算是比较悠久的,后来逐渐发展为安徽师范大学安庆教学点安庆分校,于1980发展为安庆师范学院,直到2如果在你老了或病了后住在医院里,知道是绝症没得治,并且医疗费给子女造成了沉重负担,你会选择安乐死吗?为什么?前几天,我突然呼吸不畅,憋气并心慌不咳嗽不发烧很少痰但就是感觉气管那里堵着咳不出,好难受啊北方冬天半夜里起来洗澡难受啊!那种难受简直生无可恋所以,病疼对人的折磨那是难以名状的,残酷得了腰椎间盘突出,在锻炼饮食和生活方面要注意哪些?现在腰椎间盘突出的患者是越来越多了,因为大家因为工作需要长期的久坐,负重等等原因,加之骨质疏松的就会造成腰椎间盘突出,下图呢图呢,就一个典型的磁共振矢状位,发现腰椎间盘突出的患者的
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网