spring boot starter搭建redis分布式锁项目及原理分析
本文作者:FUNKYE(陈健斌),杭州某互联网公司主程。
前言
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis为什么这么快?
(一)纯内存操作,避免大量访问数据库,减少直接读取磁盘数据,redis 将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度快;
(二)单线程操作,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
(三)采用了非阻塞I/O多路复用机制
Redis 优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
Redis 与其他 key - value 缓存产品有以下三个特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
项目搭建
1.首先我们创建一个正常的maven项目并引入如下依赖
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" |
2.创建src/main/java与src/main/resources文件夹
3.这里我们以我的demo包名为主,大家可以自定义:io.funkye.redis.lock.starter
4.在starter包下在创建我们需要的config,config.annotation(放入我们需要的注解),service及service.impl,aspect(用来使用aop增强)
如果大家创建好了,参照下图即可:
进行开发
1.我们先创建我们需要的装载redis配置的JedisLockProperties,创建在config包下
1 | package io.funkye.redis.lock.starter.config; |
2.在starter包下创建我们的装配类RedisLockAutoConfigure
1 | package io.funkye.redis.lock.starter; |
3.既然我们的工具类都已经写完了,那么需要实现我们用来做分布式事务的service,以下我们在service包下创建IRedisLockService
1 | package io.funkye.redis.lock.starter.service; |
4.接着实现该service接口,创建RedisLockServiceImpl
1 | package io.funkye.redis.lock.starter.service.impl; |
5.这下我们实现的差不多啦,接下来去config.annotation包下创建我们需要的注解类:RedisLock
1 | package io.funkye.redis.lock.starter.config.annotation; |
6.再从aspect包下创建:RedisClusterLockAspect类,用来实现aop切面功能,来实现分布式锁的功能
1 | package io.funkye.redis.lock.starter.aspect; |
7.src/main/resources下创建META-INF文件夹,在创建spring.factories(配置启动类)
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.funkye.redis.lock.starter.RedisLockAutoConfigure |
注意!!!!!,如果路径不对是不会自动装载的.
8.至此我们的redis实现分布式锁项目搭建完成,直接通过mvn clean install -DskipTests=true即可引入我们的项目到自己项目去了.如下:
1 | <dependency> |
9.引入自己项目后配置好
10.启动项目查看日志:
1 | 2020-01-18 11:43:32.732 [main] WARN org.apache.dubbo.config.AbstractConfig - |
11.使用注解并测试@RedisLock(key = “默认类路径+方法名)”,timeoutMills=超时时间默认60秒,可自定义,retry=默认50毫秒重试获取锁,lockTimeout=锁过期时间,可自定义,默认60)
1 | 2020-01-18 11:45:04.503 [http-nio-0.0.0.0-28888-exec-6] INFO i.f.r.lock.starter.aspect.RedisClusterLockAspect - |
测试了几遍效果还不错.
原理分析并总结
1.原理很简单,首先上面前言已经介绍到,redis 是单线程的.
2.setIfAbsent方法是值不存在时才会返回true,利用redis单线程特性,所以获得锁只可能是一位,所以很轻松利用这个特性来实现分布式锁.如果不明白可以看下java关键字synchronized的底层实现,大概是当你去转换成汇编语言时,原理也是得到一个值0变为1,得到锁,其它的队列hold等待.
3.了解原理及一个工具的特性时,往往可以帮你节约很多时间,比如我们用aop解决了大部分横切性的问题,用反射可以很好的动态加载类,用注解可以很好的知道执行规则,执行方案等等.