- window : mysql workbench、 navicat、sqlyog、heidisql、mysql front
- linux : mysql workbeanch 、navicat
- linux : mysql workbeanch 、navicat
- 架构1.0 - 单机单库
- 数据量太大、超过一台服务器承受范围
- 读写操作量大,超出一台 服务器承受
- 一台服务器挂了,应用也会挂掉(可用性差
- 架构 2.0 - 主从架构
- 读写分离
- 保证服务的可用
- 数据量太大超过一台服务器
- 写操作太大超出一台服务器的性能
- 架构 3.0 - 分库分表
- 写操作太大超出一台服务器的性能
- 数据如何路由是一个关键问题,一般采用范围拆分,list拆分,hash拆分
- 如何保持数据一致性是一个难题
- 架构 4.0 - 云数据库
- 使用Saas服务,服务提供商负责解决可配置性,可扩展性,多用户存储 接哦古涉及 等难点问题
约束 | 约束表达式 | 备注 |
---|---|---|
主键 | primary key | |
非空 | not null | |
唯一 | unique | |
默认 | default | |
外键 | foreign key |
关键字 | 含义 | 备注 |
---|---|---|
distinct | 去除重复项 | |
like | 模糊查询(% 任意多个字符 , _ 一个任意字符) | |
in | 非连续范围内 | |
between … and … | 一个连续的范围内 | |
count(*) | 统计总行数 | |
max() | 求最大值 | |
min() | 求最小值 | |
sum(列) | 求和 | |
avg(列) | 求平均数 | |
group by | 将查询结果按照1个或者多个字段进行分组,字段值相同的一组 | |
group by + having | 分组后查询指定条件输出的查询结果 | |
having | 只能用于group by | |
order by | 排序 (asc升序 desc降序) |
# 连接数据库
mysql -u"用户名" -p
$pwd
# 数据库退出
quit
# 查看数据库
show databases;
# 查看数据库版本
select version()
# 显示时间
select now();
# 创建数据
create database `python01` charset="utf-8";
# 展示数据库创建语句
show create database "database03"
# 查看当前使用数据库
select database();
# 使用数据库
use "数据库名称";
# 删除数据库
drop database "数据库名称";
# **数据库表的操作**
# 查看当前数据库中所有表
show tables;
# 创建表
# auto_increment表示自动增长
# not null 表示不能为空
# primary key 表示主键
# create table 数据库名称(字段 类型 约束 [字段 类型 约束]);
create table demo(id int,name varchar(20));
# 查看表结构
desc "表名";
# 创建 students 表(id、name、age、high、gender、cls_id)
create table students(
id int not null primary key auto_increment,
name varchar(30),
age tinyint unsigned default 0,
high decimal(5,2),
gender enum('男','女','中性','保密') default '保密',
cls_id int unsigned
);
# 表中插入数据
insert into student values(0,"老王",18,175.88,"男",1);
select * from student;
# 查看表的创建语句
show create table students
# 修改表 - 添加字段
# alter table 表名 add 列名 类型;
alter table students add birthday datetime;
# 修改表- 修改字段 : 不重名命名 - 修改字段类型 + 约束
# alter table 表名 modify 列名 类型及约束
alter table students modify birthday date dafault '1997-01-01'
# 修改表- 修改字段 : 重名命名 - 改变字段的名称
# alter table 表名 change 原名 新名 类型及约束;
alter table students change birthday birth date dafault '1997-01-01'
# 修改表 - 删除字段
# alter table 表名 drop 列名;
alter table students drop high;
# 删除表
drop table "表名";
drop database "数据库";
drop table "数据表";
**# 新增**
# 全列插入 insert [into] 表名 values(...)
# 主键字段 - 可以用 0 null default 来占位
# 向 classes 表中插入一个班级
insert into class values(0,"python") # 0 是 id位置 用 0 表示自增
# 枚举类型的插入 1-男 2-女
insert into students values(0,'tony',18,1,1,'1997/01/01');
# 部分插入
# insert into 表名(列1,...) values(值1,...)
insert into students(name,gender) values("小乔",2);
# 多行插入
insert into students(name,gender) values("小乔",2)("周瑜",1)("孙策",1);
**#修改**
# update 表名 set 列1=值1,列2=值2...where 条件;
update students set age=18; # 全表修改
update students set age=28 where name = "tony"; # 条件筛选修改
# 物理删除
# delete from 表名 where 条件
delete from students where name = 'tony';
# 逻辑删除 - 用一个字段表示此条数据已经不能在再用 例如 :is_delete字段
alter table students add is_delete int default 0 ;
updata students set is_delete = 1 where id = 6;
**# 基本查询**
# select 去重选项 字段列表[as 字段名] from 数据表 where [group by 子句] [having 子句] [order by 子句 ] [order by 子句] [limit子句];
# 查询全部
select * from students;
# 去除重复字段查询
select distinct name from students;
# 查询指定列
select id,name from students;
# 可以使用 as 为列或表指定别名
select id, cls_id as "班级" from students;
select student.name as "姓名" from students;
#数据库数据查询
# 从 第10行开始获取20行数据,
# 分页(从start开始,获取count条数据)
select * from students limit 10,20;
# join查询连接查询
select * from table1 inner join table2 on table1.firstid = table2.secondid;
select * from table1 left join table2 on table1.firstid = table2.secondid;
select * from table1 right join table2 on table1.firstid = table2.secondid;
# 子查询
select * from students where height = (select max(height) from students);
# 创建数据表
create table student(sid int(4) not null primary key,sname varchar(36),gid int(4) not null);
# 添加外键
alter table tablesname add constraint prinaryname foreign key(id) references table2(idname)
# 删除外键
alter table tablename1 drop foreign key prinaryname1;
**# 比较运算符**
> # 大于
< # 小于
>= #大于等于
<= #小于等于
!= / <> # 不等于
**# 逻辑运算符**
and # 和
or # 或者
not # 非/取反
**# 模糊查询**
# like
# % 替换1个或者多个
# _ 替换1个
# 查询姓名中 以 "李" 开始的名字
select * from students where name like '李%';
# 查询姓名中 带有 "李" 开始的名字
select * from students where name like '%李%';
# 查询有2个字的名字
select * from students where name like '__';
# 查询至少有2个字的名字
select * from students where name like '__%';
# where 子句(条件)范围查询
# in 表示在一个非连续的范围内
select * from students where id in(1,3,8);
select * from students where id not in(1,3,8);
# between...and... 表示在一个连续的范围内
select * from students where id between 3 and 8;
# id 3-8 男生
select * from students where id between 3 and 8 and gender=1;
select * from students where id not between 3 and 8 and gender=1;
# 判断为空 is null
# 非空 is not null
# 查询 cls_id 为空的数据
select * from students where cls_is is null;
**# 聚合函数**
# 总数 count()
# 查询学生总数
select cout(*) from students;
# 查询男性有多少人
select cout(*) as "男性人数" from students where gender = 1;
# 最大值/最小值 max / min
select max(age) from students;
# 查询男性 ID的最大值
select max(id) from students where gender=1;
# 查询女性的最高身高
select max(height) from students where gender = 2;
# 求和 sum()
# 查询男生的年龄和
select sum(age) from students where gender =1;
# 平均值 avg() 默认保留4位
# 查询未删除女生的ID的平均值
select avg(id) from students where gender=2 and is_delete=0;
# 四舍五入 round(123.23,1) 保留 1 位小数
# 计算所以人的平均年龄,保留2位小数
select round(ave(age),2) from students;
# 分组 group by
# 按照性别分组,查询所有的性别
select gender from students group by gender;
# 计算男生和女生的人数
select gender as "性别",count(*) from students where gender = 1;
select gender as "性别",count(*) from students where gender = 2;
select gender as "性别",count(*) from students group by gender;
# 男女同学最大年龄
select gender as "性别",max(age) from students group by gender;
# group_concat() 查看组内信息 ( 把所有内容写在同一个格子里)
# 查询同种性别中的姓名
select gender as "性别",max(age),group_concat(name) from students group by gender;
# 查询组内年龄,姓名(name + age) 写在同一框格中
select gender as "性别",group_concat(name,age) as "组内姓名" from students group by gender;
# having 分组之后的筛选
# 查询男生女生总数大于2s
select gender as "性别",count(*) from students group by gender having count(*) > 2;
# 查询男生 女生 总数大于 2 的姓名
select count(*),group_concat(name) from students group by gender having count(*) > 2;
# 查询平均年龄超过18岁的性别,以及姓名 having age(age) > 18
select gender,group_concat(name),avg(age) from studernt group by gender having avg(age) > 18;
# 数据库排序 order by / asc 从小到大排列,即升序,默认 / desc 降序
# 查询年龄在 18-26 岁之间的男同学 ,按照年龄从小到大排序
select * from students where age between 18 and 26 group by gender having gender =1 order by age;
select * from students where age between 18 and 26 and gender =1 order by age;
# 查询年龄在 18-34 岁之间的女同学,身高从高到矮排序
select * from students where age bewteen 18 and 34 and gender =2 order by height desc;
# order by 多个字段
# 查询年龄在18-28之间,女性,身高从高到矮,如果身高相同 按年龄从小到大排列
select * from students where age bewteen 18 and 28 and gender = 2 order by height descc,age asc;
# 按年龄从小到大,身高从高到矮
select * from students order by age asc,height desc;
# 分页 limint start count
# 限制查询出来的结果
select * from students limit 2; # 查询前两条数据
select * from students limit(3,5); # 从3位置开始 查询5条数据
# 数据库的连接查询
inner join ...on # 内连接
select * from students inner join classes on students.cls_id = class.id;
# 按照要求显示姓名、班级
select students.name,classed.name from students inner join classes on students.cls_id = class.od;
left join ...on # 左连接
light join ...on # 右连接
**# 子查询**
# 查询最高的男生的信息
select * from students where height = (select max(height) from students where gender =1 );
# 查询高于平均身高的信息
select * from students where height > (selecct avg(height) from students);
# 自关联 - 省市区三级联动
select * from provinces as p inner join cities as c on p.'provinceid' = c.'provineid' having p.province ="吉林省"
# 视图创建
create view v_stu_score as (select * from table1);
# 视图查看(查看表时会显示所有视图)
show tables;
# 使用视图((v_stu_score 视图名))
select * from v_stu_score;
# 删除视图
drop view v_stu_sco;
- 定义 : 索引是一种特殊的文件(InnoDB 数据表上的索引是表空间的一个组成部分),包含着对数据表里所有记录的引用指针
- 效率 :300 万左右数据量,可以提高一倍的查询效率
- 索引建立 :
- 主键自动建立索引
- 频繁作为查询条件的字段因该建立索引
- 查询中与 其他表关联的字段,外键关系建立索引
- 在高并发的情况下创建复合索引
- 查询中排序的字段、排序字段若通过索引去访问大大提升排序速度(建立索引的顺序跟排序的顺序保持一致)
# 查看索引
show index from "表名";
# 创建索引
create [unique] index "索引名称" on "表名(字段名称(长度))"
create index idx_name on students(name(30)); # 单值索引
# 删除索引
drop index "索引名称" on "表名";
事务:就是一系列的操作,一旦出现问题则进行回滚
#事务开启
begin "或" start transaction
# 事务提交
commit;
# 回滚事务
rollback;
# 查看user表结构
desc user;
# 查看所有用户
select host,user,authentication_string from user;
# 创建账号& 授权
grant 权限列表 on 数据库 to '用户名'@'访问主机' identified by '密码';
# 退出root登陆
quit
存储引擎 Innodb : 最大程度的支持并发
使用表空间进行数据存储 (on/off)
系统表空间会产生I/O瓶颈,刷新数据的时候是顺序进行的,所以会产生 I/O 瓶颈,独立空间可以同时向多个文件刷新数据
支持外键、支持事务、行级锁最大程度支持并发,缓存索引、缓存真实数据、表空间大
# 查看存储引擎
show engines;
show variables like 'innodb_file_per_table' # 查看是否独立表空间
set global innodb_file_per_table = off # 关闭独立表空间
存储引擎 MyISAM : 适合非事务应用,只读类应用
支持表数据压缩(linux),压缩后只能读,不能写
不支持外键,不支持事务
表级锁,不适合高并发
只缓存索引,不缓存真实数据
表空间较小
myisampack -b -f myIsam.MYI
# 表损坏修复
reapir table "表名";
存储引擎 CSV : 文本存储表的内容
.CSM 文件存储表的元数据,如表的状态、数据量等
.frm 文件存储表结构信息
适合做数据交换的中间表
不支持主键,不支持自增长,所有列不能为空
存储引擎 Memory : 内存型
支持 hash(用于等值查找)、btree索引(范围查找)
所有字段长度固定
不支持 BLOB 和 text 等大字段
使用表级锁
重启后数据会小时
- 网络链路层
- 能够与mysql服务器建立链接的客户端(java、C、python、.net)
- 服务层(msyql server)
- 包含系统管理和控制工具、链接池、SQL接口、解析器、查询优化器、缓存
- 链接池 : 负责存储和管理客户端的数据路链接,一个线程负责管理链接
- 系统管理和控制工具 :备份恢复、安全管理、集群管理
- SQL接口 :接口客户端发送过来的sql命令,并返回查询结果。
- 解析器 :负责将请求的 SQL 解析生成一个 解析树,然后根据mysql规则进行检查树是否合法。
- 查询优化器 :当解析树 通过解析器语法检查后,交给优化器及逆行转化成执行计划,然后与存储引擎交互
- 缓存 : 小缓存组成(表缓存、记录缓存、权限缓存、引擎缓存)、如果查询缓存名中,则执行查询缓存
- 引擎层
- 存储引擎层 :负责数据的存储和提取
- 系统文件层
- 负责数据库的数据和日志存储在文件系统中,并完成与存储引擎的交互
- 日志文件、数据文件、配置文件、pid文件、socket文件
- 日志文件
- error log 日志 : 错误日志 ( show variables like %log_err%)
- 慢查询日志 :记录一般查询语句 ( show variables like %general% )
- 二进制日志 (binary log):记录对数据库的执行更改记录语句发生时间,执行时长,用于数据库恢复 和主从复制。( show binary logs )
- 慢查询日志 (Slow query log)
- 配置文件
- 用于存放 mysql所有配置信息文件、比如my.cnf、my.ini等
- 数据文件
- db.opt 文件 : 记录这个库默认使用的字符集和校验规则
- frm 文件 :存储 与相关的元数据信息,包括表结构的定义信息,每一张表都会有 一个frm文件
- MYD文件 :MySAM存储引擎专用,存放MySAM表的 数据,每一张表都会有一个 .myd文件
- MYI文件 :MySAM存储引擎专用,存放MySAM表 的索引相关的信息,每一张表都会有一个 .myi文件
- ibd 文件和IBDATA文件 : 存放InnoDB的数据文件(包括引擎)。InnoDB存储引擎有两种表空间方式 :独立表空间,共享表空间 独享表空间使用.ibd文件夹存放数据,且每一张InnoDB表对应一个.ibd表对应一个 .ibd文件。共享表空间 使用.ibdata文件,所有表共享使用一个(或 多个、自行设置).ibdata文件
- ibdata1 文件 :系统表空间数据文件,存储表 元数据,Undo日志等
- ib_logfile0、ib_logfile1文件、Redo log日志文件
- pid文件 :进程文件,存放自己的进程id
- socket文件 :客户端可用不 通过TCP/IP 网络而 直接使用socket链接数据库
undo log
数据库事务开始前,将要需修改的记录存放在Undo日志里,当事务回滚时,数据崩溃时,可以利用Undo日志,撤销未提交事务 对数据库产生影响
Undo Log 产生和销毁 : Undo Log在事务开始前产生,事务在提交时,并不会立刻删除undo log , innodb会将该事务对那个的undo log放入到 删除列表中,后面会通过后台线程 purge thread进行回收处理。Undo Log 属于逻辑日志,记录一个变化 过程,发生一个动作会记录一个相反的 updata
Undo Log 存储 :采用段的方式进行管理和记录
Redo Log
以恢复操作为目的,在数据库发生意外时重新操作
在事务中修改任何数据,将最新的数据 备份存储的位置
随着事务操作执行,就 会生成,在事务提交时 会产生写入Log Buffer,而 不是将事务 提交立刻写入磁盘文件
随着事务的提交就立刻写入磁盘文件
Redo log工作原理
Bin log 日志
redo log 属于 innodb引擎所特有的日志,而 mysql server也有自己的日志,叫做binary log(二进制文件) 简称 bin log
记录数据表结构变更及表数据修改的二进制日志
以事件形式记录,包含语句所执行的消耗时间
应用场景 : 主从同步(开启 bin log,传递给从库)、数据恢复
文件记录模式 row模式(对每一行进行记录,批量操作是日志量巨大)
statment 模式(每条记录的sql都会就在master的 binlog中,在sql中含有变量时,比如last inset id 等时,不可靠)
mixed 模式(正常用sql,识别用 row模式)
# binlog 状态查询(开启状态)
show variables like 'log_bin';
# 开启bin log 文件 需要修改 my.cnf 或my.ini配置文件,在增加 log_bin = mysql_bin_log,重启mysql服务
# log-bin = ON
# log-bin-basename=mysqlbinlog
binlog-format=ROW
log-bin = mysqlbinlog
# 使用show binlog events命令
show binary logs; // 展示 bin log 文件
show master status; // 现在使用的 bin log文件
show binlog events; // 包含哪些事件
show binlog events in ‘mysqlbinlog.000001’;
# 系统cmd下查看 binlog文件
mysqlbinlog “文件名”
mysqlbinlog “文件名” > 'test.sql'
# 使用 binlog恢复数据
# 按指定时间恢复
mysqlbinlog -- start -datetime = -- stop-datetime=“2020-11-10 18:00:00” mysqlbinlog.00002 | mysql -uroot -p1234
# 按事件位置号恢复
mysqlbinlog -- start -position=154 stop-position=957 mysqlbinlog.00002 | mysql -uroot -p1234
# 删除binlog文件
purge binary logs to 'mysqlbinlog.00001'; // 删除指定文件
purge binary logs before '2020-11-10 18:00:00'; //删除指定时间之前的文件
reset master; // 清除所有文件
master 将改变记录到二进制日志。这些记录过程叫做二进制日志事件,binary log events.
slave 将 master 的binary log events 拷贝到它的中继日志
slave 重做中继器日志中的事件,将改变应用到自己的数据库中,mysql复制是异步的且串行
# 主机配置文件 - my.ini
# 开启二进制日志
# log-bin = 自己本地的路径/mydqlbin
log-bin = mysql-bin
# 指定主服务器ID
server-id =1
# 需要从新启动服务
# 从机配置文件 - mysqld.cnf
server-id = 2 # 不要和主服务器ID重复
log-bin = /自己本地的路径/mysql-bin.log
service mysql restart # 重启服务
# MySQL8.0主从配置 对从机进行授权
# 192.168.0.183 为从机IP
# 用户名 repl 密码 : 123
CREATE USER 'repl'@'192.168.0.183' IDENTIFIED WITH mysql_native_password BY '123';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.0.183';
show master status;
# 展示 :( file:mysql-bin.000004 Position : 680 )
# 对从机进行配置
# 主机IP地址
change master to master_host = '192.168.0.161',
# 主机用户名
master_user = 'juran',
# 密码
master_password = '123',
master_log_file = 'binlog.000004',
master_log_pos= 908;
定义 :根据某个区间值,特定值 或 hash函数执行数据的聚合,让那个数据根据规则分布在不同的分区中
类型 :Range分区、List分区、Hash分区
注意事项 :避免跨分区查询、最好在where从句中包含分区值,具有唯一索引的表,主键或唯一索引必须是分区键的一部分
partition # 分区
# 查看数据库支持项
show plugins;
# 数据库分区特点
# 逻辑上为一个表,在物理上存储在多个文件中
create table `login_log`(
login_id int(10) unsigned not null comment '登录用户id',
login_time timestamp not null default current_timestamp,
login_ip int(10) unsigned not null comment '登录ip'
)engine=innodb default charset=utf8
partition by hash(login_id) partitions 4; # 将这个表划分4个分区
# 删除分区(表中的数据也会删除)
alter table login_log_list drop partition p0;
# 分区的数据迁移(p1分区数据迁移到table arch_login_log_list )
alter table login_log_list exchange partition p1 with table arch_login_log_list;
根据键值的范围,将数据存储到表的不同分区中
分区需要连续,但是不能重叠,分区不包含上限值,包含下限值
常用分区 日期,时间,可以定期按分区记性清理历史数据
# 建立Range分区
create table `login_log_range`(
login_id int(10) unsigned not null comment '登录用户ID',
login_time timestamp not null default CURRENT_TIMESTAMP,
login_ip int(10) unsigned not null comment '登录ip'
)engine=innodb
partition by range(login_id)(
partition p0 values less than(10000), # 实际范围0-9999
partition p1 values less than(20000), # 实际范围10000-19999
partition p2 values less than(30000),
partition p3 values less than maxvalue # 存储大于30000的数据
);
根据 mod (分区键、分区值)把数据行存储到表的而不同分区内
数据可以平均分布在各个分区
Hash分区的键值必须是 INT 类型的值,或者通过函数可以转化为 INT类型
# 建立Hash分区表
create table `login_log`(
login_id int(10) unsigned not null comment '登录用户ID',
login_time timestamp not null default CURRENT_TIMESTAMP,
login_ip int(10) unsigned not null comment '登录ip'
)engine=innodb default charset=utf8
partition by hash(login_id) partitions 4;
# 时间类型用函数转化为整数
create table `login_log`(
login_id int(10) unsigned not null comment '登录用户ID',
login_time timestamp not null default CURRENT_TIMESTAMP,
login_ip int(10) unsigned not null comment '登录ip'
)engine=innodb default charset=utf8
partition by hash(UNIX_TIMESTAMP(login_time))partitions 4;
按分区键值取值的列表进行分区
同范围分区一样,各分区的列表值不能重复
每一行数据必须能找到对应的分区列表,否则数据插入失败
create table `login_log_list`(
login_id int(10) unsigned not null comment '登录用户ID',
login_time timestamp not null default CURRENT_TIMESTAMP,
login_ip int(10) unsigned not null comment '登录ip',
login_type int(10) not null
)engine=innodb
partition by list(login_type)(
partition p0 values in(1,3,5,7,9),
partition p1 values in(2,4,6,8)
);
import pymysql
# pymysql.connect() 此对象为pymysql连接数据库的类
# 创建Connection连接对象实例
conn = pymysql.connect(host='localhost', port=3306, user='root', password='mysql', database='python1', charset='utf8')
# 创建游标
cursor = conn.cursor()
# 数据查询
sql = "select id,name from students where id = 7"
# 数据更新
sql = 'update students set name="刘邦" where id=6'
# 数据删除
sql = 'delete from students where id=6'
# 载入查询语句, 返回的是执行成功的条数,可以用于判断是否执行成功
cursor.execute(sql)
# 接收查询结果、就要一条
result = cursor.fetchone()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
# 数据更新/或数据新增时
conn.commit() # 提交
# 事务的回滚
conn.rollback()
# 查看所有数据
cursor.fetchall()
cursor.fetchone()
cursor.fetchmany(2)
框架定义 : 一个数据库 ORM框架
原生开发模式
from sqlalchemy import create_engine # 引擎
# 连接数据库
HOSTNAME = '127.0.0.1' # IP
PORT = '3306' # 端口
DATABASE = 'demo0417' # 数据库名称
USERNAME = 'root' # 用户名
PASSWORD = 'root' # 密码
# 驱动 方式 1 — 用 pymysql 连接
DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
# 驱动 方式 2 — 用 mysqlconnector 连接
pip install mysqlconnector
DB_URI = 'mysql+mysqlconnector://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
# 创建数据库引擎
engine = create_engine(DB_URI)
# 创建连接
with engine.connect() as conn:
# 执行SQL语句,原生的SQL写法
result = conn.execute("select * from users")
print(result.fetchone()) # 一条数据,返回一个元组
print(result.fetchall()) # 多条数据
SQLAlchemy 常用数据类型
Integer # 整形。
Float # 浮点类型。
Boolean # 传递True/False进去。
DECIMAL # 定点类型。
enum # 枚举类型。
Date # 传递datetime.date()进去。
DateTime # 传递datetime.datetime()进去。
Time # 传递datetime.time()进去。
String # 字符类型,使⽤时需要指定⻓度,区别于Text类型。
Text # ⽂本类型。
LONGTEXT # ⻓⽂本类型。
Column常⽤参数
default # 默认值。
nullable # 是否可空。
primary_key # 是否为主键。
unique # 是否唯⼀。
autoincrement # 是否⾃动增⻓。
sqlalchemy # 常⽤数据类型
Column # 常⽤参数
onupdate # 更新的时候执⾏的函数。
name # 该属性在数据库中的字段映射
ORM 框架使用
通过类的方式进行操作数据库,底层转化为 select 原生语句
"""
class Users(object):
name = "lcl"
pass
user = Users(1,"lcl")
user.add()
类 :class 一张表
属性 :name 表字段
对象 : 一条记录
"""
from sqlalchemy import create_engine
# 导入ORM的类
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column,Integer,String
HOSTNAME = '127.0.0.1' # IP
PORT = '3306' # 端口
DATABASE = 'demo0417' # 数据库名称
USERNAME = 'root' # 用户名
PASSWORD = 'root' # 密码
DB_URI = 'mysql+mysqlconnector://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
engine = create_engine(DB_URI)
# 实例化ORM的类 所有模型 继承自 declarative_base
Base = declarative_base(engine)
# 使用 ORM 操作数据库, 继承 Base = declarative_base(engine) 的基类
class Students(Base):
# 定义表的名字
__tablename = "students"
# 创建一些字段 通过 Column 进行创建
id = Column(Interger, primary_key = True,autoincrement = True) # 数据类型、主键、自增
name = Column(String(50),nullable = False ) # 字符串类型、不能为空
gender = Column(Interger,default = 1 ,comment = "1 为 男,2为女")
# 模型映射到数据库中
Base.metadata.create_all()
ORM 增删改查
from sqlalchemy import create_engine
from sqlalchemy.declarative import declarative_base
from sqlalchemy import Column,Integer,String
from sqlalchemy.orm import sessionmaker
HOSTNAME = '127.0.0.1' # IP
PORT = '3306' # 端口
DATABASE = 'demo0417' # 数据库名称
USERNAME = 'root' # 用户名
PASSWORD = 'root' # 密码
DB_URI = 'mysql+mysqlconnector://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
engine = create_engine(DB_URI)
Base = declarative_base(engine)
class Article(Base):
# 定义表的名字
__tablename = "article"
# 创建一些字段 通过 Column 进行创建
id = Column(Interger, primary_key = True,autoincrement = True) # 数据类型、主键、自增
name = Column(String(50),nullable = False ) # 字符串类型、不能为空
# 模型映射到数据库中
Base.metadata.create_all()
# 数据在内存中
article = Article(name = 'lcl')
# 将数据保存到数据库中
# 实例化 sessionmaker 类
# 类的实例化 __call__ 将类变成方法 去 调用
Session = sessionmaker( bind = engine )
session = Session()
# 数据添加 - 数据尚未提交
session.add(article)
# 数据提交
session.commit()
print(article.name)
print(article.id)
# 一次添加多条数据
session.add_all([article,article1])
session.commit()
ORM 增删改查-方法封装
from sqlalchemy import create_engine
from sqlalchemy.declarative import declarative_base
from sqlalchemy import Column,Integer,String
from sqlalchemy.orm import sessionmaker
HOSTNAME = '127.0.0.1' # IP
PORT = '3306' # 端口
DATABASE = 'demo0417' # 数据库名称
USERNAME = 'root' # 用户名
PASSWORD = 'root' # 密码
DB_URI = 'mysql+mysqlconnector://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
engine = create_engine(DB_URI)
Base = declarative_base(engine)
class Article(Base):
__tablename__ = 'art'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(50), nullable=False)
content = Column(String(50))
author = Column(String(50))
def __str__(self):
return "Article(title:{},content:{},author:{})".format(self.title, self.content, self.author)
# 模型映射到数据库中
Base.metadata.create_all()
# 写入数据
def add_data():
article = Article(title='Python', content='人生苦短,我用Python', author='龟叔')
session.add(article)
session.commit()
# 数据的查询
def search_data():
data = session.query(Article).all() # all 查询所有
for item in data:
print(item)
print(item.title)
print(item.content)
# print(data)
# 条件 filter 方法 1 返回的是列表
data = session.query(Article).filter(Article.title=='Python').all()
for item in data:
print(item)
# filter_by 方法 返回的是列表
data = session.query(Article).filter_by(title='JAVA').all()
for item in data:
print(item)
print(data)
# 查询第一条
data = session.query(Article).first()
print(data)
# get 方法 传的ID 不存在 返回None
data = session.query(Article).get(1) # 查询 ID 为 1
data = session.query(Article).get(2) # 查询 ID 为 2
print(data)
# ORM 修改
def update_data():
# 查询出要修改的这条记录
article = session.query(Article).first()
article.title = 'lgcoder'
print(article.title)
# session.rollback() 回滚
# print(article.title)
session.commit()
# ORM 删除
def delete_data():
# 查询出要修改的这条记录
article = session.query(Article).first()
# 误操作
# is_delete 1 未删除 0 删除
# 修改操作 is_delete 1=>0
session.delete(article)
session.commit()
query 可用参数
"""
1. 模型对象。指定查找这个模型中所有的对象。
2. 模型中的属性。可以指定只查找某个模型的其中⼏个属性。
3. 聚合函数。
func.count:统计⾏的数量。
func.avg:求平均值。
func.max:求最⼤值。
func.min:求最⼩值。
func.sum:求和
"""
from sqlalchemy import function
# 总条数
result = session.query(func.count(Article.id)).first()
# 求平均值
result = session.query(func.avg(Article.price)).first()
过滤条件
# 这些过滤条件都是只能通过filter⽅法实现的
# 等于 equals
query.filter(User.name == 'ed').all()
# 不等于 not equals
query.filter(User.name != 'ed')
# 模糊查询 like
query.filter(User.name.like('%ed%'))
# 在...中 in
query.filter(User.name.in_(['ed','wendy','jack']))
# 同时,in也可以作⽤于⼀个Query
query.filter(User.name.in_(session.query(User.name).filter(User.name.like ('%ed%'))))
# 不在...中 not in
query.filter(~User.name.in_(['ed','wendy','jack']))
# is null 为空
query.filter(User.name==None)
query.filter(User.name.is_(None))
# 不为空 is not null
query.filter(User.name != None)
query.filter(User.name.isnot(None))
# 条件并列 and
from sqlalchemy import and_
query.filter(and_(User.name=='ed',User.fullname=='Ed Jones'))
# 或者是传递多个参数
query.filter(User.name=='ed',User.fullname=='Ed Jones')
# 或者是通过多次filter操作
query.filter(User.name=='ed').filter(User.fullname=='Ed Jones')
from sqlalchemy import or_ query.filter(or_(User.name=='ed',User.name=='wendy'))
外键&外键约束
# 通过 ForeignKey类来实现,并且可以指定表的外键约束
class Article(Base):
__tablename__ = 'article'
id = Column(Integer,primary_key=True,autoincrement=True)
title = Column(String(50),nullable=False)
content = Column(Text,nullable=False)
# 外键 user.id user 表 中的 id 字段
uid = Column(Integer,ForeignKey('user.id'))
def __repr__(self):
return "" % self.title
class User(Base):
__tablename__ = 'user'
id = Column(Integer,primary_key=True,autoincrement=True)
username = Column(String(50),nullable=False)
# 外键查询(原理)
article = session.query(Article).first()
uid = article.uid
user = session.query(User).get(uid)
print(user)
# 外键查询
user = session.query(User).filter(User.id == Article.uid).first()
# 外键约束
RESTRICT:⽗表数据被删除,会阻⽌删除。默认就是这⼀项。
NO ACTION:在MySQL中,同RESTRICT。
CASCADE:级联删除。
SET NULL:⽗表数据被删除,⼦表数据会设置为NULL
# 通过 ForeignKey类来实现,并且可以指定表的外键约束
class Article(Base):
__tablename__ = 'article'
id = Column(Integer,primary_key=True,autoincrement=True)
title = Column(String(50),nullable=False)
content = Column(Text,nullable=False)
# 指定外键关系、执行外键约束删除关系
uid = Column(Integer,ForeignKey('user.id',ondelete='CASCADE'))
数据表对应关系
# 1对 n 的关系
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
class Article(Base):
__tablename__ = 'article'
id = Column(Integer,primary_key=True)
email_address = Column(String,nullable=False)
user_id = Column(Integer,ForeignKey('users.id'))
# 一对多的关系,backref 反向访问属性
author = relationship('Author',backref="article")
def __repr__(self):
return "" % self.email_address
class Author(Base):
__tablename__ = 'author'
id = Column(Integer,primary_key=True)
name = Column(String(50))
fullname = Column(String(50))
password = Column(String(100))
# 一对多的关系 relationship, backref(反向查询)
article = relationship("Article",backref="author")
# 查询 (文章,作者)
article = session.query(article).first()
print(article.author)
# 添加单条数据
user = Article(username = "lcl")
article = Article(title = "python",context ="xxxxx")
article.author = user
session.add(article)
session.commit()
# 添加多条文章记录
user = User(username = "lcl")
article1 = Article(title="python1",context="xxxx1")
article2 = Article(title="python2",context="xxxx2")
article1.author = user
article2.author = user
session.add(article1)
session.add(article2)
session.commit()
# 1对1 的关系
class User(Base):
__tablename__ = "user"
id = Column(Integet,primary_key = True, autoincrement= True)
username = Column(String(50),nullable = False)
**# uselist= False 指定关系为 1 vs 1**
extend = relationship("UserExtend",uselist= False)
class UserExtend(Base):
__tablename__ = "user_extend"
id = Column(Integet,primary_key = True, autoincrement= True)
school = Column(String(50))
# 外键
uid = Column(Integer,Foreignkey("user.id"))
user = relationship("User",backref = "extend")
# n 对 n 的关系
# 多对多,班级对老师,老师对班级
# 班级表 —— 中间表(班级表ID + 老师表 ID) —— 老师表
from sqlalchemy import Table
# 中间表定义
techer_class = Table(
"techer_classes", # 中间表名
Base.metadata,
Column("techer_id",Interger,ForeignKey("techer.id")) # 中间表字段
Column("classes_id",Interger,ForeignKey("classes.id")) # 中间表字段
)
class Teacher(Base):
__tablename__ = "teacher"
id = Column(Integet,primary_key = True, autoincrement= True)
name = Column(String(50))
# 外键关系、指定中间表
classes = relationship("Classes",backref = "teachers",secondary= teacher_classes)
class Classes(Base):
__tablename__ = "classes"
id = Column(Integet,primary_key = True, autoincrement= True)
name = Column(String(50))
# 多对多数据的添加
teacher1 = Teacher(name= "lcl")
teacher2 = Teacher(name ="wzh")
classes1 = Classes(name = "初级班")
classes2 = Classes(name = "进阶班")
teacher1.classes.append(classes1)
teacher1.classes.append(classes2)
teacher2.classes.append(classes1)
teacher2.classes.append(classes2)
session.add(teacher1)
session.add(teacher2)
session.commit()
# 查询老师对应的班级
teacher = session.query(Teacher).first()
print(teacher)
for i in teacher.classes:
print(i)
# 班级对应的老师
classes = session.query(Classes).first()
for i in classes.techers:
print(i)
数据库高级查询
# 排序
articles = session.query(Article).order_by(Article.id).all() # 升序
articles = session.query(Article).order_by(Article.id.desc()).all() # 降序
articles = session.query(Article).order_by(-Article.id).all() # 降序
for article in articles:
print(articles)
# 默认排序方法 - __mapper_args__ 魔术方法
class Article(Base):
__tablename__ = 'article'
id = Column(Integer,primary_key=True,autoincrement=True)
title = Column(String(50),nullable=False)
__mapper_args__ = {
"order_by" : id
}
# 定义魔术方法后,查询你中无需 order_by
articles = session.query(Article).all()
for article in articles:
print(articles)
"""
limit : 可以限制每条查询的时候,只查询几条数据
offset : 可以限制查询数据的时候过滤掉前面多少条
切片 : 可以对Query 对象使用切片操作,来获取想要的数据
"""
# limit 限制 —— 前三条数据
articles = session.query(Article).limit(3).all()
for article in articles:
print(articles)
# offset偏移量 查询 3-5条数据
articles = session.query(Article).offset(3).limit(3).all()
for article in articles:
print(articles)
# 切片 取 2-4 条
articles = session.query(Article).all()[2,5]
for article in articles:
print(articles)
# 分组、having、join方法
# group_by 分组、一般和聚合 函数连用
results = session.query(User.gender).group_by(User.gender).all()
results = session.query(User.gender,func.count(User.id)).group_by(User.gender).all()
for result in results
print(result)
# having ,分组后过滤
results = session.query(User.age,func.count(User.id)).group_by(User.age).having(User.age<18).all()
print(results)
# 子查询
# 查询和 李 相同的城市 和年龄的人
user = session.query(User).filter(User.username =="李").first()
print(user.city)
print(user.age)
result = session.query(User).filter(User.city == user.city,User.age == user.age).all()
for data in result:
print(data)
sub = session.query(User.city.label("city"),User.age.label("age")).filter(User.username =="李").subquery()
result = session.query(User).filter(User.city == sub.c.city,User.age == sub.c.age).all()
Flask - SQLAlchemy插件
# 插件安装
pip install flask-sqlalchemy
from flask import Flask
from flask sqlachemy import SQLAlchemy
# localhost
HOSTNAME = '127.0.0.1' # IP
PORT = '3306' # 端口
DATABASE = 'demo0417' # 数据库名称
USERNAME = 'root' # 用户名
PASSWORD = 'root' # 密码
DB_URI = 'mysql+mysqlconnector://{}:{}@{}:{}/{}'.format(USERNAME,PASSWORD,HOSTNAME,PORT,DATABASE)
app = Flask(__name__)
# 创建引擎,加载配置文件 SQLALCHEMY_DATABASE_URL
# engine = create_engine(DB_URL)
app.config['SQLALCHEMY_DATABASE_URL'] = DB_URL
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 操作数据库,通过db 操作数据库
db = SQLAlchemy(app)
# 创建模型(继承自 db.Model)
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer,primary_key = True,autoincrement= True)
name = db.Column(db.String(50))
class Article(db.Model):
__tablename__ = 'article'
id = db.Column(db.Integer,primary_key = True,autoincrement= True)
title = db.Column(db.String(50))
# 定义外键
uid = db.Colunm(db.Integer,db.Foreignkey('users.id'))
# 表关联
author = db.relationship('User',back='articles')
# 将Model 映射到数据库中
db.create_all()
# 数据添加
user = User(name = 'lcl')
article = Artcile(title = 'python')
article.author = user
db.session.add(article)
db.session.commit()
# 查询数据
user = User.query.all()
print(user)
# 排序
users = User.query.order_by(User.id.desc()).all()
print(users)
@app.route("/")
def index():
return "首页"
if __name__ == '__main__':
app.run(debug = True)
基准测试定义 :测量和评估软件性能指标的活动,用于建立某时刻的性能基准,便于当系统发生软硬件变化时,评估变更带来的影响
对系统设置压力测试,用于评估服务器的处理能力
建立mysql服务性能基线
# 参数命名
mysqlslap --auto-generate-sql # 由系统自动生成SQL脚本进行测试
mysqlslap --auto-generate-sql-add-autoincrement # 在生成的表中增加自增ID
mysqlslap --auto-generate-sql-load-type # 指定测试中使用的查询类型
mysqlslap --auto-generate-sql-write-number # 指定初始化数据时生成的数据量
mysqlslap --concurrency # 指定并发线程的数量
mysqlslap --engine # 指定要测试表的存储引擎,可以用逗号分割多个存储引擎
mysqlslap --no-drop # 指定不清理测试数据
mysqlslap --iterations # 指定测试运行的次数
mysqlslap --number-of-queries # 指定每一个线程执行的查询数量
mysqlslap --debug-info # 指定输出额外的内存及CPU统计信息
mysqlslap --number-int-cols # 指定测试表中包含的INT类型列的数量
mysqlslap --number-char-cols # 指定测试表中包含的varchar类型的数量
mysqlslap --create-schema # 指定了用于执行测试的数据库的名字
mysqlslap --query # 用于指定自定义SQL的脚本
mysqlslap --only-print # 并不运行测试脚本,而是把生成的脚本打印出来
# 测试案例
mysqlslap --concurrency=1,50,100,200 --iterations=3 --number-int-cols=5 --number-char-
cols=5 --auto-generate-sql --auto-generate-sql-add-autoincrement --engine=myisam,innodb
--number-of-queries=10 --create-schema=test
定义 :模拟优化器执行 sql 查询语句,分析查询语句的表结构性能瓶颈
解析内容 :
- 表的读取顺序
- 数据读取操作的操作类型
- 哪些索引可以被使用
- 哪些索引实际被使用
- 表之间的引用
- 每张表有多好啊行被优化器查询
explain select * from t1,t2,t3 where t1.id=t2.id and t1.id = t3.id and t1.other_column='';
explian语句解析
- id— 表的读取加载顺序 (id相同、执行顺序由上而下,id不同、id值越大优先级越高、越先被执行)
- select_type — 数据读取操作的操作类型(simple 简单的查询、primary 查询中若包含任何负责的子部分、最外层查询则被标记,subquery 在select 或where列表中包含了子查询,union 若第二个select出现在union之后,则被标记为union,union result 从union表获取结果的select)
Sqlmap探测工具
功能定义 : 一种安全探测测试工具,用于发现SQL注入的安全漏洞,长用来进行安全扫描
# 程序开启(在文件程序对应目录下)
python sqlmap.py
# 查看当前数据库版本信息
"""
0:只显示python 的tracebacks 信息、错误信息、关键信息
1:显示普通信息,和警告信息
2:显示调试信息 [debug]
3:显示注入使用的攻击载荷[payload]
4:显示HTTP请求头
5:显示http响应头
6:显示HTTP响应体
"""
python sqlmap.py --version
# sqlmap直连数据库(用户名、密码、端口地址、数据库名字)
python3 sqlmap.py -d "mysql://用户名:密码@地址:端口/数据库名字" -f --banner --dbs --users
# sqlmap 探测定向的url地址
"""
探测分7级(0-6),参数用V标识,级别越高,探测的东西越多,时间越长,默认为V1
- u : 指定单一url地址
- m : 探测多个目标(text文件保存在sqlmap文件中,可以保存多行目标, 目标地址需要和数据库有交互的地址)
- r : 指定httl交互文件,文件中保存请求头信息(request header(view parsed))
- c :从配置文件sqlmap.conf 中读取目标文件(需要先修改target url 地址)
--method :http请求方法(get、post、cookie、user-agent)
"""
python sqlmap.py -u "地址" --banner -v2
python sqlmap.py -u "地址" -2 --banner
sqlmap -m "文件路径" --banner
# 针对post方法提交参数的地址通过提交参数来进行测试
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit" --banner
# sqlmap中设置cookie参数
#对登陆界面进行探测的时候需要携带 cookie信息 对登陆之后网站进行探测 也需要携带cookie 信息
--cookie "cookie值" # 指定cookie值
--cookie-del # 删除cookie值
--load-cookies # 在文件中加载cookie
--drop-set-ookie # 设置set-cookie的值
#sqlmap中设置 user-agent
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit" --user-agent
# 随机设置请求user-agent
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit" -- random -agent
# sqlmap 设置代理(ip代理)
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit" --proxy "http:// $haproxy ip:prot"
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit" --proxy-file
#设置http代理服务器认证信息
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit" --proxy-cred user:pwd
--ignore-proxy # 忽略系统范围内的代理服务器
# qlmap 设置探测延迟
-- delay 0.5 # 设置延迟0.5秒
# sqlmap 中设置超时
--timeout 10.5 # (超过一段时间请求没有响应,此测试跳过)
# sqlmap 设置超时连接次数
--retries count # 默认为3次
# sqlmap 中设置随机参数(每次请求期间随机更改其参数名称(长度和类型根据原始值保持一直))
--randomize "参数名称"
# sqlmap 忽略 错误站点提示 401
--ignore -401 # 继续探测
# 指定网站请求的参数分隔符
--param-del = "分隔符"
# 获取后端数据库的版本信息
-- banner
-b
# 获取DBMS当前用户
--current -user
# 获取当前数据库名称
--current -db
# 获取当前主机名
-- hostname
# 探测当前用户是否是数据库管理员
--is -dba
# 会先列举用户,在列举用户密码的hash值
--passwords
# 枚举 DBMS用户 所有用户
--users
# 列举数据库名
--dbs
# 枚举数据库表
--tables # 数据库名字
# 指定具体数据库
-D
# 枚举数据表列
--columns
-T # 指定数据库表
# 枚举数据值
-- dump
# 枚举schema 信息
--schema # (列举数据库管理系统模式,模式列表包括数据库、表、列、触发器、和他们各自的类型。同样的 可以通过参数 --exclude-sysdbs 排除系统数据库)
-sysbds # 除去数据库系统表
# 检索数据表数量
--count #(如果用户只想知道表数据条数)
# 获取数据信息
--start --stop (--start1 --stop3) # 返回前三条数据
# 设置条件获取信息
--where
# 暴力破解表名
--common -table
# 暴力破解列名
# 1、版本小于5.0的无法查询
# 2、数据库 用户的权限过低 无法读取表名
--common-columns
# 读取文件
--file-read # 路径 读取对应文件内容(此路径为绝对路径)
# 检索所有信息 参数
-a
--all # 返回所有的检索信息
# 查询参数
python sqlmap -hh
# 设置持久化http连接(长连接)
--keep-alive
python sqlmap.py -u "地址" --keep-alive --banner -v2
# 设置不接收http body(不接收消息体)
--null -connection
python sqlmap.py -u "地址" --keep-alive --null-connection --banner
# 设置多线程
--thread = 3
python sqlmap.py -u "地址" --thread=3 --banner
# 一键优化(同时添加多个参数)
-o (--keep-alive --null-connection --threads = 3)
python sqlmap.py -u "地址" -o --banner
#**sqlmap自定义检测级别**
# 设置探测等级 (1-5)
# 大于2时、会对cookie等参数进行测试、大于3时,会使用user-agent 和 referer(跳转) 进行测试
--level = 2
python sqlmap.py -u "地址" -o --banner --level = 2
# 设置风险等级
--risk = 2
# 指定探测参数
python sqlmap.py -u "地址" -p "id,user-agent" --banner
# 跳过某些注入参数
python sqlmap.py -u "地址" -skip "参数名称"
# 对注入点存在uri中的,在参数后面加 # 进行指定探测参数
python sqlmap.py -u "127.0.0.1/id#/user/"
# sqlmap检索DBMS(指定探测数据库)
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit" --dbms "数据类型"
# 执行操作系统
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit" --os
# 无效值参数的替换(替换原有的、执行替换的部分)
--invalid-bignum
--invalid-logical
sqlmap -u "url" --data="uname=admin&passwd=amdin&submit=Submit and 恶意sql语句" "
# 自定义注入负载位置
--prefix "设置sql注入payload前缀"
sqlmap -u "127.0.0.1/lesson/id=1" --prefix
--suffix "设置sql注入payload后缀"
# 设置Tamper参数
sqlmap -u "127.0.0.1/lesson/id=1" --tamper "脚本名称"
# 设置DBMS认证
--dbms-cred = username
# 参数 --technique
# 默认情况下所有的方式全部使用一遍
B:Boolean-based blind # (布尔型注入)
E:Error-based # (报错型注入)
U:Union query-based # (可联合查询注入)
S:Stacked queries # (可多语句查询注入)
T:Time-based blind # (基于时间延迟注入)
Q:Inline queries # (嵌套查询注入)
# 设置时间盲注延迟时间(默认5s)
--time-sec
# 设置union联合字段数
--union-cols 12-18
# sqlmap设置union字符
-union-char
# 设置联合查询表
--union-from "表名" # 指定联合查询的表名
# 探测目标指纹信息
-f
--fingerprint