文章目录
1、数据库比较
MySQL
- C语言编写;闭源并不完全符合SQL标准。适用于简单web应用程序或者需要简单schema、SQL执行数据库操作的应用,不适合处理大量数据的复杂应用
PostgreSQL
- C语言编写采用经典的C/S结构的开源数据库。PostgreSQL是唯一支持事务、子查询、多版本并行控制系统、数据完整性检查等特性的唯一的一种自由软件的数据库管理系统。分区不是PostgreSQL强项。
InfluxDB
Go语言编写的一个开源分布式时序、事件和指标数据库,无需外部依赖。用于存储和分析时间序列数据的开源数据库。 适合存储设备性能、日志、物联网传感器等带时间戳的数据,其设计目标是实现分布式和水平伸缩扩展。 InfluxDB 包括用于存储和查询数据,在后台处理ETL或监视和警报目的,用户仪表板以及可视化和探索数据等的API。
- 专为时间序列数据编写的自定义高性能数据存储。 TSM引擎允许高摄取速度和数据压缩
- 完全用 Go 语言编写。 它编译成单个二进制文件,没有外部依赖项
- 简单,高性能的写入和查询HTTP API
- 插件支持其他数据提取协议,如Graphite,collectd和OpenTSDB
- 专为类似SQL的查询语言量身定制,可轻松查询聚合数据
- 标签允许对系列进行索引以实现快速有效的查询
- 保留策略有效地自动使过时数据过期
- 连续查询自动计算聚合数据,以提高频繁查询的效率
- InfluxDB 的开源版本只支持一个节点,集群收费
mongoDB
C++语言编写的基于分布式文件存储的开源数据库系统,查询语法类似于面向对象的查询语言,支持bson数据结构,支持复制和故障恢复。
- 优点
- 文档结构的存储方式,能够更便捷地获取数据
- 千万级别的文档对象近10G的数据,对有索引的查询不比MySQ慢,而对非索引字段的查询则全面胜出
- 缺点
- 不支持事务操作
- 占用空间过大
- 无法进行关联操作,不适用于关系多的数据
- 不适合复杂的跨文档(表)级联查询
Clickhouse
C++编写的用于联机分析(OLAP)的列式数据库管理系统(DBMS),性能超过了市面上大部分的列式存储开源数据库。由于某一列的数据类型都是相同的,针对于数据存储更容易进行数据压缩,每一列选择更优的数据压缩算法,大大提高了数据的压缩比重,节省了磁盘空间,另一方面对于cache也有了更大的发挥空间
-
优点
- 数据压缩,多核并行处理
- 支持数据复制和数据完整性 :shard分片,replica副本
- 多服务器分布式处理。
- 向量引擎,实时数据插入,稀疏索引,适合在线查询
-
缺点
- 没有完整的事务支持。
- 缺少高频率,低延迟的修改或删除已存在数据的能力。仅能用于批量删除或修改数据]。
- 稀疏索引使得ClickHouse不适合通过其键检索单行的点查询。
DDL是操作数据库和表,DML是对表进行增删改操作,DQL是数据查询语言
mysqldump -h 127.0.0.1 -uroot -p123456 db01 > D:/db01.sql # 数据库备份,在Linux命令行下执行
root@localhost: ,OycUzgg6DDZ
2、SQL语句示例
2.1 表操作
-- 创建表格
CREATE TABLE stu(id Integer, name char, score Integer);
-- 更改表格属性
ALTER TABLE stu add column address char; -- 添加一列
ALTER TABLE stu1 RENAME TO stu; -- 表格重命名
-- 删除一列
CREATE TABLE stu1 AS SELECT id, name, score from stu; -- 创建表格
DROP TABLE stu; -- 删除旧表格
ALTER TABLE stu1 RENAME TO stu; -- 把新表格的名字改成旧表格的名字
-- 删除一条记录
DELETE FROM stu; -- 删除整个表格
DELETE FROM stu WHERE name=”wangwu”; -- 删除wangwu这条记录
-- 更新一条记录
UPDATE stu SET name='wangwu' , score=90 where id=1001;
-- 插入
INSERT INTO stu values(1001, 'zhangsan', 81); -- 可以用双引号
INSERT INTO stu (name, score)values(1003, “wangwu”); -- 部分插入
2.2 查询语句
SELECT *FROM stu WHERE score=80 and name="zhangsan"; -- 条件查询
SELECT *FROM stu WHERE name="zhangsan" or score=80;
SELECT *FROM stu WHERE Grade IS NULL; -- 分数为空
SELECT DISTINCT Sno FROM SC; -- 去重查找
SELECT *FROM stu ORDER BY Grade DES; -- 降序
-- 按范围查找
SELECT *FROM stu WHERE Sage BETWEEN 20 AND 23; -- 介于20到23
SELECT *FROM stu WHERE Sage NOT BETWEEN 20 AND 23; -- 不在20到23
SELECT *FROM stu WHERE Stept IN ('CS','MA', 'IS');
SELECT *FROM stu WHERE Stept NOT IN ('CS','MA', 'IS');
-- 通配符
SELECT *FROM stu WHERE Sname LIKE '刘%'; -- 查找刘开头的
SELECT *FROM stu WHERE Sname NOT LIKE '_阳%'; -- 第二个字不是阳的
-- 聚集函数
SELECT COUNT(*) FROM stu; -- 查询学生总数
SELECT AVG(DISTINCT Grade) FROM stu; -- 去重求平均分
SELECT MAX(*) FROM stu; -- 最大值,最小值为MIN
SELECT SUM(*) FROM stu; -- 总分
-- GROUP BY
SELECT Cno COUNT(Sno) FROM SC GROUP BY Sno -- 求课程号及相应的人数
SELECT Sno,AVG(Grade) FROM SC GROUP BY Sno -- 平均成绩大于等于90学生的学号和平均成绩
HAVING AVG(Grade)>=90; WHERE -- 不能用聚集函数作为条件表达式
-- 连接查询
SELECT Student.*,SC.* FROM Student,SC WHERE Student.Sno=SC.Sno;
-- 嵌套查询
SELECT *FROM stu WHERE Sno IN (SELECT Sno FROM SC WHERE Cno='2'); -- 不在用NOT EXISTS
SELECT *FROM stu WHERE Sage <ALL(SELECT Sage FROM SC WHERE Sdept='CS'); -- ALL为所有值,ANY为任意值
SELECT *FROM Student WHERE Sdept='CS' EXCEPT SELECT * FROM Student WHERE Sage <=19; -- 计算机系学生年龄不大于19的学生
SELECT Sno FROM SC WHERE Cno='1' INTERSECT SELECT Sno FROM SC WHERE Cno='2'; -- 即选择了课程1又选择了课程2的学生
3、索引
3.1 索引介绍
索引是帮助MySQL高效获取数据的有序数据结构。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优缺点:提高查询效率和排序效率;例如select * from user where age = 45; 如果age不是索引会进行全表扫描,如果是索引就会去索引数据结构查age=45的记录,然后找到对应的行。但占用磁盘空间,降低了更新表的速度(例如INSERT,UPDATE,DELETE)
3.2 索引的使用
CREATE INDEX index_name ON table_name(col_name, ...); -- 创建常规索引
CREATE UNIQUE INDEX index_name ON table_name(col_name, ...); -- 创建唯一索引
SHOW INDEX FROM table_name; -- 查看索引
DROP INDEX index_name ON table_name; -- 删除索引
3.3 索引数据结构
磁盘中的.MYI就是存储索引数据结构的,索引用到的数据结构是特殊的B+树,首先为什么不使用二叉树
B树
B树是一种平衡的多路查找树,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构
B+树
在B树基础上,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。
3.4 索引分类
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 针对表中主键创建索引 | 自动创建,只能有一个 | PRIMARY |
唯一索引 | 避免同一个表中某数据列中的值重复 | 可以有多个 | UNIQUE |
常规索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找的是文本中的关键词,而不是比较索引中的值 | 可以有多个(用的少) | FULLTEXT |
在InnoDB存储引擎中,根据索引存储形式又分飞聚集索引和二级索引
分类 | 含义 | 特点 |
---|---|---|
聚集索引 | 将数据存储和索引放到一块,索引结构的叶子节点保存了行数据 | 必须有,只有一个 |
二级索引 | 将数据索引分开存储,索引结构的叶子节点关联的对应的主键 | 可以有多个 |
聚集索引选取规则,存在主键主键就是聚集索引;否则如果存在UNIQUE索引作为聚集索引,否则InnoDB自己创建一个隐藏的聚集索引。
3.5 性能分析
-
查看select,insert,update等指令执行频率
show global status like 'Com_______'; -- 查看数据库指令执行频率
-
慢查询日志,日志文件在/var/lib/mysql/localhost-slow.log
开启方法:在/etc/my.conf中添加下面两行,然后重启MySQL
slow_query_log=1 # 慢查询日志开关
long_query_time=2 # 慢查询超时时间show variables like 'slow_query_log'; -- 查看是否开启慢查询日志
-
profile批量查看耗时
SELECT @@have_profiling; -- 查看MySQL是否支持profile SELECT @@profiling; -- 查看是否开启profile SET profiling=1; -- 开启profile --执行一系列业务SQL操作后,通过如下指令查看耗时 show profiles; -- 查看每条SQL的耗时情况 show profile for query query_id; -- 查看指定查询id的SQL各个阶段耗时 show profile cpu for query query_id; -- 查看指定query_id的CPU使用情况
-
explain查看SQL语句执行计划(用的最多)
只需要在select语句前面加上explain关键字就可以获取执行情况:
id:查询序列号,id相同从上到下执行,id越大越先执行
partitions:分区
type:连接类型,性能由好到差:NULL、system、const、eq_ref、ref、range、index、all
possible_keys:可能用到的索引,如果没有则为NULL
key:实际用到的索引
key_len:索引长度,索引字段最大可能长度
rows:执行查询行数,在innodb中是一个估计值,不总是准确。
filtered:返回结果行数占需要读取行数的百分比,filtered值越大越好。
ps: 如果命令行界面展示变形,可以在;前面加上\G转换成列展示。
4、事务
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提交或者撤销操作请求,即这些操作要么同时成功,要么同时失败。
4.1 事务的使用
例如A向B转账100元,A的存款减少100元和B的账户增加100元必须同时生效。
select @@autocommit; -- 1为自动提交事务,0为手动提交事务
set @@autocommit = 0; -- 设置事务为手动提交,全局生效
... -- 操作数据库
commit; -- 提交事务
rollback; -- 回滚事务
start transaction 或 begin; -- 开启事务,只对当前操作生效
UPDATE account set salary = salary - 1000 where id = 1001; -- 李四减1千,张三向李四借1千块钱
UPDATE account set salary = salary + 1000 where id = 1000; -- 张三加1千
commit; -- 上面两条必须同时执行成功
rollback;
4.2 事务四大特性
-
原子性:事务是不可分割的最小操作单元,要么成功,要么全部失败
-
一致性:事务完成时,必须使所有的数据保持一致
-
隔离性:数据库提供隔离机制,保证事务在不受外部并发操作影响的独立环境运行
-
持久性:事务一旦提交或回滚,它对数据库中的数据的改变是永久的
4.3 并发事务问题
- 脏读:一个事务读到另一个事务还没提交的事务
- 不可重复读:一个事务先后读取同一条记录,但两次读取的数据不一致(中间被修改了)
- 幻读:一个事务按照条件查询没有对应数据行,但插入时又有数据存在(插入前被修改了)
事务隔离级别
隔离界别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | ✔ | ✔ | ✔ |
Read committed | X | ✔ | ✔ |
Repeatable Read(默认) | X | X | ✔ |
Serializable | X | X | X |
Read uncommitted性能最好,但数据安全性最差; Serializable隔离级别最高但性能最差。
select @@TRANSACTION_ISOLATION; -- 查看事务隔离级别
5、视图
视图是一张虚拟存在的表,可以像操作表一样操作视图。视图只保存SQL查询逻辑,不保存查询结果
-- 创建或替换视图,with cascaded check option表示插入检查where条件
create or replace view stu_1 as select id, name from student where id <= 10 with cascaded check option;
alter view stu_1 as select id, name from student where id <= 10; -- 修改视图
select * from stu_1 where id < 3; -- 查询视图里的字段
insert into stu_1 values(30, 'Tom') -- id > 10,插入报错
show create view stu_1; -- 查看创建视图语句
drop view stu_1; -- 删除视图
6、存储过程
6.1 介绍
存储过程是事先经过编译并存储在数据库中的一段SQL语句集合,调用存储过程可以简化应用开发人员工作,减少在数据库和服务器之间的传输。存储过程就是数据库SQL语言层面代码封装与重用。
create procedure p1(in score int, out result varchar(10)) -- 创建存储过程,入参score,return result
begin
declare result varchar(10);
set result := '优秀';
select result;
select count(*) from student;
end;
create procedure p2(in score int, out result varchar(10)) -- 创建存储过程,入参score,return result
begin
if score >= 85 then
set result := '优秀';
elseif score >= 60 then
set result := '及格';
else
set result := '不及格';
end if;
end
call p1(); -- 调用存储过程,相当于执行p1里的所有语句
call p2(68, @result); -- 传参68,并定义局部变量
select @result; -- 查询返回值
select * from information_schema.ROUTINES where ROUTINE_SCHEMA = 'test' -- 查询指定数据库存储过程状态信息
show create procecure p1; -- 查看某个存储过程的定义
drop procedure if exists p1; -- 删除存储过程
6.2 存储函数
是有返回值的存储过程,存储函数的参数只能是in类型(用的少,一般用存储过程即可)
create function fun1(n int)
returns int deterministic
begin
declare total int default 0;
while n > 0 do -- 求1~n的和
set total := total + n;
set n := n - 1;
end while;
return total;
end
select fun1(100); -- 调用存储函数
7、触发器
7.1 介绍
触发器是与表有关的数据库对象,指在insert/update/delete之前或者之后,出发并行执行触发器中定义的SQL语句集合。可以协助应用在数据库中保证数据完整性,日志记录,数据校验等操作。
触发器类型 | NEW和OLD |
---|---|
INSERT型触发器 | NEW表示将要或者已经新增的数据 |
UPDATE型触发器 | OLD表示修改之前的数据,NEW表示将要或已经修改后的数据 |
DELETE型触发器 | OLD表示将要或者已经删除的数据 |
7.2 应用案例-日志记录
create table user_logs( -- 准备工作:日志表
id int(11) not null auto_increment,
operation varchar(20) not null comment '操作类型,insert/update/delete',
operation_time datetime not null comment '操作时间',
operation_id int(11) not null comment '操作ID',
operation_params varchar(500) comment '操作参数',
primary key(`id`)
)engine=innodb default charset=utf8;
create trigger tb_user_insert_trigger -- 创建一个触发器,当插入一条数据同步记录一条日志
after insert on tb_user for each row
begin
-- 插入一条日志,记录插入的内容
insert into user_logs(id, operation, operation_time, operation_id, operation_params) VALUES
(null, 'insert', now(), new.id, concat('插入的数据内容为:id=', new.id, ',name=', new.name, ',phone=', NEW.phone, ',email=', NEW.email, ',profession=', NEW.profession));
end
show triggers; -- 查看触发器
drop trigger tb_user_insert_trigger; -- 删除触发器
8、锁
8.1 全局锁
锁定数据库中所有元素
flush tables with read lock; -- 加全局锁
unlock tables; -- 释放锁
8.2 表级锁
每次操作锁住整张表
8.3 行级锁
每次操作锁住对应的行数据
9、InnoDB
从Mysql5.5版本开始,InnoDB是默认的表存储引擎。其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读、同时被设计用来最有效的利用以及使用内存和CPU。
9.1 后台线程
InnoDB后台有多个不同的线程,用来负责不同的任务。主要有如下:
- Master Thread
这是最核心的一个线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括赃页的刷新、合并插入缓冲、UNDO 页的回收等. - IO Thread
在 InnoDB 存储引擎中大量使用了异步 IO 来处理写 IO 请求, IO Thread 的工作主要是负责这些 IO 请求的回调处理。 - Purge Thread
事务被提交之后, undo log 可能不再需要,因此需要 Purge Thread 来回收已经使用并分配的 undo页. InnoDB 支持多个 Purge Thread, 这样做可以加快 undo 页的回收。 - Page Cleaner Thread
Page Cleaner Thread 是在InnoDB 1.2.x版本新引入的,其作用是将之前版本中脏页的刷新操作都放入单独的线程中来完成,这样减轻了 Master Thread 的工作及对于用户查询线程的阻塞。
9.2 BufferPool 缓冲池
BufferPool 作为内存中的一块区域,在Innodb访问表的时候会将数据页和索引页缓存到缓冲池中来提高数据库的整体性能。Innodb索引都是以页为单位存储在磁盘空间中的,当Innodb访问某个页的数据时候,会先把这个页整体加载到缓冲池中,然后在进行读写操作。在操作执行完毕后,也不会立即删除将这个页缓冲从缓冲池中删除,而是将它缓存起来,这样当下次再有操作访问这个页时,会直接从缓冲池获取,这样省去了磁盘IO开销,加快了数据访问速度。
10、SQL调优
10.1 插入insert优化
1. 批量执行插入语句(一条insert语句插入多条数据,但不建议大于1k条)
2. 手动提交事务,默认是每执行一条语句就提交会影响性能
3. 主键顺序插入,性能高于乱序插入
4. 大批量插入建议使用load加载本地文件方式而不是insert语句
10.2 主键优化
1. 主键尽可能端
2. 顺序插入
3. 推荐使用AUTO_INCREMENT字段而不是UUID(主键自增)
10.3 索引优化
最左前缀法则:指在使用联合索引(一个索引关联多个字段),查询从索引最左列开始,不能跳过索引中间的列,否则后面的字段索引失效。
范围查询:联合索引中出现范围查询(>, <),范围查询右侧索引列表失效。业务允许时尽量使用>=或<=可以规避
索引列运算:尽量不要在索引上进行运算,索引将失效。例如select * from user where substring(phone, 10, 2)=‘15’;
模糊索引:尽量不要使用尾部模糊匹配,如:select * from user where phone like ‘%88’;
or连接查询:如果or连接的条件有一个没有建立索引,那么其他的索引也会失效。
前缀索引:当字段类型为字符串(varchar,text等)时,有时候需要索引很长的字符串,会让索引变得很大,查询时浪费大量磁盘IO。可以只将字符串一部分前缀建立索引,提升搜索效率。例如:CREATE INDEX idx_name ON table_name(column(n))
10.3 其他优化
count:本身比较消耗性能,count(字段) < count(主键id) < count(1) 和 count(*)
limit:使用覆盖索引 + 子查询方式
order by和group by优化:使用索引
update优化:尽量根据索引,避免行锁升级为表锁
11、分表分库
垂直分库:以表名名为依据,将不同的表拆分到不同的数据库中
垂直分表:以字段名为依据,将不同字段拆分到不同的表中,每个库表结构都不一样一般通过一列主键关联
水平分库:以字段的值为依据,将一个库拆分到多个库中,每个库表结构都一样
水平分表:以字段的值为依据,将一个表拆分到多个表中,每个表结构都一样
12、主从复制
主从复制指将主数据库的DDL和DML操作通过二进制日志传送到从数据库中,然后在从数据库中对这些日志重复执行,从而使得从库和主库数据保持同步。MySQL支持一台主库同时向多台从库进行复制,从库也可以作为其他从库的主库,实现链状复制。
- 主库出现问题,可以快速切换到从库提供服务
- 实现读写分离,降低主库访问压力
- 在从库中执行备份,避免备份期间影响主库服务
12.1 服务搭建
1、主站master搭建
-- 修改配置文件/etc/my.conf后重启MySQL
server-id=1 # 保证在集群中是唯一的
read-only=0 # 1代表只读,0代表读写
-- 创建远程连接的账号(jiang)密码(Root@123456),并授予主从复制权限
CREATE USER 'jiang'@'%' IDENTIFIED WITH mysql_native_password BY 'Root@123456';
GRANT REPLICATION SLAVE ON *.* TO 'jiang'@'%';
-- 查看二进制日志坐标
show master status; -- File即为binlog日志文件,用于slave配置
2、从库slave操作
-- 修改配置文件/etc/my.conf后重启MySQL
server-id=2 # 保证在集群中是唯一的
read-only=1 # 1代表只读,0代表读写
-- 设置主库,log信息在主库中获取(show master status)
CHANGE REPLICATION SOURCE TO SOURCE_HOST='127.0.0.1', SOURCE_USER='jiang', SOURCE_PASSWORD='Root@123456', SOURCE_LOG_FILE='binlog.000004', SOURCE_LOG_POS=663;
-- 开启同步操作
start replica; -- 旧版本使用start slave;
show replica status\G; -- 查看同步状态,旧版本用show slave status;
参数名 | 含义 |
---|---|
SOURCE_HOST | 主库ip地址 |
SOURCE_USER | 连接主库的用户名 |
SOURCE_PASSWORD | 连接主库的密码 |
SOURCE_LOG_FILE | binlog日志文件名 |
SOURCE_LOG_POS | binlog日志文件位置 |
12.2、读写分离
数据库读操作和写操作使用不同服务器,主数据库提供写操作,从数据库提供读操作,减轻单台数据库压力。
13、MySQL客户端代码
13.1 MySQL服务搭建
下载地址:https://dev.mysql.com/downloads/mysql/
安装目录下新建 my.ini 配置文件,输入如下内容,然后运行mysqld.exe启动服务
[client]
default-character-set=utf8 # 设置mysql客户端默认字符集
[mysqld]
port = 3306 # 设置3306端口
basedir=C:\\web\\mysql-8.0.11 # 设置mysql的安装目录
max_connections=20 # 允许最大连接数
character-set-server=utf8 # 服务端使用的字符集默认为8比特编码的latin1字符集
default-storage-engine=INNODB # 创建新表时将使用的默认存储引擎
# 设置 mysql数据库的数据的存放目录,MySQL 8+ 不需要以下配置,系统自己生成即可,否则有可能报错
# datadir=C:\\web\\sqldata
13.2 示例代码(go版本)
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
//用户结构体
type Users struct {
UserId int `db:"user_id"`
Username string `db:"username"`
Sex string `db:"sex"`
Email string `db:"email"`
}
//数据库指针
var db *sqlx.DB
//初始化数据库连接,init()方法系统会在动在main方法之前执行。
func init() {
database, err := sqlx.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/mytest")
if err != nil {
fmt.Println("open mysql failed,", err)
}
db = database
}
func main() {
sql := "insert into user(username,sex, email)values (?,?,?)"
value := [3]string{"user01", "man", "[email protected]"}
//执行SQL语句
r, err := db.Exec(sql, value[0], value[1], value[2])
if err != nil {
fmt.Println("exec failed,", err)
return
}
//查询最后一天用户ID,判断是否插入成功
id, err := r.LastInsertId()
if err != nil {
fmt.Println("exec failed,", err)
return
}
fmt.Println("insert succ", id)
}