Java并发调用mysql存储过程_java并发编程学习21--基于springboot的秒杀系统实现3--存储过程...

【什么是存储过程

所谓的存储过程是指:是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。可以用控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。

【为什么使用存储过程

现在的情况是:由于有update操作,所以需要使用事务保证操作的原子性,但是现在事务的控制是交给spring来控制的,那么中间就会有网络延时,GC的耗时(GC时会挂起所有线程,并发数越高GC越频繁)操作。而被update的记录会被行级锁控制住,所有的操作只能串行所以会大量阻塞,影响用户体验。

但是经过测试MySQL自身的update是40000次/秒,是一个相当不错的数据,所以我们这里将事务直接交给MySQL,使用存储过程来降低行级锁的持有时间。

【存储过程代码

使用存储过程之前必须保证数据库已经创建了存储过程。DELIMITER 表示使用‘$$’在存储过程中替代‘;’,最后需要DELIMITER ;还原回来。我们这里的out r_result表示输出,而这里的输出需要和项目中的KillStatus枚举值对应,来表明一次秒杀的结果。

DELIMITER $$

CREATE PROCEDURE `seckill`.`execute_seckill`

(in v_id VARCHAR(36),in v_kill_product_id VARCHAR(36),in v_mobile BIGINT,in v_kill_time TIMESTAMP,out r_result int)

BEGIN

DECLARE insert_count int DEFAULT 0;

START TRANSACTION;

INSERT IGNORE INTO kill_item(id,kill_product_id,mobile) values(v_id,v_kill_product_id,v_mobile);

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 kill_product SET number = number - 1

WHERE id = v_kill_product_id AND number >= 1 AND end_time > v_kill_time AND start_time < v_kill_time;

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;

$$

DELIMITER ;

【通过springdataJPA调用存储过程

Entity上需要标明相应的注解告诉spring这个Entity拥有相应的存储过程:

import lombok.Data;

import javax.persistence.*;

import java.util.Date;

/**

* 秒杀明细实体类

* @author ibm

* @since 0

* @date 2018/3/22

*/

@Entity

@Table(name = "kill_item")

@NamedStoredProcedureQuery(name = "executeSeckill", procedureName = "execute_seckill", parameters = {

@StoredProcedureParameter(mode = ParameterMode.IN, name = "v_id", type = String.class),

@StoredProcedureParameter(mode = ParameterMode.IN, name = "v_kill_product_id", type = String.class),

@StoredProcedureParameter(mode = ParameterMode.IN, name = "v_mobile", type = Long.class),

@StoredProcedureParameter(mode = ParameterMode.IN, name = "v_kill_time", type = Date.class),

@StoredProcedureParameter(mode = ParameterMode.OUT, name = "r_result", type = Integer.class) })

@Data

public class KillItem {

/**

* 记录ID

*/

@Id

@Column(name = "id")

private String id;

/**

* 秒杀产品id

*/

@Column(name = "kill_product_id")

private String killProductId;

/**

* 用户手机号码

*/

@Column(name = "mobile")

private String mobile;

/**

* 秒杀成功时间

*/

@Column(name = "kill_time")

private Date killTime;

}

对应的Repository上也需要告诉方法(executeProcedure),该方法需要调用哪一个存储过程:注意一点这个方法的返回值就是存储过程的 out ,入参就是存储过程的 in。

import com.example.seckill.dao.entity.KillItem;

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.data.jpa.repository.Modifying;

import org.springframework.data.jpa.repository.Query;

import org.springframework.data.jpa.repository.query.Procedure;

import org.springframework.data.repository.query.Param;

import java.util.Date;

import java.util.List;

/**

* @author ibm

* @since 0

* @date 2018/3/22

*/

public interface KillItemJpaRepo extends JpaRepository {

/**

* 查看秒杀商品的秒杀记录

* @param killProductId 秒杀商品Id

* @return 秒杀记录详情

*/

List findAllByKillProductIdOrderByKillTimeDesc(String killProductId);

/**

* 保存秒杀记录

* @param id 预生成的主键

* @param killProductId 秒杀商品id

* @param mobile 执行秒杀用户手机号

* @return 执行的行数

*/

@Modifying

@Query(value = "INSERT IGNORE INTO kill_item(id,kill_product_id,mobile) values(?1,?2,?3)",

nativeQuery = true)

int insertKillItem(String id,String killProductId,long mobile);

@Procedure(procedureName = "execute_seckill")

int executeProcedure(@Param("v_id")String killItemId,

@Param("v_kill_product_id")String killProductId,

@Param("v_mobile")long mobile,

@Param("v_kill_time")Date killTime);

}

【总结

至此所有的优化已经完成:

1.用户大量刷新 -> CDN静态化处理(css,js)

1.加速用户获取数据的系统

2.部署在离用户最近的网络节点

3.CDN不需要访问后端服务器

2.秒杀地址接口分析 -> 无法使用CDN,随着时间接口状态会发生变化

1.使用服务端缓存:redis

2.缓存没有再访问MySQL

3.秒杀操作接口分析 -> 无法使用CDN,因为库存动态刷新也不能使用服务器缓存,行数据竞争激烈。

1.减少行级锁的持有时间

2.把客户端逻辑放到MySQL服务器执行

3.使用存储过程,将事务交由MySQL完成

4.前端控制 -> 暴露接口,按钮防止重复提交

【项目地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值