一、目的

实验目的:

理解缓存中间件Redis基本原理

掌握SpringBoot整合Redis,了解Redis的优缺点

实验要求:

独立完成实验操作,并撰写实验报告

实验内容:

Redis安装与使用

(1)安装Redis,启动redis_server.exe;

(2)运行redis_cli.exe,连接server,并测试添加、查询命令行。附注:建议测试四种常用的数据结构(字符串、集合、列表、Hash),具体测试内容自定。

SpringBoot中整合Redis

在实验1搭建的工程基础上,完成如下操作:

(1)在pom.xml中添加redis支持;

(2)在控制器中添加一个RequestMapping,用于测试Redis集合元素的添加(类型不限,可以考虑使用Jackson序列化),并把所有集合元素显示到页面中。

秒杀程序

为工程添加一个秒杀模块,完成用户抢单的并发可行性操作。

(1)合理设计数据库,相应地在工程中添加驱动、MyBatis配置;

(2)设计基于Redis的秒杀程序,包括发布秒杀信息、秒杀抢单操作(注意封装);

(3)测试时(使用jmeter或其他),模拟多个用户并发抢单,并查看秒杀结果;

(4)修改不同参数(商品数量、并发用户数),测试程序是否能够实现如下基本功能要求:抢到的总数不超过商品总数,且每个用户最多抢到一次。

说明:本题可以是模块(可参照教材的例子),也可以完整的网页,但要预留可并发测试的接口。

二、实验内容与设计思想

2.1 设计思路

Redis安装与使用

(1)安装Redis,启动redis_server.exe;

(2)运行redis_cli.exe,连接server,并测试添加、查询命令行。附注:建议测试四种常用的数据结构(字符串、集合、列表、Hash),具体测试内容自定。

SpringBoot中整合Redis

在实验1搭建的工程基础上,完成如下操作:

(1)在pom.xml中添加redis支持;

(2)在控制器中添加一个RequestMapping,用于测试Redis集合元素的添加(类型不限,可以考虑使用Jackson序列化),并把所有集合元素显示到页面中。

秒杀程序

为工程添加一个秒杀模块,完成用户抢单的并发可行性操作。

(1)合理设计数据库,相应地在工程中添加驱动、MyBatis配置;

(2)设计基于Redis的秒杀程序,包括发布秒杀信息、秒杀抢单操作(注意封装);

(3)测试时(使用jmeter或其他),模拟多个用户并发抢单,并查看秒杀结果;

(4)修改不同参数(商品数量、并发用户数),测试程序是否能够实现如下基本功能要求:抢到的总数不超过商品总数,且每个用户最多抢到一次。

说明:本题可以是模块(可参照教材的例子),也可以完整的网页,但要预留可并发测试的接口。

2.2 主要数据结构

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Custom implements Serializable {

private static final long serialVersionUID = 320539504981592510L;

private Integer customId;

private String name;

private Integer age;

private String sex;

}

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Goods implements Serializable {

private static final long serialVersionUID = -42557906261804648L;

private Integer goodsId;

/**库存*/ private Integer inventory;

}

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Record implements Serializable

{

private static final long serialVersionUID = -85697347674864643L;

private Integer goodsId;

private Date createTime;

private Integer customId;

}

2.3 主要代码结构

└── service/

└── controller/

├── CustomController.java

├── GoodsController.java

├── RecordController.java

├── RedisController.java

├── RedisSetController.java

├── SeckillController.java

└── domain/

├── Custom.java

├── Goods.java

├── Record.java

└── mapper/

├── CustomMapper.java

├── GoodsMapper.java

├── RecordMapper.java

└── service/

├── CustomService.java

├── GoodsService.java

└── impl/

├── CustomServiceImpl.java

├── GoodsServiceImpl.java

├── RecordServiceImpl.java

├── RedisServiceImpl.java

├── SeckillServiceImpl.java

├── RecordService.java

├── RedisService.java

├── SeckillService.java

├── ServiceApplication.java

└── util/

├── Config.java

├── CORSFilter.java

├── R.java

├── RedisConfig.java

三、实验使用环境

平台:win10

软件:idea

实验步骤和调试过程

4.1 实验步骤

4.1.1 Redis安装与使用

(1)安装Redis,启动redis_server.exe;

(2) 运行redis_cli.exe,连接server,并测试添加、查询命令行。附注:建议测试四种常用的数据结构(字符串、集合、列表、Hash),具体测试内容自定。

字符串操作:

设置键值对:SET key value

获取键的值:GET key

追加值到键的值末尾:APPEND key value

获取指定范围内的子字符串:GETRANGE key start end

集合操作:

添加一个或多个元素到集合中:sadd key2 value1 value2

获取集合中的所有元素:SMEMBERS key2

获取集合中元素的数量:SCARD key2

判断元素是否在集合中:SISMEMBER key2 value1,SISMEMBER key2 value3

列表操作:

在列表的头部添加一个或多个元素:LPUSH key3 value1 value2

在列表的尾部添加一个或多个元素:RPUSH key3 value1 value2

获取列表指定范围的元素:LRANGE key3 start end

获取列表所有元素:LRANGE key3 0 -1

获取列表的长度:LLEN key3

删除列表:DEL key3

哈希操作:

设置哈希中的字段和值:HSET key4 field1 value1 field2 value2

获取哈希中指定字段的值:HGET key4 field

获取哈希中所有字段和值:HGETALL key4

获取哈希中字段的数量:HLEN key4

4.1.2. SpringBoot中整合Redis

(1)在pom.xml添加redis支持

(2)在控制器中添加一个RequestMapping,用于测试Redis集合元素的添加(类型不限,可以考虑使用Jackson序列化),并把所有集合元素显示到页面中。

RedisConfig.java

@Configuration

public class RedisConfig {

@Autowired(required = false)

private RedisConnectionFactory redisConnectionFactory; //Redis连接工厂(redis-cli窗口)

@Bean

public RedisTemplate redisTemplate(){ //相当于redis-cli.exe的命令行

RedisTemplate redisTemplate=new RedisTemplate();

redisTemplate.setConnectionFactory(redisConnectionFactory);

redisTemplate.setKeySerializer(new StringRedisSerializer()); //指定大key序列化策略为为String序列化

// redisTemplate.setKeySerializer(new JdkSerializationRedisSerializer());

redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());

redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //JSON泛型

// redisTemplate.setHashKeySerializer(new StringRedisSerializer()); //指定hashKey序列化策略为String序列化

return redisTemplate;

}

/**

* 缓存redis-stringRedisTemplate

*/

@Bean

public StringRedisTemplate stringRedisTemplate(){

//采用默认配置即可-后续有自定义配置时则在此处添加即可

StringRedisTemplate stringRedisTemplate=new StringRedisTemplate();

stringRedisTemplate.setConnectionFactory(redisConnectionFactory);

return stringRedisTemplate;

}

}

RedisSetController.java

@RestController

@RequestMapping("/redis")

public class RedisSetController

{

@Autowired

private RedisTemplate redisTemplate;

@GetMapping

public R getAllValue()

{

Set keys = redisTemplate.keys("*");

keys.remove("name");

System.out.println("keys" + keys);

Map data = new HashMap<>();

if (keys != null)

{

for (String key : keys)

{

System.out.println(key);

Object value = redisTemplate.opsForValue().get(key);

data.put(key, value);

}

}

return R.ok().setData(data);

}

@DeleteMapping

public R deleteValue()

{

try

{

Set keys = redisTemplate.keys("*");

keys.remove("name");

for (String key : keys)

{

redisTemplate.delete(key);

}

return R.ok().setData(true);

} catch (Exception e)

{

return R.exp().setData(e.getMessage());

}

}

@GetMapping("/{key}")

public R getValue(@PathVariable String key)

{

return R.ok().setData(redisTemplate.opsForSet().members(key));

}

@PostMapping("/{key}")

public R addValue(@PathVariable String key, @RequestParam String[] values)

{

try

{

for (String value : values)

redisTemplate.opsForSet().add(key, value);

return R.ok().setData(redisTemplate.opsForSet().members(key));

} catch (Exception e)

{

e.printStackTrace();

return R.exp().setData(e.getMessage());

}

}

@DeleteMapping("/{key}")

public R deleteValue(@PathVariable String key,@RequestParam String[] values)

{

try

{

for(String value:values)

redisTemplate.opsForSet().remove(key, value);

return R.ok().setData(true);

} catch (Exception e)

{

return R.exp().setData(e.getMessage());

}

}

@DeleteMapping("/all/{key}")

public R deleteValue(@PathVariable String key)

{

try{

redisTemplate.delete(key);

return R.ok().setData(true);

}catch (Exception e)

{

return R.exp().setData(e.getMessage());

}

}

@PutMapping("/{key}")

@ResponseBody

public R updateValue(@PathVariable String key, @RequestParam(required = false) String[] values)

{

try

{

System.out.println("==="+values);

redisTemplate.delete(key);

for (String value : values)

redisTemplate.opsForSet().add(key, value);

return R.ok().setData("更新成功");

} catch (Exception e)

{

e.printStackTrace();

return R.exp().setData(e.getMessage());

}

}

}

4.1.3 秒杀程序

(1)合理设计数据库,相应地在工程中添加驱动、MyBatis配置;

记录顾客的信息:custom表:

custom_id: int 类型,自增字段,自定义表的唯一标识符。 name: varchar(255) 类型,字符型字段,表示客户的姓名。 age: int 类型,整型字段,表示客户的年龄。 sex: varchar(255) 类型,字符型字段,表示客户的性别。

记录商品的信息:goods 表:

goods_id: int 类型,自增字段,商品表的唯一标识符。 inventory: int 类型,整型字段,表示商品的库存数量。

记录顾客购买商品的信息:record 表:

goods_id: int 类型,表示商品的唯一标识符,同时也是 record 表的外键,关联 goods 表中的 goods_id 字段。 create_time: datetime 类型,表示记录创建时间的日期和时间。 custom_id: int 类型,表示客户的唯一标识符,同时也是 record 表的外键,关联 custom 表中的 custom_id 字段。

(2)设计基于Redis的秒杀程序,包括发布秒杀信息、秒杀抢单操作(注意封装);

下面展示SeckillController,SeckillService,SeckillServiceImpl,RedisService,RedisServiceImpl等主要程序的代码。(关于数据库三个表代码太多了,所以仅展示custom表的Controller,Service,Impl,domain作为典型案例)

@RestController

@RequestMapping("/seckill")

public class SeckillController

{

@Resource

private SeckillService seckillService;

@GetMapping

public int getValue(int goodsId, int customId)

{

System.out.println("货物id:"+goodsId+",顾客id:"+customId);

return seckillService.seckill(goodsId, customId);

}

@GetMapping("/init")

public R init()

{

return seckillService.init();

}

}

public interface SeckillService

{

R init();

int seckill(int goodsId, int customId);

}

@Service("seckillService")

public class SeckillServiceImpl implements SeckillService

{

@Resource

private RedisService redisService;

@Resource

private RecordService recordService;

@Resource

private GoodsService goodsService;

@Override

public R init()

{

//清空redis

redisService.deleteValue();

// 加载已有记录到redis

List records = (List) recordService.queryAll(null).getData();

for (Record record : records)

redisService.addValue(record.getGoodsId() + ":" + record.getCustomId(), 1);

// 加载货物剩余数量到redis

List goodss = (List) goodsService.queryAll(null).getData();

for (Goods goods : goodss)

redisService.addValue(goods.getGoodsId() + ":total", goods.getInventory());

return R.ok().setData(redisService.getAllValue());

}

@Override

public int seckill(int goodsId, int customId)

{

String robId = goodsId + ":" + customId; //记录是否已经抢到货物

String goodsTotal = goodsId + ":total";

//1. 查询是否已经抢到货物,若已经抢到,则返回-1

Object obj = redisService.getValue(robId).getData();

if (obj != null && !"".equals("" + obj))

return -1;

//2. 加锁

Boolean lock = (Boolean) redisService.reidsLock(robId + "-lock", "lock").getData();

if (lock)

{

while(!(Boolean) redisService.reidsLock(goodsId + "-lock", "lock").getData());

//2. 若剩余货物列表为空,则返回-1。

int total = (int) redisService.getValue(goodsTotal).getData();

if (total <= 0)

return -1;

//3. 否则,取一个货物,并返回1。

System.out.println("======> " + customId + "抢到" + goodsId);

//update total

redisService.decrement(goodsTotal);

//取消锁

redisService.reidsUnlock(goodsId+"-lock");

//update robId

redisService.addValue(robId, 1);

//update mysql

recordService.insert(new Record(goodsId, new Date(System.currentTimeMillis()), customId));

goodsService.updateLow(goodsId);

return 1;

} else

return -1;

}

}

public interface RedisService

{

R getValue(String key);

R getAllValue();

R addValue(String key, Object value);

R setWithExpiration(String key, int expirationInSeconds, Object value);

R deleteValue(String key);

R updateValue(String key, Object value);

R reidsLock(String key, String value);

R reidsUnlock(String key);

R decrement(String key);

R deleteValue();

}

@Service("redisService")

public class RedisServiceImpl implements com.jihuiting.service.service.RedisService

{

@Autowired

private RedisTemplate redisTemplate;

@Override

public R getValue(String key)

{

return R.ok().setData(redisTemplate.opsForValue().get(key));

}

@Override

public R getAllValue()

{

Set keys = redisTemplate.keys("*");

keys.remove("name");

System.out.println("keys" + keys);

Map data = new HashMap<>();

if (keys != null)

{

for (String key : keys)

{

System.out.println(key);

Object value = redisTemplate.opsForValue().get(key);

data.put(key, value);

}

}

return R.ok().setData(data);

}

@Override

public R addValue(String key, Object value)

{

try

{

System.out.println("post:key:" + key);

redisTemplate.opsForValue().set(key, value);

return R.ok().setData(true);

} catch (Exception e)

{

e.printStackTrace();

return R.exp().setData(e.getMessage());

}

}

@Override

public R setWithExpiration(String key, int expirationInSeconds, Object value)

{

try

{

System.out.println("post:key:" + key);

ValueOperations valueOperations = redisTemplate.opsForValue();

valueOperations.set(key, value);

redisTemplate.expire(key, expirationInSeconds, TimeUnit.SECONDS);

return R.ok().setData(true);

} catch (Exception e)

{

e.printStackTrace();

return R.exp().setData(e.getMessage());

}

}

@Override

public R deleteValue(String key)

{

try{

System.out.println("delete:key:" + key);

redisTemplate.delete(key);

return R.ok().setData(true);

}catch (Exception e)

{

return R.exp().setData(e.getMessage());

}

}

@Override

public R updateValue(String key, Object value)

{

try{

redisTemplate.opsForValue().set(key, value);

return R.ok().setData(true);

}catch (Exception e)

{

return R.exp().setData(e.getMessage());

}

}

@Override

public R reidsLock(String key, String value)

{

return R.ok().setData(redisTemplate.opsForValue().setIfAbsent(key,value));

}

@Override

public R reidsUnlock(String key)

{

return R.ok().setData(redisTemplate.delete(key));

}

@Override

public R decrement(String key)

{

return R.ok().setData(redisTemplate.opsForValue().decrement(key));

}

@Override

public R deleteValue()

{

try

{

Set keys = redisTemplate.keys("*");

keys.remove("name");

for (String key : keys)

{

redisTemplate.delete(key);

}

return R.ok().setData(true);

} catch (Exception e)

{

return R.exp().setData(e.getMessage());

}

}

}

/**

* (Custom)表控制层

*

* @since 2023-10-18 11:10:10

*/

@RestController

@RequestMapping("/custom")

public class CustomController {

/**

* 服务对象

*/

@Resource

private CustomService customService;

/**

* 全查询

*

* @param custom 筛选条件

* @return 查询结果

*/

@GetMapping

public R queryAll(Custom custom) {

return this.customService.queryAll(custom);

}

/**

* 分页查询

*

* @param custom 筛选条件

* @param pageSize 每页多少航

* @param page 第几页

* @return 查询结果

*/

@GetMapping("/page")

public R queryAllPage(Custom custom, Integer pageSize, Integer page) {

return this.customService.queryAllPage(custom,pageSize,page);

}

/**

* 模糊查找

*

* @param searchText 查找关键字

* @return 查询结果

*/

@GetMapping("/search/{searchText}")

public R queryBySearchText(@PathVariable("searchText") String searchText) {

return this.customService.queryBySearchText(searchText);

}

/**

* 通过主键查询单条数据

*

* @param id 主键

* @return 单条数据

*/

@GetMapping("{id}")

public R queryById(@PathVariable("id") Integer id) {

return this.customService.queryById(id);

}

/**

* 统计总行数

*

* @param custom 筛选条件

* @return 查询结果

*/

@GetMapping("/count")

public R getCount(Custom custom) {

return this.customService.getCount(custom);

}

/**

* 新增数据

*

* @param custom 实体

* @return 新增结果

*/

@PostMapping

public R add(@RequestBody Custom custom) {

return this.customService.insert(custom);

}

/**

* 更新数据

*

* @param custom 实体

* @return 更新结果

*/

@PutMapping

public R update(@RequestBody Custom custom) {

return this.customService.update(custom);

}

/**

* 删除数据

*

* @param id 主键

* @return 删除是否成功

*/

@DeleteMapping

public R deleteById(Integer id) {

return this.customService.deleteById(id);

}

}

/**

* 实体类

*

* @since 2023-10-18 11:10:10

*/

@Data

@AllArgsConstructor

@NoArgsConstructor

public class Custom implements Serializable {

private static final long serialVersionUID = 320539504981592510L;

private Integer customId;

private String name;

private Integer age;

private String sex;

}

}

/**

* (Custom)表服务接口

*

* @since 2023-10-18 11:10:10

*/

public interface CustomService {

/**

* 通过ID查询单条数据

*

* @param customId 主键

* @return 实例对象

*/

R queryById(Integer customId);

/**

* 全查询

*

* @param custom 筛选条件

* @return 查询结果

*/

R queryAll(Custom custom);

/**

* 分页查询

*

* @param custom 筛选条件

* @param pageSize 每页多少航

* @param page 第几页

* @return 查询结果

*/

R queryAllPage(Custom custom, Integer pageSize ,Integer page);

/**

* 模糊查找

*

* @param searchText 查找关键字

* @return 查询结果

*/

R queryBySearchText(String searchText);

/**

* 统计总行数

*

* @param custom 筛选条件

* @return 查询结果

*/

R getCount(Custom custom);

/**

* 新增数据

*

* @param custom 实例对象

* @return 实例对象

*/

R insert(Custom custom);

/**

* 修改数据

*

* @param custom 实例对象

* @return 实例对象

*/

R update(Custom custom);

/**

* 通过主键删除数据

*

* @param customId 主键

* @return 是否成功

*/

R deleteById(Integer customId);

}

/**

* (Custom)表服务实现类

*

* @since 2023-10-18 11:10:10

*/

@Service("customService")

public class CustomServiceImpl implements CustomService {

@Resource

private CustomMapper customMapper;

/**

* 通过ID查询单条数据

*

* @param customId 主键

* @return 实例对象

*/

@Override

public R queryById(Integer customId) {

return R.ok().setData(this.customMapper.queryById(customId));

}

/**

* 分页查询

*

* @param custom 筛选条件

* @param pageSize 每页多少航

* @param page 第几页

* @return 查询结果

*/

@Override

public R queryAllPage(Custom custom,Integer pageSize, Integer page) {

return R.ok().setData(this.customMapper.queryAllPage(custom,pageSize,page));

}

/**

* 全查询

*

* @param custom 筛选条件

* @return 查询结果

*/

@Override

public R queryAll(Custom custom) {

return R.ok().setData(this.customMapper.queryAll(custom));

}

/**

* 模糊查找

*

* @param searchText 查找关键字

* @return 查询结果

*/

@Override

public R queryBySearchText(String searchText){

return R.ok().setData(this.customMapper.queryBySearchText(searchText));

}

/**

* 统计总行数

*

* @param custom 筛选条件

* @return 查询结果

*/

@Override

public R getCount(Custom custom) {

return R.ok().setData(this.customMapper.count(custom));

}

/**

* 新增数据

*

* @param custom 实例对象

* @return 实例对象

*/

@Override

public R insert(Custom custom) {

if(custom.getCustomId()!=null && this.customMapper.queryById( custom.getCustomId() ) != null )

return R.error("已经存在");

this.customMapper.insert(custom);

return R.ok().setData(custom);

}

/**

* 修改数据

*

* @param custom 实例对象

* @return 实例对象

*/

@Override

public R update(Custom custom) {

this.customMapper.update(custom);

return R.ok().setData(this.queryById(custom.getCustomId()));

}

/**

* 通过主键删除数据

*

* @param customId 主键

* @return 是否成功

*/

@Override

public R deleteById(Integer customId) {

try{

this.customMapper.deleteById(customId);

return R.ok();

} catch (Exception e)

{

e.printStackTrace();

return R.exp().setData(e.getMessage());

}

}

}

/**

* (Custom)表数据库访问层

*

* @since 2023-10-18 11:10:10

*/

@Mapper

public interface CustomMapper {

/**

* 通过ID查询单条数据

*

* @param customId 主键

* @return 实例对象

*/

Custom queryById(Integer customId);

/**

* 全查询

*

* @param custom 查询条件

* @return 对象列表

*/

List queryAll(Custom custom);

/**

* 分页查询

*

* @param custom 查询条件

* @param pageSize 每页多少航

* @param page 第几页

* @return 对象列表

*/

List queryAllPage(@Param("custom")Custom custom,

@Param("pageSize") Integer pageSize ,@Param("page") Integer page);

/**

* 模糊查找

*

* @param searchText 查找关键字

* @return 查询结果

*/

List queryBySearchText(String searchText);

/**

* 统计总行数

*

* @param custom 查询条件

* @return 总行数

*/

long count(Custom custom);

/**

* 新增数据

*

* @param custom 实例对象

* @return 影响行数

*/

int insert(Custom custom);

/**

* 批量新增数据(MyBatis原生foreach方法)

*

* @param entities List 实例对象列表

* @return 影响行数

*/

int insertBatch(@Param("entities") List entities);

/**

* 批量新增或按主键更新数据(MyBatis原生foreach方法)

*

* @param entities List 实例对象列表

* @return 影响行数

* @throws org.springframework.jdbc.BadSqlGrammarException 入参是空List的时候会抛SQL语句错误的异常,请自行校验入参

*/

int insertOrUpdateBatch(@Param("entities") List entities);

/**

* 修改数据

*

* @param custom 实例对象

* @return 影响行数

*/

int update(Custom custom);

/**

* 通过主键删除数据

*

* @param customId 主键

* @return 影响行数

*/

int deleteById(Integer customId);

}

4.2 测试数据设计

4.2.1 Redis安装与使用

数据设计:

分别测试Redis字符串,集合,列表,哈希操作。

结果在下一章节4.3.1中详细描述。

字符串测试数据设计:

设置键值对:SET key value

获取键的值:GET key

追加值到键的值末尾:APPEND key value

获取指定范围内的子字符串:GETRANGE key start end

集合测试数据设计:

添加一个或多个元素到集合中:sadd key2 value1 value2

获取集合中的所有元素:SMEMBERS key2

获取集合中元素的数量:SCARD key2

判断元素是否在集合中:SISMEMBER key2 value1,SISMEMBER key2 value3

列表测试数据设计:

在列表的头部添加一个或多个元素:LPUSH key3 value1 value2

在列表的尾部添加一个或多个元素:RPUSH key3 value1 value2

获取列表指定范围的元素:LRANGE key3 start end

获取列表所有元素:LRANGE key3 0 -1

获取列表的长度:LLEN key3

删除列表:DEL key3

哈希测试数据设计:

设置哈希中的字段和值:HSET key4 field1 value1 field2 value2

获取哈希中指定字段的值:HGET key4 field

获取哈希中所有字段和值:HGETALL key4

获取哈希中字段的数量:HLEN key4

4.2.2 SpringBoot中整合Redis

数据设计:

Redis集合元素的添加,删除,查询,更新。

结果在下一章节4.3.2中详细展示。

4.2.3 秒杀程序

测试数据设计:

测试单用户对单件商品的多次抢购 测试多用户对单件商品的超数量抢购 测试多用户对多商品的超数量抢购

结果在下一章节4.3.3中详细展示。

4.3 测试结果分析

4.3.1 Redis安装与使用

字符串操作:

集合操作:

列表操作:

哈希操作:

4.3.2 SpringBoot中整合Redis

测试添加:

key:key1 value:[aaa,bbb] (测试向key1添加两个元素aaa和bbb)

key:key1 value:[ccc] (测试向key1添加一个元素ccc)

key:key1 value:[ddd] (测试向key1添加一个元素ddd)

key:key2 value:[CCC] (测试向key2添加元素CCC)

测试查询:

分别查询key1,查询key2,查询key3

测试更新:

测试删除:

4.3.3 秒杀程序

首先进行初始化,将redis中键值清空,将用户秒杀记录和货物剩余库存信息载入到redis中:

结果如上:总共有1~10的10件货物,每件货物余量为5。且暂无用户抢购记录。

测试1:测试单用户对单件商品的多次抢购

模拟单用户并发抢购单商品,测试"单用户不能多次抢购同一件商品"规则。

模拟过程:用户1对商品1发出10次请求。

模拟结果:

redis中1号商品数量-1,变成了4;并出现了"1:1",代表1号用户抢购1号商品的抢购记录。

数据库seckill的record表中出现了抢购记录

数据库seckill的goods表中1号货物数量-1,变成了4。

测试2:测试多用户对单件商品的超数量抢购

模拟1~10号顾客对1号商品发起的1000个请求

测试结果:

redis结果如下,1号商品的总量更新为0;分别是1,3,4,9,10号顾客抢中了1号商品。

抢购记录:1,3,4,9,10号顾客抢中了1号商品

测试3:测试多用户对多商品的超数量抢购

添加商品csv文件

将商品和顾客的值改成

g

o

o

d

s

I

d

{goodsId}和

goodsId和{customId}

测试结果:

redis结果(redis结果比较杂乱,所以下面从货物数量和抢购记录进行验证):

数据库中货物数量,可以看到所有库存都清空了,变成0:

数据库抢购记录,可以看到商品抢购记录正好50条,说明程序正确:

五、实验小结

实验中遇到的问题及解决过程 无实验中产生的错误及原因分析 无实验体会和收获。 无

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: