国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java高并發(fā)秒殺系統(tǒng)【觀后總結(jié)】

mengbo / 925人閱讀

摘要:項(xiàng)目簡(jiǎn)介在慕課網(wǎng)上發(fā)現(xiàn)了一個(gè)項(xiàng)目,內(nèi)容講的是高并發(fā)秒殺,覺得挺有意思的,就進(jìn)去學(xué)習(xí)了一番。比如重復(fù)秒殺,秒殺關(guān)閉這些都是屬于秒殺的業(yè)務(wù)。秒殺操作是與數(shù)據(jù)庫(kù)的事務(wù)相關(guān)的,不能使用緩存來(lái)替代了。

項(xiàng)目簡(jiǎn)介

在慕課網(wǎng)上發(fā)現(xiàn)了一個(gè)JavaWeb項(xiàng)目,內(nèi)容講的是高并發(fā)秒殺,覺得挺有意思的,就進(jìn)去學(xué)習(xí)了一番。

記錄在該項(xiàng)目中學(xué)到了什么玩意..

該項(xiàng)目源碼對(duì)應(yīng)的gitHub地址(由觀看其視頻的人編寫,并非視頻源代碼):https://github.com/codingXiaxw/seckill

我結(jié)合其資料和觀看視頻的時(shí)候整理出從該項(xiàng)目學(xué)到了什么...

項(xiàng)目Dao層 日志記錄工具:

    
Mybatis之前沒注意到的配置屬性:

使用jdbc的getGeneratekeys獲取自增主鍵值,這個(gè)屬性還是挺有用的。




    
    
        
        
        
        

        
        
    

Mybatis返回的對(duì)象如果有關(guān)聯(lián)字段,除了使用resultMap還有下面這種方式(雖然我還是覺得resultMap會(huì)方便一點(diǎn))

    
數(shù)據(jù)庫(kù)連接池可能用到的屬性:
        
        
        
        
        

        
        
        
        
spring與Junit整合:
/**
 * Created by codingBoy on 16/11/27.
 * 配置spring和junit整合,這樣junit在啟動(dòng)時(shí)就會(huì)加載spring容器
 */
@RunWith(SpringJUnit4ClassRunner.class)
//告訴junit spring的配置文件
@ContextConfiguration({"classpath:spring/spring-dao.xml"})
public class SeckillDaoTest {

    //注入Dao實(shí)現(xiàn)類依賴
    @Resource
    private SeckillDao seckillDao;


    @Test
    public void queryById() throws Exception {
        long seckillId=1000;
        Seckill seckill=seckillDao.queryById(seckillId);
        System.out.println(seckill.getName());
        System.out.println(seckill);
    }
}
Mybatis參數(shù)為一個(gè)以上時(shí)

之前在學(xué)習(xí)MyBatis的時(shí)候,如果參數(shù)超過了一個(gè),那么是使用Map集合來(lái)進(jìn)行裝載的!

在這次教程中發(fā)現(xiàn),可以不用Map集合(如果都是基本數(shù)據(jù)類型)!

例子:使用@Param就行了!

int reduceNumber(@Param("seckillId") long seckillId, @Param("killTime") Date killTime);

在XML文件中可以直接忽略parameterType了!

避免重復(fù)插入數(shù)據(jù)時(shí)拋出異常

如果主鍵重復(fù)插入數(shù)據(jù)的時(shí)候,Mybatis正常是會(huì)拋出異常的,我們又不希望它拋出異常,那么我們可以這樣做:

寫ignore..

Service層 tdo
一個(gè)dto包作為傳輸層,dto和entity的區(qū)別在于:entity用于業(yè)務(wù)數(shù)據(jù)的封裝,而dto用于完成web和service層的數(shù)據(jù)傳遞。

對(duì)于dto這個(gè)概念,在之前我是接觸過一次的,但是沒有好好地實(shí)踐起來(lái)。這次看到了它的用法了。

我的理解是:Service與Web層數(shù)據(jù)傳遞數(shù)據(jù)的再包裝了一個(gè)對(duì)象而已。因?yàn)楹芏鄷r(shí)候Service層返回的數(shù)據(jù)如果使用的是POJO,POJO很多的屬性是多余的,還有一些想要的數(shù)據(jù)又包含不了。此時(shí),dto又可以再一次對(duì)要傳輸?shù)臄?shù)據(jù)進(jìn)行抽象,封裝想獲取的數(shù)據(jù)

定義多個(gè)異常對(duì)象

之前的異常對(duì)象都是針對(duì)整個(gè)業(yè)務(wù)的,其實(shí)還是可以細(xì)分多個(gè)異常類的出來(lái)的。比如“重復(fù)秒殺”,”秒殺關(guān)閉“這些都是屬于秒殺的業(yè)務(wù)。

這樣做的好處就是看到拋出的異常就能夠知道是具體哪部分錯(cuò)了。

對(duì)于視頻中在Service層就catch住了很多異常,我覺得可以在Service層直接拋出,在Controller也能拋出,直接使用統(tǒng)一異常處理器類來(lái)管理會(huì)更加方便!

提倡使用注解方式使用事務(wù)


我覺得就是代碼更加清晰吧,使用注解的話。

在視頻下面還有同學(xué)說(shuō)如果在Service中調(diào)用事務(wù)方法會(huì)有些坑,我暫時(shí)還沒遇到過。先存起來(lái)吧:

并發(fā)性上不去是因?yàn)楫?dāng)多個(gè)線程同時(shí)訪問一行數(shù)據(jù)時(shí),產(chǎn)生了事務(wù),因此產(chǎn)生寫鎖,每當(dāng)一個(gè)獲取了事務(wù)的線程把鎖釋放,另一個(gè)排隊(duì)線程才能拿到寫鎖,QPS和事務(wù)執(zhí)行的時(shí)間有密切關(guān)系,事務(wù)執(zhí)行時(shí)間越短,并發(fā)性越高,這也是要將費(fèi)時(shí)的I/O操作移出事務(wù)的原因。
關(guān)于同類中調(diào)用事務(wù)方法的時(shí)候有個(gè)坑,同學(xué)們需要注意下AOP切不到調(diào)用事務(wù)方法。事務(wù)不會(huì)生效,解決辦法有幾種,可以搜一下,找一下適合自己的方案。本質(zhì)問題時(shí)類內(nèi)部調(diào)用時(shí)AOP不會(huì)用代理調(diào)用內(nèi)部方法。
“關(guān)于同類中調(diào)用事務(wù)方法的時(shí)候有個(gè)坑”  解決方案 
1、如果是基于接口動(dòng)態(tài)代理 是沒有問題的,直接使用接口調(diào)用
2、如果是基于class的動(dòng)態(tài)代理 可以用 AopContext.currentProxy() 解決,注意剝離方法一定是public 修飾 !!
MD5暴露接口

其實(shí)我也在想MD5暴露出去的url是不是真的有用,也見到有人提問了。

https://www.imooc.com/qadetail/164058

回答者:

不能說(shuō)沒作用,如果不加密,用戶截取了你的訪問地址,他看到了當(dāng)前秒殺ID為1000,他完全可以推測(cè)出其他的秒殺地址,或者說(shuō)他可以造出一批地址;視頻中秒殺在數(shù)據(jù)庫(kù)中判斷了秒殺時(shí)間,其他時(shí)間他自然是秒殺不到,但是對(duì)數(shù)據(jù)庫(kù)也有一定的沖擊,如果他用定時(shí)器或者循環(huán)秒殺軟件,你的系統(tǒng)承受力是個(gè)問題;另一方面對(duì)于一些還沒開始的秒殺,他模擬地址以后,完全可以用定時(shí)器一直訪問。加密以后由于他拿不到混淆碼,就只能通過點(diǎn)擊鏈接進(jìn)行秒殺……

簡(jiǎn)單理解:通過MD5加密以后,用戶在秒殺之前模擬不出真實(shí)的地址,還是有一定作用的。

枚舉類
在return new SeckillExecution(seckillId,1,"秒殺成功",successKilled);代碼中,我們返回的state和stateInfo參數(shù)信息應(yīng)該是輸出給前端的,但是我們不想在我們的return代碼中硬編碼這兩個(gè)參數(shù),所以我們應(yīng)該考慮用枚舉的方式將這些常量封裝起來(lái)

public enum SeckillStatEnum {

    SUCCESS(1,"秒殺成功"),
    END(0,"秒殺結(jié)束"),
    REPEAT_KILL(-1,"重復(fù)秒殺"),
    INNER_ERROR(-2,"系統(tǒng)異常"),
    DATE_REWRITE(-3,"數(shù)據(jù)篡改");

    private int state;
    private String info;

    SeckillStatEnum(int state, String info) {
        this.state = state;
        this.info = info;
    }

    public int getState() {
        return state;
    }


    public String getInfo() {
        return info;
    }


    public static SeckillStatEnum stateOf(int index)
    {
        for (SeckillStatEnum state : values())
        {
            if (state.getState()==index)
            {
                return state;
            }
        }
        return null;
    }
}
Web層開發(fā)技巧 Restful接口設(shè)計(jì)學(xué)習(xí)

之前就已經(jīng)接觸過RESTful這樣的思想理念的,可是在第一個(gè)項(xiàng)目中是沒有用起來(lái)的。因?yàn)檫€是不大習(xí)慣,怕寫成不倫不類的RESTful接口,打算在第二個(gè)項(xiàng)目中將RESTful全部應(yīng)用起來(lái)。




參考博文:http://kb.cnblogs.com/page/512047/

SpringMVC之前不知道的細(xì)節(jié)

@DateTimeFormat注解對(duì)時(shí)間進(jìn)行格式化!(這個(gè)我暫時(shí)沒有試驗(yàn))

    
    
    

    
    
    

返回統(tǒng)一格式的JSON

之前在Web層與Service中封裝了dto來(lái)進(jìn)行這兩層的數(shù)據(jù)進(jìn)行傳輸,而我們一般都是在Controller返回JSON給前端進(jìn)行解析。

最好的做法就是將JSON的格式也統(tǒng)一化。這樣做就能夠很好地形成規(guī)范了!

//將所有的ajax請(qǐng)求返回類型,全部封裝成json數(shù)據(jù)
public class SeckillResult {

    private boolean success;
    private T data;
    private String error;

    public SeckillResult(boolean success, T data) {
        this.success = success;
        this.data = data;
    }

    public SeckillResult(boolean success, String error) {
        this.success = success;
        this.error = error;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }
}
獲取JSON數(shù)據(jù)方式

之前獲取JSON都是使用object.properties的方式來(lái)獲取的,這次還看到了另一種方式:

JavaScript模塊化

之前在項(xiàng)目中寫JS代碼都是要什么功能,寫到哪里的。看了這次視頻,發(fā)現(xiàn)JS都可以模塊化!!!

JS模塊化起來(lái)可讀性還是比之前要好的,這是我之前沒有接觸過的,以后寫JS代碼就要注意了!

下面貼上一段代碼來(lái)感受一下:

/**
 *  模塊化javaScript
 * Created by jianrongsun on 17-5-25.
 */
var seckill = {
    // 封裝秒殺相關(guān)的ajax的url
    URL: {
        now: function () {
            return "/seckill/time/now";
        },
        exposer: function (seckillId) {
            return "/seckill/" + seckillId + "/exposer";
        },
        execution: function (seckillId, md5) {
            return "/seckill/" + seckillId + "/" + md5 + "/execution";
        }
    },
    // 驗(yàn)證手機(jī)號(hào)碼
    validatePhone: function (phone) {
        return !!(phone && phone.length === 11 && !isNaN(phone));
    },
    // 詳情頁(yè)秒殺業(yè)務(wù)邏輯
    detail: {
        // 詳情頁(yè)開始初始化
        init: function (params) {
            console.log("獲取手機(jī)號(hào)碼");
            // 手機(jī)號(hào)驗(yàn)證登錄,計(jì)時(shí)交互
            var userPhone = $.cookie("userPhone");
            // 驗(yàn)證手機(jī)號(hào)
            if (!seckill.validatePhone(userPhone)) {
                console.log("未填寫手機(jī)號(hào)碼");
                // 驗(yàn)證手機(jī)控制輸出
                var killPhoneModal = $("#killPhoneModal");
                killPhoneModal.modal({
                    show: true,  // 顯示彈出層
                    backdrop: "static",  // 靜止位置關(guān)閉
                    keyboard: false    // 關(guān)閉鍵盤事件
                });

                $("#killPhoneBtn").click(function () {
                    console.log("提交手機(jī)號(hào)碼按鈕被點(diǎn)擊");
                    var inputPhone = $("#killPhoneKey").val();
                    console.log("inputPhone" + inputPhone);
                    if (seckill.validatePhone(inputPhone)) {
                        // 把電話寫入cookie
                        $.cookie("userPhone", inputPhone, {expires: 7, path: "/seckill"});
                        // 驗(yàn)證通過 刷新頁(yè)面
                        window.location.reload();
                    } else {
                        // todo 錯(cuò)誤文案信息寫到前端
                        $("#killPhoneMessage").hide().html("").show(300);
                    }
                });
            } else {
                console.log("在cookie中找到了電話號(hào)碼,開啟計(jì)時(shí)");
                // 已經(jīng)登錄了就開始計(jì)時(shí)交互
                var startTime = params["startTime"];
                var endTime = params["endTime"];
                var seckillId = params["seckillId"];
                console.log("開始秒殺時(shí)間=======" + startTime);
                console.log("結(jié)束秒殺時(shí)間========" + endTime);
                $.get(seckill.URL.now(), {}, function (result) {
                    if (result && result["success"]) {
                        var nowTime = seckill.convertTime(result["data"]);
                        console.log("服務(wù)器當(dāng)前的時(shí)間==========" + nowTime);
                        // 進(jìn)行秒殺商品的時(shí)間判斷,然后計(jì)時(shí)交互
                        seckill.countDown(seckillId, nowTime, startTime, endTime);
                    } else {
                        console.log("結(jié)果:" + result);
                        console.log("result" + result);
                    }
                });
            }

        }
    },
    handlerSeckill: function (seckillId, mode) {
        // 獲取秒殺地址
        mode.hide().html("");
        console.debug("開始進(jìn)行秒殺地址獲取");
        $.get(seckill.URL.exposer(seckillId), {}, function (result) {
            if (result && result["success"]) {
                var exposer = result["data"];
                if (exposer["exposed"]) {
                    console.log("有秒殺地址接口");
                    // 開啟秒殺,獲取秒殺地址
                    var md5 = exposer["md5"];
                    var killUrl = seckill.URL.execution(seckillId, md5);
                    console.log("秒殺的地址為:" + killUrl);
                    // 綁定一次點(diǎn)擊事件
                    $("#killBtn").one("click", function () {
                        console.log("開始進(jìn)行秒殺,按鈕被禁用");
                        // 執(zhí)行秒殺請(qǐng)求,先禁用按鈕
                        $(this).addClass("disabled");
                        // 發(fā)送秒殺請(qǐng)求
                        $.post(killUrl, {}, function (result) {
                            var killResult = result["data"];
                            var state = killResult["state"];
                            var stateInfo = killResult["stateInfo"];
                            console.log("秒殺狀態(tài)" + stateInfo);
                            // 顯示秒殺結(jié)果
                            mode.html("" + stateInfo + "");

                        });

                    });
                    mode.show();
                } else {
                    console.warn("還沒有暴露秒殺地址接口,無(wú)法進(jìn)行秒殺");
                    // 未開啟秒殺
                    var now = seckill.convertTime(exposer["now"]);
                    var start = seckill.convertTime(exposer["start"]);
                    var end = seckill.convertTime(exposer["end"]);
                    console.log("當(dāng)前時(shí)間" + now);
                    console.log("開始時(shí)間" + start);
                    console.log("結(jié)束時(shí)間" + end);
                    console.log("開始倒計(jì)時(shí)");
                    console.debug("開始進(jìn)行倒計(jì)時(shí)");
                    seckill.countDown(seckillId, now, start, end);
                }
            } else {
                console.error("服務(wù)器端查詢秒殺商品詳情失敗");
                console.log("result" + result.valueOf());
            }
        });
    },
    countDown: function (seckillId, nowTime, startTime, endTime) {
        console.log("秒殺的商品ID:" + seckillId + ",服務(wù)器當(dāng)前時(shí)間:" + nowTime + ",開始秒殺的時(shí)間:" + startTime + ",結(jié)束秒殺的時(shí)間" + endTime);
        //  獲取顯示倒計(jì)時(shí)的文本域
        var seckillBox = $("#seckill-box");
        //  獲取時(shí)間戳進(jìn)行時(shí)間的比較
        nowTime = new Date(nowTime).valueOf();
        startTime = new Date(startTime).valueOf();
        endTime = new Date(endTime).valueOf();
        console.log("轉(zhuǎn)換后的Date類型當(dāng)前時(shí)間戳" + nowTime);
        console.log("轉(zhuǎn)換后的Date類型開始時(shí)間戳" + startTime);
        console.log("轉(zhuǎn)換后的Date類型結(jié)束時(shí)間戳" + endTime);
        if (nowTime < endTime && nowTime > startTime) {
            // 秒殺開始
            console.log("秒殺可以開始,兩個(gè)條件符合");
            seckill.handlerSeckill(seckillId, seckillBox);
        }
        else if (nowTime > endTime) {
            alert(nowTime > endTime);
            // console.log(nowTime + ">" + startTime);
            console.log(nowTime + ">" + endTime);

            // 秒殺結(jié)束
            console.warn("秒殺已經(jīng)結(jié)束了,當(dāng)前時(shí)間為:" + nowTime + ",秒殺結(jié)束時(shí)間為" + endTime);
            seckillBox.html("秒殺結(jié)束");
        } else {
            console.log("秒殺還沒開始");
            alert(nowTime < startTime);
            // 秒殺未開啟
            var killTime = new Date(startTime + 1000);
            console.log(killTime);
            console.log("開始計(jì)時(shí)效果");
            seckillBox.countdown(killTime, function (event) {
                // 事件格式
                var format = event.strftime("秒殺倒計(jì)時(shí): %D天 %H時(shí) %M分 %S秒");
                console.log(format);
                seckillBox.html(format);
            }).on("finish.countdown", function () {
                // 事件完成后回調(diào)事件,獲取秒殺地址,控制業(yè)務(wù)邏輯
                console.log("準(zhǔn)備執(zhí)行回調(diào),獲取秒殺地址,執(zhí)行秒殺");
                console.log("倒計(jì)時(shí)結(jié)束");
                seckill.handlerSeckill(seckillId, seckillBox);
            });
        }
    },
    cloneZero: function (time) {
        var cloneZero = ":00";
        if (time.length < 6) {
            console.warn("需要拼接時(shí)間");
            time = time + cloneZero;
            return time;
        } else {
            console.log("時(shí)間是完整的");
            return time;
        }
    },
    convertTime: function (localDateTime) {
        var year = localDateTime.year;
        var monthValue = localDateTime.monthValue;
        var dayOfMonth = localDateTime.dayOfMonth;
        var hour = localDateTime.hour;
        var minute = localDateTime.minute;
        var second = localDateTime.second;
        return year + "-" + monthValue + "-" + dayOfMonth + " " + hour + ":" + minute + ":" + second;
    }
};
高并發(fā)性能優(yōu)化

前三篇已經(jīng)做好了這個(gè)系統(tǒng)了,但是作為一個(gè)秒殺系統(tǒng)而言,它能支持的并發(fā)量是很低的。那我們現(xiàn)在要考慮怎么調(diào)優(yōu)。

分析

秒殺的地址接口可以借助redis來(lái)進(jìn)行優(yōu)化,不用多次訪問數(shù)據(jù)庫(kù)。

秒殺操作是與數(shù)據(jù)庫(kù)的事務(wù)相關(guān)的,不能使用緩存來(lái)替代了。下面給出的方案是需要修改源碼的,難度是比較難的。





下面分析瓶頸究竟在哪:

Mysql執(zhí)行單條的SQL語(yǔ)句其實(shí)是非常快的。

主要是行級(jí)鎖事務(wù)的等待,網(wǎng)絡(luò)的延遲和GC回收!






解決思路:

解決秒殺接口

對(duì)于秒殺接口而言,需要使用到Redis將數(shù)據(jù)進(jìn)行緩存起來(lái)。那么用戶就訪問就不用去訪問數(shù)據(jù)庫(kù)了,我們給Redis緩存的數(shù)據(jù)就好了。

這次使用Jedis來(lái)操作Redis.

還有值得 注意的地方:我們可以使用ProtostuffIOUtil來(lái)代替JDK的序列化,因?yàn)檫@個(gè)的序列化功能比JDK的要做得好很多!

package com.suny.dao.cache;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import com.suny.entity.Seckill;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * 操作Redis的dao類
 * Created by 孫建榮 on 17-5-27.下午4:44
 */
public class RedisDao {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final JedisPool jedisPool;

    private RuntimeSchema schema = RuntimeSchema.createFrom(Seckill.class);

    public RedisDao(String ip, int port) {
        jedisPool = new JedisPool(ip, port);
    }

    public Seckill getSeckill(long seckillId) {
        // redis操作業(yè)務(wù)邏輯
        try (Jedis jedis = jedisPool.getResource()) {
            String key = "seckill:" + seckillId;
            // 并沒有實(shí)現(xiàn)內(nèi)部序列化操作
            //get->byte[]字節(jié)數(shù)組->反序列化>Object(Seckill)
            // 采用自定義的方式序列化
            // 緩存獲取到
            byte[] bytes = jedis.get(key.getBytes());
            if (bytes != null) {
                // 空對(duì)象
                Seckill seckill = schema.newMessage();
                ProtostuffIOUtil.mergeFrom(bytes, seckill, schema);
                // seckill被反序列化
                return seckill;
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }

    public String putSeckill(Seckill seckill) {
        //  set Object(Seckill) -> 序列化 -> byte[]
        try (Jedis jedis = jedisPool.getResource()) {
            String key = "seckill:" + seckill.getSeckillId();
            byte[] bytes = ProtostuffIOUtil.toByteArray(seckill, schema,
                    LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
            // 超時(shí)緩存
            int timeout=60*60;
            return jedis.setex(key.getBytes(), timeout, bytes);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }


}

        
        
            redis.clients
            jedis
            2.9.0
        

        
        
            com.dyuproject.protostuff
            protostuff-core
            1.1.1
        
        
            com.dyuproject.protostuff
            protostuff-runtime
            1.1.1
        

RedisDao并不受Mybatis的代理影響,于是是需要我們自己手動(dòng)創(chuàng)建的。

最終,我們的service邏輯就會(huì)變成這樣子:

秒殺操作優(yōu)化

再次回到我們的秒殺操作,其實(shí)需要優(yōu)化的地方就是我們的GC和行級(jí)鎖等待的時(shí)間。

我們之前的邏輯是這樣的:先執(zhí)行減庫(kù)存操作,再插入購(gòu)買成功的記錄

其實(shí),我們可以先插入成功購(gòu)買的記錄,再執(zhí)行減庫(kù)存的操作!

那這兩者有啥區(qū)別呢???減庫(kù)存的操作會(huì)導(dǎo)致行級(jí)鎖的等待,而我們先進(jìn)行insert的話,那么就不會(huì)被行級(jí)鎖所干擾了。并且,我們這中兩個(gè)操作是在同一個(gè)事物中的,并不會(huì)出現(xiàn)“超賣”的情況!

關(guān)于先執(zhí)行insert與先執(zhí)行update的區(qū)別,兩個(gè)事務(wù)同時(shí)insert的情況下,沒有鎖競(jìng)爭(zhēng),執(zhí)行速度會(huì)快,當(dāng)兩個(gè)事務(wù)先update同一行數(shù)據(jù),會(huì)有一個(gè)事務(wù)獲得行鎖,鎖在事務(wù)提交之前都不會(huì)釋放,所以讓鎖被持有的時(shí)間最短能提升效率

所以我們service層的邏輯可以改成這樣:

這不是最終的方案,如果為了性能的優(yōu)化我們還可以將SQL在Mysql中運(yùn)行,不受Spring的事務(wù)來(lái)管理。在Mysql使用存儲(chǔ)過程來(lái)進(jìn)行提交性能


-- 秒殺執(zhí)行儲(chǔ)存過程
DELIMITER $$ -- console ; 轉(zhuǎn)換為
$$
-- 定義儲(chǔ)存過程
-- 參數(shù): in 參數(shù)   out輸出參數(shù)
-- row_count() 返回上一條修改類型sql(delete,insert,update)的影響行數(shù)
-- row_count:0:未修改數(shù)據(jù) ; >0:表示修改的行數(shù); <0:sql錯(cuò)誤
CREATE PROCEDURE `seckill`.`execute_seckill`
  (IN v_seckill_id BIGINT, IN v_phone BIGINT,
   IN v_kill_time  TIMESTAMP, OUT r_result INT)
  BEGIN
    DECLARE insert_count INT DEFAULT 0;
    START TRANSACTION;
    INSERT IGNORE INTO success_killed
    (seckill_id, user_phone, create_time)
    VALUES (v_seckill_id, v_phone, v_kill_time);
    SELECT row_count()
    INTO insert_count;
    IF (insert_count = 0)
    THEN
      ROLLBACK;
      SET r_result = -1;
    ELSEIF (insert_count < 0)
      THEN
        ROLLBACK;
        SET r_result = -2;
    ELSE
      UPDATE seckill
      SET number = number - 1
      WHERE seckill_id = v_seckill_id
            AND end_time > v_kill_time
            AND start_time < v_kill_time
            AND number > 0;
      SELECT row_count()
      INTO insert_count;
      IF (insert_count = 0)
      THEN
        ROLLBACK;
        SET r_result = 0;
      ELSEIF (insert_count < 0)
        THEN
          ROLLBACK;
          SET r_result = -2;
      ELSE
        COMMIT;
        SET r_result = 1;

      END IF;
    END IF;
  END;
$$
--  儲(chǔ)存過程定義結(jié)束
DELIMITER ;
SET @r_result = -3;
--  執(zhí)行儲(chǔ)存過程
CALL execute_seckill(1003, 13502178891, now(), @r_result);
-- 獲取結(jié)果
SELECT @r_result;

Mybatis調(diào)用存儲(chǔ)過程其實(shí)和JDBC是一樣的:

在使用存儲(chǔ)過程的時(shí)候,我們需要4個(gè)參數(shù),其實(shí)result是在存儲(chǔ)過程中被賦值的。我們可以通過MapUtils來(lái)獲取相對(duì)應(yīng)的值。這是我之前沒有接觸過的。

最后,對(duì)于部署的系統(tǒng)架構(gòu)應(yīng)該是這樣子的:

總結(jié)

花了點(diǎn)時(shí)間看了該視頻教程,覺得還是學(xué)到了不少的東西的。之前沒有接觸過優(yōu)化的相關(guān)問題,現(xiàn)在給我打開了思路,以及學(xué)到了不少的開發(fā)規(guī)范的問題,也是很贊的。如果是初學(xué)者的話是可以去學(xué)學(xué)的。

如果文章有錯(cuò)的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章,想要獲取更多的Java資源的同學(xué),可以關(guān)注微信公眾號(hào):Java3y

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/68821.html

相關(guān)文章

  • 移動(dòng)商城項(xiàng)目【總結(jié)

    摘要:有必要建一個(gè)資源服務(wù)器存放靜態(tài)資源。一些用戶級(jí)別的數(shù)據(jù)輕量可以考慮存儲(chǔ)在中。存儲(chǔ)的是值,可以通過來(lái)對(duì)和對(duì)象之間的轉(zhuǎn)換如果我們的數(shù)據(jù)是在后臺(tái)傳過去或者轉(zhuǎn)換而成的,在前臺(tái)上并沒有做什么改變的話。 移動(dòng)商城項(xiàng)目總結(jié) 移動(dòng)商城項(xiàng)目是我第二個(gè)做得比較大的項(xiàng)目,該項(xiàng)目系統(tǒng)來(lái)源于傳智Java168期,十天的視頻課程(想要視頻的同學(xué)關(guān)注我的公眾號(hào)就可以直接獲取了) 通過這次的項(xiàng)目又再次開闊了我的視野,...

    BlackHole1 評(píng)論0 收藏0
  • Java3y文章目錄導(dǎo)航

    摘要:前言由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 前言 由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 由于更新比較頻繁,因此隔一段時(shí)間才會(huì)更新目錄導(dǎo)航哦~想要獲取最新原創(chuàng)的技術(shù)文章歡迎關(guān)注我的公眾號(hào):Java3y Java3y文章目錄導(dǎo)航 Java基礎(chǔ) 泛型就這么簡(jiǎn)單 注解就這么簡(jiǎn)單 Druid數(shù)據(jù)庫(kù)連接池...

    KevinYan 評(píng)論0 收藏0
  • 并發(fā) - 收藏集 - 掘金

    摘要:在中一般來(lái)說(shuō)通過來(lái)創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說(shuō)明同步異步阻塞非阻塞,我們以小明去買奶茶為例。 AbstractQueuedSynchronizer 超詳細(xì)原理解析 - 后端 - 掘金今天我們來(lái)研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個(gè)類所提供的隊(duì)列式...

    levius 評(píng)論0 收藏0
  • 并發(fā) - 收藏集 - 掘金

    摘要:在中一般來(lái)說(shuō)通過來(lái)創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說(shuō)明同步異步阻塞非阻塞,我們以小明去買奶茶為例。 AbstractQueuedSynchronizer 超詳細(xì)原理解析 - 后端 - 掘金今天我們來(lái)研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個(gè)類所提供的隊(duì)列式...

    fantix 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<