Technologieaustausch

Cache-verteiltes Sperrprinzip und grundlegende Verwendung

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Prinzip und Verwendung des verteilten Schlosses

 

Drehen

  1. public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
  2. Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, Lock, Duration.ofMinutes(1));
  3. if (!b) {
  4. int i = 10;
  5. while (i > 0) {
  6. Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
  7. try {
  8. TimeUnit.MILLISECONDS.sleep(100);
  9. } catch (InterruptedException e) {
  10. throw new RuntimeException(e);
  11. }
  12. if (result != null) {
  13. System.out.println("命中缓存 db lock");
  14. return (Map<String, List<Catelog2Vo>>) result;
  15. }
  16. i--;
  17. }
  18. throw new RuntimeException("系统繁忙,请重新访问");
  19. }
  20. //1.查出所有1级分类
  21. List<CategoryEntity> selectList = baseMapper.selectList(null);
  22. /**
  23. * 将数据库的多次查询变成一次
  24. */
  25. System.out.println("查询了数据库");
  26. //2. 封装数据
  27. List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
  28. Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
  29. //1.每一个的一级分类,查到1级分类的所有二级分类
  30. List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
  31. List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
  32. Catelog2Vo catelog2Vo = new Catelog2Vo();
  33. catelog2Vo.setId(c.getCatId().toString());
  34. catelog2Vo.setName(c.getName());
  35. catelog2Vo.setCatalog1Id(v.getCatId().toString());
  36. List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
  37. List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
  38. Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
  39. catelog3Vo.setId(c3.getCatId().toString());
  40. catelog3Vo.setName(c3.getName());
  41. catelog3Vo.setCatalog2Id(c.getCatId().toString());
  42. return catelog3Vo;
  43. }).collect(Collectors.toList());
  44. catelog2Vo.setCatalog3List(collect);
  45. return catelog2Vo;
  46. }).collect(Collectors.toList());
  47. return catelog2VoList;
  48. }));
  49. if (map == null) {
  50. /**
  51. * 解决缓存穿透
  52. */
  53. map = new HashMap<>();
  54. }
  55. redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
  56. redisTemplate.delete(Lock);
  57. return map;
  58. }

  1. public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
  2. String uuid = UUID.randomUUID().toString();
  3. Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, uuid, Duration.ofMinutes(1));
  4. if (!b) {
  5. int i = 10;
  6. while (i > 0) {
  7. Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
  8. try {
  9. TimeUnit.MILLISECONDS.sleep(100);
  10. } catch (InterruptedException e) {
  11. throw new RuntimeException(e);
  12. }
  13. if (result != null) {
  14. System.out.println("命中缓存 db lock");
  15. return (Map<String, List<Catelog2Vo>>) result;
  16. }
  17. i--;
  18. }
  19. throw new RuntimeException("系统繁忙,请重新访问");
  20. }
  21. //1.查出所有1级分类
  22. List<CategoryEntity> selectList = baseMapper.selectList(null);
  23. /**
  24. * 将数据库的多次查询变成一次
  25. */
  26. System.out.println("查询了数据库");
  27. //2. 封装数据
  28. List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
  29. Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
  30. //1.每一个的一级分类,查到1级分类的所有二级分类
  31. List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
  32. List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
  33. Catelog2Vo catelog2Vo = new Catelog2Vo();
  34. catelog2Vo.setId(c.getCatId().toString());
  35. catelog2Vo.setName(c.getName());
  36. catelog2Vo.setCatalog1Id(v.getCatId().toString());
  37. List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
  38. List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
  39. Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
  40. catelog3Vo.setId(c3.getCatId().toString());
  41. catelog3Vo.setName(c3.getName());
  42. catelog3Vo.setCatalog2Id(c.getCatId().toString());
  43. return catelog3Vo;
  44. }).collect(Collectors.toList());
  45. catelog2Vo.setCatalog3List(collect);
  46. return catelog2Vo;
  47. }).collect(Collectors.toList());
  48. return catelog2VoList;
  49. }));
  50. if (map == null) {
  51. /**
  52. * 解决缓存穿透
  53. */
  54. map = new HashMap<>();
  55. }
  56. redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
  57. Object o = redisTemplate.opsForValue().get(Lock);
  58. if (o != null && o.equals(uuid)) {
  59. redisTemplate.delete(Lock);
  60. }
  61. return map;
  62. }

Es gibt immer noch ein Problem

Da der Übertragungsprozess einige Zeit in Anspruch nimmt, lassen Sie, wenn der SCHLÜSSEL zu diesem Zeitpunkt abläuft, andere Threads eintreten, um den SCHLÜSSEL zu erstellen. Anschließend werden die Daten an den vorherigen Thread zurückgegeben. Wenn der SCHLÜSSEL gelöscht wird, werden die von anderen neu hinzugefügten Schlüssel gelöscht gelöscht.

Wertevergleich abrufen + erfolgreich vergleichen, löschen = atomare Operation

Redis+Lua-Skriptimplementierung

 public static final String Lock = "Lock";
  1. public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {
  2. String uuid = UUID.randomUUID().toString();
  3. Boolean b = redisTemplate.opsForValue().setIfAbsent(Lock, uuid, Duration.ofMinutes(5));
  4. if (!b) {
  5. System.out.println("获取分布式锁失败,等待重试");
  6. int i = 10;
  7. while (i > 0) {
  8. Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
  9. try {
  10. TimeUnit.MILLISECONDS.sleep(100);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. if (result != null) {
  15. System.out.println("命中缓存 db lock");
  16. return (Map<String, List<Catelog2Vo>>) result;
  17. }
  18. i--;
  19. }
  20. throw new RuntimeException("系统繁忙,请重新访问");
  21. }
  22. //1.查出所有1级分类
  23. /**
  24. * 将数据库的多次查询变成一次
  25. */
  26. System.out.println("获取分布式锁成功");
  27. //2. 封装数据
  28. Map<String, List<Catelog2Vo>> map = null;
  29. try {
  30. System.out.println("查询了数据库");
  31. List<CategoryEntity> selectList = baseMapper.selectList(null);
  32. List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
  33. map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
  34. //1.每一个的一级分类,查到1级分类的所有二级分类
  35. List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
  36. List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
  37. Catelog2Vo catelog2Vo = new Catelog2Vo();
  38. catelog2Vo.setId(c.getCatId().toString());
  39. catelog2Vo.setName(c.getName());
  40. catelog2Vo.setCatalog1Id(v.getCatId().toString());
  41. List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
  42. List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
  43. Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
  44. catelog3Vo.setId(c3.getCatId().toString());
  45. catelog3Vo.setName(c3.getName());
  46. catelog3Vo.setCatalog2Id(c.getCatId().toString());
  47. return catelog3Vo;
  48. }).collect(Collectors.toList());
  49. catelog2Vo.setCatalog3List(collect);
  50. return catelog2Vo;
  51. }).collect(Collectors.toList());
  52. return catelog2VoList;
  53. }));
  54. if (map == null) {
  55. /**
  56. * 解决缓存穿透
  57. */
  58. map = new HashMap<>();
  59. }
  60. redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
  61. } catch (Exception e) {
  62. e.printStackTrace();
  63. } finally {
  64. //lua脚本解锁
  65. //如果获取key等于传过来的值,就执行删除操作,否则就不执行
  66. String script="if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
  67. Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(Lock), uuid);
  68. if (execute==1){
  69. System.out.println("原子删锁成功");
  70. }else {
  71. System.out.println("原子删锁失败");
  72. }
  73. }
  74. return map;
  75. }

Nur einer hat die Datenbank abgefragt