首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >浅谈Retrofit 源码

浅谈Retrofit 源码

作者头像
用户1337002
发布于 2018-03-09 02:24:59
发布于 2018-03-09 02:24:59
1K00
代码可运行
举报
文章被收录于专栏:猿份到猿份到
运行总次数:0
代码可运行

现在的项目界面都是按iOS的风格,虽然界面无法发挥出我们大Android的风格,网络框架动不动就是mvp+ okhttp +retrofit+rxjava,mvvm+databind的模式,但是网络框架什么的,我们也可以与时俱进的嘛。

今天就在此聊聊retrofit的相关内容,也可以算是给自己的这几周没有更新文章的一个交代吧。

首先关于使用方法,我们可以从官网上了解。

官网地址(http://square.github.io/retrofit/)

网络基本请求方法(官网)

Introduction

Retrofit turns your HTTP API into a Java interface.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

The Retrofit class generates an implementation of the GitHubService interface.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Call<List<Repo>> repos = service.listRepos("octocat");

Use annotations to describe the HTTP request:

  • URL parameter replacement and query parameter support
  • Object conversion to request body (e.g., JSON, protocol buffers)
  • Multipart request body and file upload

根据上述官网描述,简单总结为以下的几点:

1、创建一个接口来进行HTTP请求;

2、通过new Retrofit.Builde的builder构建出一个retrofit对象;

3、使用retrofit.create获取该网络接口请求实例;

4、由请求接口实例返回一个Call对象;

5、进行网络(同步、异步)请求

下面分析上述的几点:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final class Retrofit {
  private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();

  private final okhttp3.Call.Factory callFactory;
  private final HttpUrl baseUrl;
  private final List<Converter.Factory> converterFactories;
  private final List<CallAdapter.Factory> adapterFactories;
  private final Executor callbackExecutor;
  private final boolean validateEagerly;

  Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
      Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory;
    this.baseUrl = baseUrl;
    this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
    this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;

首先通过Retorfit源码我们可以了解到它底层网络框架是基于okhttp实现的,整体的实现过程就是客户端通过给retrofit发送网络请求,构建一个Request请求,如队列,然后通过Excutor执行,进入Looper循环查找该出该request并执行,执行完成通过解析返回call对象然后交给客户端做相关处理。图解如下:

下面分析下总结的几点:

1、创建一个接口来进行HTTP请求,这就是写一个API接口类来存放所需要实现的服务端接口类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

new Retrofit.Builde构建对象,创建Retrofit实例,我们现在通过源码分析下Builder到底做了什么?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

Builder源码部分:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static final class Builder {
  private Platform platform;
  private okhttp3.Call.Factory callFactory;
  private HttpUrl baseUrl;
  private List<Converter.Factory> converterFactories = new ArrayList<>();
  private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
  private Executor callbackExecutor;
  private boolean validateEagerly;

  Builder(Platform platform) {
    this.platform = platform;
    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters that consume all types.
    converterFactories.add(new BuiltInConverters());
  }

  public Builder() {
    this(Platform.get());
  }

  public Builder client(OkHttpClient client) {
    return callFactory(checkNotNull(client, "client == null"));
  }

  public Builder callFactory(okhttp3.Call.Factory factory) {
    this.callFactory = checkNotNull(factory, "factory == null");
    return this;
  }

  public Builder baseUrl(String baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    HttpUrl httpUrl = HttpUrl.parse(baseUrl);
    if (httpUrl == null) {
      throw new IllegalArgumentException("Illegal URL: " + baseUrl);
    }
    return baseUrl(httpUrl);
  }


  public Builder addConverterFactory(Converter.Factory factory) {
    converterFactories.add(checkNotNull(factory, "factory == null"));
    return this;
  }

  public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
    adapterFactories.add(checkNotNull(factory, "factory == null"));
    return this;
  }

  public Builder callbackExecutor(Executor executor) {
    this.callbackExecutor = checkNotNull(executor, "executor == null");
    return this;
  }

  public Builder validateEagerly(boolean validateEagerly) {
    this.validateEagerly = validateEagerly;
    return this;
  }


  public Retrofit build() {
    if (baseUrl == null) {
      throw new IllegalStateException("Base URL required.");
    }

    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
      callFactory = new OkHttpClient();
    }

    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
      callbackExecutor = platform.defaultCallbackExecutor();
    }

    // Make a defensive copy of the adapters and add the default Call adapter.
    List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
    adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

    // Make a defensive copy of the converters.
    List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

    return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
        callbackExecutor, validateEagerly);
  }
}

在源码中的builder方法我们可以看到除了baseurl是必须的参数,其他配置参数都可以为空(选配参数)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 if (baseUrl == null) {
      throw new IllegalStateException("Base URL required.");
    }

简单说下

Converter<F, T>:数据转换器,用来将HTTP请求返回结果由F类型转换为T类型,或者将HTTP请求类型F转换为T,也就是我们通过添加的转换器

addConverterFactory方法,可以将call对象的数据返回成我们所需要的数据格式。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.addConverterFactory(RDConverterFactory.create())

CallAdapter<R, T>:请求适配器,用于将retrofit2.Call<R>网络请求类型转换为T类型,简单的说就是我们网络请求参数封装的格式。不过到目前为止我还没有用这个

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.build()

之前看下builder构造方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Builder(Platform platform) {
  this.platform = platform;
  // Add the built-in converter factory first. This prevents overriding its behavior but also
  // ensures correct behavior when using converters that consume all types.
  converterFactories.add(new BuiltInConverters());
}

public Builder() {
  this(Platform.get());
}

当我们调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

首先进去Builder构造方法,在该方法中我们的converterFactories添加了一个

BuiltInConverters对象,我们知道converterFactories是处理HTTP返回结果进行

类型转换或者将HTTP请求体进行类型转换。现在看下源码中BuiltInConverters式怎样进行转换的?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
final class BuiltInConverters extends Converter.Factory {
  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    if (type == ResponseBody.class) {
      if (Utils.isAnnotationPresent(annotations, Streaming.class)) {
        return StreamingResponseBodyConverter.INSTANCE;
      }
      return BufferingResponseBodyConverter.INSTANCE;
    }
    if (type == Void.class) {
      return VoidResponseBodyConverter.INSTANCE;
    }
    return null;
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
      return RequestBodyConverter.INSTANCE;
    }
    return null;
  }

  @Override public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    if (type == String.class) {
      return StringConverter.INSTANCE;
    }
    return null;
  }

  static final class StringConverter implements Converter<String, String> {
    static final StringConverter INSTANCE = new StringConverter();

    @Override public String convert(String value) throws IOException {
      return value;
    }
  }

  static final class VoidResponseBodyConverter implements Converter<ResponseBody, Void> {
    static final VoidResponseBodyConverter INSTANCE = new VoidResponseBodyConverter();

    @Override public Void convert(ResponseBody value) throws IOException {
      value.close();
      return null;
    }
  }

  static final class RequestBodyConverter implements Converter<RequestBody, RequestBody> {
    static final RequestBodyConverter INSTANCE = new RequestBodyConverter();

    @Override public RequestBody convert(RequestBody value) throws IOException {
      return value;
    }
  }

  static final class StreamingResponseBodyConverter
      implements Converter<ResponseBody, ResponseBody> {
    static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();

    @Override public ResponseBody convert(ResponseBody value) throws IOException {
      return value;
    }
  }

  static final class BufferingResponseBodyConverter
      implements Converter<ResponseBody, ResponseBody> {
    static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();

    @Override public ResponseBody convert(ResponseBody value) throws IOException {
      try {
        // Buffer the entire body to avoid future I/O.
        return Utils.buffer(value);
      } finally {
        value.close();
      }
    }
  }

  static final class ToStringConverter implements Converter<Object, String> {
    static final ToStringConverter INSTANCE = new ToStringConverter();

    @Override public String convert(Object value) {
      return value.toString();
    }
  }
}

返回结果转换的时候,有3种情况:

1、如果返回类型是ResponseBody类型,那么可能返回的是:StreamingResponseBodyConverter.INSTANCE或者BufferingResponseBodyConverter.INSTANCE,再跟踪进去这两个类里面的convert函数:

@Override public ResponseBody convert(ResponseBody value){···}

发现传入的是ResponseBody,返回的也是ResponseBody(BufferingResponseBodyConverter里面有进行特殊处理,主要是为了去除future I/O),所以我们可以认为BuiltInConverters对HTTP返回结果没有进行转换处理。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static final class StreamingResponseBodyConverter
    implements Converter<ResponseBody, ResponseBody> {
  static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();

  @Override public ResponseBody convert(ResponseBody value) throws IOException {
    return value;
  }
}

static final class BufferingResponseBodyConverter
    implements Converter<ResponseBody, ResponseBody> {
  static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();

  @Override public ResponseBody convert(ResponseBody value) throws IOException {
    try {
      // Buffer the entire body to avoid future I/O.
      return Utils.buffer(value);
    } finally {
      value.close();
    }
  }
}

2、如果返回类型是Void类型,那么最终就将ResponseBody资源关闭后,直接返回null。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static final class VoidResponseBodyConverter implements Converter<ResponseBody, Void> {
  static final VoidResponseBodyConverter INSTANCE = new VoidResponseBodyConverter();

  @Override public Void convert(ResponseBody value) throws IOException {
    value.close();
    return null;
  }
}

3、如果返回类型不是ResponseBody和Void类型,直接返回null。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
    Retrofit retrofit) {
  if (type == ResponseBody.class) {
    if (Utils.isAnnotationPresent(annotations, Streaming.class)) {
      return StreamingResponseBodyConverter.INSTANCE;
    }
    return BufferingResponseBodyConverter.INSTANCE;
  }
  if (type == Void.class) {
    return VoidResponseBodyConverter.INSTANCE;
  }
  return null;
}

同理可知

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
    Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
  if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
    return RequestBodyConverter.INSTANCE;
  }
  return null;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static final class RequestBodyConverter implements Converter<RequestBody, RequestBody> {
  static final RequestBodyConverter INSTANCE = new RequestBodyConverter();

  @Override public RequestBody convert(RequestBody value) throws IOException {
    return value;
  }
}

如果是RequestBody就不转换,直接返回,如果不是RequestBody则返回null。

从结构图来看,BuiltInConverter只转换ResponseBody和RequestBody,其他的都不进行转换。

分析了Builder的构造后终于可以进入到builder构造方法构建出一个Retrofit对象了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Create the {@link Retrofit} instance using the configured values.
 * <p>
 * Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
 * OkHttpClient} will be created and used.
 */
public Retrofit build() {
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }
  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
       //callFactory默认为OkHttpClient
       callFactory = new OkHttpClient();
  }
  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }
  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
}

从源码中我们知道callFactory默认是OKhttpClient,所以说我们除了需要传递一个baseUrl参数,其他可选配置参数都会为我们提供一个默认值。

好了build我们就先了解这么多了,至于深入的就需要读者自己去研究了。

现在我们来说下create方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
GitHubService service = retrofit.create(GitHubService.class);
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public <T> T create(final Class<T> service) {
  Utils.validateServiceInterface(service);
  if (validateEagerly) {
    eagerlyValidateMethods(service);
  }
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
        private final Platform platform = Platform.get();

        @Override public Object invoke(Object proxy, Method method, Object... args)
            throws Throwable {
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
          if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void eagerlyValidateMethods(Class<?> service) {
  Platform platform = Platform.get();
  for (Method method : service.getDeclaredMethods()) {
    if (!platform.isDefaultMethod(method)) {
      loadServiceMethod(method);
    }
  }
}

ServiceMethod loadServiceMethod(Method method) {
  ServiceMethod result;
  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      result = new ServiceMethod.Builder(this, method).build();
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

我们可以看到create方法返回的是一个 (T) Proxy.newProxyInstance产生的一个动态代理对象,通过InvocationHandler拦截进入到invoke函数中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                      InvocationHandler invocationHandler)
        throws IllegalArgumentException {

    if (invocationHandler == null) {
        throw new NullPointerException("invocationHandler == null");
    }
    Exception cause;
    try {
        return getProxyClass(loader, interfaces)
                .getConstructor(InvocationHandler.class)
                .newInstance(invocationHandler);
    } catch (NoSuchMethodException e) {
        cause = e;
    } catch (IllegalAccessException e) {
        cause = e;
    } catch (InstantiationException e) {
        cause = e;
    } catch (InvocationTargetException e) {
        cause = e;
    }
    AssertionError error = new AssertionError();
    error.initCause(cause);
    throw error;
}

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

最重要的就是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);

主要做的就是对接口方法注解进行解析,最终生成一个Request。关于create方法先说到这。

最后通过callInit.enqueue方法进行真正发送网络请求,通过OkHttp代码片段可以

可以看到,请求成功的时候,会调用parseResponse得到一个我们需要的Response<T>类型对象,在这个方法中可以将我们的数据转换成我们自己需要的数据格式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//OkHttpCall.enqueue片段代码
call.enqueue(new okhttp3.Callback()

{
    @Override

    public void onResponse (okhttp3.Call call, okhttp3.Response rawResponse)throws IOException {
    Response<T> response;
    try {
        response = parseResponse(rawResponse);
    } catch (Throwable e) {
        callFailure(e);
        return;
    }
    callSuccess(response);
}
    @Override public void onFailure (okhttp3.Call call, IOException e){
    try {
        callback.onFailure(OkHttpCall.this, e);
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

private void callFailure(Throwable e) {
    try {
        callback.onFailure(OkHttpCall.this, e);
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

private void callSuccess(Response<T> response) {
    try {
        callback.onResponse(OkHttpCall.this, response);
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

retrofit就到此结束了,其中的分析过程不是那么好,那么的详细完整,也算使用这个框架的部分心得了吧,如果你有更好的见解,欢迎探讨学习,期待你的回复。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-12-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿份到 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MySQL索引底层:B+树详解
当我们发现SQL执行很慢的时候,自然而然想到的就是加索引。对于范围查询,索引的底层结构就是B+树。今天我们一起来学习一下B+树哈~
捡田螺的小男孩
2021/03/15
8820
Mysql为什么最终用B+树做索引?
索引能极大的减少存储引擎需要扫描的数据量 索引可以把随机IO变成顺序IO(索引指向(左小右大)) 索引可以帮助我们在进行分组、排序等操作时,避免使用临时表
名字是乱打的
2021/12/22
1.3K0
Mysql为什么最终用B+树做索引?
从B 树、B+ 树、B* 树谈到R 树
说明:本文从B树开始谈起,然后论述B+树、B*树,最后谈到R 树。其中B树、B+树及B*树部分由weedge完成,R 树部分由Frankie完成,全文最终由July统稿修订完成。
bear_fish
2018/09/14
2.4K0
从B 树、B+ 树、B* 树谈到R 树
It's Design——为什么MySQL使用B+树?
相信每一个后台开发工程师在面试过程中,都曾经被问到过“MySQL的默认存储引擎是什么?MySQL索引是什么数据结构?”这样的问题。相信准备充分(熟读八股文)的大家都能很容易的回答出“MySQL的默认存储引擎是InnoDB,MySQL索引使用的是B+树。”这样的答案。但是为什么当初写MySQL的程序员大叔要这样子来设计呢?
yuann
2021/04/15
9640
It's Design——为什么MySQL使用B+树?
讲透学烂二叉树(二):图中树的定义&各类型树的特征分析
日常中我们见到的二叉树应用有,Java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,以及B-Tree,B+-Tree在文件系统,都是通过红黑树去实现的。虽然之前写过《再谈堆排序:堆排序算法流程步骤透解—最大堆构建原理》但是二叉树的基本性质,对我来说,从入门到放弃是搞了好几回。
周陆军
2020/06/06
1.7K0
图解:什么是B-树、B+树、B*树
6.非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
你好戴先生
2020/09/03
11.2K4
为什么MySQL数据库索引选择使用B+树?
我们在MySQL中的数据一般是放在磁盘中的,读取数据的时候肯定会有访问磁盘的操作,磁盘中有两个机械运动的部分,分别是盘片旋转和磁臂移动。盘片旋转就是我们市面上所提到的多少转每分钟,而磁盘移动则是在盘片旋转到指定位置以后,移动磁臂后开始进行数据的读写。那么这就存在一个定位到磁盘中的块的过程,而定位是磁盘的存取中花费时间比较大的一块,毕竟机械运动花费的时候要远远大于电子运动的时间。当大规模数据存储到磁盘中的时候,显然定位是一个非常花费时间的过程,但是我们可以通过B树进行优化,提高磁盘读取时定位的效率。
用户3467126
2019/08/05
1.6K0
为什么MySQL数据库索引选择使用B+树?
mysql 中的innoDB 引擎的B+树索引
在优化慢接口的时候,遇到一个问题,在通过索引查询数据库表的时候根据时间区间去扫描表的时候,开始时间时表扫描的其实位置吗?或者说根据时间日期B+索引能一次性定位到具体的时间位置吗?是的不能。那为什么不能呢? 接下来我们来看看b+树索引的底层数据结构。
袁新栋-jeff.yuan
2020/08/26
9940
mysql 中的innoDB 引擎的B+树索引
理解 B+ 树算法
本文介绍了B+树的基本概念、特点、结构以及其在数据库和文件系统中的应用。B+树通过平衡数据存储和查询效率,在插入、删除和查询操作中表现出较好的性能。主要应用在数据库索引和文件系统中,如NTFS、ReiserFS和InnoDB存储引擎等。
serena
2017/10/20
2.8K0
理解 B+ 树算法
红黑树、B树、B+树
现代操作系统都使用虚拟内存来印射到物理内存,内存大小有限且价格昂贵,所以数据的持久化是在磁盘上。虚拟内存、物理内存、磁盘都使用页作为内存读取的最小单位。一般一页为4KB(8个扇区,每个扇区512B,8*512B=4KB)。
派大星在吗
2021/12/05
7660
Mysql InnoDB 为啥选择B+树索引 转
Mysql数据库中的常见索引有多种方式,例如Hash索引,B-树索引,B+树索引,但是为啥mysql中默认是采用B+树索引索引呢?下面对这三种索引学习总结一下。B+树到底有啥优势? B-树
双面人
2019/04/10
7180
InnoDB为什么要选择B+树来存储数据
关于InnoDB索引,我们可能知道InnDB索引是用B+树实现的,而B+树就是一种能优化查询速度的数据结构。但我们又没想过这样一个问题,能优化查询速度的数据结构有很多,为什么InnoDB要采用B+树?
weylan
2021/11/09
1.9K0
MySQL索引为什么要用B+树实现?
在从一堆数据中查找指定的数据时,我们常用的数据结构是哈希表和二叉查找树,表本质上就是一堆数据的集合,所以MySQL数据库用了B+树和哈希表来实现索引
Java识堂
2019/08/13
6590
MySQL索引底层为什么用B+树?看完这篇文章,轻松应对面试
面试官: 你知道MySQL索引底层数据结构为啥用B+树?而不用B树、红黑树或者普通二叉树?
一灯架构
2022/09/26
8270
MySQL索引底层为什么用B+树?看完这篇文章,轻松应对面试
MySQL数据库索引选择为什么使用B+树而不是跳表?
在进一步分析为什么MySQL数据库索引选择使用B+树之前,我相信很多小伙伴对数据结构中的树还是有些许模糊的,因此我们由浅入深一步步探讨树的演进过程,在一步步引出B树以及为什么MySQL数据库索引选择使用B+树!
小冷coding
2023/05/24
9640
MySQL数据库索引选择为什么使用B+树而不是跳表?
MySql进阶索引篇01——深度讲解索引的数据结构:B+树
索引是存储引擎中一种用于快速找到数据的存储结构,他就像《新华字典》的目录,可以使我们查每个字的速度大大提升。
半旧518
2022/10/26
2.6K0
MySql进阶索引篇01——深度讲解索引的数据结构:B+树
【数据结构】B树,B+树,B*树
1. 在内存中搜索效率高的数据结构有AVL树,红黑树,哈希表等,但这是在内存中,如果在外部存储设备中呢?比如数据量非常的大,以致于内存中无法存的下这么多数据,从而只能将大部分的数据存储到磁盘上,那如果要在磁盘上进行查找呢?我们还用内查找效率高的这些数据结构吗? 由于大部分数据都在磁盘上,所以如果要查找某个数据,则只能先通过文件读取,将数据读取到内存中,然后在内存里面进行该数据的检索,如果存储结构是二叉搜索树,AVL树,红黑树,那树的高度是会比较大的,假设有10亿个数据,那么高度就将近30层,如果每层都做一次文件读取,那效率会非常的低,因为磁盘的访问速度和内存相比差距很大,算法导论上给出的数据,两者的访问速度相差大约10w倍,而且30层的高度,那总体下来的运行时间就是内存访问速度的300w倍,那search算法的效率瓶颈就全部压到了磁盘读取上,所以内查找优秀的这几个数据结构也不适用,有人说那哈希表呢?哈希表其实也不行,同时哈希表本身还有表空间的占用,数据量过大的情况下,内存用哈希表也是存不下的,同时哈希冲突厉害的情况下,还需要用红黑树来代替链表作哈希桶,高度依旧是很高的,所以内查找的这些数据结构都不适用于磁盘上数据的查找,此时就有大佬想到了新的数据结构,B树。
举杯邀明月
2024/02/29
3550
【数据结构】B树,B+树,B*树
MySQL和B树的不知道的那些事
若左子树不空,则左子树上所有节点的值均小于它的根节点的值 若右子树不空,则右子树上所有节点的值均大于它的根节点的值 它的左、右子树也分别为二叉排序数(递归定义)
终有救赎
2023/12/14
3080
MySQL和B树的不知道的那些事
MySQL为什么要使用B+树索引
搞懂这个问题之前,我们首先来看一下MySQL表的存储结构,再分别对比二叉树、多叉树、B树和B+树的区别就都懂了。
BUG弄潮儿
2021/02/03
5990
MySQL为什么要使用B+树索引
MySQL为什么选择B+树存储索引
为什么加索引? 如果上面的表,我们执行SQL语句 select * from table where Col2=89; 这样就会造成全表扫描,从第一行读取到倒数第二行,然后拿到这个89这个对应的值的位
码农编程进阶笔记
2022/06/29
6220
MySQL为什么选择B+树存储索引
相关推荐
MySQL索引底层:B+树详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档