我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解redis缓存与数据库一致性问题解决

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解redis缓存与数据库一致性问题解决

数据库与缓存读写模式策略

写完数据库后是否需要马上更新缓存还是直接删除缓存?

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

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

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

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

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

场景一解决方案 

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

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

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

在高并发下解决场景二要注意的问题

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

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

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

商品库存实体


package com.shux.inventory.entity;

public class InventoryProduct {
 private Integer productId;
 private Long InventoryCnt;
 
 public Integer getProductId() {
  return productId;
 }
 public void setProductId(Integer productId) {
  this.productId = productId;
 }
 public Long getInventoryCnt() {
  return InventoryCnt;
 }
 public void setInventoryCnt(Long inventoryCnt) {
  InventoryCnt = inventoryCnt;
 }
 
}

请求接口



public interface Request {
 public void process();
 public Integer getProductId();
 public boolean isForceFefresh();
}

数据更新请求


package com.shux.inventory.request;
 
import org.springframework.transaction.annotation.Transactional;
 
import com.shux.inventory.biz.InventoryProductBiz;
import com.shux.inventory.entity.InventoryProduct;
 

public class InventoryUpdateDBRequest implements Request{
 private InventoryProductBiz inventoryProductBiz;
 private InventoryProduct inventoryProduct;
 
 public InventoryUpdateDBRequest(InventoryProduct inventoryProduct,InventoryProductBiz inventoryProductBiz){
  this.inventoryProduct = inventoryProduct;
  this.inventoryProductBiz = inventoryProductBiz;
 }
 @Override
 @Transactional
 public void process() {
  inventoryProductBiz.removeInventoryProductCache(inventoryProduct.getProductId());
  inventoryProductBiz.updateInventoryProduct(inventoryProduct);
 }
 @Override
 public Integer getProductId() {
  // TODO Auto-generated method stub
  return inventoryProduct.getProductId();
 }
 @Override
 public boolean isForceFefresh() {
  // TODO Auto-generated method stub
  return false;
 }
 
}

查询请求


package com.shux.inventory.request;
 
import com.shux.inventory.biz.InventoryProductBiz;
import com.shux.inventory.entity.InventoryProduct;
 

public class InventoryQueryCacheRequest implements Request {
 private InventoryProductBiz inventoryProductBiz;
 private Integer productId;
 private boolean isForceFefresh;
 
 public InventoryQueryCacheRequest(Integer productId,InventoryProductBiz inventoryProductBiz,boolean isForceFefresh) {
  this.productId = productId;
  this.inventoryProductBiz = inventoryProductBiz;
  this.isForceFefresh = isForceFefresh;
 }
 @Override
 public void process() {
  InventoryProduct inventoryProduct = inventoryProductBiz.loadInventoryProductByProductId(productId);
  inventoryProductBiz.setInventoryProductCache(inventoryProduct);
 }
 @Override
 public Integer getProductId() {
  // TODO Auto-generated method stub
  return productId;
 }
 public boolean isForceFefresh() {
  return isForceFefresh;
 }
 public void setForceFefresh(boolean isForceFefresh) {
  this.isForceFefresh = isForceFefresh;
 }
 
}

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


package com.shux.inventory.thread;
 
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import com.shux.inventory.request.Request;
import com.shux.inventory.request.RequestQueue;
import com.shux.utils.other.SysConfigUtil;
 

public class RequestProcessorThreadPool {
 private static final int blockingQueueNum = SysConfigUtil.get("request.blockingqueue.number")==null?10:Integer.valueOf(SysConfigUtil.get("request.blockingqueue.number").toString());
 private static final int queueDataNum = SysConfigUtil.get("request.everyqueue.data.length")==null?100:Integer.valueOf(SysConfigUtil.get("request.everyqueue.data.length").toString());
 private ExecutorService threadPool = Executors.newFixedThreadPool(blockingQueueNum);
 private RequestProcessorThreadPool(){
  for(int i=0;i<blockingQueueNum;i++){//初始化队列
   ArrayBlockingQueue<Request> queue = new ArrayBlockingQueue<Request>(queueDataNum);//每个队列中放100条数据
   RequestQueue.getInstance().addQueue(queue);
   threadPool.submit(new RequestProcessorThread(queue));//把每个queue交个线程去处理,线程会处理每个queue中的数据
  }
 }
 public static class Singleton{
  private static RequestProcessorThreadPool instance;
  static{
   instance = new RequestProcessorThreadPool();
  }
  public static RequestProcessorThreadPool getInstance(){
   return instance;
  }
 }
 public static RequestProcessorThreadPool getInstance(){
  return Singleton.getInstance();
 }
 
 public static void init(){
  getInstance();
 }
}

请求处理线程


package com.shux.inventory.thread;
 
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
 
import com.shux.inventory.request.InventoryUpdateDBRequest;
import com.shux.inventory.request.Request;
import com.shux.inventory.request.RequestQueue;
 

public class RequestProcessorThread implements Callable<Boolean>{
 private ArrayBlockingQueue<Request> queue;
 public RequestProcessorThread(ArrayBlockingQueue<Request> queue){
  this.queue = queue;
 }
 @Override
 public Boolean call() throws Exception {
  Request request = queue.take();
  Map<Integer,Boolean> flagMap = RequestQueue.getInstance().getFlagMap();
  //不需要强制刷新的时候,查询请求去重处理
   if (!request.isForceFefresh()){
    if (request instanceof InventoryUpdateDBRequest) {//如果是更新请求,那就置为false
     flagMap.put(request.getProductId(), true);
    } else {
     Boolean flag = flagMap.get(request.getProductId());
     
     if ( flag == null) {
      flagMap.put(request.getProductId(), false);
     }
     
     if ( flag != null && flag) {
      flagMap.put(request.getProductId(), false);
     }
     
     if (flag != null && !flag) {
      flagMap.put(request.getProductId(), false);
      return true;
     } 
    }
   }
   request.process();
  return true;
 } 
}

请求队列


package com.shux.inventory.request;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
 

public class RequestQueue {
 private List<ArrayBlockingQueue<Request>> queues = new ArrayList<>();
 
 private Map<Integer,Boolean> flagMap = new ConcurrentHashMap<>();
 private RequestQueue(){
  
 }
 private static class Singleton{
  private static RequestQueue queue;
  static{
   queue = new RequestQueue();
  }
  public static RequestQueue getInstance() {
   return queue;
  }
 }
 
 public static RequestQueue getInstance(){
  return Singleton.getInstance();
 }
 public void addQueue(ArrayBlockingQueue<Request> queue) {
  queues.add(queue);
 }
 
 public int getQueueSize(){
  return queues.size();
 }
 public ArrayBlockingQueue<Request> getQueueByIndex(int index) {
  return queues.get(index);
 }
 
 public Map<Integer,Boolean> getFlagMap() {
  return this.flagMap;
 }
}

spring 启动初始化线程池类 


package com.shux.inventory.listener;
 
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
 
import com.shux.inventory.thread.RequestProcessorThreadPool;
 

public class InitListener implements ApplicationListener<ContextRefreshedEvent>{
 
 @Override
 public void onApplicationEvent(ContextRefreshedEvent event) {
  // TODO Auto-generated method stub
  if(event.getApplicationContext().getParent() != null){
   return;
  }
  RequestProcessorThreadPool.init();
 }
}

异步处理请求接口


package com.shux.inventory.biz; 
import com.shux.inventory.request.Request;
 

public interface IRequestAsyncProcessBiz {
 void process(Request request);
}

异步处理请求接口实现


package com.shux.inventory.biz.impl;
 
import java.util.concurrent.ArrayBlockingQueue;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
 
import com.shux.inventory.biz.IRequestAsyncProcessBiz;
import com.shux.inventory.request.Request;
import com.shux.inventory.request.RequestQueue;
 
 

@Service("requestAsyncProcessService")
public class RequestAsyncProcessBizImpl implements IRequestAsyncProcessBiz {
 private Logger logger = LoggerFactory.getLogger(getClass());
 @Override
 public void process(Request request) {
  // 做请求的路由,根据productId路由到对应的队列
  ArrayBlockingQueue<Request> queue = getQueueByProductId(request.getProductId());
  try {
   queue.put(request);
  } catch (InterruptedException e) {
   logger.error("产品ID{}加入队列失败",request.getProductId(),e);
  }
 }
 
 private ArrayBlockingQueue<Request> getQueueByProductId(Integer productId) {
  RequestQueue requestQueue = RequestQueue.getInstance();
  String key = String.valueOf(productId);
  int hashcode;
  int hash = (key == null) ? 0 : (hashcode = key.hashCode())^(hashcode >>> 16);
  //对hashcode取摸
  int index = (requestQueue.getQueueSize()-1) & hash;
  return requestQueue.getQueueByIndex(index);
 }
}

package com.shux.inventory.biz.impl; 
import javax.annotation.Resource;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.shux.inventory.biz.InventoryProductBiz;
import com.shux.inventory.entity.InventoryProduct;
import com.shux.inventory.mapper.InventoryProductMapper;
import com.shux.redis.biz.IRedisBiz;
 

@Service("inventoryProductBiz")
public class InventoryProductBizImpl implements InventoryProductBiz {
 private @Autowired IRedisBiz<InventoryProduct> redisBiz;
 private @Resource InventoryProductMapper mapper;
 @Override
 public void updateInventoryProduct(InventoryProduct inventoryProduct) {
  // TODO Auto-generated method stub
  mapper.updateInventoryProduct(inventoryProduct);
 }
 
 @Override
 public InventoryProduct loadInventoryProductByProductId(Integer productId) {
  // TODO Auto-generated method stub
  return mapper.loadInventoryProductByProductId(productId);
 }
 
 @Override
 public void setInventoryProductCache(InventoryProduct inventoryProduct) {
  redisBiz.set("inventoryProduct:"+inventoryProduct.getProductId(), inventoryProduct);
  
 }
 
 @Override
 public void removeInventoryProductCache(Integer productId) {
  redisBiz.delete("inventoryProduct:"+productId);
  
 }
 
 @Override
 public InventoryProduct loadInventoryProductCache(Integer productId) {
  // TODO Auto-generated method stub
  return redisBiz.get("inventoryProduct:"+productId);
 }
}

数据更新请求controller


package com.shux.inventory.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
import com.shux.inventory.biz.IRequestAsyncProcessBiz;
import com.shux.inventory.biz.InventoryProductBiz;
import com.shux.inventory.entity.InventoryProduct;
import com.shux.inventory.request.InventoryUpdateDBRequest;
import com.shux.inventory.request.Request;
import com.shux.utils.other.Response;
 

@Controller("/inventory")
public class InventoryUpdateDBController {
 private @Autowired InventoryProductBiz inventoryProductBiz;
 private @Autowired IRequestAsyncProcessBiz requestAsyncProcessBiz;
 @RequestMapping("/updateDBInventoryProduct")
 @ResponseBody
 public Response updateDBInventoryProduct(InventoryProduct inventoryProduct){
  Request request = new InventoryUpdateDBRequest(inventoryProduct,inventoryProductBiz);
  requestAsyncProcessBiz.process(request);
  return new Response(Response.SUCCESS,"更新成功");
 }
}

数据查询请求controller


package com.shux.inventory.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
import com.shux.inventory.biz.IRequestAsyncProcessBiz;
import com.shux.inventory.biz.InventoryProductBiz;
import com.shux.inventory.entity.InventoryProduct;
import com.shux.inventory.request.InventoryQueryCacheRequest;
import com.shux.inventory.request.Request;
 

@Controller("/inventory")
public class InventoryQueryCacheController {
 private @Autowired InventoryProductBiz inventoryProductBiz;
 private @Autowired IRequestAsyncProcessBiz requestAsyncProcessBiz;
 @RequestMapping("/queryInventoryProduct")
 public InventoryProduct queryInventoryProduct(Integer productId) {
   Request request = new InventoryQueryCacheRequest(productId,inventoryProductBiz,false);
   requestAsyncProcessBiz.process(request);//加入到队列中
   long startTime = System.currentTimeMillis();
   long allTime = 0L;
   long endTime = 0L;
   InventoryProduct inventoryProduct = null;
   while (true) {
    if (allTime > 200){//如果超过了200ms,那就直接退出,然后从数据库中查询
     break;
    }
    try {
     inventoryProduct = inventoryProductBiz.loadInventoryProductCache(productId);
     if (inventoryProduct != null) {
      return inventoryProduct;
     } else {
      Thread.sleep(20);//如果查询不到就等20毫秒
     } 
     endTime = System.currentTimeMillis();
     allTime = endTime - startTime;
    } catch (Exception e) {
    } 
   }
   
   inventoryProduct = inventoryProductBiz.loadInventoryProductByProductId(productId);
   if (inventoryProduct != null) {
    Request forcRrequest = new InventoryQueryCacheRequest(productId,inventoryProductBiz,true);
    requestAsyncProcessBiz.process(forcRrequest);//这个时候需要强制刷新数据库,使缓存中有数据
    return inventoryProduct;
   }
   return null;
   
  }
}

到此这篇关于详解redis缓存与数据库一致性问题解决的文章就介绍到这了,更多相关redis缓存与数据库一致性内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解redis缓存与数据库一致性问题解决

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

redis缓存和数据库一致性问题如何解决

在使用Redis缓存时,常常会遇到与数据库一致性的问题。当数据发生变更时,需要保证Redis缓存与数据库的数据保持一致。以下是几种常见的解决方案:1. 缓存穿透:在查询缓存时,如果缓存中不存在对应的数据,会直接查询数据库。但是当数据库中也不
2023-08-24

Redis缓存和数据库的数据一致性的问题解决

目录前言一、谈谈一致性二、 情景分析2.1 针对读场景2.2 针对写场景三、同步策略3.1 先更新缓存,再更新数据库3.2 先更新数据库,再更新缓存3.3 先删除缓存,后更新数据库3.4 先更新数据库,后删除缓存四、解决办法4.1 双写一致
Redis缓存和数据库的数据一致性的问题解决
2024-09-20

如何解决 Java @Cacheable 缓存与数据库一致性问题?(Java @Cacheable缓存与数据库一致性解决方案)

在Java开发中,@Cacheable注解常用于缓存数据,以提高系统的性能。然而,当缓存与数据库的数据不一致时,就会出现问题。本文将介绍Java@Cacheable缓存与数据库一致性的解决方案。一、@Cacheable注解的基本原理
如何解决 Java @Cacheable 缓存与数据库一致性问题?(Java @Cacheable缓存与数据库一致性解决方案)
Java2024-12-15

redis数据库一致性问题解决

redis数据库通过以下机制解决数据一致性问题:主从复制:主服务器将写操作同步复制到从服务器。redis sentinel:监控redis服务器并执行故障转移和故障恢复,保持数据库可用性和数据一致性。redis cluster:使用一致性哈
redis数据库一致性问题解决
2024-04-19

一文讲透数据库与 Redis 缓存一致性问题

所谓缓存,实际上就是用空间换时间,准确地说是用更高速的空间来换时间,从而整体上提升读的性能。

redis缓存一致性问题怎么解决

在使用 Redis 缓存时,可能会遇到缓存一致性问题,即缓存中的数据与数据库中的数据不一致。以下是一些常见的解决方法:缓存更新策略:在数据更新时,同时更新缓存。可以使用“更新即删除”或“更新即写入”策略。具体来说,当数据更新时,先更新数据库
2023-10-26

Redis与缓存一致性问题

当数据在原始数据源(如数据库)中发生变化时,如何确保缓存中的数据与数据源保持一致,是开发者需要关注的关键问题。

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

场景一一般来说,只要你用到了缓存,不管是Redis还是memcache,就可能会涉及到数据库缓存与数据的一致性问题,这里我们以Redis为例。我们该如何保证Redis与数据库的一致性呢? So easy:更新的时候,先更新数据库,然后再删除缓存。读的时候,先读
redis系列之数据库与缓存数据一致性解决方案
2020-08-13

redis缓存和数据库不一致解决

解决 redis 缓存和数据库不一致问题需要:使用数据一致性协议(如 redis 事务或分布式锁)防止并发写入导致不一致。采用缓存失效策略(如过期时间或更新触发器)确保缓存及时更新。优化缓存架构(如分区缓存或二级缓存)减少对数据库的直接访问
redis缓存和数据库不一致解决
2024-04-19

缓存和数据库一致性问题

1、想要提高应用的性能,可以引入「缓存」来解决2、引入缓存后,需要考虑缓存和数据库一致性问题,可选的方案有:「更新数据库 + 更新缓存」、「更新数据库 + 删除缓存」3、更新数据库 + 更新缓存方案,在「并发」场景下无法保证缓存和数据一致性,且存在「缓存资源浪
缓存和数据库一致性问题
2021-04-23

redis缓存与数据库双写不一致如何解决

为解决 redis 缓存和数据库双写不一致问题,可采用以下方法:使用队列:将数据更新请求放入队列,确保先写入数据库再更新缓存。使用乐观锁:更新时检查数据是否被修改,若已被修改则取消更新并通知重试。使用事件机制:当数据库更新时触发事件通知应用
redis缓存与数据库双写不一致如何解决
2024-04-20

图解Redis,Redis更新策略、缓存一致性问题

Redis的集群主节点数量一般不会超过1000个。集群中节点越多,心跳包的消息体内的数据就越多,如果节点过多,也会造成网络拥堵。因此Redis的作者Salvatore Sanfilippo不建议Redis Cluster的节点超过1000个

缓存系列:缓存一致性问题的解决思路

今天我们来聊聊缓存一致性问题,对于这个问题,不管在工作中还是面试中,都是一个非常常见的问题。
缓存一致性2024-12-01

保证缓存和数据库的数据一致性详解

在实际开发过程中,缓存的使用频率是非常高的,只要使用缓存和数据库存储,就难免会出现双写时数据一致性的问题,本文主要介绍了如何保证缓存和数据库的数据一致性,需要的小伙伴可以参考阅读
2023-05-15

一碰就头大的高并发缓存与数据库双写一致性问题,立马解决

一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求缓存 + 数据库必须保持一致性的话,最好不要做这个方案。即:读请求和写请求串行化,串到一个内存队列里去。

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录