Controller 层
@RestController public class RankingController { @Autowired private RangingService rankingService; @RequestMapping("/addScore") public String addRank(String uid, Integer score) { rankingService.rankAdd(uid, score); return "success"; } @RequestMapping("/increScore") public String increScore(String uid, Integer score) { rankingService.increSocre(uid, score); return "success"; } @RequestMapping("/rank") public Map<String, Long> rank(String uid) { Map<String, Long> map = new HashMap<>(); map.put(uid, rankingService.rankNum(uid)); return map; } @RequestMapping("/score") public Long rankNum(String uid) { return rankingService.score(uid); } @RequestMapping("/scoreByRange") public Set<ZSetOperations.TypedTuple<Object>> scoreByRange(Integer start, Integer end) { return rankingService.rankWithScore(start,end); } @RequestMapping("/sale/increScore") public String increSaleScore(String uid, Integer score) { rankingService.increSaleSocre(uid, score); return "success"; } @RequestMapping("/sale/userScore") public Map<String,Object> userScore(String uid,String name) { return rankingService.userRank(uid,name); } @RequestMapping("/sale/top") public List<Map<String,Object>> reverseZRankWithRank(long start,long end) { return rankingService.reverseZRankWithRank(start,end); } @RequestMapping("/sale/scoreByRange") public List<Map<String,Object>> saleScoreByRange(Integer start, Integer end) { return rankingService.saleRankWithScore(start,end); } }
Service 层
@Service public class RangingService implements InitializingBean { private static final String RANKGNAME = "user_score"; private static final String SALESCORE = "sale_score_rank:"; @Autowired private RedisService redisService; @Autowired private UserMapper userMapper; @Autowired private ScoreFlowMapper scoreFlowMapper; @Autowired private UserScoreMapper userScoreMapper; public void rankAdd(String uid, Integer score) { redisService.zAdd(RANKGNAME, uid, score); } public void increSocre(String uid, Integer score) { redisService.incrementScore(RANKGNAME, uid, score); } public Long rankNum(String uid) { return redisService.zRank(RANKGNAME, uid); } public Long score(String uid) { Long score = redisService.zSetScore(RANKGNAME, uid).longValue(); return score; } public Set<ZSetOperations.TypedTuple<Object>> rankWithScore(Integer start, Integer end) { return redisService.zRankWithScore(RANKGNAME, start, end); } public void rankSaleAdd() { UserScoreExample example = new UserScoreExample(); example.setOrderByClause("id desc"); List<UserScore> userScores = userScoreMapper.selectByExample(example); userScores.forEach(userScore -> { String key = userScore.getUserId() + ":" + userScore.getName(); redisService.zAdd(SALESCORE, key, userScore.getUserScore()); }); } /** * 添加用户积分 * * @param uid * @param score */ public void increSaleSocre(String uid, Integer score) { User user = userMapper.find(uid); if (user == null) { return; } int uidInt = Integer.parseInt(uid); long socreLong = Long.parseLong(score + ""); String name = user.getUserName(); String key = uid + ":" + name; scoreFlowMapper.insertSelective(new ScoreFlow(socreLong, uidInt, name)); userScoreMapper.insertSelective(new UserScore(uidInt, socreLong, name)); redisService.incrementScore(SALESCORE, key, score); } public Map<String, Object> userRank(String uid, String name) { Map<String, Object> retMap = new LinkedHashMap<>(); String key = uid + ":" + name; Integer rank = redisService.zRank(SALESCORE, key).intValue(); Long score = redisService.zSetScore(SALESCORE, key).longValue(); retMap.put("userId", uid); retMap.put("score", score); retMap.put("rank", rank); return retMap; } public List<Map<String, Object>> reverseZRankWithRank(long start, long end) { Set<ZSetOperations.TypedTuple<Object>> setObj = redisService.reverseZRankWithRank(SALESCORE, start, end); List<Map<String, Object>> mapList = setObj.stream().map(objectTypedTuple -> { Map<String, Object> map = new LinkedHashMap<>(); map.put("userId", objectTypedTuple.getValue().toString().split(":")[0]); map.put("userName", objectTypedTuple.getValue().toString().split(":")[1]); map.put("score", objectTypedTuple.getScore()); return map; }).collect(Collectors.toList()); return mapList; } public List<Map<String, Object>> saleRankWithScore(Integer start, Integer end) { Set<ZSetOperations.TypedTuple<Object>> setObj = redisService.reverseZRankWithScore(SALESCORE, start, end); List<Map<String, Object>> mapList = setObj.stream().map(objectTypedTuple -> { Map<String, Object> map = new LinkedHashMap<>(); map.put("userId", objectTypedTuple.getValue().toString().split(":")[0]); map.put("userName", objectTypedTuple.getValue().toString().split(":")[1]); map.put("score", objectTypedTuple.getScore()); return map; }).collect(Collectors.toList()); return mapList; } // @Override // public void run(ApplicationArguments args) throws Exception { // System.out.println("======enter run bean======="); // Thread.sleep(100000); // this.rankSaleAdd(); // } @Override public void afterPropertiesSet() throws Exception { System.out.println("======enter init bean======="); this.rankSaleAdd(); } }
工具类
@Service public class RedisService { @Autowired private RedisTemplate redisTemplate; private static double size = Math.pow(2, 32); /** * 写入缓存 * * @param key * @param offset 位 8Bit=1Byte * @return */ public boolean setBit(String key, long offset, boolean isShow) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.setBit(key, offset, isShow); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 写入缓存 * * @param key * @param offset * @return */ public boolean getBit(String key, long offset) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.getBit(key, offset); } catch (Exception e) { e.printStackTrace(); } return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 写入缓存设置时效时间 * * @param key * @param value * @return */ public boolean set(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 批量删除对应的value * * @param keys */ public void remove(final String... keys) { for (String key : keys) { remove(key); } } /** * 删除对应的value * * @param key */ public void remove(final String key) { if (exists(key)) { redisTemplate.delete(key); } } /** * 判断缓存中是否有对应的value * * @param key * @return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * 读取缓存 * * @param key * @return */ public Object get(final String key) { Object result = null; ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); result = operations.get(key); return result; } /** * 哈希 添加 * * @param key * @param hashKey * @param value */ public void hmSet(String key, Object hashKey, Object value) { HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); hash.put(key, hashKey, value); } /** * 哈希获取数据 * * @param key * @param hashKey * @return */ public Object hmGet(String key, Object hashKey) { HashOperations<String, Object, Object> hash = redisTemplate.opsForHash(); return hash.get(key, hashKey); } /** * 列表添加 * * @param k * @param v */ public void lPush(String k, Object v) { ListOperations<String, Object> list = redisTemplate.opsForList(); list.rightPush(k, v); } /** * 列表获取 * * @param k * @param l * @param l1 * @return */ public List<Object> lRange(String k, long l, long l1) { ListOperations<String, Object> list = redisTemplate.opsForList(); return list.range(k, l, l1); } /** * 集合添加 * * @param key * @param value */ public void add(String key, Object value) { SetOperations<String, Object> set = redisTemplate.opsForSet(); set.add(key, value); } /** * 集合获取 * * @param key * @return */ public Set<Object> setMembers(String key) { SetOperations<String, Object> set = redisTemplate.opsForSet(); return set.members(key); } /** * 有序集合添加 * * @param key * @param value * @param scoure */ public void zAdd(String key, Object value, double scoure) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); zset.add(key, value, scoure); } /** * 有序集合获取 * * @param key * @param scoure * @param scoure1 * @return */ public Set<Object> rangeByScore(String key, double scoure, double scoure1) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); redisTemplate.opsForValue(); return zset.rangeByScore(key, scoure, scoure1); } //第一次加载的时候将数据加载到redis中 public void saveDataToRedis(String name) { double index = Math.abs(name.hashCode() % size); long indexLong = new Double(index).longValue(); boolean availableUsers = setBit("availableUsers", indexLong, true); } //第一次加载的时候将数据加载到redis中 public boolean getDataToRedis(String name) { double index = Math.abs(name.hashCode() % size); long indexLong = new Double(index).longValue(); return getBit("availableUsers", indexLong); } /** * 有序集合获取排名 * * @param key 集合名称 * @param value 值 */ public Long zRank(String key, Object value) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); return zset.rank(key,value); } /** * 有序集合获取排名 * * @param key */ public Set<ZSetOperations.TypedTuple<Object>> zRankWithScore(String key, long start,long end) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); Set<ZSetOperations.TypedTuple<Object>> ret = zset.rangeWithScores(key,start,end); return ret; } /** * 有序集合添加 * * @param key * @param value */ public Double zSetScore(String key, Object value) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); return zset.score(key,value); } /** * 有序集合添加分数 * * @param key * @param value * @param scoure */ public void incrementScore(String key, Object value, double scoure) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); zset.incrementScore(key, value, scoure); } /** * 有序集合获取排名 * * @param key */ public Set<ZSetOperations.TypedTuple<Object>> reverseZRankWithScore(String key, long start,long end) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); Set<ZSetOperations.TypedTuple<Object>> ret = zset.reverseRangeByScoreWithScores(key,start,end); return ret; } /** * 有序集合获取排名 * * @param key */ public Set<ZSetOperations.TypedTuple<Object>> reverseZRankWithRank(String key, long start, long end) { ZSetOperations<String, Object> zset = redisTemplate.opsForZSet(); Set<ZSetOperations.TypedTuple<Object>> ret = zset.reverseRangeWithScores(key, start, end); return ret; } }
虽然 Redis 帮助我们快速存读数据,但这些数据还是得落到数据库中进行保存,数据库表设计过程中几个关键要点:
1、表设计过程中应该注意:数据类型
- 更小的通常更好,控制字节长度。
- 使用合适的数据类型。如 tinyint 只占8个位,char(1024) 与 varchar(1024) 的对比——char 用于类似定长数据存储比 varchar 节省空间,如:uuid(32),可以用 char(32)。
- 尽量避免NULL,建议使用 NOT NULL,DEFAULT '' 创建字段。NULL 的列会让索引统计和值比较都更复杂。可为 NULL 的列会占据更多的磁盘空间,在 Mysql 中也需要更多复杂的处理程序。
2、表设计过程中应该注意:索引
- 选择唯一性索引。唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录,保证物理上面唯一。
- 为经常需要排序、分组和联合操作的字段建立索引。经常需要 ORDER BY、GROUP BY、DISTINCT 和 UNION 等操作的字段,排序操作会浪费很多时间。
- 常作为查询条件的字段建立索引。如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度 。
- 数据少的地方不必建立索引。
3、表使用过程中应该注意:sql 优化
- 能够用 BETWEEN 的就不要用 IN
- 能够用 DISTINCT 的就不用 GROUP BY
- 避免数据类型强转
- 学会采用 explain 查看执行计划(注意:扫描行数会影响CPU运行,占用大量内存)
有些缓存数据并不是用户访问触发的,而是在项目启动时就加载到缓存中的,Spring Boot 实现初始化加载配置(实现缓存预热),主要有两种方法:
- 采用实现 ApplicationRunner 接口。该方法仅在 SpringApplication.run(…) 完成之前调用
- 采用实现 InitializingBean 接口。InitializingBean 接口为 bean 提供了初始化方法的方式,它只包括 afterPropertiesSet() 方法。在 Spring 初始化 bean 的时候,如果 bean 实现了 InitializingBean 接口,在对象的所有属性被初始化后之后才会调用 afterPropertiesSet() 方法
两种方式的区别在于:实现 ApplicationRunner 不会影响项目启动,而实现 InitializingBean 则项目必须等待方法走完才会真正启动。