仅用几行代码就撸了个数据库!

运维 数据库运维
数据库通常不会默认建好索引,需要开发人员或数据库管理员自行选择。想要以最小的代价换取最大收益,不仅需要对应用的查询模式有准确把握,还需要对数据库内部索引技术有一定了解。

[[429476]]

本文转载自微信公众号「小菜学编程」,作者fasionchan 。转载本文请联系小菜学编程公众号。

最近重读《数据密集型应用系统设计》这本书,看到第三章《数据存储与检索》,主要讲数据库内部的索引技术。

从本质上讲,数据库主要是做两件事情:

  1. 当你给它数据时,它帮你保存数据(存储);
  2. 当你查询数据时,它帮你取回数据(检索);

这两件事情看似简单,背后却暗含玄机。那么,数据库内部到底是如何存储数据的呢?又是如何检索数据的呢?

你可能会有这样的疑问:我作为一个开发人员,为什么需要知道数据库内部是怎么工作的呢?

我们显然不会自己从零开始,撸一个存储引擎。但由于市面上有太多数据库产品,我们需要从中挑一个最适合自己应用场景的。为了提高性能,我们也需要根据应用负载特征,对数据库进行调优。因此,必须对数据库底层技术有一些了解,知道它在背后都干了什么。

根据应用负载特征,数据库大致可以分为两种:

  • 一种针对 在线事务型 业务,多采用 行式存储 方式;
  • 一种针对 离线分析型 业务,多采用 列式存储 方式;

为了提高检索速度,索引必不可少。不同存储引擎采用的索引技术也不太一样,大致可以分为两种:

  • 日志式存储引擎( log-structured ),比如 LSM树 ;
  • 页式存储引擎( page-oriented ),比如 B树 ;

为了引出这些概念,作者用几行 Shell 代码,写了一个玩具数据库抛砖引玉:

  1. #!/bin/bash 
  2.  
  3. db_set() { 
  4.     echo "$1,$2" >> database 
  5.  
  6. db_get() { 
  7.     grep "^$1," database | sed -e "s/^$1,//" | tail -n 1 

这两个简短的 Shell 函数实现了一个 KV 式数据库引擎。麻雀虽小,五脏俱全!它提供了两个基本操作:

  • db_set ,保存键值对数据,即将键值用逗号分隔追加到名为 database 的数据文件;
  • db_get ,按键取出对应的数据,即从 database 文件中过滤出最后一行包含指定键的数据;

这个玩具数据库可以这样用,比如保存键值对数据:

  1. fasion@SmartPro [ ~ ]  ➜ db_set 1 fasionchan.com 
  2. fasion@SmartPro [ ~ ]  ➜ db_set 2 小菜学编程 
  3. fasion@SmartPro [ ~ ]  ➜ db_set 3 Python源码剖析 

数据最终保存在 database 文件中,每个键值对一行,格式大致长这样:

  1. fasion@SmartPro [ ~ ]  ➜ cat database 
  2. 1,fasionchan.com 
  3. 2,小菜学编程 
  4. 3,Python源码剖析 

随后可以这样查询数据:

  1. fasion@SmartPro [ ~ ]  ➜ db_get 1 
  2. fasionchan.com 
  3. fasion@SmartPro [ ~ ]  ➜ db_get 3 
  4. Python源码剖析 

修改数据时,新记录被追加到 database 文件末尾,旧数据不会删:

  1. fasion@SmartPro [ ~ ]  ➜ db_set 3 Python源码深度剖析 
  2. fasion@SmartPro [ ~ ]  ➜ cat database 
  3. 1,fasionchan.com 
  4. 2,小菜学编程 
  5. 3,Python源码剖析 
  6. 3,Python源码深度剖析 

因此,我们查询数据时,db_set 最后需要执行 tail 命令,以最新的那条为准:

  1. fasion@SmartPro [ ~ ]  ➜ db_get 3 
  2. Python源码深度剖析 

这个玩具看上去挺有意思的,但它的性能到底如何呢?

db_set 操作的性能非常好,因为它只是将数据记录追加到 database 文件的末尾,这通常都很快。

跟 db_set 类似,很多数据库内部也有一个只追加的数据文件,一般叫做操作日志。虽然实际数据库需要考虑更多因素,包括并发控制、磁盘空间重用、错误处理等等,但基本原理都是一样的。

然而,如果数据库中有大量数据,db_get 操作的性能会非常差。因为每次你查询一个键,db_get 都必须扫描整个数据库文件!这是一个典型的 操作,数据库记录数增加一倍,查询开销就会增大一倍!这可不太好!

为了在数据库中快速检索数据,我们还需要另一个数据结构:索引 。索引是一些额外的元数据( metadata ),就像路标一样,可以帮我们快速定位数据。如果你的数据有多种查询条件,则可能需要建多个索引。

索引可以加快查询,但也会带来额外的开销,特别是对写操作。任何索引都会降低写性能,因为每次数据写入后,都要更新索引。因此,每个存储系统都需要根据实际情况做出权衡:

  • 索引选得好可以加快查询;
  • 索引一定会降低写性能;

因此,数据库通常不会默认建好索引,需要开发人员或数据库管理员自行选择。想要以最小的代价换取最大收益,不仅需要对应用的查询模式有准确把握,还需要对数据库内部索引技术有一定了解。

 

责任编辑:武晓燕 来源: 小菜学编程
相关推荐

2021-12-12 18:18:15

代码元宇宙Python

2024-03-07 13:02:57

PythonSQLite数据库

2021-04-15 11:10:40

GitHub代码开发者

2021-07-23 08:00:00

深度学习框架模型

2021-08-09 07:26:33

瀑布流布局代码

2015-06-01 15:11:37

数据库update

2018-09-28 09:32:57

2021-01-29 17:25:29

ERP中台IT架构

2022-05-11 09:02:27

Python数据库Excel

2020-04-30 14:25:13

代码项目JS

2009-07-02 09:35:02

hibernate访问

2009-09-28 13:33:48

Hibernate访问

2012-02-07 09:17:13

2012-06-18 09:29:38

2021-12-02 09:31:22

Python 代码进度条

2010-04-16 10:18:10

Import性能

2022-11-28 09:06:40

Oracle数据库MySQL

2022-10-24 14:21:09

数据库应用数据库数据管理

2010-05-12 15:07:50

MySQLSQL数据库

2012-02-03 10:32:46

Java
点赞
收藏

51CTO技术栈公众号