中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

山東一建建設(shè)有限公司網(wǎng)站什么是企業(yè)營(yíng)銷型網(wǎng)站

山東一建建設(shè)有限公司網(wǎng)站,什么是企業(yè)營(yíng)銷型網(wǎng)站,wordpress 如何開發(fā),地方門戶網(wǎng)站系統(tǒng)redis優(yōu)惠券秒殺 為什么訂單表訂單ID不采用自增長(zhǎng)? id規(guī)律性太明顯,容易被用戶猜測(cè)到(比如第一天下訂單id10,第二天下訂單id100,在昨天的1天內(nèi)只賣出90商品)受單表數(shù)據(jù)量限制(訂單數(shù)據(jù)量大&am…

redis優(yōu)惠券秒殺

為什么訂單表訂單ID不采用自增長(zhǎng)?

  • id規(guī)律性太明顯,容易被用戶猜測(cè)到(比如第一天下訂單id10,第二天下訂單id100,在昨天的1天內(nèi)只賣出90商品)
  • 受單表數(shù)據(jù)量限制(訂單數(shù)據(jù)量大,日積月累單張表就存不下去這么多數(shù)據(jù)記錄)如果單張表存不了,我們使用多張表的話,表獨(dú)立的,自增長(zhǎng)的話id會(huì)出現(xiàn)重復(fù),到時(shí)用戶買到的商品有問題售后就麻煩了

這時(shí)我們就要使用全局ID生成器,是一種在分布式系統(tǒng)下生成全局唯一ID的工具,滿足以下特點(diǎn):唯一性、高可用、高性能、遞增性、安全性

為了增加id的安全性,我們可以直接不使用redis自增的數(shù)值,而是拼接一些其他的信息?

定義一個(gè)類RedisIdWorker類并且配置@Component由spring管理的bean

package com.hmdp.utils;import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;@Component
public class RedisIdWorker {@Resourceprivate StringRedisTemplate stringRedisTemplate;private static final long BEGIN_TIMESTAMP=1672531200;private static final int COUNT_BIT=32;public long nextId(String keyPrefix){// 1.生成時(shí)間戳LocalDateTime now = LocalDateTime.now();long nowSecond = now.toEpochSecond(ZoneOffset.UTC);long timestamp=nowSecond-BEGIN_TIMESTAMP;//當(dāng)前時(shí)間戳// 2.生成序列號(hào)// 獲取當(dāng)前日期String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));// 自增長(zhǎng)// 需要加上日期 因?yàn)樽栽錾舷奘?^64,日積月累的銷量越來越多,出32位容易,未來就存不下去,不能使用同一個(gè)keyLong count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);// 3.拼接并返回// 我們以為時(shí)間戳在最低位 要讓他處于高位必須移位 移多少位得看給序列化號(hào)足夠的位置(這里是32位)return timestamp<<COUNT_BIT|count;}/*public static void main(String[] args) {LocalDateTime time = LocalDateTime.of(2023, 1, 1, 0, 0, 0);long second = time.toEpochSecond(ZoneOffset.UTC);System.out.println(second);//1672531200}*/
}

需要加上日期,不然整個(gè)業(yè)務(wù)訂單一直采用同一個(gè)key自增長(zhǎng),時(shí)隔幾年,訂單越來越多,redis單個(gè)key的值自增上限是2的64次方(好處就是每天業(yè)務(wù)訂單量都是不同的key,一天的訂單量不會(huì)超過2^32)

package com.hmdp;import com.hmdp.entity.Shop;
import com.hmdp.service.impl.ShopServiceImpl;
import com.hmdp.utils.CacheClient;
import com.hmdp.utils.RedisIdWorker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;@SpringBootTest
class HmDianPingApplicationTests {@Resourceprivate CacheClient cacheClient;@Resourceprivate ShopServiceImpl shopService;@Resourceprivate RedisIdWorker redisIdWorker;private ExecutorService es = Executors.newFixedThreadPool(500);@Testpublic void testIdWorker() throws InterruptedException {CountDownLatch latch = new CountDownLatch(300);Runnable task = () -> {for (int i = 0; i < 100; i++) {long id = redisIdWorker.nextId("order");System.out.println("id=" + id);}latch.countDown();};long begin = System.currentTimeMillis();for (int i = 0; i < 300; i++) {es.submit(task);//線程池異步所以使用CountDownLatch}latch.await();//等待所有countDown結(jié)束long end = System.currentTimeMillis();System.out.println("time=" + (end - begin));}@Testvoid testSaveShop() throws InterruptedException {//shopService.saveShop2Redis(1L,10L);Shop shop = shopService.getById(1L);cacheClient.setWithLogicalExpire(CACHE_SHOP_KEY + 1L, shop, 10L, TimeUnit.SECONDS);}
}

?

添加秒殺劵(這里我們沒有編寫后臺(tái)管理系統(tǒng),為了方便起見,使用postman測(cè)試)

{"shopId": 1,"title": "100元代金券","subTitle": "周一到周五均可使用","rules": "全場(chǎng)通用\\n無需預(yù)約\\n可無限疊加\\不兌現(xiàn)、不找零\\n僅限堂食","payValue": 8000,"actualValue": 10000,"type": 1,"stock": 100,"beginTime":"2023-02-23T19:00:00","endTime":"2023-02-23T20:00:00"
}

注意,我們點(diǎn)擊發(fā)送請(qǐng)求前需要添加請(qǐng)求頭信息 控制臺(tái)打印其值即token,否則會(huì)報(bào)401錯(cuò)

?添加成功

優(yōu)惠券秒殺下單功能?

package com.hmdp.service.impl;import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.time.LocalDateTime;/*** <p>*  服務(wù)實(shí)現(xiàn)類* </p>** @author 虎哥* @since 2021-12-22*/
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Resourceprivate ISeckillVoucherService seckillVoucherService;@Resourceprivate RedisIdWorker redisIdWorker;@Override@Transactionalpublic Result seckillVoucher(Long voucherId) {// 1.查詢優(yōu)惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);// 2.判斷秒殺是否開始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {// 尚未開始return Result.fail("秒殺尚未開始!");}// 3.判斷秒殺是否結(jié)束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail("秒殺已經(jīng)結(jié)束!");}// 4.判斷庫(kù)存是否充足if (voucher.getStock()<1) {// 庫(kù)存不足return Result.fail("庫(kù)存不足!");}// 5.扣減庫(kù)存// eq代表where條件boolean success=seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id",voucherId).update();if (!success){// 扣減失敗return Result.fail("庫(kù)存不足!");}// 6.創(chuàng)建訂單VoucherOrder voucherOrder = new VoucherOrder();// 6.1. 訂單idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);// 6.2. 用戶idLong userId = UserHolder.getUser().getId();voucherOrder.setUserId(userId);// 6.3. 代金券idvoucherOrder.setVoucherId(voucherId);save(voucherOrder);// 7.返回訂單Idreturn Result.ok(orderId);}
}

超賣

版本號(hào)法+CAS法(compare and set)?

// 5.扣減庫(kù)存// eq代表where條件boolean success=seckillVoucherService.update().setSql("stock=stock-1")// set stock=stock-1.eq("voucher_id",voucherId).eq("stock",voucher.getStock())// where id=? and stock=?.update();

樂觀鎖成功率太低(因?yàn)橹灰獛?kù)存不一致就失敗)優(yōu)化:就是庫(kù)存大于0即可

樂觀鎖的方案是在更新數(shù)據(jù)時(shí)候去使用的

// 5.扣減庫(kù)存// eq代表where條件boolean success=seckillVoucherService.update().setSql("stock=stock-1")// set stock=stock-1.eq("voucher_id",voucherId).gt("stock",0)// where id=? and stock>0.update();

一人一單(知識(shí)點(diǎn):動(dòng)態(tài)代理、事務(wù)、aop、悲觀鎖、鎖的范圍)

這里是新增數(shù)據(jù),無法判斷是否有無修改過,因?yàn)樗淮嬖?#xff0c;判斷是否存在,只能用悲觀鎖方案解決(從查詢訂單到判斷訂單到新增訂單這一段邏輯加上悲觀鎖)

雖然userId值一樣,但是每次調(diào)用toString方法都是新的對(duì)象?

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency>

暴露代理對(duì)象?

package com.hmdp;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan("com.hmdp.mapper")
@SpringBootApplication
public class HmDianPingApplication {public static void main(String[] args) {SpringApplication.run(HmDianPingApplication.class, args);}}

之前在方法內(nèi)部添加鎖可能會(huì)發(fā)生情況:開啟事務(wù)執(zhí)行,獲取鎖查詢完減庫(kù)存提交訂單釋放鎖才會(huì)提交事務(wù),函數(shù)執(zhí)行完之后由spring去提交事務(wù),鎖釋放意味著其他線程也可以進(jìn)來了,而此時(shí)事務(wù)尚未提交,其他線程趁此時(shí)進(jìn)來,去查詢訂單的話,剛剛新增的訂單很有可能還未寫進(jìn)數(shù)據(jù)庫(kù),因?yàn)檫€未提交,查詢依然不存在,產(chǎn)生并發(fā)問題,?可知我們剛才鎖定的范圍縮小了,解決方案:把整個(gè)函數(shù)鎖起來,先事務(wù)提交再釋放鎖,那我們就要把鎖放在函數(shù)外面,這時(shí)就出現(xiàn)事務(wù)問題消失 因?yàn)榇藭r(shí)調(diào)用方法的直接是當(dāng)前目標(biāo)對(duì)象IVoucherOrderService對(duì)象,沒有事務(wù)功能,事務(wù)要想生效,其實(shí)是我們spring對(duì)當(dāng)前這個(gè)類進(jìn)行動(dòng)態(tài)代理, 拿到當(dāng)前類的代理對(duì)象,用它來作事務(wù)處理?

package com.hmdp.service.impl;import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.time.LocalDateTime;/*** <p>*  服務(wù)實(shí)現(xiàn)類* </p>** @author 虎哥* @since 2021-12-22*/
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Resourceprivate ISeckillVoucherService seckillVoucherService;@Resourceprivate RedisIdWorker redisIdWorker;@Overridepublic Result seckillVoucher(Long voucherId) {// 1.查詢優(yōu)惠券SeckillVoucher voucher = seckillVoucherService.getById(voucherId);// 2.判斷秒殺是否開始if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {// 尚未開始return Result.fail("秒殺尚未開始!");}// 3.判斷秒殺是否結(jié)束if (voucher.getEndTime().isBefore(LocalDateTime.now())) {return Result.fail("秒殺已經(jīng)結(jié)束!");}// 4.判斷庫(kù)存是否充足if (voucher.getStock()<1) {// 庫(kù)存不足return Result.fail("庫(kù)存不足!");}// 5.一人一單Long userId = UserHolder.getUser().getId();synchronized (userId.toString().intern()) {// 獲取代理對(duì)象(事務(wù))IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);}}// 方法加上同步鎖肯定是線程安全的 this指向IVoucherOrderService// 不建議加上方法上 因?yàn)榇砣魏斡脩魜砹硕家由湘i 串行執(zhí)行 性能低// 我們只需要同一用戶進(jìn)來才需要加鎖@Transactionalpublic Result createVoucherOrder(Long voucherId){// 5.一人一單Long userId = UserHolder.getUser().getId();// 當(dāng)用戶id值一樣時(shí),鎖就一樣// synchronized (userId.toString().intern()) {// 5.1.查詢訂單int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();// 5.2.判斷是否存在if (count > 0) {//用戶已經(jīng)購(gòu)買過return Result.fail("用戶已經(jīng)購(gòu)買過一次!");}// 6.扣減庫(kù)存// eq代表where條件boolean success = seckillVoucherService.update().setSql("stock=stock-1")// set stock=stock-1.eq("voucher_id", voucherId).gt("stock", 0)// where id=? and stock>0.update();if (!success) {// 扣減失敗return Result.fail("庫(kù)存不足!");}// 7.創(chuàng)建訂單VoucherOrder voucherOrder = new VoucherOrder();// 7.1. 訂單idlong orderId = redisIdWorker.nextId("order");voucherOrder.setId(orderId);// 7.2. 用戶idvoucherOrder.setUserId(userId);// 7.3. 代金券idvoucherOrder.setVoucherId(voucherId);save(voucherOrder);// 8.返回訂單Idreturn Result.ok(orderId);//}}
}

上述處理方案雖然解決了一人一單的并發(fā)安全問題(通過加鎖可以解決單機(jī)情況下的問題),但是在集群模式下就不行了?

通過加鎖可以解決在單機(jī)情況下的一人一單安全問題,但是在集群模式下就不行了(每個(gè)jvm都有自己的鎖監(jiān)視器,集群模式下各個(gè)服務(wù)器的鎖不共享)。
因此,我們的解決方案就是實(shí)現(xiàn)一個(gè)共享的鎖監(jiān)視器,即:
分布式鎖:滿足分布式系統(tǒng)或集群模式下多進(jìn)程可見并且互斥的鎖。?

?

http://www.risenshineclean.com/news/6287.html

相關(guān)文章:

  • 呼和浩特市建設(shè)委員會(huì)官方網(wǎng)站網(wǎng)站建設(shè)需求模板
  • 如何注冊(cè)騰訊企業(yè)郵箱店鋪seo是什么意思
  • 下沙網(wǎng)站建設(shè)谷歌優(yōu)化seo
  • 網(wǎng)站建設(shè)合約網(wǎng)上學(xué)電腦培訓(xùn)中心
  • 江西住房和城鄉(xiāng)建設(shè)廳網(wǎng)站windows優(yōu)化大師免費(fèi)
  • 為什么做金融網(wǎng)站犯法seo是指搜索引擎營(yíng)銷
  • 導(dǎo)航網(wǎng)站怎么做seo霸屏推廣
  • 做網(wǎng)站軟件j短視頻營(yíng)銷成功的案例
  • 自助手機(jī)建站網(wǎng)站站點(diǎn)
  • 安徽專業(yè)網(wǎng)站建設(shè)大全推薦新網(wǎng)站推廣方法
  • 做網(wǎng)站商城必須要買空間嗎app運(yùn)營(yíng)方案
  • 怎么查看網(wǎng)站的dns武漢網(wǎng)站運(yùn)營(yíng)專業(yè)樂云seo
  • 哈爾濱 做網(wǎng)站白度指數(shù)
  • wordpress 微博】蘇州網(wǎng)站建設(shè)優(yōu)化
  • seo是怎么優(yōu)化的寧波優(yōu)化系統(tǒng)
  • 工商局網(wǎng)站官網(wǎng)平臺(tái)推廣是什么
  • 描述建設(shè)一個(gè)網(wǎng)站的基本步驟網(wǎng)站建設(shè)企業(yè)
  • 如何在網(wǎng)站發(fā)廣告做seo推廣一年大概的費(fèi)用
  • 網(wǎng)站怎樣維護(hù)seo入門培訓(xùn)
  • 網(wǎng)站的優(yōu)化與網(wǎng)站建設(shè)有關(guān)嗎如何制作自己的鏈接
  • 男做直播網(wǎng)站的磁力搜索引擎
  • 長(zhǎng)沙網(wǎng)站建設(shè)專家遼寧好的百度seo公司
  • 國(guó)外做外掛的網(wǎng)站中國(guó)免費(fèi)廣告網(wǎng)
  • 鄭州做品牌網(wǎng)站的公司免費(fèi)網(wǎng)站 推廣網(wǎng)站
  • 網(wǎng)站密碼如何找回密碼百度企業(yè)官網(wǎng)
  • asp與java做網(wǎng)站效果百度seo優(yōu)化軟件
  • ui設(shè)計(jì)是什么專業(yè)的鄭州seo
  • 深度蘇州自媒體公司抖音優(yōu)化是什么意思
  • 有哪些網(wǎng)站開發(fā)框架網(wǎng)上國(guó)網(wǎng)推廣
  • 做網(wǎng)站建設(shè)掙錢嗎專業(yè)網(wǎng)絡(luò)推廣機(jī)構(gòu)