springboot+redis+mybatis体会布隆过滤器
1.建立数据库表和对应实体类
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uname` varchar(50) DEFAULT NULL,
`usex` varchar(20) DEFAULT NULL,
`uage` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1319 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
public class User {
private String id;
private String uname;
private String usex;
private String uage;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUsex() {
return usex;
}
public void setUsex(String usex) {
this.usex = usex;
}
public String getUage() {
return uage;
}
public void setUage(String uage) {
this.uage = uage;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
2.编写Controller,新增用户和获取用户
@Controller
public class BloomController {
@Autowired
private BloomService bloomService;
@RequestMapping(value = "/addUser")
@ResponseBody
public void addUser(){
for(int i=0;i<100;i++){
User user = new User();
user.setUsex(i + "");
user.setUname("user:" + i);
user.setUage("26");
bloomService.addUser(user);
}
}
@RequestMapping(value = "/getUser")
@ResponseBody
public User getUser(HttpServletRequest request){
String userId = request.getParameter("userId");
User user = bloomService.getUser(userId);
return user;
}
}
3.编写servie,实现具体的业务逻辑
public interface BloomService {
void addUser(User user);
User getUser(String userId);
List<String> getUserIds();
}
@Service
public class BloomServiceImpl implements BloomService {
public static final String redisKey = "USER:";
@Resource
private BloomDao bloomDao;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private BloomCheckUtil bloomCheckUtil;
@Override
public void addUser(User user) {
int i = bloomDao.addUser(user);
if(i>0){
//mysql插入成功,将数据存入redis
User currentUser = bloomDao.getUser(user.getId());
redisTemplate.opsForValue().set(redisKey+user.getId(), JSONObject.toJSONString(currentUser));
}
}
@Override
public User getUser(String userId) {
User user = null;
//1.先从布隆过滤器中获取数据,若没有则直接返回
if(!bloomCheckUtil.check(userId)){
System.out.println("该用户id:" + userId +"不在合法用户内!!!!");
return user;
}
//2.从redis中查找
String rediskey = redisKey + userId;
String jsonObj = Optional.ofNullable(redisTemplate.opsForValue().get(rediskey)).map(Object::toString).orElse(null);
if(jsonObj == null){
//3.redis中没有,查询数据库
user = bloomDao.getUser(userId);
if(user != null){
//4.redis无mysql有,把查询出的数据写入到redis缓存
redisTemplate.opsForValue().set(rediskey,JSONObject.toJSONString(user));
}
return user;
}
user =JSONObject.parseObject(jsonObj, User.class) ;
return user;
}
@Override
public List<String> getUserIds() {
return bloomDao.getUserIds();
}
}
4.编写dao和mapper,实现数据库操作
public interface BloomDao {
int addUser(@Param("user") User user);
User getUser(@Param("id")String id);
List<String> getUserIds();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.cn.study.redis.dao.BloomDao">
<insert id="addUser" parameterType="com.cn.study.redis.pojo.User" useGeneratedKeys="true" keyProperty="id">
insert into user(uname,usex,uage) values (#{user.uname},#{user.usex},#{user.uage})
</insert>
<select id="getUser" resultType="com.cn.study.redis.pojo.User">
select u.id ,u.uname ,u.usex ,u.uage from user u where u.id = #{id}
</select>
<select id="getUserIds" resultType="String">
select id from user
</select>
</mapper>
5.编写布隆过滤器和工具类
@Component
public class BloomFilter {
public static final String key = "LEGITIMATE_USER";
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private BloomService bloomService;
@PostConstruct
public void init(){
//初始化布隆过滤器
//1.从数据库查出user表中的所有id,计算hash值
List<String> userIdList = bloomService.getUserIds();
for(int i=0;i<userIdList.size();i++){
//对每个userId计算hash值并取绝对值
int hashValue = Math.abs(("USER:" + userIdList.get(i)).hashCode());
//使用hash值对2的32次方取余,得到下标
long index = (long)(hashValue % Math.pow(2,32));
System.out.println("id为:" + userIdList.get(i)+"的用户产生的hash值为:" + hashValue +",下标为:" + index);
//将redis中对应下标的bitmap的值更新成1
redisTemplate.opsForValue().setBit(key,index,true);
}
}
}
@Component
public class BloomCheckUtil {
private static final String key = "LEGITIMATE_USER";
@Autowired
private RedisTemplate redisTemplate;
public boolean check(String userId){
boolean flag = false;
int hashValue = Math.abs(("USER:" + userId).hashCode());
long index = (long)(hashValue % Math.pow(2,32));
flag = redisTemplate.opsForValue().getBit(key,index);
return flag;
}
}
7.验证
由于第一次启动项目的时候,BloomFilter 类中的init方法会在一开始执行,此时我还并没有调用addUser方法添加用户,数据库中没有任何用户信息,所以第一次启动项目的时候,布隆过滤器中不会有任何的数据。
我们启动项目后,手动调用下addUser方法,新增100条用户数据。
然后重启项目,将用户信息加载到布隆过滤器中。
我们调用getUser方法尝试获取用户信息,当我们传了一个数据库不存在的用户id时,当请求到布隆过滤器,布隆过滤器发现没有此数据,则直接返回,不会再往下查redis和mysql