单线程序,基于IO多路复用,基于内存和c语言编写,性能高。redis官方命令
1 数据结构
1.1 key的层级
redis的key可以通过冒号(:)来划分层级,如下图mms:company:order,但系统中可以看到有不少没有的,这些都是不符合开发规则的,研发工程师偷懒。这些事情应该严格要求,否则留下来的人就会很麻烦,根本无法猜到这个key是从哪里来的。
1.2 数据结构
动画解析Redis为什么用跳表而不是红黑树?我看有些面试官就是很无趣,就好像高考阅读的出题人,总以为比写文章的人还理解,人家作者就是因为跳表比红黑树容易实现,性能也不差就用了,搞一堆题目为难面试者,搞得他们也能写出一个redis一样。
2 场景
2.1 分布式锁
先看一下面的代码,下面虽然根据year爬虫,但是再多并发测试,一定会出问题。
@PostMapping("add")
public ResponseResult add(String str){
log.info("接收参数:{}",str);
List<ZpBaziInfo> records = JsonUtil.parseArray(str,ZpBaziInfo.class);
zpBaziInfoService.batchAdd(records);
return new ResponseResult(true,"添加成功");
}
@Override
@Async("zpExecutor")
public void batchAdd(List<ZpBaziInfo> records) {
for(ZpBaziInfo record : records){
add(record);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void add(ZpBaziInfo record) {
if (!isExist(record.getYear())){
log.info("开始新增数据,线程:{},时间:{},年:{}",Thread.currentThread().getName(),System.currentTimeMillis(),record.getYear());
record.setId(RandomUtil.randomString(11));
save(record);
}
}
创建10个线程随机获取数据,然后调用上面的代码,于是在没有锁的情况下,重复数据产生。
@Test
public void test_add_thread_1() throws InterruptedException {
ZpBaziInfoTest main = new ZpBaziInfoTest();
ExecutorService executor = Executors.newFixedThreadPool(10);
IntStream.range(0, 10)
.forEach(i -> executor.submit(main::addData));
// 关闭线程池,表示不再接受新的任务
executor.shutdown();
//
executor.awaitTermination(10, TimeUnit.HOURS); // 等待所有线程完成,最长等待1小时
}
private String getData(){
String[] years = {"甲子","乙丑","丙寅","丁卯","戊辰","己巳","庚午","辛未","壬申","癸酉","甲戌","乙亥","丙子","丁丑","戊寅","己卯","庚辰","辛巳","壬午","癸未","甲申","乙酉","丙戌","丁亥","戊子","己丑","庚寅","辛卯","壬辰","癸巳","甲午","乙未","丙申","丁酉","戊戌","己亥","庚子","辛丑","壬寅","癸卯","甲辰","乙巳","丙午","丁未","戊申","己酉","庚戌","辛亥","壬子","癸丑","甲寅"};
List<ZpBaziInfo> records = IntStream.range(0,20)
.mapToObj(i->{
ZpBaziInfo record = new ZpBaziInfo();
record.setYear(years[new Random().nextInt(years.length)]);
record.setMonth("甲子");
return record;
}).collect(Collectors.toList());
return JsonUtil.toJson(records);
}
@Test
public void test_add(){
addData();
}
private void addData(){
Map<String,Object> params = new HashMap<>();
params.put("str",getData());
String result = HttpUtil.post(url,params);
//
System.out.println(Thread.currentThread().getName() + ": " + result);
}
于是相当给代码加锁,问题暂时解决,单因为ReentrantLock 是单进程的,在集群环境下这个重复问题依旧会出现,于是就用到了redis的分布式锁。
private final ReentrantLock lock = new ReentrantLock();
@Override
@Async("zpExecutor")
public void batchAdd(List<ZpBaziInfo> records) {
for(ZpBaziInfo record : records){
lock.lock();
try {
add(record);
} finally {
lock.unlock();
}
}
}