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

分布式锁中基于Redis的实现需避坑Jedis篇

  一、redis介绍
  Redis应该是目前最受欢迎的高性能的缓存数据库了,在五一期间看到一则Redis7。0发布的消息后,回想起多年前学习黄健宏老师《Redis从入门到精通》2。x的月伴时光,不由得感慨Reids发展之迅速。搜集了一下3。0及之后各版本的知名特性,整理出来方便读者朋友们有个简单了解(感兴趣的朋友还需自行深入研究),情况大致如下:3。0开始支持cluster集群模式4。0开发的lazyfree和PSYNC2解决了Redis长久的大key删除阻塞问题及同步中断无法续传的问题5。0新增了stream数据结构使Redis具备功能完整的轻量级消息队列能力6。0更是发布了诸多企业级特性如threadedio、TLS和ACL等,大幅提升了Redis的性能和安全性7。0Function彻底解决了过去Lua脚本同步丢失的问题;MultiPartAOF增强了Redis的数据持久化的可靠性1。1特性介绍
  为满足本篇目标所需,这里着重介绍以下几个关键特性:数据组织:Redis中支持多种数据结构,将他们灵活组合搭配即可满足分布式锁在不同场景下的功能需求:Jedis和Lettuce这类框架中常使用String来做简易的锁信息存储Redisson中使用Hash结构来存储更多维度的锁信息,如:业务名称作为key,uuid线程id作为field,加锁次数作为valueRedisson中在公平锁的场景下引入List和ZSet,List类型用于线程排队,Zset类型存放等待线程的顺序,分数score是等待线程的超时时间戳。
  Redis的数据结构(来自网络)集群模式:Redis采用集群模式分片存储数据,整个集群拥有固定的2的32次方个槽位,数据被分配到这些槽位中,每个实例只分管一部分槽位,而非如etcd、ZK这种每个实例中的数据都一致;集群模式提供的是数据规模扩大后的横向AP能力,应对单节点的风险需再加上主从模式,但当某个master节点挂之后,slave节点可能还未同步到全部数据,会导致数据丢失;一致性保障能力偏弱
  Redis的集群模式(来自网络)顺序变更:一种简单的抢锁逻辑是判断key是否已存在,Redis中没有给变更操作附加顺序信息(如etcd中的Revision),但服务端以串行方式处理数据的变更,那就可以结合其他数据结构来记录请求顺序信息,如公平锁的实现也会依赖其他数据结构存储信息,用于判断锁状态;但当用到的数据类型和指令变多后,由于是非原子性操作,自然就会遇到结果与预期不一致这类问题,Redis提供的lua脚本机制可用于解决此类问题,用户在客户端编排自定义脚本逻辑:可用多个指令操控多个数据,然后将脚本发送给服务端,服务端执行lua脚本,并保障一个lua脚本内的所有操作是原子性的
  Redislua脚本的工作机制(来自网络)TTL机制:TTL(TimeToLive)机制是给单个key设置存活时间,超过时间后Redis自动删除这个key1。2特性总结
  Redis的分布式锁正是基于以上特性来实现的,简单来说是:TTL机制:用于支撑异常情况下的锁自动释放的能力顺序变更:用于支撑获取锁和排队等待的能力集群主从模式:用于支撑锁服务的高可用
  Redis没有提供对分布式锁亲和的监听机制,需要客户端主动轮询感知数据变更。二。加锁解锁的流程描述
  使用Jedis指令实现分布式锁的核心流程如下图所示:
  准备客户端、key和value若key不存在,指定过期时间成功写入KeyValue则抢锁成功,并定时推后key的过期时间若key已存在,则采用重试策略间歇性抢锁。解锁时,删除key并撤销推后key过期时间的逻辑
  其中第2和第4是核心环节,有几个版本的演进很有趣味:插入key和设置过期时间并非原子操作:setnxexpire加锁和设置过期是两个分开的独立操作;若发生异常,导致设置过期操作未执行,则此锁就成了永恒锁,其他客户端就再也抢不到了以原子性操作完成插入key和设置过期时间:使用set的扩展指令,如下:SETkeyvalue〔EXseconds〕〔PXmilliseconds〕〔NXXX〕复制代码NX:当key不存在时,才插入KeyXX:当插入key时,指定值为固定的lockValueEXsecond:设置key的过期时间单位秒(PXEX二选一)PXmillisecond:设置键的过期时间单位毫秒(PXEX二选一)if(jedis。set(key,lockValue,NX,EX,100)1){加锁成功try{dowork执行业务这里缺点什么?}catch(Exceptione){。。。}finally{jedis。del(key);释放锁,这里可能误删其他client的锁key}}复制代码引入lockValue的随机值校验,避免误释放其它客户端的锁,场景如下:client1加锁成功,key10s后过期,完成逻辑后,删除key之前,因GC导致持锁超过10s,Redis自动删除了key,之后其他客户端可以抢锁假如是client2接下来成功抢锁,开始处理持锁后的逻辑。而此时client1GC结束了会继续执行删除key的操作,但此时释放的其实是client2的key
  解决办法是:加锁时指定的lockValue为随机值,每次加锁时的值都是唯一的,释放锁时若lockValue与加锁时的值一致才可释放,否则什么都不做,逻辑如下:if(jedis。set(key,randomLockValue,NX,EX,100)1){加锁try{dosomething业务处理}catch(){}finally{判断是不是当前线程加的锁,是才释放但判断和释放锁两个操作不是原子性的if(randomLockValue。equals(jedis。get(key))){jedis。del(key);释放锁}}}复制代码
  以上代码遗留的问题是判断randomlockValue和释放锁两个操作不是原子性的。引入lua脚本,保障判断randomlockValue和删除key这两个操作的原子性,逻辑如下:Stringscriptifredis。call(get,KEYS〔1〕)ARGV〔1〕thenreturnredis。call(del,KEYS〔1〕)elsereturn0end;Objectresultjedis。eval(script,Collections。singletonList(key),Collections。singletonList(randomLockValue));if(1。equals(result。toString())){returntrue;}复制代码
  至此依然存在的一个问题是:若持锁后,业务逻辑执行耗时超过了key的过期时间,则锁Key会被Reids主动删除。引入watchDog定时推后key的过期时间,避免业务未执行完时,key过期被Redis删除。if(jedis。set(key,randomLockValue,NX,EX,100)1){加锁成功try{dowork执行业务watchDog定时延后Key的过期时间}catch(Exceptione){。。。}finally{Stringscriptifredis。call(get,KEYS〔1〕)ARGV〔1〕thenreturnredis。call(del,KEYS〔1〕)elsereturn0end;try{Objectresultjedis。eval(script,Collections。singletonList(key),Collections。singletonList(randomLockValue));if(1。equals(result。toString())){returntrue;}returnfalse;}catch(Exceptione){。。。}}}复制代码三。Jedis分布式锁的能力
  可能读者是单篇阅读,这里引入第一篇《分布式锁上初探》中的一些内容,一个分布式锁应具备这样一些功能特点:互斥性:在同一时刻,只有一个客户端能持有锁安全性:避免死锁,如果某个客户端获得锁之后处理时间超过最大约定时间,或者持锁期间发生了故障导致无法主动释放锁,其持有的锁也能够被其他机制正确释放,并保证后续其它客户端也能加锁,整个处理流程继续正常执行可用性:也被称作容错性,分布式锁需要有高可用能力,避免单点故障,当提供锁的服务节点故障(宕机)时不影响服务运行,这里有两种模式:一种是分布式锁服务自身具备集群模式,遇到故障能自动切换恢复工作;另一种是客户端向多个独立的锁服务发起请求,当某个锁服务故障时仍然可以从其他锁服务读取到锁信息(Redlock)可重入性:对同一个锁,加锁和解锁必须是同一个线程程,即不能把其他线程持有的锁给释放了高效灵活:加锁、解锁的速度要快;支持阻塞和非阻塞;支持公平锁和非公平锁
  基于上文对Jedis分布式锁的介绍,这里简单总结一下Jedis的能力矩阵,ZK请看《分布式锁中基于Zookeeper的实现》,etcd请看《分布式锁中基于etcd的实现很优雅》,表格中标题使用Redis简单锁,主要是跟RedLock做区分,这种简单锁使用Jedis、Lettuce、Redisson都能实现,任何一把锁的信息只保存在一个Redismaster实例中,而RedLock是Redisson提供的高阶分布式锁,它需要客户端同时跟多个Redismaster实例协作才能完成,即一把锁的信息同时存在于多个master实例中。它的情况会在后续文章中补充(感兴趣的读者可以关注本号【架构染色】,文章完成时会主动推送给你)
  能力
  ZK
  etcd
  Redis简单锁
  Redlock
  MySql
  互斥
  是
  是
  是
  安全
  链接异常时,session丢失自动释放锁
  基于租约,超时自动释放锁
  基于TTL,超时自动释放锁
  可用性
  相对可用性还好
  好
  好
  可重入
  服务端非可重入,本地线程可重入
  服务端非可重入,Resission本地线程可重入
  服务端非可重入,本地线程可重入需自研
  加解锁速度
  速度不算快
  速度快,GRPC协议优势以及服务端能力的优势
  速度快
  阻塞非阻塞
  客户端两种能力都提供
  jetcdcore中,阻塞非阻塞由Futureget支撑
  Jedis非阻塞,Redission提供阻塞能力
  公平非公平
  公平锁
  公平锁
  非公平锁,Redission提供公平锁
  可续期
  天然支持
  天然支持
  Jedis需自研watchDog,Redission自带
  其他因素
  技术栈偏老,性能不佳
  多数公司不熟悉
  容易受业务缓存操作干扰
  四、Jedis库实现分布式锁
  Jedis是Redis官方推出的用于通过Java连接Redis客户端的一个工具包,提供了Redis的各种命令支持。4。1pom依赖dependencygroupIdredis。clientsgroupIdjedisartifactIdversion4。3。0versiondependency复制代码4。2相关的API介绍使用SET的扩展指令加锁(SETkeyvalue〔EXseconds〕〔pxmilliseconds〕〔NXXX〕)SetParamsparamsSetParams。setParams()。nx()。ex(lockState。getLeaseTTL());Stringresultclient。set(lockState。getLockKey(),lockState。getLockValue(),params);复制代码使用lua解锁Stringscriptifredis。call(get,KEYS〔1〕)ARGV〔1〕thenreturnredis。call(del,KEYS〔1〕)elsereturn0end;Objectresultclient。eval(script,1,lockState。getLockKey(),lockState。getLockValue());复制代码4。3分布式锁示例锁的封装packagecom。rock。dlock。jedis;importcom。rock。dlock。common。DtLockException;importcom。rock。dlock。common。KeepAliveAction;importcom。rock。dlock。common。KeepAliveTask;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importredis。clients。jedis。JedisPooled;importredis。clients。jedis。params。SetParams;importjava。net。SocketTimeoutException;importjava。util。concurrent。TimeUnit;authorzsdate202211134:44PMpublicclassDemoJedisLock{privatefinalstaticLoggerlogLoggerFactory。getLogger(DemoJedisLock。class);privateJedisPooledclient;privateLockStatelockState;privateKeepAliveTaskkeepAliveTask;privateintsleepMillisecond;privatefinalstaticStringRESULTOKOK;privatestaticfinalLongUNLOCKSUCCESS1L;classLockState{privateStringlockKey;privateStringlockValue;privateStringerrorMsg;privateintleaseTTL;privatelongleaseId;privatebooleanlockSuccess;publicLockState(StringlockKey,intleaseTTL){this。lockKeylockKey;this。leaseTTLleaseTTL;}publicLockState(StringlockKey,Stringvalue,intleaseTTL){this。lockKeylockKey;this。lockValuevalue;this。leaseTTLleaseTTL;}publicStringgetLockKey(){returnlockKey;}publicvoidsetLockKey(StringlockKey){this。lockKeylockKey;}publicStringgetLockValue(){returnlockValue;}publicvoidsetLockValue(StringlockValue){this。lockValuelockValue;}publicStringgetErrorMsg(){returnerrorMsg;}publicvoidsetErrorMsg(StringerrorMsg){this。errorMsgerrorMsg;}publiclonggetLeaseId(){returnleaseId;}publicvoidsetLeaseId(longleaseId){this。leaseIdleaseId;}publicbooleanisLockSuccess(){returnlockSuccess;}publicvoidsetLockSuccess(booleanlockSuccess){this。lockSuccesslockSuccess;}publicintgetLeaseTTL(){returnleaseTTL;}publicvoidsetLeaseTTL(intleaseTTL){this。leaseTTLleaseTTL;}}publicDemoJedisLock(JedisPooledclient,Stringkey,Stringvalue,intttlSeconds){1。准备客户端this。clientclient;this。lockStatenewLockState(key,value,ttlSeconds);this。sleepMillisecond(ttlSeconds1000)3;抢锁的重试间隔可由用户指定}publicbooleantryLock(longwaitTime,TimeUnitwaitUnit)throwsDtLockException{longtotalMillisSecondswaitUnit。toMillis(waitTime);longstartSystem。currentTimeMillis();重试,直到成功或超过指定时间while(true){抢锁try{SetParamsparamsSetParams。setParams()。nx()。ex(lockState。getLeaseTTL());Stringresultclient。set(lockState。getLockKey(),lockState。getLockValue(),params);if(RESULTOK。equals(result)){manualKeepAlive();log。info(〔jedislock〕locksuccess线程:{}加锁成功,key:{},value:{},Thread。currentThread()。getName(),lockState。getLockKey(),lockState。getLockValue());lockState。setLockSuccess(true);returntrue;}else{if(System。currentTimeMillis()starttotalMillisSeconds){returnfalse;}Thread。sleep(sleepMillisecond);}}catch(Exceptione){Throwablecausee。getCause();if(causeinstanceofSocketTimeoutException){忽略网络抖动等异常}log。error(〔jedislock〕lockfailed:e);thrownewDtLockException(〔jedislock〕lockfailed:e。getMessage(),e);}}}此实现中忽略,网络通信异常部分的处理,可参考tryLockpublicvoidunlock()throwsDtLockException{try{首先停止续约if(keepAliveTask!null){keepAliveTask。close();}Stringscriptifredis。call(get,KEYS〔1〕)ARGV〔1〕thenreturnredis。call(del,KEYS〔1〕)elsereturn0end;Objectresultclient。eval(script,1,lockState。getLockKey(),lockState。getLockValue());if(UNLOCKSUCCESS。equals(result)){log。info(〔jedislock〕unlocksuccess线程:{}解锁成功,锁key:{},路径:{},Thread。currentThread()。getName(),lockState。getLockKey(),lockState。getLockValue());}else{log。info(〔jedislock〕unlockdelkeyfailed,线程:{}解锁成功,锁key:{},路径:{},Thread。currentThread()。getName(),lockState。getLockKey(),lockState。getLockValue());}}catch(Exceptione){log。error(〔jedislock〕unlockfailed:e。getMessage(),e);thrownewDtLockException(〔jedislock〕unlockfailed:e。getMessage(),e);}}定时将Key的过期推迟privatevoidmanualKeepAlive(){finalStringtkeylockState。getLockKey();finalinttttllockState。getLeaseTTL();keepAliveTasknewKeepAliveTask(newKeepAliveAction(){Overridepublicvoidrun()throwsDtLockException{刷新值try{client。expire(tkey,tttl);}catch(Exceptione){e。printStackTrace();}}},tttl);keepAliveTask。start();}}复制代码异常类的简单实现packagecom。rock。dlock。common;publicclassDtLockExceptionextendsRuntimeException{publicDtLockException(Stringmessage){super(message);}publicDtLockException(Stringmessage,Throwablecause){super(message,cause);}publicstaticDtLockExceptionclientException(){returnnewDtLockException(clientisempty);}}复制代码watchDog的任务抽象packagecom。rock。dlock。common;publicinterfaceKeepAliveAction{voidrun()throwsDtLockException;}复制代码watchDog的简单实现packagecom。rock。dlock。common;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importjava。util。concurrent。TimeUnit;authorzsdate20221174:20PMpublicclassKeepAliveTaskextendsThread{privatestaticfinalLoggerLOGGERLoggerFactory。getLogger(KeepAliveTask。class);publicvolatilebooleanisRunningtrue;过期时间,单位sprivatelongttlSeconds;privateKeepAliveActionaction;publicKeepAliveTask(KeepAliveActionaction,longttlSeconds){this。ttlSecondsttlSeconds;this。actionaction;this。setDaemon(true);}Overridepublicvoidrun(){finallongsleepthis。ttlSeconds10003;每隔三分之一过期时间,续租一次while(isRunning){try{1、续租,刷新值action。run();LOGGER。debug(续租成功!);TimeUnit。MILLISECONDS。sleep(sleep);}catch(InterruptedExceptione){close();}catch(DtLockExceptione){close();}}}publicvoidclose(){isRunningfalse;this。interrupt();}}复制代码4。4测试锁importcom。rock。dlock。jedis。DemoJedisLock;importredis。clients。jedis。JedisPooled;importjava。util。UUID;importjava。util。concurrent。TimeUnit;authorzsdate202211134:51PMpublicclassTestJedisLock{publicstaticvoidmain(String〔〕args){JedisPooledjedisnewJedisPooled(127。0。0。1,6379);DemoJedisLockdemoEtcdLock1newDemoJedisLock(jedis,rock,UUID。randomUUID()。toString(),10);DemoJedisLockdemoEtcdLock2newDemoJedisLock(jedis,rock,UUID。randomUUID()。toString(),10);booleanlock1demoEtcdLock1。tryLock(20,TimeUnit。SECONDS);if(lock1){try{System。out。printf(dosomething);}finally{demoEtcdLock1。unlock();}}demoEtcdLock1。tryLock(20,TimeUnit。SECONDS);demoEtcdLock2。tryLock(20,TimeUnit。SECONDS);等待锁,超时后放弃}}复制代码五、使用Jedis的一些注意事项
  通常分布式锁服务会和业务逻辑使用同一个Redis集群,自然也使用同一个Jedis客户端;当业务逻辑侧对Redis的读写并发提高时,会给Redis集群和Jedis客户度带来压力;为应对一些异常情况,我们除了解功能层面的API,还需要了解一下客户端的一些配置调优,主要是池化管理和网络通信两个方面5。1池化管理
  在使用Jedis时可以配置JedisPool连接池,池化处理有许多好处,如:提高响应的速度、降低资源的消耗、方便管理和维护;JedisPool配置参数大部分是由JedisPoolConfig的对应项来赋值的,在生产中我们需要关注它的配置并合理的赋值,如此能够提升Redis的服务性能,降低资源开销。下边是对一些重要参数的说明、默认及设置建议:
  参数
  说明
  默认值
  建议
  maxTotal
  资源池中的最大连接数
  8hrmaxIdle
  资源池允许的最大空闲连接数
  8hrminIdle
  资源池确保的最少空闲连接数
  0hrblockWhenExhausted
  当资源池用尽后,调用者是否要等待。只有当值为true时,下面的maxWaitMillis才会生效。
  true
  建议使用默认值。
  maxWaitMillis
  当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)。
  1(表示永不超时)
  不建议使用默认值。
  testOnBorrow
  向资源池借用连接时是否做连接有效性检测(ping)。检测到的无效连接将会被移除。
  false
  业务量很大时候建议设置为false,减少一次ping的开销。
  testOnReturn
  向资源池归还连接时是否做连接有效性检测(ping)。检测到无效连接将会被移除。
  false
  业务量很大时候建议设置为false,减少一次ping的开销。
  jmxEnabled
  是否开启JMX监控
  true
  建议开启,请注意应用本身也需要开启。
  空闲Jedis对象的回收检测由以下四个参数组合完成,testWhileIdle是该功能的开关。
  名称
  说明
  默认值
  建议
  testWhileIdle
  是否开启空闲资源检测。
  false
  true
  timeBetweenEvictionRunsMillis
  空闲资源的检测周期(单位为毫秒)
  1(不检测)
  建议设置,周期自行选择,也可以默认也可以使用下方JedisPoolConfig中的配置。
  minEvictableIdleTimeMillis
  资源池中资源的最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除。
  180000(即30分钟)
  可根据自身业务决定,一般默认值即可,也可以考虑使用下方JeidsPoolConfig中的配置。
  numTestsPerEvictionRun
  做空闲资源检测时,每次检测资源的个数。
  3hr可根据自身应用连接数进行微调,如果设置为1,就是对所有连接做空闲监测。
  通过源码可以发现这些配置是GenericObjectPoolConfig对象的属性,这个类实际上是rg。apache。commons。pool2。implapache提供的,也就是说jedis的连接池是依托于apache提供的对象池来,这个对象池的声明周期如下图,感兴趣的可以看下:
  5。2网络调优maxredirects:这个是集群模式下,重定向的最大数量;举例说明,比如第一台挂了,连第二台,第二台挂了连第三台,重新连接的次数不能超过这个值timeout:客户端超时时间,单位是毫秒
  Rsdis节点故障或者网络抖动时,这两个值如果不合理可能会导致很严重的问题,比如timeout设置为1000,maxRedirect为2,一旦出现redis连接问题,将会导致请求阻塞3s左右。而这个3秒的阻塞在可能导致常规业务流量下的线程池耗尽,需根据业务场景调整。
  原文链接:https:juejin。cnpost7166131629348356104

对抗路三大巨头现状分析,马超老夫子跌落神坛,木兰也将步入后尘大家好我是指尖,最近王者荣耀的英雄调整的确引起了不小的反响,不管是苏烈的突然崛起,还是桑启的削弱都是如此,而关于问题修复,马超玩家的确是有话要说的,这次的问题修复,甚至让马超胜率大大傻成奎安从月薪60到一天50万,娶两房妻子,54岁患癌去世一九一三年,随着第一部香港本土影片庄子试妻的出现,一个新的时代便已经悄然而至。香港电影随后如雨后春笋,出现众家争鸣百花齐放盛况,其中经典的影片举不胜举,让人看后身陷其中,不能自拔。蓝台一哥华少走到今天这个地步,真的怪不得别人2012年,一档音乐综艺节目中国好声音横空出世。节目刚开播,主持人华少就凭借着47秒读出350字广告词的成绩一战封神,成为浙江卫视的一哥。那个时候观众有多喜欢他,现在就有多厌恶他。王安石提出变法,苏轼为何坚决反对,还给皇上上书11069年,此时距离宋江起义大约还有50年。北宋在熙宁二年,做了一次重大的政治变革。在传统的掌管财务的三司部门之外又另设一个三司条例司,专门负责颁布新法,并派使者去全国各地去推行大肠杆菌也能致命?如何预防是关键相信大家都看到过因为吃海鲜而得溶血尿毒综合征的新闻。这种疾病一旦发生,有可能会发展为慢性肾衰,需要终身接受透析。那么到底什么是溶血尿毒综合征?在日常生活中要如何避免呢?什么是溶血尿东哥的一封信各位京东兄弟们,我想大家已经看到了集团以及物流健康相继发布的第三季度业绩或简报!历经十几年埋头苦干,京东终于迎来了全面盈利的重要时刻,这表明我们以客户体验为核心的长期战略是正确的,在沙特阿拉伯否认有关欧佩克增加供应的报道后,油价暴跌后反弹周一油价在震荡的交易时段小幅收低。有报道称,欧佩克正在权衡增加产量,这将有助于抵消俄罗斯原油供应的损失,但在沙特阿拉伯断然否认后,油价暴跌后反弹。国际基准布伦特原油收跌0。2,至8从市场角度思考人单合一的普遍适用性近日,哈佛商业评论意大利语杂志刊发人单合一意大利研究中心主任伊曼纽尔昆塔雷利的署名文章从市场的角度重新思考组织架构。文章中,伊曼纽尔列举了由海尔首创的人单合一模式,在不同国家的企业江铃控股被申请破产审查,危在旦夕?来源盖世汽车马振旗企查查显示,11月21日,江铃控股有限公司新增破产审查案件,申请人为上海德梅柯汽车装备制造有限公司,经办法院为江西省南昌市中级人民法院。资料显示,江铃控股成立于210月日系车销量盘点凯美瑞排第二,CRV仅第六,缤智销量大跌?2022年10月日系车销量盘点据乘联会近日公布的乘用车销量数据显示,2022年10月日系车市场零售销量349,084辆,同比减少10。0市场份额为18。9,比去年减少3。7个百分点怎样喝酒不易醉?几个小妙招怎样喝酒不易醉1切勿空腹喝酒。喝酒前可以先垫垫肚子,减少空腹喝酒对肠胃带来的刺激,空腹喝酒不仅容易醉酒,长时间如此还会伤肝,严重的话可能会引起胃炎,胃溃疡等问题,大家可以在喝酒前喝
Angew石墨烯电极上电化学赝电容pH跳跃的直接探测在电化学中,对埋置电极界面处的界面水进行分子水平的了解是必不可少的,但界面的光谱探测仍然具有挑战性。在这里,使用表面特异性外差探测和频产生(HDSFG)光谱,作者直接探测到与氟化钙向新势力妥协提高交互体验宝马将为其全新曲面屏开发电子游戏日前,我们从相关渠道获取到了一组宝马为全新车机系统引入电子游戏的最新消息。据悉,宝马品牌正在与总部位于瑞士的AirConsole集团展开合作,将旗下游戏引入自家曲面显示屏。两家公司电子半导体洁净室设备龙头,美埃科技三大优势构筑核心竞争力(报告出品方分析师东吴证券袁理马天翼任逸轩)1。国内电子半导体洁净室设备龙头,产能扩张成长加速1。1。聚焦工业级超洁净技术,二十年打造国内电子半导体洁净室设备龙头聚焦工业级超洁净技首次研制出氧化镓垂直槽栅场效应晶体管!近日,中国科学技术大学微电子学院龙世兵教授课题组联合中科院苏州纳米所加工平台在氧化镓功率电子器件领域取得重要进展,分别采用氧气氛围退火和N离子注入技术,首次研制出了氧化镓垂直槽栅场引发汽车降价潮的幕后原因汽车市场的竞争越来越激烈,各大品牌为了争夺市场份额,不断推出新车型,降低售价,促销活动等手段来吸引消费者。消费者对汽车的需求不断变化,越来越注重性价比和个性化需求,价格成为购车的重100从ERP导出数据同类项单元格下面是空行的话如何快速填充从ERP导出数据的时候,由于系统本身的设置或者是设置没有填充同类项目的话,就会在每一行的下面留下连续的空单元格,此时如果需要做进一步数据分析或者引用会发现不好操作。如下图,如果需要骑手含泪多跑两小时,人大代表建议降低单价来保证外卖员休息近年来,随着社会的发展,人们的生活变得越来越便利,很多以前都无法想象的便利成为了现实。我们只需要动动手机,就能买到千里之外的商品,就能在家门口拿到心心念念的美食。可是这种便利也带来中国房地产之父百姓拿出三分之一的存款买房中国经济就恢复了!被媒体称为中国房地产之父的孟晓苏日前建议只要让中国老百姓拿出自己三分之一的存款去买房,恢复对房子的购买装修和其他的购买,中国经济就能恢复。此观点一出,瞬间引起网友的热议,对此有人赞19校复试线公布!多校复试名单已出,并确定线下复试,提前准备!第一批院校将于本周五周日开始复试,如上交大MBA吉林大学物理学院武汉大学西安美术学院等,均采取线下复试方式。截至目前,已经有12所自主划线院校公布复试线(武汉大学东北大学中山大学四电解液一哥天赐材料营收首破200亿,高毛利率或成历史界面新闻记者尹靖霏3月12日盘后,天赐材料(002709。SZ)发布2022年度业绩快报,公司当期实现营业收入224。11亿元,同比增长102。07归属于上市公司股东的净利润57。2月国内动力电池榜宁德时代市占率再下降比亚迪大涨132月,随着我国新能源汽车销量的回暖,动力电池装车量也随之上涨。据中国汽车动力电池产业创新联盟最新数据,2月,我国动力电池装车量21。9GWh,同比增长60。4,环比36。2月,虽然
友情链接:快好知快生活快百科快传网中准网文好找聚热点快软网