人人都能看懂的全栈开发教程——数据库

人人都能看懂的全栈开发教程——数据库

Chris Yue One comment
Posts

回忆一下我们的需求:提交的任务需要保存到网站,所以我们得想办法把任务存在什么地方。

前一篇文章里我们有提到 XML 这个文件,并且也提到 XML 文档就是用来传递信息的,那是否我们可以规定一个描述任务的 XML 文档,代码通过它来读取任务列表的数据呢?

我们的确可以这么做,并且我听说早先的水木清华论坛,也是采用直接用文件存储信息(但不一定是 XML),只不过,用文件存储会有以下几个棘手的问题:

  1. 查找,筛选,排序是常见的业务需求,如果使用文件,那么这些功能需要自己花时间来实现。
  2. 文件只能让本地的程序读取,如果想让多台机器读取,得做额外的工作,然而这种事情在规模稍大的网站也经常会有
  3. 为了让搜索更快,你需要给数据建立『索引』,而这些也需要你花时间实现
  4. 可能你还需要让你的数据支持『事务』,这你也的自己来处理
  5. 文件还要面临『锁』的问题,这也需要你自己来处理

可能有些读者并不知道什么叫『索引』,『事务』,『锁』,目前可以暂时忽略它们,以后自然会遇到。总之列了这么多,其实就是想告诉大家,如果你使用数据库来解决存储的问题,有很多事情你都不用考虑了。除了上面说的那几点,还有好多比如『外键约束』,『唯一(联合)键』,『字符集』等等等等……

目前市面上有很多数据库,从种类上说,就有关系型数据库和非关系型数据库,而关系型数据库又有很多选择,比如 SQL Server,Oracle,MySQL,PostgreSQL,SQLite,而非关系型数据库又有 Redis,MongoDB,Cassandra,HBase 等,这里先不说关系型数据库和非关系型数据库区别是什么,我的建议是,如果你可以通过实际操作去感受,就别费劲心思去理解别人的总结,这两种数据库,我们后面都会有机会接触。

总之,现在我们就选择使用关系型数据库作为我们的存储就行了,原因我们以后会说。

但关系型数据库又有很多可选项,我们应该如何选择呢?这里我先介绍一下 SQLite,作为一个『轻型数据库』(lite 即 light,即轻),它的『轻』体现在它并没有『服务』的概念,即只能用于本地的数据库,而无法给别的机器提供服务。这里再简单说一下什么叫『服务』,留个印象,这也是开发工程师经常遇到的一个名词,『服务』是能对多台机器提供某种『服务』的东西,既然是服务于多台机器,那么必定跟『网络』有关系,既然跟网络有关系,那很有可能有『端口』,除非它只给本机提供服务。

说回 SQLite,不是『服务』是它的一个缺点,却也是它的一个优点,因为不需要提供『服务』所必须的功能,程序相对小,资源也占用少,这也使得它经常出现在各种嵌入式微机中,另外好多只需要本地数据库的软件也可以直接将 SQLite 库引入来读取本地数据库文件,比如火狐就是用的 SQLite 来管理访问记录,收藏夹等各种用户数据的;后面我们要说的 PHP 也可以载入 SQLite 库来读取本地数据,这样就无需再启动另外一个数据库服务了。我也写过好多程序就是用的 SQLite,实际上我们要做的任务列表项目,就目前的需求看来,用 SQLite 也是很合适的。但作为教程,我还是打算先介绍目前使用最为广泛的 MySQL,顺便让大家更多感受感受,作为一个『服务』应该是什么样的。

为什么 MySQL 用的人这么多呢?具体原因我也不知道,可能是因为 MySQL 历史悠久,提供的功能多,性能稳定性安全性等各方面都没有大毛病,并且还有一个很大的优点,是的,还是免费。

这里以 Ubuntu 为例,展示如何安装 MySQL。

Ubuntu 安装 MySQL 的方式很多,有用图形界面的,也有用命令行的。这里我只告诉大家命令行的方式,对于图形界面工具,结合自己的经验我的看法是:图形界面是用来增加工作效率的操作方式,但非常不适合学习,因为图形界面为了工作效率,为了易用性,往往会隐藏一些细节,而这些细节很有可能是我们了解一个事情是如何工作的重点。

为了使用命令行,我们需要打开『终端』,我们可以在 Ubuntu 里搜索『terminal』来打开终端,我们还可以通过快捷键 Ctrl + Alt + T 来打开终端。我建议大家尽量记住快捷键,这是增加工作效率的小技巧。想象一下,当你可以用手指机械反应作出很多操作之后,你是不是可以在不知不觉中比一般人做更多的事情?这也是为什么我现在都用 Vim 做我的主要编辑器的原因,Vim 包含了大量按键操作,当然 Vim 也不太好入门,这里也就不多说了,大家有兴趣可以网上搜索一下 Vim 的入门教程。

之前提到 MySQL 是一个『服务』,可能大家更熟悉的服务,就是网站。而各位要使用一个网站,是不是需要浏览器才行?实际上浏览器就是网站服务的『客户端』,而你要想浏览操作 MySQL 里面的数据,也得有一个『客户端』,所以我们安装 MySQL 实际上要装两个东西,一个是 MySQL 服务端,一个是 MySQL 客户端。

sudo apt install mysql-server mysql-client

上面命令里,sudo 是用来暂时提升权限的。在 *nix 的世界里,一般来说,如果你要做一些操作可能会影响到系统的其他用户,就会需要用到 sudo。另外不是每一个用户都可以 sudo 的,这个话题以后会专门说。

在安装的过程当中,应该会提示你输入 MySQL 服务的 root 密码,对于生产环境来说,密码当然是越复杂越好,复杂是指位数长,字符类型多,大小写数字特殊字符都应当有,这是一个好的习惯,我也写过一个自动生成复杂密码的小程序。当然对于开发来说,数据没那么宝贵,随便起一个自己能记住的简单密码,甚至没有密码都是可以的。

等全部安装好之后,mysql-server 应该就已经自己启动了。安装命令返回结果大家一定要仔细看。对于不熟悉的命令多看看回显信息,是一个好习惯,很多知识和信息其实已经很直白得喂给你了。另外一个好习惯是,每步操作,都尽量确认是否真的操作成功。我们可以通过以下方式来确认 MySQL 服务是否已经运行:

sudo systemctl status mysql

确认 MySQL 服务正常运行,我们可以通过下面的命令,开启客户端去连接服务了。假设我们服务器的密码为 123456:

mysql -u root -p 123456

注意:-u 参数是指用户,MySQL 都会有一个默认用户叫 root,-p 参数是密码。如果你们没有设置密码,则不需要 -p 参数

当你看到光标前面有 mysql> 时,则表示你已经成功接上服务器了。

小提示:在 Ubuntu 里命令行大部分命令都是可以自动补全的,不仅程序名称可以补全,参数也可以补全,可以增加效率和改善使用感受,方法就是在你还没有输入完某个单词的时候,按一下 Tab 键,当然这个功能在现在大部分 Linux 发行版都有。最后我再推荐大家使用 Zsh + Oh-My-Zsh 进一步改善使用感受,保证你会爱上使用命令行

SQL

我们即将接触第二种编程语言,Structured Query Language,简称 SQL。大家也不用太在意定义,先感受 SQL 能做什么。

也许大家早就发现好像很多关系型数据库好像名字里都带有 SQL,那是因为只要是数据库就会被操作数据,而操作数据的指令,都是用的 SQL 这个语言。

以 MySQL 来说,我们需要给任务列表项目创建一个『库』,我们模仿我的世界(Minecraft),起一个名字叫我的任务清单(Minetodo)吧,我们可以通过以下命令创建库,这个命令就是 SQL:

create database minetodo;

回车之后,库就应该创建好了。我们可以通过以下命令来查看目前存在哪些数据库:

show databases;

返回的结果应该是不只有 minetodo,还有很多别的库,先不用管这些库是用来干嘛的,只用知道一台服务能创建很多库就行了。

创建好了『库』之后,我们还得在库里面创建『表』。先执行 use minetodo; 命令用来进入库,然后我们创建一个名为任务(task)的表,并且按照我们之前的计划,task 应该有以下字段:255 字符长的并且不能为空的内容字段(content),时间戳类型的 created_at 字段

create table task (content varchar(255) not null, created_at timestamp default current_timestamp);

显示 Query OK 即表示创建成功,当然你也可以使用以下命令确认是否创建成功,而且以后都可以通过此命令来查看当时是怎么创建的表及其字段的:

show create table task;

在我本机,上面的命令返回的是

CREATE TABLE `task` (
  `content` varchar(255) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8

大家可以发现返回的结果基本上跟我们之前输入的命令差不多,但又有点小区别,这里只重点说 DEFAULT CHARSET=utf8 这一部分。可能有的同学的结果跟我的不一样,charset 是指这个表里的字符的编码是什么,而『字符编码』也是一个开发工程师经常遇见的一个词语。计算机其实是用若干个字节来表示一个字符的,字符即 Byte,一个字节有 8 位即 bit,一个 bit 可以表示 0 和 1 两个数,那么 8 个 bit 组合在一起可以表示 2 的 8 次方个数,也可以表示 2 的 8 次方个字符。以前计算机的字符只有英文数字和一些特殊字符,总数不超过 2 的 8 次方,所以用一个字节表示一个字符就够了,但这样就满足不了别的语言,比如中文,那是不可能用一个字节能表示得完的,所以后来就有了 GB2312 或者 GBK 这种编码,某个文本软件按照 GBK 这种编码格式去读取文件,那么它便知道它应该按照一个字节或者两个字节的方式去读取而不是单纯一个字节(汉字是两个字节组成的,而 ASCII 码的前 128 个字符还是一个字节),但 GBK 又无法满足中文和其他语言比如日文韩文甚至现在大行其道的表情符(emoji)一起显示,所以兼容性更好的编码方式是 UTF-8,它使用 1 到 4 个字节来表示一个字符,比 GBK 多包含了好几个量级的字符,这里引出这个概念,是因为我们经常都会遇到字符的问题,可能你在 Windows 里编辑的文本文件,我在 Mac 或者 Linux 下打开就是乱码,或者你写代码想在页面输出文字,浏览器里打开却是一堆乱码,这都是不注意统一编码而容易出现的问题。

对于 MySQL 来说,这里需要多说一句,MySQL 里面的 utf8,其实指代是不准确的,MySQL 只用了 1 到 3 个字节来表示一个字符,很显然,MySQL 的 utf8 编码是覆盖不了所有的 UTF-8 字符的,这对我们的项目来说也不是什么特别要紧的事情,但如果你想让你的任务清单项目支持 emoji(特别是新加的),还是需要确认一下是否是 utf8mb4,如果不是可以通过下面的命令修改:

alter table task convert to charset utf8mb4;

因为是教学,所以这里也多教大家一些:你也可以通过 drop table task; 将表删除之后,再将上面 show create table task; 返回的内容作为命令重新执行一遍就行,但记得执行前需要把 CHARSET= 后面改成 utf8mb4

还有一个更省事儿的方法,数据库本身也是可以设置默认的字符编码的,我们可以通过以下命令将 minetodo 数据库的默认编码改成 utf8mb4 ,创建表的时候就不用再指定了:

alter database minetodo default character set utf8mb4;
# 这行由井号开头的是 SQL 的注释,类似与 HTML 的 <!-- xxx -->
# 下面这种写法也可以
alter database minetodo default charset=utf8mb4;

当然也可以在创建库的时候就指定:

create database minetodo default charset=utf8mb4;

插入数据

执行以下语句我们将插入我们的第一条任务:

insert into task values('第一条任务', null);
# 或者
insert into task (content) values ('第一条任务');
# 或者
insert into task set content = '第一条任务';

其中第一种写法 values 里有两个值,分别对应了第一个字段和第二个字段,null 在计算机里也是一个常见的单词,表示『没有值』,它跟 0,或者空字符串还不一样,至于哪不一样这里先不说,总之这里使用 null 表示不对第二个字段赋值,让他自己给自己赋默认值,其实跟第二种或者第三种写法效果是一样的。还记得创建 created_at 字段时有一句 default current_timestamp 吗?意思就是『如果不主动指定,默认为当前的时间

查询数据

我们可以通过下面的命令来查询当前 task 表里的数据:

select * from task;

关于查询数据其实有很多可说的,只不过现在就先知道上面的命令就行,更多用法我们后面都会遇到的。包括数据的删除,数据的更新,我们都遇到再说。

其他 MySQL 客户端

MySQL 的客户端有很多,好多都是基于图形界面的,我用过的比如 SQLyog,sequelPro,phpMyAdmin,mycli 都各有优点。我依然是建议大家不要一来就使用图形界面的客户端(mycli 除外,它是加强版的 mysql-client,强烈推荐给喜欢命令行下操作的同行),还是那句话,图形界面的好处是提高工作效率,但对学习没有啥益处,反而因为替用户做太多隐藏了你应该知道的细节。

Migration

我们在数据库里执行的命令只有我们自己知道,但我们的项目却很有可能会有更多的人来加入建设,所以我们应该告诉其他人,初始化项目的时候,我们需要对数据库执行一些什么操作。

不仅是初始化项目阶段,当我们的项目发展到一定规模,需要让数据库保存更多的数据时,我们也应该将新执行的 SQL 语句记录下来让其开发知道。这些对数据库做操作的语句,就叫 Migration,中文有种译法,叫做『数据迁移』。

我们先创建一个 migrations 目录,里面放上我们初始化的 SQL 记录文件 init.sql 并将之前我们用于创建数据库和表的 SQL 语句,一行一句全部写里面。实际上我们有这些 .sql 文件之后,就可以在 MySQL 客户端里直接运行

# source 后面接 sql 文件的路径
source migrations/init.sql

就可以自动创建数据库和表了。

init.sql 的内容可以在这里查看。大家可以会对此文件里的代码大小写有困惑,这里解释一下,SQL 的代码习惯是关键字大写,『关键字』是指 SQL 自己提供的一些固定的语句,而非关键字就是我们自己提供的部分,比如数据库名,表名,字段名。建议大家写 SQL 文件的时候也遵守这个习惯。

人人都能看懂的全栈开发教程——数据库 by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

微信赞赏码

写作累,服务器还越来越贵
求分担,祝愿好人一生平安
天使打赏人

One comment

asdf

十月 17, 2020 在 10:39 上午

good

发表评论

4 + 6 =