τα στοιχεία επικοινωνίας μου
Ταχυδρομείο[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Αναφέρεται στην αναζήτηση δεδομένων που δεν πρέπει να υπάρχουν Εφόσον η κρυφή μνήμη δεν έχει χτυπηθεί, η βάση δεδομένων θα υποβληθεί σε ερώτημα, αλλά η βάση δεδομένων δεν έχει τέτοια εγγραφή Σε κάθε αίτημα για αυτά τα ανύπαρκτα δεδομένα πρέπει να μεταβείτε στο επίπεδο αποθήκευσης για να κάνετε ερώτημα, το οποίο χάνει την έννοια της προσωρινής αποθήκευσης.
κίνδυνος:
Η χρήση ανύπαρκτων δεδομένων για την πραγματοποίηση επιθέσεων θα αυξήσει την στιγμιαία πίεση στη βάση δεδομένων και τελικά θα οδηγήσει σε κατάρρευση.
λύσει:
Το μηδενικό αποτέλεσμα αποθηκεύεται στην κρυφή μνήμη και προστίθεται ένας σύντομος χρόνος λήξης.
Χιονοστιβάδα προσωρινής μνήμης: Η χιονοστιβάδα κρυφής μνήμης σημαίνει ότι όταν ορίζουμε την κρυφή μνήμη, το Κλειδί υιοθετεί τον ίδιο χρόνο λήξης, με αποτέλεσμα η κρυφή μνήμη να γίνει άκυρη σε μια συγκεκριμένη στιγμή, όλα τα αιτήματα προωθούνται στο DB και το DB βρίσκεται υπό στιγμιαία πίεση και χιονοστιβάδες.
Λύση: Προσθέστε μια τυχαία τιμή στον αρχικό χρόνο λήξης, όπως 1-5 λεπτά τυχαία, έτσι ώστε ο ρυθμός επανάληψης κάθε αποθηκευμένου χρόνου λήξης στην κρυφή μνήμη θα μειωθεί, καθιστώντας δύσκολη την πρόκληση συλλογικών συμβάντων αποτυχίας.
Ανάλυση προσωρινής μνήμης:
λύσει:
Κλειδαριά
Ένας μεγάλος όγκος συγχρονισμού επιτρέπει μόνο σε έναν να κάνει έλεγχο και οι άλλοι να περιμένουν Μετά τον έλεγχο, το κλείδωμα απελευθερώνεται και οι άλλοι αποκτούν το κλείδωμα, τοποθετήστε το δείκτη του ποντικιού, ελέγξτε πρώτα τη μνήμη cache και θα υπάρχουν δεδομένα, χωρίς να μεταβείτε στο db.
Λύστε πρώτα τη διείσδυση κρυφής μνήμης και τη χιονοστιβάδα
- private static final String CATALOG_JSON="CATALOG_JSON";
- @Override
- public Map<String, List<Catelog2Vo>> getCatalogJson() {
-
- /**
- * 空结果缓存:解决缓存穿透
- * 设置过期时间(加随机值) 缓存雪崩
- * 加锁 解决缓存击穿
- */
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if(result!=null){
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
- Map<String, List<Catelog2Vo>> map = getCatalogJsonFromDB();
- if (map==null){
- /**
- * 解决缓存穿透
- */
- map=new HashMap<>();
- }
- redisTemplate.opsForValue().set(CATALOG_JSON,map, Duration.ofDays(1));
- return map;
- }
Το αντικείμενο κοντέινερ εκκίνησης είναι σε λειτουργία μονής γραμμής από προεπιλογή, επομένως το ίδιο αντικείμενο μπορεί να συγχρονιστεί και να κλειδωθεί Όταν χρησιμοποιείτε τη λειτουργία διπλής ανίχνευσης, μπορεί να εκτελεστεί ταυτόχρονα.
- public synchronized Map<String, List<Catelog2Vo>> getCatalogJsonFromDB() {
-
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if (result != null) {
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
-
- //1.查出所有1级分类
- List<CategoryEntity> selectList = baseMapper.selectList(null);
- /**
- * 将数据库的多次查询变成一次
- */
-
- //2. 封装数据
- List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
- Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
- //1.每一个的一级分类,查到1级分类的所有二级分类
- List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
-
- List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
- Catelog2Vo catelog2Vo = new Catelog2Vo();
- catelog2Vo.setId(c.getCatId().toString());
- catelog2Vo.setName(c.getName());
- catelog2Vo.setCatalog1Id(v.getCatId().toString());
-
- List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
- List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
- Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
- catelog3Vo.setId(c3.getCatId().toString());
- catelog3Vo.setName(c3.getName());
- catelog3Vo.setCatalog2Id(c.getCatId().toString());
- return catelog3Vo;
- }).collect(Collectors.toList());
-
- catelog2Vo.setCatalog3List(collect);
-
- return catelog2Vo;
- }).collect(Collectors.toList());
-
-
- return catelog2VoList;
- }));
- return map;
- }
- public Map<String, List<Catelog2Vo>> getCatalogJson() {
-
- /**
- * 空结果缓存:解决缓存穿透
- * 设置过期时间(加随机值) 缓存雪崩
- * 加锁 解决缓存击穿
- */
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if (result != null) {
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
- Map<String, List<Catelog2Vo>> map = getCatalogJsonFromDB();
- if (map == null) {
- /**
- * 解决缓存穿透
- */
- map = new HashMap<>();
- }
- redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
- return map;
- }
Θα εξακολουθήσουν να υπάρχουν προβλήματα με τη λογική του παραπάνω κώδικα Όταν εκτελείται ταυτόχρονα, το νήμα Νο. 1 θα ολοκληρώσει τον έλεγχο της βάσης δεδομένων και θα απελευθερώσει το κλείδωμα πριν τοποθετηθεί στη μνήμη cache για κανένα στοιχείο και ελέγξτε ξανά τη βάση δεδομένων Δεν υπάρχει εγγύηση ότι θα υπάρχει μόνο ένα νήμα για έλεγχο της βάσης δεδομένων
Σωστή προσέγγιση
- public synchronized Map<String, List<Catelog2Vo>> getCatalogJsonFromDB() {
-
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if (result != null) {
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
-
- //1.查出所有1级分类
- List<CategoryEntity> selectList = baseMapper.selectList(null);
- /**
- * 将数据库的多次查询变成一次
- */
-
- //2. 封装数据
- List<CategoryEntity> level1Category = selectList.stream().filter(s -> s.getParentCid().equals(0L)).collect(Collectors.toList());
- Map<String, List<Catelog2Vo>> map = level1Category.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
- //1.每一个的一级分类,查到1级分类的所有二级分类
- List<CategoryEntity> categoryEntities = selectList.stream().filter(s -> s.getParentCid().equals(v.getCatId())).collect(Collectors.toList());
-
- List<Catelog2Vo> catelog2VoList = categoryEntities.stream().map(c -> {
- Catelog2Vo catelog2Vo = new Catelog2Vo();
- catelog2Vo.setId(c.getCatId().toString());
- catelog2Vo.setName(c.getName());
- catelog2Vo.setCatalog1Id(v.getCatId().toString());
-
- List<CategoryEntity> categoryEntities1 = selectList.stream().filter(s -> s.getParentCid().equals(c.getCatId())).collect(Collectors.toList());
- List<Catelog2Vo.Catelog3Vo> collect = categoryEntities1.stream().map(c3 -> {
- Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo();
- catelog3Vo.setId(c3.getCatId().toString());
- catelog3Vo.setName(c3.getName());
- catelog3Vo.setCatalog2Id(c.getCatId().toString());
- return catelog3Vo;
- }).collect(Collectors.toList());
-
- catelog2Vo.setCatalog3List(collect);
-
- return catelog2Vo;
- }).collect(Collectors.toList());
-
-
- return catelog2VoList;
- }));
- if (map == null) {
- /**
- * 解决缓存穿透
- */
- map = new HashMap<>();
- }
- redisTemplate.opsForValue().set(CATALOG_JSON, map, Duration.ofDays(1));
- return map;
- }
Βάλτε τη λειτουργία αποθήκευσης στην κρυφή μνήμη στο μπλοκ κώδικα συγχρονισμού
- @Override
- public Map<String, List<Catelog2Vo>> getCatalogJson() {
-
- /**
- * 空结果缓存:解决缓存穿透
- * 设置过期时间(加随机值) 缓存雪崩
- * 加锁 解决缓存击穿
- */
- Object result = redisTemplate.opsForValue().get(CATALOG_JSON);
- if (result != null) {
- return (Map<String, List<Catelog2Vo>>) result;
- }
-
- Map<String, List<Catelog2Vo>> map = getCatalogJsonFromDB();
-
- return map;
- }
Οι τοπικές κλειδαριές μπορούν να κλειδώσουν μόνο την τρέχουσα διαδικασία, επομένως χρειαζόμαστε κατανεμημένες κλειδαριές
Δηλαδή, κάθε κλείδωμα μπορεί να κλειδώσει μόνο την τρέχουσα διαδικασία, δηλαδή κάθε υπηρεσία θα ελέγχει τη βάση δεδομένων.