天天都在CRUD,你知道数据库如何工作的吗?

news2024/12/24 9:03:52

作为一个天天都在CRUD的程序员,你有没有想过,数据库是如何工作的?

今天,让我们从一个最最最简单的模型开始,揭开数据库神秘的一角。

对我们使用者而言,数据库就像是一个黑盒子,你可以往它里面写入数据,也可以从它里面读出数据。

让我们暂时抛却SQL、网络连接、数据库等等概念,就来看这个最基本的需求:如果我们来实现一个可以对外提供读写数据的黑盒子,该怎么做?

假设我们的黑盒子很简单,里面只有一张表:user_info,用来存储用户信息。

表里面也很简单,只有三个字段,分别记录用户的ID、姓名和手机号。

user_id(uint32)
name(char[8])
phone(char[20])

我们可以用一个简单的结构体(或者一个class)来表示一条数据:

struct DataRecord{
  uint32 user_id;
  char name[8];
  char phone[20];
};

user_id是一个uint32类型,占4个字节,name占8个字节,phone占20个字节,加起来一条数据总共占32个字节。

我们选择用一个文件来存储这些数据,存储非常简单,只需要一条一条地码在一起就行了,就像这样:

数据存储方式有了,接下来就是如何来读写了,我们来提供两个函数,分别来插入(insert)和查询(select)数据:

// 伪代码
void insert(Table* table, DataRecord record) {
  fp = open(table->file_name);
  fseek(fp, FILE_END);
  write(fp, sizeof(DataRecord));
  close(fp);
}

插入很简单,直接打开表对应的数据文件,然后把文件指针移动到文件尾部,接着追加数据,最后关闭文件,大功告成。这和把大象关进冰箱的步骤是一样一样的。

接下来是查询,我们提供一个可以通过id来查询用户的函数:

// 伪代码
DataRecord select(uint32 user_id) {

  DataRecord result;
  fp = open(table->file_name);
  
  while (1) {
    if (feof(fp)) 
      break;
      
    DataRecord record;
    read(fp, &record, sizeof(DataRecord));
    if (record.user_id == user_id) {
      result = record;
      break;
    }
  }
  
  close(fp);
  
  return result;
}

查询也很简单,我们打开文件,一条一条读取数据,然后比较用户id和给定的参数是否相同,直到找到符合条件的数据返回。

好了,以上,我们就实现了一个最最最基础的黑盒子:它里面有一张表,然后可以往里面写数据,从里面查数据。

现在来思考一下:

如果我们写了很多数据进去,比如几十万条,我们要查一个指定id的数据,要按照这样一条条比对,那不得等到猴年马月去了?

为什么要一条条比对呢?是因为我们的数据全都是一条一条码在一起的,完全没有顺序,所以要查询,只能这样一条条检查——全表扫描

如果我们的数据是有顺序的呢?

假如我们插入数据的时候,是按照id从小到大排列着,这样我们就能用二分法快速找到指定id的数据了。

看上面这张图,假设我们要查找id为9的数据,我们可以读取第一条数据的id是1,就知道id为9的数据肯定在它后面。然后再读取最后一条数据id是12,就知道id为9的数据肯定在它前面,然后选择中间的数据读取,如此二分查找,很快就能锁定目标,不用每条数据都读取了。

因为每条数据都是32个字节,所以可以非常方便定位任意一条数据的位置:第n条数据的位置在32*(n-1)偏移处。

通过改变数据的存储组织形式,我们可以把数据查找的时间复杂度从O(N)下降到O(LogN)。

但如此一来,查找是变快了,但插入就麻烦了。以前的时候,我们直接把数据塞到文件最后就拍拍屁股走人了。但现在不行了,我们得把数据按照顺序,插入到合适的位置。

最麻烦的是,我们的数据是一条一条挨个码在一起的,如果中间某个位置要插入数据,就得把那个位置及其以后的数据通通往后移动,这就涉及到大量的数据复制移动,开销非常大。

要是每来一条insert操作就数据大量迁徙,那不得累得半死?

(其实不止插入,删除数据delete也同样如此麻烦。)

就没有一种办法,可以同时插入快,查询也快吗?


仔细思考一下,前面我们把数据按顺序一条一条码在一起,查起来为什么快?

是因为做了排序以后,数据的存储位置有一条隐含的信息:如果id比我们要找的小,那我们要找的肯定在它后面;反之,如果id比我们要找的大,那我们要找的肯定在它前面。

之所以有这个规律,是因为我们按id的大小进行了排序存储。

那如果现在回到最开始的那种方式,不排序了,还是一条一条顺序来写入,就没有这个信息了。

但如果,我们在每条数据记录中增加一些额外的信息,用来指示id比它小的在哪里,id比它大的又在哪里,是不是就能顺着这些额外的信息“顺藤摸瓜”找到要找的数据呢?

或许,聪明的你已经看出来了,这是按照“二叉树”的形式在记录数据位置信息。

但实际上,二叉树也才两个分叉,如果数据量很大的话,这棵树就会很高很瘦。

而每一次走入一个分支,就对应着一次文件I/O,所以在实际使用中,不会使用二叉树,而是使用开了非常多个叉的树——B树或者B+树。

如果用B树或者B+树来将文件中的数据在逻辑上组织起来,要查找数据就会快得多。

用id来查找数据问题解决了,但如果要用name来查找又该怎么办呢?

想一想,如果另外有一个文件,记录了每个name和这个name对应的数据记录在文件中的偏移位置,就像这样:

user_id数据位置(偏移)
xuanyuan0
shuaidi31
april63
dibingfa95

有了这个东西,是不是就简单很多了?只需要在这里搜一遍,拿到数据的位置,然后打开文件,定位到对应的偏移,一下就读出来了。

我们可以另外准备一个文件,同样使用B树或者B+树的形式来组织上面表格中的对应关系。

聪明的你可能已经看出来了,这玩意儿其实就是索引

当然,实际中的数据库系统的索引实现或多或少有一些差别,但道理是通用的。

这里,再给大家推荐一个Github的开源项目,手把手教大家写一个最简单的数据库出来。

https://cstack.github.io/db_tutorial/

很多朋友简历上的项目经历,要么是XXX管理系统,要么是一个Web服务器,这些都太烂大街了,以后要是简历上来一个手写数据库系统,那绝对能让面试官眼前一亮。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/14431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

神策 SCRM 正式发布,打通企业全域数据,聚焦私域精细化运营

随着企业微信能力的不断延展和客户接受度的持续提升,越来越多的企业开始基于企业微信生态搭建私域流量池,通过高粘性的专属服务和沟通提升客户满意度和转化效率。在企微私域运营过程中,他们发现: 管理难:客户分散在每位…

【Nginx】初识与环境准备

文章目录Nginx简介Nginx的优点及功能特性Nginx常用功能模块及组成Nginx系统环境准备Nginx安装方式介绍Nginx目录结构分析Nginx服务器启停命令方式一:Nginx服务的信号控制方式二:Nginx的命令行控制Nginx服务操作的问题Nginx配置成系统服务可能会出现的问题Nginx命令配置到系统环…

酷开科技 | 强势出圈,酷开系统一举突破媒介价值纵深

从起势到成熟,从无序到理性,从单一到多维,如今的OTT市场,早已是一个全新的舞台,不仅OTT的功能与服务承载更加丰富,产业的竞争维度也更加多元。踔厉奋发的OTT行业,在新的时代背景和产业环境下&am…

关于Python自动化的就业真相

作为近几年来特别受欢迎的编程语言之一,Python一直以来被众多行业内人士寄予厚望,今后有可能替代老牌霸主Java,成为新一代编程语言榜首。 为什么懂技术的人必学Python? 与其他语言相比,Python中的语言语法特别简单&a…

ABBYY FineReader PDF2023版本有什么新特点?

ABBYY FineReader PDF Windows版更加智能的 PDF 解决方案在数字化工作场所转换、编辑、共享和协作处理数字化工作场所中的 PDF 和扫描件。 在阅读某些扫描得到的PDF文档时,我经常因为它不可编辑而烦恼,直到我遇到了ABBYY。打开ABBYY FineReader PDF软件…

【MySQL进阶】深入理解B+树索引底层原理

【MySQL进阶】深入理解B树索引底层原理 文章目录【MySQL进阶】深入理解B树索引底层原理一、前言——没有索引的查找1、在一个页中的查找2、在很多页中查找3、总结二、索引1、一个简单的索引方案2、InnoDB中的索引方案3、B 树4、聚簇索引5、二级索引6、回表7、联合索引三、InnoD…

vivado跑完发邮件

前言 vivado 综合实现要跑好长时间,耍会儿手机不过分吧。然而我不想时不时抬头看有没有跑完,于是产生了该脚本 一. QQ邮箱配置 参考:https://blog.csdn.net/qq_40608730/article/details/104904398?spm1001.2014.3001.5502 (注…

c++11特性(2)

目录 1.新增了两个默认成员函数 2.新增了几个关键字 3.可变参数模板 1.新增了两个默认成员函数 a.移动构造函数 b.移动赋值运算符重载 默认生成的前提条件:没有实现析构函数,拷贝构造,拷贝赋值重载中的任意一个。 为什么要实现移动的版…

三菱PLC单轴运动控制

1、什么是运动控制 ​ 运动控制,也可叫做电力拖动控制;它是自动化的一个分支,其动力源大部分都基于电动机。 也就是说,运动控制其实是基于电动机,实现物体对于角位移、速度、转矩等物理量改变的控制。 这里面&#xf…

Linux开发工具(2)——vim

文章目录多模式编辑器——vimvim的基本操作命令模式(Normal mode)插入模式(Insert mode)底行模式(Last line mode)vim的基本配置原理配置sudoers文件多模式编辑器——vim vim是一种多模式编辑器&#xff0c…

MYSQL语法一:创建表和查看表的所有列和所有数据库等

前言:接下里,我们即将开启mysql的旅行之路。首先是它的基础知识 1.mysql的基本知识点 mysql是客户端加服务器的模式。 客户端给服务器发送的数据称为请求request 服务器给客户端返回的数据称为响应response 客户端和服务器可以在不同主机上,…

轻松学习jQuery事件和动画

✅作者简介:热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏:前端开发者…

计算机网络-网络层详细讲解(持续更新中)

网络层概述 网络层的主要任务是实现网络互连,进而实现数据包在各网络之间的传输。 要实现网络层主要任务,需要解决以下主要问题: 网络层向运输层提供怎样的服务(“可靠传输”还是“不可靠传输‘)网络层寻址问题路由…

小啊呜产品读书笔记001:《邱岳的产品手记-04》第07+08讲 关于需求变更

小啊呜产品读书笔记001:《邱岳的产品手记-04》第0708讲 关于需求变更一、今日阅读计划二、泛读&知识摘录1、07讲 关于需求变更(上):需求背后的需求2、08讲 关于需求变更(下):化变更于无形三…

【Pytorch with fastai】第 14 章 :ResNet

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎 📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃 🎁欢迎各位→点赞…

自动化测试selenium在小公司的成功实践

前言 可能提到自动化测试selenium,大家都会想到用python语言来编写脚本。但我们选择了java语言,因为我相信大部分公司java程序员比python程序员多得多。而对于很多测试人员,并不能熟练使用编程语言,所以他们需要别人指导。与其使…

iOS 16.2 的7个惊人变化

看起来 iOS 16 正在不断扩展 你已经知道 iOS 16 和 iOS 16.1 有多么不可思议。所以我认为下一次更新已经没有多少了——iOS 16.2。 Apple 刚刚通过 iOS 16.2 Developer Beta 1 消除了这些信念。 Developer Beta 2 中还有 7 个变化。相信我,它们也很棒。 #1、锁定屏幕的药物…

qemu创建linux虚拟机(亲测有效,virt-manger方式)

1,网桥的搭建 Bridge方式原理 Bridge方式即虚拟网桥的网络连接方式,是客户机和子网里面的机器能够互相通信。可以使虚拟机成为网络中具有独立IP的主机。 桥接网络(也叫物理设备共享)被用作把一个物理设备复制到一台虚拟机。网桥多…

Chrome 扩展教程之如何使用 React 构建 Chrome 扩展(教程含源码)

创建自己的 Chrome 扩展程序比许多人想象的要容易。这很有趣。我们可以尽情发挥我们的创造力,根据需要修改每个网站。 在本文中,我将首先向您展示如何设置 Chrome 扩展程序。之后,我们将设置一个 React 应用程序并将其加载到任何网站上。 设置 在我们开始之前,让我们在我…

对称加密和非对称加密

对称加密 加密和解密时使用的是同一个秘钥,这种加密方法称为对称加密,也称为单密钥加密。 优点:算法公开、计算量小、加密速度快、加密效率高。 缺点:如果一方的秘钥被泄露,那么加密信息也就不安全了。 示例AES pri…