简述
昨天写了一篇 mongodb 的一些操作,但是在真实业务中 mysql 确实使用最多的。在缓存方面的我们有了 redis 这样的 nosql 数据库,而 mongodb 在业务等级和 mysql 基本是平级的,当然从使用程度上说,mysql 这样关系型数据库统计地位确实根深蒂固的。
另外拓展一下,在目前在缓存方面 memcached 和 redis 是最常见的解决方案,而业务层面 mongodb 和 mysql ,不过主要场景还是用 mysql ,在一些涉及一对多场景,比方说聊天等会用 mongodb。而大数据领域有列式数据库 HBase ,另外数据关系领域在一对多领域衍生出来某个对象需要的对象关系跟自己平级,那就出现了图数据库,目前主流图数据库还是 neo4j。
回到 mysql ,关于他的讲述,如今各种视频资料已经漫天遍野,本人自然无法聊出更多所以就根据其常见的机制简单介绍。
索引
几乎聊到数据库,索引是必然会聊到的,主键索引和唯一索引是开发必须考虑的。
创建索引的语句如下:
代码语言:sql复制CREATE INDEX index_name ON table_name (column1, column2, ...);
设计较为简单,但是在实际开发过程中,需要考虑很多事情,最常见的是空值情况,现在的注册方式有很多,邮件注册,手机号注册,正常注册,这种情况会带来很多空值问题,比方说我在手机号注册,那么邮件很可能是空值,这时候在设置索引或者主键时候就需要注意了。
另外还有很多其他东西,包括数据量过大时候的分库分表,当然基于各自业务方面就看项目内部情况。
索引机制
提及索引,就离不开索引的机制,也就离不开 mysql 的引擎 innodb 和 myisam 也是必不可少的。这里主要说的是 innodb。
innodb 引擎在索引中使用的是 b 树。b 树是多叉树,其结构如下:
这样做的有以下几种原因,首先就是多叉树可以降低树的高度,增加了查询效率,同时数据只存在叶子结点使用了链表连接,这一点和 b 树有所区别,b 树即有指针有含有数据。
需要说明的是,因为空间原因最后子节点每个只有一个链接结点,实际上可以有多个。
另外就是一些其他的面试问题了,比方说聚簇索引和非聚簇索引,索引和数据存储在一个位置就是聚簇,否则就不是。
面试中经常会问为什么使用自增主键?MySQL的主键是一个聚簇索引,它的叶子节点存放了数据。
在使用自增主键的情况下,会保证树的分裂照着单方向分裂的,这会大概率导致物理页的分裂也是朝着单方向进行的,即连续的。
在不使用自增主键的情况下,如果在已经满的页里面插入,会导致MySQL页分裂,虽然逻辑上页依旧是连续的,但是物理页已经不连续了。
当然,需要记住的是,索引只是加快了索引到数据的速度,并不能加快其他方面的速度,我们实际生产过程网络 i/o ,访问数据量大小都会影响访问速度。
事务
除了索引,事务算是 mysql 另一个特点。
mvcc
mvcc 算是 mysql 的另一个机制,mvcc 实现机制需要依靠两个内容,一个是锁,一个是多版本控制。
多版本控制,主要依赖两个元素,undo log 和 read view。当数据修改时候会生成undo log 记录版本信息。然后数据库根据隔离机制让你查看你的权限生成 readview,来判断可以读取什么样的内容。
理解起来较为简单,比方说视频网站,他们没发布一个电视剧都会有会员观看也会有非会员观看,而且随着更新会员和非会员能看到视频也发生变化,那天会员能看什么视频,非会员能看什么视频需要记录呀, undo log 就是记录这样信息。而根据你是会员还是非会员能看到什么内容,就会生成什么内容,这就是 readview 。
锁理解起来跟开发基本一致,不过根据 mysql 情况有表锁,行锁。
其相关隔离级别就不具体介绍。这里主要看的是主从复制情况。
主从复制和事务提交
mysql 有很多 log,上边我们提到了 undolog 用于版本控制,除此之外还有 binlog 和 redolog。主从复制中有 relay log
mysql 的所有信息复制在 binlog 中,如果从节点需要复制主节点信息,需要读取主节点的 binlog 写入到 relay log,然后在从 relay log 中 进行数据同步。
大家可能会好奇为什么还多加一个 relay log ,这是开发中默认的一个想法,网络是不可靠的,同时数据之间需要缓冲,如果从节点,读取后直接同步,那么网络出错了,可能会产生错误数据,万一有请求来了,我一遍同步一遍接受请求压力山大呀,所以还不如我让一个线程写到一个文件爱你中国年,等我空闲下来同步一下。
而 redo log 是用于事务提交的。事务预提交阶段会写入 redo log 和 undo log,然后提交完成后会提交给 bin log 用于主从复制。
这里需要提及的是,数据库是应用层软件,他并不是与硬件层面交互的,所以就算写入日志,需要真正刷盘才会真正存储,这其中需要经过操作系统。这里边是有时间差的,虽然这时间很短,但是也可能会丢数据。
代码实现
mysql 代码写之前也需要驱动,请自己安装并引入编译器相关库函数。
代码比较简单直接上全部代码:
代码语言:c复制#include <mysql/mysql.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
MYSQL *conn; //连接
MYSQL_RES *res; //回复
MYSQL_ROW row;
char server[] = "127.0.0.1";
char user[] = "root";
char password[] = "pass";
char database[] = "database";
conn = mysql_init(NULL); //初始化
if (mysql_real_connect(conn, server, user, password, database, 0, NULL, 0)) { //创建连接 database 后应该是端口,如果是 0 默认表示 3306, null 表示不使用 unix 套接字,使用tcp/ip,最后 0 表示不设置
printf("%sn", mysql_error(conn));
}
char sql[128] = {0};
sprintf(sql, "insert into user_info(`name`,title,money)values('king', '', 3);");
if (mysql_query(conn, sql)) { //查询
printf("%sn", mysql_error(conn));
}
if (mysql_query(conn, "select * from user_info")) {
printf("%sn", mysql_error(conn));
}
res = mysql_use_result(conn); //conn转化为返回结果
while ((row = mysql_fetch_row(res)) != NULL) {//将结果转化为数组
printf("%st", row[0]);
printf("%st", row[1]);
printf("%st", row[2]);
printf("%sn", row[3]);
}
mysql_free_result(res); //释放res
mysql_close(conn);
return 0;
}
mysql 算是开发中最简单的内容了,之前也说过 c/c 不适合做后台开发,因为涉及太底层,一个操作都要耗费很多步骤。越往底层操作的粒度会越来越小,但是效率就会很低。