redis系列之数据库与缓存数据一致性解决方案

数据库与缓存读写模式策略
写完数据库后是否需要马上更新缓存还是直接删除缓存?

(1)、如果写数据库的值与更新到缓存值是一样的,不需要经过任何的计算,可以马上更新缓存,但是如果对于那种写数据频繁而读数据少的场景并不合适这种解决方案,因为也许还没有查询就被删除或修改了,这样会浪费时间和资源

(2)、如果写数据库的值与更新缓存的值不一致,写入缓存中的数据需要经过几个表的关联计算后得到的结果插入缓存中,那就没有必要马上更新缓存,只有删除缓存即可,等到查询的时候在去把计算后得到的结果插入到缓存中即可。

所以一般的策略是当更新数据时,先删除缓存数据,然后更新数据库,而不是更新缓存,等要查询的时候才把最新的数据更新到缓存

数据库与缓存双写情况下导致数据不一致问题

场景一
当更新数据时,如更新某商品的库存,当前商品的库存是100,现在要更新为99,先更新数据库更改成99,然后删除缓存,发现删除缓存失败了,这意味着数据库存的是99,而缓存是100,这导致数据库和缓存不一致。

场景一解决方案

这种情况应该是先删除缓存,然后在更新数据库,如果删除缓存失败,那就不要更新数据库,如果说删除缓存成功,而更新数据库失败,那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。

场景二
在高并发的情况下,如果当删除完缓存的时候,这时去更新数据库,但还没有更新完,另外一个请求来查询数据,发现缓存里没有,就去数据库里查,还是以上面商品库存为例,如果数据库中产品的库存是100,那么查询到的库存是100,然后插入缓存,插入完缓存后,原来那个更新数据库的线程把数据库更新为了99,导致数据库与缓存不一致的情况

场景二解决方案
遇到这种情况,可以用队列的去解决这个问,创建几个队列,如20个,根据商品的ID去做hash值,然后对队列个数取摸,当有数据更新请求时,先把它丢到队列里去,当更新完后在从队列里去除,如果在更新的过程中,遇到以上场景,先去缓存里看下有没有数据,如果没有,可以先去队列里看是否有相同商品ID在做更新,如果有也把查询的请求发送到队列里去,然后同步等待缓存更新完成。
这里有一个优化点,如果发现队列里有一个查询请求了,那么就不要放新的查询操作进去了,用一个while(true)循环去查询缓存,循环个200MS左右,如果缓存里还没有则直接取数据库的旧数据,一般情况下是可以取到的。

在高并发下解决场景二要注意的问题
(1)读请求时长阻塞
 由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时间内返回,该解决方案最大的风险在于可能数据更新很频繁,导致队列中挤压了大量的更新操作在里面,然后读请求会发生大量的超时,最后导致大量的请求直接走数据库,像遇到这种情况,一般要做好足够的压力测试,如果压力过大,需要根据实际情况添加机器。
(2)请求并发量过高
 这里还是要做好压力测试,多模拟真实场景,并发量在最高的时候QPS多少,扛不住就要多加机器,还有就是做好读写比例是多少
(3)多服务实例部署的请求路由
可能这个服务部署了多个实例,那么必须保证说,执行数据更新操作,以及执行缓存更新操作的请求,都通过nginx服务器路由到相同的服务实例上
(4)热点商品的路由问题,导致请求的倾斜
某些商品的读请求特别高,全部打到了相同的机器的相同丢列里了,可能造成某台服务器压力过大,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以更新频率不是太高的话,这个问题的影响并不是很大,但是确实有可能某些服务器的负载会高一些。

数据库与缓存数据一致性解决方案流程图

数据库与缓存数据一致性解决方案对应代码
商品库存实体

 1 package com.shux.inventory.entity;
2 /**
3 **********************************************
4 * 描述:
5 * Simba.Hua
6 * 2017年8月30日
7 **********************************************
8 **/
9 public class InventoryProduct {
10 private Integer productId;
11 private Long InventoryCnt;
12
13 public Integer getProductId() {
14 return productId;
15 }
16 public void setProductId(Integer productId) {
17 this.productId = productId;
18 }
19 public Long getInventoryCnt() {
20 return InventoryCnt;
21 }
22 public void setInventoryCnt(Long inventoryCnt) {
23 InventoryCnt = inventoryCnt;
24 }
25
26 }
27

请求接口

 1 /**
2 **********************************************
3 * 描述:
4 * Simba.Hua
5 * 2017年8月27日
6 **********************************************
7 **/
8 public interface Request {
9 public void process();
10 public Integer getProductId();
11 public boolean isForceFefresh();
12 }

数据更新请求

 1 package com.shux.inventory.request;
2
3 import org.springframework.transaction.annotation.Transactional;
4
5 import com.shux.inventory.biz.InventoryProductBiz;
6 import com.shux.inventory.entity.InventoryProduct;
7
8 /**
9 **********************************************
10 * 描述:更新库存信息
11 * 1、先删除缓存中的数据
12 * 2、更新数据库中的数据
13 * Simba.Hua
14 * 2017年8月30日
15 **********************************************
16 **/
17 public class InventoryUpdateDBRequest implements Request{
18 private InventoryProductBiz inventoryProductBiz;
19 private InventoryProduct inventoryProduct;
20
21 public InventoryUpdateDBRequest(InventoryProduct inventoryProduct,InventoryProductBiz inventoryProductBiz){
22 this.inventoryProduct = inventoryProduct;
23 this.inventoryProductBiz = inventoryProductBiz;
24 }
25 @Override
26 @Transactional
27 public void process() {
28 inventoryProductBiz.removeInventoryProductCache(inventoryProduct.getProductId());
29 inventoryProductBiz.updateInventoryProduct(inventoryProduct);
30 }
31 @Override
32 public Integer getProductId() {
33 // TODO Auto-generated method stub
34 return inventoryProduct.getProductId();
35 }
36 @Override
37 public boolean isForceFefresh() {
38 // TODO Auto-generated method stub
39 return false;
40 }
41
42 }

查询请求

 1 package com.shux.inventory.request;
2
3 import com.shux.inventory.biz.InventoryProductBiz;
4 import com.shux.inventory.entity.InventoryProduct;
5
6 /**
7 **********************************************
8 * 描述:查询缓存数据
9 * 1、从数据库中查询
10 * 2、从数据库中查询后插入到缓存中
11 * Simba.Hua
12 * 2017年8月30日
13 **********************************************
14 **/
15 public class InventoryQueryCacheRequest implements Request {
16 private InventoryProductBiz inventoryProductBiz;
17 private Integer productId;
18 private boolean isForceFefresh;
19
20 public InventoryQueryCacheRequest(Integer productId,InventoryProductBiz inventoryProductBiz,boolean isForceFefresh) {
21 this.productId = productId;
22 this.inventoryProductBiz = inventoryProductBiz;
23 this.isForceFefresh = isForceFefresh;
24 }
25 @Override
26 public void process() {
27 InventoryProduct inventoryProduct = inventoryProductBiz.loadInventoryProductByProductId(productId);
28 inventoryProductBiz.setInventoryProductCache(inventoryProduct);
29 }
30 @Override
31 public Integer getProductId() {
32 // TODO Auto-generated method stub
33 return productId;
34 }
35 public boolean isForceFefresh() {
36 return isForceFefresh;
37 }
38 public void setForceFefresh(boolean isForceFefresh) {
39 this.isForceFefresh = isForceFefresh;
40 }
41
42
43 }

spring启动时初始化队列线程池

 1 package com.shux.inventory.thread;
2
3 import java.util.concurrent.ArrayBlockingQueue;
4 import java.util.concurrent.ExecutorService;
5 import java.util.concurrent.Executors;
6
7 import com.shux.inventory.request.Request;
8 import com.shux.inventory.request.RequestQueue;
9 import com.shux.utils.other.SysConfigUtil;
10
11 /**
12 **********************************************
13 * 描述:请求处理线程池,初始化队列数及每个队列最多能处理的数量
14 * Simba.Hua
15 * 2017年8月27日
16 **********************************************
17 **/
18 public class RequestProcessorThreadPool {
19 private static final int blockingQueueNum = SysConfigUtil.get("request.blockingqueue.number")==null?10:Integer.valueOf(SysConfigUtil.get("request.blockingqueue.number").toString());
20 private static final int queueDataNum = SysConfigUtil.get("request.everyqueue.data.length")==null?100:Integer.valueOf(SysConfigUtil.get("request.everyqueue.data.length").toString());
21 private ExecutorService threadPool = Executors.newFixedThreadPool(blockingQueueNum);
22 private RequestProcessorThreadPool(){
23 for(int i=0;i<blockingQueueNum;i++){//初始化队列
24 ArrayBlockingQueue<Request> queue = new ArrayBlockingQueue<Request>(queueDataNum);//每个队列中放100条数据
25 RequestQueue.getInstance().addQueue(queue);
26 threadPool.submit(new RequestProcessorThread(queue));//把每个queue交个线程去处理,线程会处理每个queue中的数据
27 }
28 }
29 public static class Singleton{
30 private static RequestProcessorThreadPool instance;
31 static{
32 instance = new RequestProcessorThreadPool();
33 }
34 public static RequestProcessorThreadPool getInstance(){
35 return instance;
36 }
37 }
38 public static RequestProcessorThreadPool getInstance(){
39 return Singleton.getInstance();
40 }
41 /**
42 * 初始化线程池
43 */
44 public static void init(){
45 getInstance();
46 }
47 }

请求处理线程

 1 package com.shux.inventory.thread;
2
3 import java.util.Map;
4 import java.util.concurrent.ArrayBlockingQueue;
5 import java.util.concurrent.Callable;
6
7 import com.shux.inventory.request.InventoryUpdateDBRequest;
8 import com.shux.inventory.request.Request;
9 import com.shux.inventory.request.RequestQueue;
10
11 /**
12 **********************************************
13 * 描述:请求处理线程
14 * Simba.Hua
15 * 2017年8月27日
16 **********************************************
17 **/
18 public class RequestProcessorThread implements Callable<Boolean>{
19 private ArrayBlockingQueue<Request> queue;
20 public RequestProcessorThread(ArrayBlockingQueue<Request> queue){
21 this.queue = queue;
22 }
23 @Override
24 public Boolean call() throws Exception {
25 Request request = queue.take();
26 Map<Integer,Boolean> flagMap = RequestQueue.getInstance().getFlagMap();
27 //不需要强制刷新的时候,查询请求去重处理
28 if (!request.isForceFefresh()){
29 if (request instanceof InventoryUpdateDBRequest) {//如果是更新请求,那就置为false
30 flagMap.put(request.getProductId(), true);
31 } else {
32 Boolean flag = flagMap.get(request.getProductId());
33 /**
34 * 标志位为空,有三种情况
35 * 1、没有过更新请求
36 * 2、没有查询请求
37 * 3、数据库中根本没有数据
38 * 在最初情况,一旦库存了插入了数据,那就好会在缓存中也会放一份数据,
39 * 但这种情况下有可能由于redis中内存满了,redis通过LRU算法把这个商品给清除了,导致缓存中没有数据
40 * 所以当标志位为空的时候,需要从数据库重查询一次,并且把标志位置为false,以便后面的请求能够从缓存中取
41 */
42 if ( flag == null) {
43 flagMap.put(request.getProductId(), false);
44 }
45 /**
46 * 如果不为空,并且flag为true,说明之前有一次更新请求,说明缓存中没有数据了(更新缓存会先删除缓存),
47 * 这个时候就要去刷新缓存,即从数据库中查询一次,并把标志位设置为false
48 */
49 if ( flag != null && flag) {
50 flagMap.put(request.getProductId(), false);
51 }
52 /**
53 * 这种情况说明之前有一个查询请求,并且把数据刷新到了缓存中,所以这时候就不用去刷新缓存了,直接返回就可以了
54 */
55 if (flag != null && !flag) {
56 flagMap.put(request.getProductId(), false);
57 return true;
58 }
59 }
60 }
61 request.process();
62 return true;
63 }
64
65 }

请求队列

 1 package com.shux.inventory.request;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.concurrent.ArrayBlockingQueue;
7 import java.util.concurrent.ConcurrentHashMap;
8
9 /**
10 **********************************************
11 * 描述:请求队列
12 * Simba.Hua
13 * 2017年8月27日
14 **********************************************
15 **/
16 public class RequestQueue {
17 private List<ArrayBlockingQueue<Request>> queues = new ArrayList<>();
18
19 private Map<Integer,Boolean> flagMap = new ConcurrentHashMap<>();
20 private RequestQueue(){
21
22 }
23 private static class Singleton{
24 private static RequestQueue queue;
25 static{
26 queue = new RequestQueue();
27 }
28 public static RequestQueue getInstance() {
29 return queue;
30 }
31 }
32
33 public static RequestQueue getInstance(){
34 return Singleton.getInstance();
35 }
36 public void addQueue(ArrayBlockingQueue<Request> queue) {
37 queues.add(queue);
38 }
39
40 public int getQueueSize(){
41 return queues.size();
42 }
43 public ArrayBlockingQueue<Request> getQueueByIndex(int index) {
44 return queues.get(index);
45 }
46
47 public Map<Integer,Boolean> getFlagMap() {
48 return this.flagMap;
49 }
50 }

spring 启动初始化线程池类

 1 package com.shux.inventory.listener;
2
3 import org.springframework.context.ApplicationListener;
4 import org.springframework.context.event.ContextRefreshedEvent;
5
6 import com.shux.inventory.thread.RequestProcessorThreadPool;
7
8 /**
9 **********************************************
10 * 描述:spring 启动初始化线程池类
11 * Simba.Hua
12 * 2017年8月27日
13 **********************************************
14 **/
15 public class InitListener implements ApplicationListener<ContextRefreshedEvent>{
16
17 @Override
18 public void onApplicationEvent(ContextRefreshedEvent event) {
19 // TODO Auto-generated method stub
20 if(event.getApplicationContext().getParent() != null){
21 return;
22 }
23 RequestProcessorThreadPool.init();
24 }
25 }

异步处理请求接口

 1 package com.shux.inventory.biz;
2
3 import com.shux.inventory.request.Request;
4
5 /**
6 **********************************************
7 * 描述:请求异步处理接口,用于路由队列并把请求加入到队列中
8 * Simba.Hua
9 * 2017年8月30日
10 **********************************************
11 **/
12 public interface IRequestAsyncProcessBiz {
13 void process(Request request);
14 }
15

异步处理请求接口实现

 1 package com.shux.inventory.biz.impl;
2
3 import java.util.concurrent.ArrayBlockingQueue;
4
5 import org.slf4j.Logger;
6 import org.slf4j.LoggerFactory;
7 import org.springframework.stereotype.Service;
8
9 import com.shux.inventory.biz.IRequestAsyncProcessBiz;
10 import com.shux.inventory.request.Request;
11 import com.shux.inventory.request.RequestQueue;
12
13
14 /**
15 **********************************************
16 * 描述:异步处理请求,用于路由队列并把请求加入到队列中
17 * Simba.Hua
18 * 2017年8月30日
19 **********************************************
20 **/
21 @Service("requestAsyncProcessService")
22 public class RequestAsyncProcessBizImpl implements IRequestAsyncProcessBiz {
23 private Logger logger = LoggerFactory.getLogger(getClass());
24 @Override
25 public void process(Request request) {
26 // 做请求的路由,根据productId路由到对应的队列
27 ArrayBlockingQueue<Request> queue = getQueueByProductId(request.getProductId());
28 try {
29 queue.put(request);
30 } catch (InterruptedException e) {
31 logger.error("产品ID{}加入队列失败",request.getProductId(),e);
32 }
33 }
34
35 private ArrayBlockingQueue<Request> getQueueByProductId(Integer productId) {
36 RequestQueue requestQueue = RequestQueue.getInstance();
37 String key = String.valueOf(productId);
38 int hashcode;
39 int hash = (key == null) ? 0 : (hashcode = key.hashCode())^(hashcode >>> 16);
40 //对hashcode取摸
41 int index = (requestQueue.getQueueSize()-1) & hash;
42 return requestQueue.getQueueByIndex(index);
43 }
44
45
46
47 }
 1 package com.shux.inventory.biz.impl;
2
3 import javax.annotation.Resource;
4
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.stereotype.Service;
7
8 import com.shux.inventory.biz.InventoryProductBiz;
9 import com.shux.inventory.entity.InventoryProduct;
10 import com.shux.inventory.mapper.InventoryProductMapper;
11 import com.shux.redis.biz.IRedisBiz;
12
13 /**
14 **********************************************
15 * 描述
16 * Simba.Hua
17 * 2017年8月30日
18 **********************************************
19 **/
20 @Service("inventoryProductBiz")
21 public class InventoryProductBizImpl implements InventoryProductBiz {
22 private @Autowired IRedisBiz<InventoryProduct> redisBiz;
23 private @Resource InventoryProductMapper mapper;
24 @Override
25 public void updateInventoryProduct(InventoryProduct inventoryProduct) {
26 // TODO Auto-generated method stub
27 mapper.updateInventoryProduct(inventoryProduct);
28 }
29
30 @Override
31 public InventoryProduct loadInventoryProductByProductId(Integer productId) {
32 // TODO Auto-generated method stub
33 return mapper.loadInventoryProductByProductId(productId);
34 }
35
36 @Override
37 public void setInventoryProductCache(InventoryProduct inventoryProduct) {
38 redisBiz.set("inventoryProduct:"+inventoryProduct.getProductId(), inventoryProduct);
39
40 }
41
42 @Override
43 public void removeInventoryProductCache(Integer productId) {
44 redisBiz.delete("inventoryProduct:"+productId);
45
46 }
47
48 @Override
49 public InventoryProduct loadInventoryProductCache(Integer productId) {
50 // TODO Auto-generated method stub
51 return redisBiz.get("inventoryProduct:"+productId);
52 }
53
54 }

数据更新请求controller

 1 package com.shux.inventory.biz.impl;
2
3 import javax.annotation.Resource;
4
5 import org.springframework.beans.factory.annotation.Autowired;
6 import org.springframework.stereotype.Service;
7
8 import com.shux.inventory.biz.InventoryProductBiz;
9 import com.shux.inventory.entity.InventoryProduct;
10 import com.shux.inventory.mapper.InventoryProductMapper;
11 import com.shux.redis.biz.IRedisBiz;
12
13 /**
14 **********************************************
15 * 描述
16 * Simba.Hua
17 * 2017年8月30日
18 **********************************************
19 **/
20 @Service("inventoryProductBiz")
21 public class InventoryProductBizImpl implements InventoryProductBiz {
22 private @Autowired IRedisBiz<InventoryProduct> redisBiz;
23 private @Resource InventoryProductMapper mapper;
24 @Override
25 public void updateInventoryProduct(InventoryProduct inventoryProduct) {
26 // TODO Auto-generated method stub
27 mapper.updateInventoryProduct(inventoryProduct);
28 }
29
30 @Override
31 public InventoryProduct loadInventoryProductByProductId(Integer productId) {
32 // TODO Auto-generated method stub
33 return mapper.loadInventoryProductByProductId(productId);
34 }
35
36 @Override
37 public void setInventoryProductCache(InventoryProduct inventoryProduct) {
38 redisBiz.set("inventoryProduct:"+inventoryProduct.getProductId(), inventoryProduct);
39
40 }
41
42 @Override
43 public void removeInventoryProductCache(Integer productId) {
44 redisBiz.delete("inventoryProduct:"+productId);
45
46 }
47
48 @Override
49 public InventoryProduct loadInventoryProductCache(Integer productId) {
50 // TODO Auto-generated method stub
51 return redisBiz.get("inventoryProduct:"+productId);
52 }
53
54 }

数据查询请求controller

 1 package com.shux.inventory.controller;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.stereotype.Controller;
5 import org.springframework.web.bind.annotation.RequestMapping;
6
7 import com.shux.inventory.biz.IRequestAsyncProcessBiz;
8 import com.shux.inventory.biz.InventoryProductBiz;
9 import com.shux.inventory.entity.InventoryProduct;
10 import com.shux.inventory.request.InventoryQueryCacheRequest;
11 import com.shux.inventory.request.Request;
12
13 /**
14 **********************************************
15 * 描述:提交查询请求
16 * 1、先从缓存中取数据
17 * 2、如果能从缓存中取到数据,则返回
18 * 3、如果不能从缓存取到数据,则等待20毫秒,然后再次去数据,直到200毫秒,如果超过200毫秒还不能取到数据,则从数据库中取,并强制刷新缓存数据
19 * Simba.Hua
20 * 2017年9月1日
21 **********************************************
22 **/
23 @Controller("/inventory")
24 public class InventoryQueryCacheController {
25 private @Autowired InventoryProductBiz inventoryProductBiz;
26 private @Autowired IRequestAsyncProcessBiz requestAsyncProcessBiz;
27 @RequestMapping("/queryInventoryProduct")
28 public InventoryProduct queryInventoryProduct(Integer productId) {
29 Request request = new InventoryQueryCacheRequest(productId,inventoryProductBiz,false);
30 requestAsyncProcessBiz.process(request);//加入到队列中
31 long startTime = System.currentTimeMillis();
32 long allTime = 0L;
33 long endTime = 0L;
34 InventoryProduct inventoryProduct = null;
35 while (true) {
36 if (allTime > 200){//如果超过了200ms,那就直接退出,然后从数据库中查询
37 break;
38 }
39 try {
40 inventoryProduct = inventoryProductBiz.loadInventoryProductCache(productId);
41 if (inventoryProduct != null) {
42 return inventoryProduct;
43 } else {
44 Thread.sleep(20);//如果查询不到就等20毫秒
45 }
46 endTime = System.currentTimeMillis();
47 allTime = endTime - startTime;
48 } catch (Exception e) {
49 }
50 }
51 /**
52 * 代码执行到这来,只有以下三种情况
53 * 1、缓存中本来有数据,由于redis内存满了,redis通过LRU算法清除了缓存,导致数据没有了
54 * 2、由于之前数据库查询比较慢或者内存太小处理不过来队列中的数据,导致队列里挤压了很多的数据,所以一直没有从数据库中获取数据然后插入到缓存中
55 * 3、数据库中根本没有这样的数据,这种情况叫数据穿透,一旦别人知道这个商品没有,如果一直执行查询,就会一直查询数据库,如果过多,那么有可能会导致数据库瘫痪
56 */
57 inventoryProduct = inventoryProductBiz.loadInventoryProductByProductId(productId);
58 if (inventoryProduct != null) {
59 Request forcRrequest = new InventoryQueryCacheRequest(productId,inventoryProductBiz,true);
60 requestAsyncProcessBiz.process(forcRrequest);//这个时候需要强制刷新数据库,使缓存中有数据
61 return inventoryProduct;
62 }
63 return null;
64
65 }
66 }

解决Redis中数据不一致问题的更多相关文章

  1. 使用Sqoop,最终导入到hive中的数据和原数据库中数据不一致解决办法

            Sqoop是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysql.postgresql...)间进行数据的传递,可以将一个关系型数据库(例如 : MySQL , ...

  2. 如何解决Redis中的key过期问题

    最近我们在Redis集群中发现了一个有趣的问题.在花费大量时间进行调试和测试后,通过更改key过期,我们可以将某些集群中的Redis内存使用量减少25%. Twitter内部运行着多个缓存服务.其中一 ...

  3. BlockingQueue阻塞队列(解决多线程中数据安全问题 可用于抢票,秒杀)

    案例:一个线程类中 private static BlockingQueue<Map<String, String>> dataQueue = new LinkedBlocki ...

  4. 解决InputStream中数据读取不完整问题

    转载:https://blog.csdn.net/lilidejing/article/details/37913627 当需要用到InputStream获取数据时,这时就需要读取InputStrea ...

  5. Redis 中如何保证数据的不丢失,Redis 中的持久化是如何进行

    Redis 中数据的持久化 前言 AOF 持久化 什么是 AOF 持久化 为什么要后记录日志呢 AOF 的潜在风险 AOF 文件的写入和同步 AOF 文件重写机制 AOF 的数据还原 RDB 持久化 ...

  6. SpringBoot 结合 Spring Cache 操作 Redis 实现数据缓存

    系统环境: Redis 版本:5.0.7 SpringBoot 版本:2.2.2.RELEASE 参考地址: Redus 官方网址:https://redis.io/ 博文示例项目 Github 地址 ...

  7. (Redis基础教程之六)如何使用Redis中的List

    如何在ubuntu18.04上安装和保护redis 如何连接到Redis数据库 如何管理Redis数据库和Keys 如何在Redis中管理副本和客户端 如何在Redis中管理字符串 如何在Redis中 ...

  8. redis中关于过期键的删除策略

    我们已经了解到了Redis是一种内存数据库,Redis中数据都是以key-value的形式存储在内存中.由Redisserver来维护和管理这部分内存,内存是何足珍贵,不须要的数据或者是已经使用过的无 ...

  9. Redis面试题记录--缓存双写情况下导致数据不一致问题

    转载自:https://blog.csdn.net/lzhcoder/article/details/79469123 https://blog.csdn.net/u013374645/article ...

随机推荐

  1. day02初识判断和循环

    ​ 新的一天朝气满满,今天小东补充一些昨天没有涉及的新知识! if判断语句 if语句就像是常说的如果....那么,它会做判断,if语句有几种方式: #示例一:判断一是否等于一,等于则输出一等一,否则输 ...

  2. MFiX-DEM中的并行碰撞搜索

    基于MFiX-19.2.2 DEM并行程序中的颗粒循环 在DEM并行程序中,每个进程只循环该进程包含的颗粒,并且每个进程还有一层ghost cell,用来存放另一个进程发送过来的颗粒信息. 下面添加一 ...

  3. DLL转存为IL文件及修改后重新生成DLL文件

    DLL反编译工具有:dotPeek (jetbrains) .ILSpy DLL转存IL使用:ildasm 打开DLL文件,选择file->dump. il .res重新生成DLL工具: ila ...

  4. 看完这篇 final、finally 和 finalize 和面试官扯皮就没问题了

    我把自己以往的文章汇总成为了 Github ,欢迎各位大佬 star https://github.com/crisxuan/bestJavaer 已提交此篇文章 final 是 Java 中的关键字 ...

  5. websocket报400错误

    解决方案看了下讨论区说的方案,问题出现在nginx的配置文件,需要修改nginx.conf文件.在linux终端中敲入vim /etc/nginx/nginx.conf,找到location这个位置, ...

  6. 微信小程序——【百景游戏小攻略】

    微信小程序--[百景游戏小攻略] 本次课程小项目中的图片以及文章还未获得授权!请勿商用!未经授权,请勿转载! 博客班级 https://edu.cnblogs.com/campus/zjcsxy/SE ...

  7. MySQL全面瓦解6:查询的基本操作

    概述 提到查询,就回到我们第四篇的SQL语言分类了,DQL(Data QueryLanguage),也就是数据查询语言,实际就是从数据库中获取数据的一种命令方式.我们给数据库发送一个查询语句的命令,数 ...

  8. Maven依赖管理系统

    [背景] 之前在领英上读到一篇软文,<Managing Software Dependency at Scale>,讲述了领英是如何处理模块之间的依赖解析,以及如何去更好的处理依赖关系以及 ...

  9. 如何将别人Google云端硬盘中的数据进行保存

    查了好久终于知道! 如何将别人Google云端硬盘中的数据进行copy,而不是右键发现只有添加快捷方式 只要shift+z就可以保存了! 之后等我弄清楚怎么将别人家的云盘中的数据集导到colab再来详 ...

  10. PHP直播平台源码搭建教程

    直播源码市场火爆,但是PHP直播平台源码的搭建过程较为复杂,本文就简单为大家概述一下直播的实现过程以及PHP直播平台源码是如何搭建的. 一.直播的定义 如今PHP直播平台源码绝大部分情况下是指在现场架 ...