1.MySQL 在 Centos 7环境安装
1.1 卸载不要的环境
-
ps ajx |grep mariadb # 先检查是否有 mariadb 存在
-
systemctl stop mariadb.service # 停⽌ mariadb 服务
-
ps ajx |grep mariadb # 再 检查是否有 mariadb 存在
1.2 删除多余的安装包
-
rpm -qa | grep mysql #查看默认安装包
-
rpm -qa | grep mysql | xargs yum -y remove #删除所有默认安装包
1.3 获取mysql官⽅yum源
- Index of /232905
-
cat /etc/redhat-release #查看版本
-
找到一个和自己的系统一致的并安装
1.4 安装mysql yum 源,对⽐前后yum源
- rpm -Uvh 安装包 #安装mysql yum源
1.5 查看是否能正常工作
- yum list | grep mysql #查看所有的mysql文件
1.6 安装mysql服务
-
yum install -y mysql-community-serve #安装mysql服务
-
Failing package is: mysql-community-client-5.7.39-1.el7.x86_64GPG Keys are configured as: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
-
rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 #解决秘钥过期问题
-
1.7 数据存储位置 查看配置⽂件
- ls /var/lib/mysql#查看数据存储位置
- ls /etc/my.cnf #查看配置文件
1.8 启动服务
-
systemctl start mysqld.service #启动服务
- vim /etc/my.cnf #打开配置文件
-
skip-grant-tables 选项 , 并保存退出
-
systemctl restart mysqld #重启mysqld服务
- root直接使用:mysql -uroot
- 或mysql -h 127.0.0.1 -P 3306 -u root -p
- 其他用户可以使用: mysql -h 127.0.0.1 -P 3306
2. 数据库基础
2.1 什么是数据库
- 已特定的格式保存好的文件,我们叫做数据库,
- 默认存放路径 /var/lib/mysql
- 提供较为便捷的数据的存取服务的软件集合,解决方案 -- mysql数据库
2.1 为什么有数据库
- 文件或者数据库,其实都可以进行数据的存储
- 如果用文件,数据内容的管理工作,需要程序员自己做!
- 数据库的本质: 是对文件的内容提供基本的内容操作,不用程序员(用户)手动的进行数据管理
- 数据库也是"文件系统"
2.2 创建一个数据库的本质
图形化界面的访问数据库的软件 -- client
mysql的生态中,也会提供第三方库,让语言也能直接访问mysql -- client
- 建立一个数据库 create database XXX本质是在linux下建立了一个目录
- 建一张表 的本质是在linux下创建对应的文件
2.3 MySQL架构
- MySQL 是一个可移植的数据库,几乎能在当前所有的操作系统上运行,如 Unix/Linux、Windows、Mac 和 Solaris
- 各种系统在底层实现方面各有不同,但是 MySQL 基本上能保证在各个平台上的物理体系结构的一致性。
2.4 SQL分类
- DDL【data definition language】 数据定义语言,用来维护存储数据的结构 代表指令: create, drop, alter
- DML【data manipulation language】 数据操纵语言,用来对数据进行操作 代表指令: insert,delete,update
- DML中又单独分了一个DQL,数据查询语言,代表指令: select
- DCL【Data Control Language】 数据控制语言,主要负责权限管理和事务 代表指令: grant,revoke,commit
2.5 存储引擎
- show engines;// 查看存储引擎的命令
3. 库的操作
3.1 创建数据库
- create database 名字;// 创建库
- crate database '特殊名字';// 创建特殊名字的库,''会起到转移字符的作用
3.2 字符集和校验规则
字符集->将数据按照特定的编码存储 == mysql的存数据
校验规则->读取数据时候的校验编码 == mysql的取数据
一般字符集和校验规则(集) 匹配的
3.2.1 查看系统默认字符集以及校验规则
- show variables like 'character_set_database';查看系统默认字符集
- show variables like 'collation_database';查看系统默认校验规则
3.3.2 查看数据库支持的字符集 && 校验规则
-
show charset;// 字符集
- show collation;// 效验集
3.4 校验规则对数据库的影响
- 数据库的编码和效验规则,本质会影响对应的数据库内部的表,比如查数据,比较数据
3.5 补充数据库的创建
- create database 库名字 character set 字符集 collate 效验规则
- create database 库名字 charset=字符集 collate 效验规则
3.6 操纵数据库
3.6.1 查看数据库 && 显示创建语句
- show databases; //查看数据库
-
show create database 数据库名;// 显示创建语句
-
MySQL 建议我们关键字使用大写,但是不是必须的。数据库名字的 反引号`` , 是为了防止使用的数据库名刚好是关键字/*!40100 default.... */ 这个 不是注释 ,表示当前 mysql 版本大于 4.01 版本,就执行这句话
3.6.2 修改数据库
- alter database 名字 charset=字符集 collation 效验规则
- 对数据库的修改主要指的是修改数据库的字符集,校验规则
3.6.3 数据库删除
- drop database 名字;
- 对应的数据库文件夹被删除,级联删除,里面的数据表全部被删
- 建议不要随便的删除库
3.6.4 备份和恢复
- 备份: mysqldump -P3306 -u root -p 密码 -B 数据库名 > 数据库备份存储的文件路径
- 一般备份是历史上所有的命令语句
- 恢复: mysql> source 数据库备份存储的文件路径
单独备份数据库的表
- mysqldump -u root -p 数据库名 表名1 表名2 > 数据库备份存储的文件路径
备份多个数据库
-
mysqldump -u root -p -B 数据库名1 数据库名 2 ... > 数据库存放路径
3.6 查看连接情况
- use 库名字; // 进入这个库 或 使用这个库
- show processlist;// 查看数据库连接情况
4.表的操作
4.1 创建表
- t2 表存储引擎是mysiam,在数据目录中有三个不同的文件
- t2.frm: 表结构
- t2.MYD: 表数据
- t2.MYI: 表索引
4.2 查看表
- Field Type Null Default Extra
- 字段名字 字段类型 是否允许为空 默认值 扩充
4.3 修改表
4.3.1 添加字段
- alter table 表名字 add
- 插入新字段后,对原来表中的数据没有影响:
4.3.2 修改字段
- 修改name,将其长度改成60
- alter table 表名字 modify
4.3.3 删除字段
- alter table 表名字 drop
- 注意: 但删除到只有一个字段的时候就不能删除了
除非使用删除表的命令
4.4 删除表
- drop table 表名
5. 数据类型
5.1 数据类型分类
- 数据类型本质就是一种约束
5.2 数值类型
5.2.1 tinyint类型
-
在 MySQL 中,整型可以指定是 有符号 的和 无符号 的,默认是有符号的。
- mysql中是不会发生数据截断的,超过范围就会直接报错
5.2.2 bit类型
-
bit 字段在 显示 时,是按照 ASCII 码对应的值显示
5.2.3 float类型
float [(m, d)] [ unsigned ] : M 指定显示 长度 , d 指定小数 位数 ,占用空间 4 个字节
- 在mysql中是不会发生取整规则的,但它会在保存值时会进行四舍五入
- 当然float类型也有无符号类型,float unsigned
5.2.4 decimal类型
decimal (m, d) [ unsigned ] : 定点数 m 指定 长度 , d 表示小数点的 位数
- decimal整数最大位数m为65。支持小数最大位数d是30。
如果d被省略,默认为0.如果m被省略,默认是10。 - decimal和float很像,也有无符号decimal unsigned,
但是二者的精度是不一样的,float的精度是7
5.3 文本 && 二进制类型
5.3.1 char类型
char (L): 固定长度字符串, L 是可以 存储的长度 ,单位为字符,最大长度值可以为 255
- char(2) 表示可以存放两个字符,可以是字母或汉字,但是不能超过2个, 最多只能是255
5.3.2 varchar类型
varchar (L): 可变长度字符串, L 表示 字符长度 ,最大长度 65535个字节
- varchar长度可以指定为0到65535之间的值,但是有1 - 3 个字节用于记录数据大小,所以说有效字节数是65532。
- 当我们的表的编码是utf8时,varchar(n)的参数n最大值是65532/3=21844[因为utf中,一个字符占用3个字节),
- 如果编码是gbk,varchar(n)的参数n最大是65532/2=32766(因为gbk中,一个字符占用2字节)
5.3.3 char和varchar比较
-
如果数据确定长度都 一样 ,就使用 定长
-
如果数据长度 有变化 , 就使用 变长(varchar) ,
5.4 日期和时间类型
-
date : 日期 'yyyy - mm - dd' ,占用三字节
-
datetime :时间日期格式 'yyyy - mm - dd HH:ii:ss' 表示范围从 1000 到 9999 ,占用八字节
-
timestamp :时间戳,从 1970 年开始的 yyyy - mm - dd HH:ii:ss 格式和 datetime 完全一致,占用四字节
- 添加数据时,时间戳自动补齐
- 更新数据,时间戳会更新
5.5 string类型
5.5.1 enum和set 类型
enum :枚举, “ 单选 ” 类型;enum(' 选项 1',' 选项 2',' 选项 3',...);
-
该设定只是提供了若干个选项的值,最终一个单元格中,实际只存储了其中一个值;
- 而且出于效率考虑,实际存储的是“数字”,因为这些选项的每个选项值依次对应如下数字:1,2,3,....最多65535个(位图结构)
5.5.1 集合查询使用find_ in_ set函数
find_in_set(sub,str_list) :如果 sub 在 str_list 中,则返回下标;如果不在,返回 0 ; str_list 用逗号分隔的字符串。
6. 表的约束
6.1 空属性
null(默认的)和not null(不为空)
- 在设计myclass表的时候,就约束了插入的数据->班级名和教室,不能为空
6.2 默认值
- 这里的default默认值就像是c++的缺省值一样
6.3 列描述
- 这里的commet就像是c++中的注释,虽然不影响数据,但这是一种规范
6.4 zerofill
- int(11): 整数后面的数字代表的不是字节,而是宽度
- 而这个数字只有在设置zerofill之后才会生效,而zerofill最大的作用就是格式化输出
6.5 主键
primary key 用来唯一的约束该字段里面的数据, 不能重复 , 不能为空 ,一张表中 最多只能有一个主键 ;主键所在的列通常是整数类型
6.5.1 创建主键 && 主键约束
- 一般给业务无关的字段设置成主键
6.5.2 删除主键 && 追加主键
- alter table 表名 add primary key(字段列表/名称) // 在没有主键的情况下,追加主键
-
alter table 表名 drop primary key // 在有主键的情况下, 删除主键
6.5.3 复合主键
-
一般在表创建时就应该设置主键,
-
如果有多个字段作为主键,可以使用 复合主键
6.6 自增长
auto_increment :当对应的字段,不给值,会 自动的被系统触发 ,系统会从当前字段中已经有的 最大值 +1 操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。
- 这里的auto_increment和c++中的enum枚举很像,
- auot_incremment在需要查找索引值的时候有用
6.7 唯一键
一张表中有往往有很多字段需要唯一性,数据不能重复,但是一张表中只能有一个主键:唯一键就可以解决表中有多个字段需要唯一性约束的问题。
主键更多的是标识唯一性的。而唯一键更多的是保证在业务上不要和别的信息出现重复
- 唯一键的本质和主键差不多,唯一键允许为空,而且可以多个为空,空字段不做唯一性比较。
6.8 外键
外键约束主要 定义在从表 上,主表则必须是有主键约束或 unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null
foreign key (字段名) references 主表(列)
- 那么当用户插入不符合业务逻辑的数据的时候,mysql不允许你插入
7. 表的增删改查
7.1 creat->增加
7.1.1 单行数据 + 全列插入
7.1.2 多行数据 + 指定列插入
7.1.3 插入否则更新
insert...on duplicate key update column = value...
- select row_count();// 可以查看最近一次语句的影响行数
- 0 row affected: 表中有冲突数据,但冲突数据的值和 update 的值相等
-
1 row affected: 表中没有冲突数据,数据被插入
-
2 row affected: 表中有冲突数据,并且数据已经被更新
7.1.4 替换
- 1 row affected: 表中没有冲突数据,数据被插入
-
2 row affected: 表中有冲突数据,删除后重新插入
7.2 Retrieve->查找
7.2.1 全列查询
- select * from 表名
7.2.2 指定列查询
- select 字段名 from 表名
7.2.3 查询字段为表达式
- select 表达式 from 表名
7.2.4 为查询结果指定别名
- select 表达式 as 别名 from 表名
7.2.5 结果去重
- select distinct 字段 from 表名
7.2.6 比较运算符 && 逻辑运算符
比较运算符:
运算符 | 说明 |
>,>=,<,<= | 大于,大于等于,小于,小于等于 |
= | 等于,null 不安全,例如 null = null 的结果是null |
<=> | 等于,null安全,例如 null <=> null的结果是true(1) |
!=,<> | 不等于 |
between a and b | 范围匹配,[a,b],如果 a <= value <= b,返回true(1) |
in(option) |
如果是
option
中的任意一个,返回
TRUE(1)
|
is null
| 是null |
is not null | 不是null |
like | 模糊匹配,%表示任意多个(包括0个) 任意字符;_表示任意一个字符 |
运算符 | 说明 |
and | 多个条件必须都为true(1),结果才是true(1) |
or | 任意一个条件为true(1),结果为true(1) |
not | 条件为true(1),结果为false(0) |
- 模糊匹配的%和_一般配合like使用
- where 条件中使用表达式,别名不能用在where条件中,
- null 与 null的比较建议使用<=>(是否相等),<>(是否不相等)
7.2.7 结果排序
- asc 为升序(从小到大),desc 为降序(从大到小),默认为 ASC
- 且如果有字段为null,则null在排序中是最小的
7.2.8 筛选分页结果
- select ... from 表名 [where ...] [order by ...] limit n;
- select ... from 表名 [where ...] [order by ...] limit s,n;
- select ... from 表名 [where ...] [order by ...] limit n offset s;
7.3 Updat->改
-
update table_name set column = expr [,column = expr ...]
[where ...][order by ...][limit ...]
7.4 Delete->删
7.4.1 删除表中数据
- delete from 表名 [where ...] [order by ...] [limit ...]
7.4.2 截断表
truncate [table] table_name
- 只能对整表操作,不能像 delete 一样针对部分数据操作;
- 实际上 MySQL 不对数据操作,所以比 delete更快,但是truncate在删除数据的时候,并不经过真正的事物,所以无法回滚
- 会重置 auto_increment项
7.4.3 插入查询结果
insert into 表名 [(column) [,colum ...])] select ...
案例: 删除表中的重复记录,重复的数据只能有一份
- 创建一张空表 no_duplicate_table,结构和 duplicate_table 一样
-
将 duplicate_table 的去重数据插入到 no_duplicate_table
-
通过重命名表,实现原子的去重操作
7.5 聚合函数
聚合统计一定是直接或者间接统计列方向的某些数据
函数
|
说明
|
count
([distinct] expr)
|
返回查询到的数据的 数量
|
sum
([distinct] expr)
|
返回查询到的数据的 总和,不是数字没有意义
|
avg
([distinct] expr)
|
返回查询到的数据的 平均值,不是数字没有意义
|
max
([distinct] expr)
|
返回查询到的数据的 最大值,不是数字没有意义
|
min
([distinct] expr)
|
返回查询到的数据的 最小值,不是数字没有意义
|
7.5.1 count 统计数据数量
7.5.2 sum 统计数据总和
7.5.3 avg 统计数据平均总分
7.5.4 max 求数据的最大值
7.5.5 min求数据的最小值
7.6 group by子句的使用
mysql可以支持按照指定的列进行对数据做分组,我们可以让特定的sql在特定的组内进行某种操作
7.6.1 测试一
7.6.2 测试二
- 在emp表中: 显示薪水大于 1000 的部门,并从中筛选出平均工资小于 2000的 ( 部门, 平均工资)
- having经常和group by搭配使用,它是对聚合统计之后的结果进行筛选,作用有点像些where。
注意: having 在查询语句中必须依赖于group by
7.6.3 小结
- group by是通过分组这样的手段,为未来进行聚合统计提供基本的功能支持(group by一定是配合聚合统计使用的)
- group by后面跟的都是分组的字段依据,只有在group by后面出现的字段,未来在聚合统计的时候,在select 中才能出现
- where vs having 这两个不是冲突的,是相互补充的,但是二者的先后顺序是不一样的
having通常: 是在完成整个分组聚合统计之后,才进行筛选的
where通常: 是在表中数据初步被筛选的时候,才起效果的
7.7 sql查询中各个关键字的执行先后顺序
- from > on > join > where > group by > with > having > select > distinct > order by > limit
8. 函数
8.1 日期函数
函数名称 | 描述 |
current_date() | 当前日期 |
current_time() | 当前时间 |
current_timestamp() | 当前时间戳 |
date(datetime) | 返回datetime参数的日期部分 |
date_add(date,interval d_value_type) | 在date中添加日期或时间 interval后的数据单位可以是: year minute second day |
date_sub(date,interval d_value_type) | 在date中减去日期或时间 interval后的数据单位可以是: year minute second day |
datediff(date1,date2) | 两个日期的差,单位是天 |
now() | 当前日期时间 |
8.1.1 留言表
- select * from msg where date_add(sendtime, interval 2 minute) > now();
8.2 字符串函数
函数名称 | 描述 |
charset(str) | 返回字符串字符集 |
concat(string2 [,...]) | 连接字符串 |
instr(string,substring) | 返回substring在string中出现的位置,没有返回0 |
ucase(string2) | 转换成大写 |
lcase(string2) | 转换成小写 |
left(string2,length) | 从string2中的左边起取length个字符 |
length(string) | string的长度 |
replace(str,search_str,replace_str) | 在str中用replace_str替换search_str |
strcmp(string1,string2) | 逐字符比较两个字符串的大小 |
substring(str,position,[,lenth]) | 从str的postion开始,取length个字符 |
ltrim(string) rtrim(string) trim(string) | 去除前空格或后空格 |
8.2.1charset 获取字符集
- select charset(ename) from emp;
8.2.2concat 连接字符串
- select concat(name,'的语文是',Chinese,'分,数学是',math,'分')as '分数' from student;
8.2.3length 显示字符串长度(以字节为单位)
- select length(name),name from student;
-
length函数返回字符串长度,以字节为单位。如果是多字节字符则计算多个字节数; 如果是单字节字符则算作一个字节。
-
字母,数字算作一个字节,中文表示多个字节数 (与字符集编码有关)
8.2.4 replace 字符串替换
- select replace(ename,'S','上海') , ename from emp;
- 将emp表中所有名字中有S的替换成'上海'
8.2.5 substring字符串提取
- select substring(ename,2,2) ,ename from emp;
-
截取 emp 表中 ename 字段的第二个到第三个字符
8.2.6 lcase 字符串转换(小写)
- select concat(lcase(substring(ename,1,1)),substring(ename,2)) from emp;
8.3 数学函数
函数名称 | 描述 |
abs(number) | 绝对值函数 |
bin(decimal_number) | 十进制转换二进制 |
hex(decimal_number) | 转换成十六进制 |
conv(number,from_base,to_base) | 进制转换 |
ceiling(number) | 向上取整 |
floor(number) | 向下取整 |
format(number,deinmal_places) | 格式化,保留小数位数 |
hex(decimalNumber) | 转换成十六进制 |
rand() | 返回随机浮点数,范围[0.0,1.0) |
mod(number,denominator) | 取模,求余数 |
8.4 其他函数
8.4.1 user() 查询当前用户
8.4.2 md5加密
8.4.3 database()显示当前正在使用的数据库
8.4.4 对用户加密
- select password('root');
8.4.5ifnull 函数
9. 复合查询(重点)
9.1 解决多表查询的思路:
- 先读题,确定都和那些表有关
- ''无脑''组合形成一张表 -- 多张表转成一张表
- 将多表查询,看做成为一张表的查询
9.2 多表查询
显示部门号为 10 的部门名,员工名和工资
- select ename,sal,dname from emp,dept where emp.depton = dept.deptno
and dept.deptno = 10;
9.3 自连接
自连接是指在同一张表连接查询
显示员工FORD的上级领导的编号和姓名(mgr是员工领导的编号--empno)
9.3.1 使用的子查询
9.3.2 使用多表查询(自查询)
- from emp leader, emp worker,给自己的表起别名,
因为要先做笛卡尔积,所以这里的别名会先识别
9.4 子查询
9.4.1 单行子查询
显示SMITH同一部门的员工
9.4.2 多行子查询
in关键字的使用
all关键字的使用
any关键字的使用
9.4.3 多列子查询
- 这里加上(),where就能多列匹配了
9.4.4 终极解决方案 - > 在from子句中使用子查询
select查询到的结果也是一张表,自然也可以跟其他表连接
显示每个高于自己部门平均工资的员工的姓名、部门、工资、平均工资
- 获取各个部门的平均工资,将其看作临时表,
- 注意: 这里的新表需要加别名,像自连接一样,以示区分
8.4.5 合并查询
union && union all 关键字的使用
- union会取得两个结果集的并集,并自动去掉结果集中的重复行
- 如何使用的是union all的话,则不会去掉重复行
10 .表的内连和外连
10.1 内连接
显示 SMITH 的名字和部门名称
10.1.1 简略写法
-
select ename, dname from EMP, DEPT where EMP .deptno =DEPT .deptno andename= 'SMITH' ;
10.1.2 标准写法
- select ename, dname from EMP inner join DEPT on EMP.deptno=DEPT.deptno and ename='SMITH';
10.2 外连接
10.2 .1 左外连接
查询所有学生的成绩,如果这个学生没有成绩,也要将学生的个人信息显示出来
- 以左为主,没有为空补充
10 .2.1 右外连接
- 以右为主,没有为空补充
11.索引
11.0 创建海量数据
drop database if exists `bit_index`;
create database if not exists `bit_index` default character set utf8;
use `bit_index`;
-- 构建一个8000000条记录的数据
-- 构建的海量表数据需要有差异性,所以使用存储过程来创建, 拷贝下面代码就可以了,暂时不用理解
-- 产生随机字符串
delimiter $$
create function rand_string(n INT)
returns varchar(255)
begin
declare chars_str varchar(100) default
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
declare return_str varchar(255) default '';
declare i int default 0;
while i < n do
set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));
set i = i + 1;
end while;
return return_str;
end $$
delimiter ;
-- 产生随机数字
delimiter $$
create function rand_num( )
returns int(5)
begin
declare i int default 0;
set i = floor(10+rand()*500);
return i;
end $$
delimiter ;
-- 创建存储过程,向雇员表添加海量数据
delimiter $$
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0;
set autocommit = 0;
repeat
set i = i + 1;
insert into EMP values ((start+i)
,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
until i = max_num
end repeat;
commit;
end $$
delimiter ;
-- 雇员表
CREATE TABLE `EMP` (
`empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号',
`ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名',
`job` varchar(9) DEFAULT NULL COMMENT '雇员职位',
`mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号',
`hiredate` datetime DEFAULT NULL COMMENT '雇佣时间',
`sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪',
`comm` decimal(7,2) DEFAULT NULL COMMENT '奖金',
`deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号'
);
-- 执行存储过程,添加8000000条记录
call insert_emp(100001, 8000000);
11. 1 常见索引
- 主键索引(primary key)
- 唯一索引(unique)
- 普通索引(index)
- 全文索引(fulltext)--解决中子文索引问题。
11.2 索引的重要性
- 在对数据进行增删查改的时候,本质是在内存中进行的
- 而索引的本质就是协助查找
11.3 MySQL 与磁盘交互基本单位
-
而 MySQL 作为一款 应用软件 ,可以想象成一种 特殊的文件系统 。它有着更高的 IO 场景,
-
所以,为了提高基本的IO 效率, MySQL 进行 IO 的基本单位是 16KB ( 后面统一使用 InnoDB 存储引擎讲解 )
11.4 索引的理解
11.4.1 mysql的所有操作都是在内存中的
- mysql本质就是一个进程,一定是在OS之上运行的
- 对mysql内部的数据等做操作(CURD)的时候,本质其实就是操作文件内容
- 文件必须先被打开,对文件内容做任何操作,都不是直接在磁盘设备上做操作的
- 任何磁盘数据,在进程中要进行操作,本质都必须在内存中进行
msql内部一定要有自己的内存管理,且它启动时会预先申请一批内存空间
11.4.2 理解单个Page
- MySQL 中要管理很多数据表文件,而要管理好这些文件,就需要 先描述,在组织 ,
- 我们目前可以简单理解成一个个独立文件是由一个或者多个Page构成的
11.5 页目录 && 索引所使用的数据结构
页目录
- 虽然插入的时候是乱序的,但查的时候却是有序的,或者说page内部的数据是有序的
- 且又因为page内部的数据是有序的,则就引入页内目录
单页情况
多页情况
目录页
- 不同的page,在mysql中,都是16kb,使用prev和next构成双向链表
- 所有的数据,全部都在叶子节点
- 表中的所有带有主键的数据,都是以B+的形式呈现的,
其中,B+数和数据是耦合在一起的,聚簇索引->InnoDB
小结:
- Page分为目录页和数据页。目录页只放各个下级Page的最小键值。
- 查找的时候,自顶向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数
主键索引:
- 具有主键的表,一表一个B+树
- 如果没有主键,mysql 会自动形成隐藏主键
- B+中所有的叶子节点,路上节点,都是按需加载到mysql page中的
InnoDB 在建立索引结构来管理数据的时候,其他数据结构为何不行?
- 链表,线性遍历不行
-
二叉搜索树,可能退化成为线性结构
-
AVL && 红黑树,深度太深
-
Hash, 范围查找不行
11.6 聚簇索引 VS 非聚簇索引
- myisam存储引擎,数据和主键索引分开的
- innodb存储引擎,数据和主键索引放在一起的
11.7 索引操作
11.7.1 创建主键索引
-
方式一: 在创建表的时候,直接在字段名后指定 primary key
create table user1(id int primary key, name varchar ( 30 )); -
方式二: 在创建表的最后,指定某列或某几列为主键索引create table user2(id int , name varchar ( 30 ), primary key(id));
-
方式三: 创建表以后再添加主键
alter table user3 add primary key(id);
11.7.2 唯一索引的创建
- 方式一: 在表定义时,在某列后直接指定unique唯一属性。
create table user4(id int primary key, name varchar(30) unique); -
方式二: 创建表时,在表的后面指定某列或某几列为unique
create table user5(id int primary key, name varchar ( 30 ), unique(name)); -
方式三: 创建表以后再添加唯一键
alter table user6 add unique(name);
11.7.3 普通索引的创建
- 第一种方式-> 在表的定义最后,指定某列为索引
-
第二种方式->创建完表以后指定某列为普通索引
alter table user9 add index(name); -
第三种方式->创建一个索引名为 idx_name 的索引
create index idx_name on user10(name); -
注意: 第三种方法可以给索引起一个新名字
普通索引的特点:
-
一个表中可以有 多个普通索引 ,普通索引在实际开发中用的比较多
-
如果某列需要创建索引,但是该列有 重复的值 ,那么我们就应该使用 普通索引
11.7.4 全文索引的创建
当对文章字段或有大量文字的字段进行检索时,会使用到全文索引。 MySQL 提供全文索引机制,但是有要求,要求表的存储引擎必须是MyISAM ,而且默认的全文索引支持英文, 不支持中文 。如果对中文进行全文检索,可以使用sphinx 的中文版 (coreseek);
直接查询
- 可以用explain工具看一下,是否使用到索引
全文索引查询
11.7.5 查询索引
- show keys from 表名
- show index from 表名;
-
desc 表名;
11.7.6 删除索引
-
alter table 表名 drop primary key ;
-
alter table 表名 drop index 索引名;
-
drop index 索引名 on 表名
12. 事务管理
一个或多个sql语句的集合,就是事务,事务 = 数据结构 + 算法
12.1 例子
业务需求 : 张三给李四转50
- 需要保证张三账号少了50,李四账号多了50,
- 一件或多件事,必须做完(绝对成功,绝对失败),不要出现中间操作这样的概念 -- 原子
小结
-
上层看来比较简单的需求,可能对应的后端要做很多的工作,
组合起来才是一个完整的需求解决的方案 -
事务本身不是数据库类软件天然有的,
事务本质工作其实是为了简化程序员的工作模型
12.2 事务的特性
-
原子性:
一个事务( transaction )中的所有操作,要么全部完成,要么全部不完成,不会结束在中 间某个环节。
事务在执行过程中发生 错误 ,会被 回滚 (Rollback )到事务开始前的状态,就像这个 事务从来没有执行过一样。 -
一致性:
在 事务开始之前 和 事务结束 以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,
这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。 -
隔离性:
数据库允许 多个并发事务 同时对其数据进行 读写 和 修改 的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括
读未提交( Read uncommitted)、读提交( read committed) 、
可重复读( repeatable read) 和串行化(Serializable) - 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失
-
这四个属性,可以简称为 ACID (单词的首字母)
12.3 事务的版本支持
- 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务, MyISAM 不支持
12.4 事务提交方式
默认的提交方式是自动提交
- 自动提交: set autocommit = 1;
-
手动提交: set autocommit = 0;
12.5 事务常见操作方式
12.5.1 证明事务的开始与回滚
- begin : 开始一个事务
- savepoint: 创建一个保存点
-
rollback : 回滚到保存点(默认回滚到最开始)
12.5.2 commit对mysql数据的影响
- 终端A未commit,客户端崩溃,MySQL自动会回滚(隔离级别设置为读未提交)
- 终端A commit了,客户端崩溃,MySQL数据不会在受影响,已经持久化
12.5.3 begin操作会自动更改提交方式,不会受MySQL是否自动提交影响
小结-> 证明原子性 , 一致性
-
只要输入begin或者start transaction,事务便必须要通过commit提交,才会持久化,与是否设置set autocommit 无关 。
-
事务可以手动回滚,同时,当操作异常,MySQL会自动回滚
-
对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。(select有特殊情况,因为MySQL 有 MVCC)
事务操作注意事项
-
如果没有设置保存点,也可以回滚,只能回滚到事务的开始。直接使用 rollback(前提是事务还没有提交)
-
如果一个事务被提交了(commit),则 不可以回退 (rollback)
-
可以选择回退到哪个保存点
- InnoDB 支持事务, MyISAM 不支持事务
-
开始事务可以使 start transaction 或者 begin
12. 6事务隔离级别
-
读未提交【 Read Uncommitted 】
-
读提交【 Read Committed 】
-
可重复读【 Repeatable Read 】
-
串行化【 Serializable 】
12.6.1 查看与设置隔离性
- select @@global.tx_isolation;// 查看全局隔离性
-
select @@session .tx_isolation ;// 查看会话隔离性,
-
select @@tx_isolation;// 查看当前隔离性
- 注意: 会话隔离性只在当前终端有效
12.6.2 读未提交【Read Uncommitted】
set global transaction isolation level read uncommitted ;
- 终端A事务在没有commit的时候,终端B也会拿到终端A事务的数据(脏读)
12.6.3 读提交【Read Committed】
set global transaction isolation level read committed;
- 终端A事务在没有commit的时候,终端B得不到终端A事务的数据
- 注意: 这可能引起在两个终端在两个事务中,数据重复的现象(幻读)-> 不可重复读
12.6.4 可重复读【Repeatable Read】
set global transaction isolation level repeatable read ;
- 可重复度本质其实就是在一个事务内部,不受任何事务操作的影响,每次查到的数据都是一致性
12.6.5 串行化【serializable】
set global transaction isolation level serializable;
- 对所有操作全部加锁,进行串行化,不会有问题,
- 它在每个读的数据行上面加上共享锁,。但是可能会导致超时和锁竞争效率很低,几乎完全不会被采用
12.6.6 小结
13. 视图
13.1 基本操作
- 创建视图: create view 视图名 as select语句;
- 修改了视图,对基表数据有影响
- 修改了基表,对视图有影响
- 删除视图: drop view 视图名
13.2 视图规则和限制
- 与表一样,必须唯一命名(不能出现同名视图或表名)
- 创建视图数目无限制,但要考虑复杂查询创建为视图之后的性能影响
- 视图不能添加索引,也不能有关联的触发器或者默认值
- 视图可以提高安全性,必须具有足够的访问权限
- order by 可以用在视图中,但是如果从该视图检索数据 select 中也含有 order by ,那么该视图 中的 order by 将被覆盖
- 视图可以和表一起使用
14. 用户管理
- 用户管理: 是普通用户只能管理某一个数据库,这样就增强了安全性
14.1 用户
14.1.1 用户信息
- MySQL中的用户,都存储在系统数据库mysql的user表中
-
authentication_string :是密码通过md5加密之后形成的
14.1.2 创建用户
create user '用户名'@'登陆主机/ip' identified by '密码';
- 刷新权限: flush privileges;
14.1.3 删除用户
drop user ' 用户名 ' @ ' 主机名 '
14.1.4 用户登录
mysql -u用户 -h'登录方式' -p
13.1.5 修改用户密码
-
自己改自己密码 : set password=password( ' 新的密码 ' );
-
root用户修改指定用户的密码
set password for ' 用户名 ' @ ' 主机名 ' =password( ' 新的密码 ' );
13.2 数据库的权限
13.2.1 给用户授权
grant 权限列表 on 库.对象名 to '用户名'@'登陆位置' [identified by '密码']
-
grant select on ...grant select , delete , create on ....
grant all [privileges] on ... -- 表示赋予该用户在该对象上的所有权限 - *.* : 代表本系统中的所有数据库的所有对象(表,视图,存储过程等)
-
库.* : 表示某个数据库中的所有数据对象 ( 表,视图,存储过程等 )
-
identified by可选 。 如果用户存在,赋予权限的同时修改密码 , 如果该用户不存在,就是创建用户
注意:如果发现赋权限后,没有生效,执行如下指令:
- flush privileges;
14.2.2 回收权限
revoke 权限列表 on 库 . 对象名 from ' 用户名 ' @ ' 登陆位置 ' ;
15. mysql connect
- 保证mysql服务有效
- 在官网上下载合适自己平台的mysql connect库,以备后用
mysqlClient.cc
#include <iostream>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <cstring>
#include <mysql/mysql.h>
using namespace std;
string host = "127.0.0.1"; //'localhost'
string user = "whb";
string passwd = "123456";
string db = "104_105_db";
unsigned int port = 8080;
int main()
{
// cout << "mysql client version: " << mysql_get_client_info() << endl;
// 0. 初始化mysql对象
MYSQL *msql = mysql_init(nullptr);
if (msql == nullptr)
{
cerr << "mysql_init error" << endl;
exit(1);
}
// 1. 登陆认证
if (mysql_real_connect(msql, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
cerr << "mysql_real_connect error" << endl;
exit(2);
}
mysql_set_character_set(msql, "utf8");
cout << "mysql_real_connect success" << endl;
// string sql = "insert into emp values (666, '张飞', 789.987)";
// string delSql = "delete from emp where id=666";
// string updateSql = "update emp set name='赵云' where id=666";
// string selectSql = "select * from emp";
char sql[1024];
while (true)
{
printf("mysql> ");
fgets(sql, sizeof sql, stdin); //' select * from user '
// 调用成功的时候,返回值是0, 否则就是1
int n = mysql_query(msql, sql);
if (strcasestr(sql, "select") && n == 0)
{
cout << "result: " << n << endl;
// 对结果进行解析
MYSQL_RES *res = mysql_store_result(msql);
if (res == nullptr)
exit(0);
int rows = mysql_num_rows(res);
int fields = mysql_num_fields(res);
MYSQL_FIELD *fname = mysql_fetch_fields(res);
for (int j = 0; j < fields; j++)
cout << fname[j].name << "\t|\t";
cout << endl;
MYSQL_ROW line;
for (int i = 0; i < rows; i++)
{
line = mysql_fetch_row(res); // 按行获取文件的内容,自动会更新行数
for (int j = 0; j < fields; j++)
cout << line[j] << "\t|\t";
cout << endl;
}
printf("%d rows in set\n", rows);
}
else
{
cout << "execl sql : " << sql << " done" << endl;
}
}
// 关闭mysql对象
mysql_close(msql);
return 0;
}
makefile
mysqlClient:mysqlClient.cc
g++ -o $@ $^ -std=c++11 -L/lib64/mysql -lmysqlclient
.PHONY:clean
clean:
rm -rf mysqlClient
-
初始化接口: MYSQL *mysql_init(MYSQL *mysql) ;
- 链接数据库接口: MYSQL *mysql_real_connect(MYSQL *mysql, const char *host,
const char * user , const char * passwd , const char * db , unsigned int port , const char *unix_socket , unsigned long clientflag )
-
发送mysql请求: int mysql_query ( MYSQL * mysql , const char * q );
-
读取mysql结果: MYSQL_RES * mysql_store_result ( MYSQL * mysql );
-
结果行数: my_ulonglong mysql_num_rows ( MYSQL_RES * res );
- 获取结果列数: unsigned int mysql_num_fields(MYSQL_RES *res);
- 获取列名: MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
-
获取结果内容: MYSQL_ROW mysql_fetch_row ( MYSQL_RES * result );
它会返回一个 MYSQL_ROW 变量, MYSQL_ROW 其实就是 char **. 就当成一个二维数组来用吧。
另外,mysql C api还支持事务等常用操作
- my_boolSTDCALLmysql_autocommit(MYSQL*mysql,my_boolauto_mode);
- my_boolSTDCALLmysql_commit(MYSQL*mysql);
- my_boolSTDCALLmysql_rollback(MYSQL*mysql);