导图社区 数据库及其管理系统(DBMS)
本人目前已知的数据库管理系统及sql语言,数据库管理系统Database management system(简称DBMS),它们都是独立可运行的软件,我们用java写的程序可以连接这些软件让其负责对数据进行维护。
编辑于2023-05-25 10:51:00 山东省数据库及其管理系统(DBMS)
Database
数据库DataBase(简称DB)
存储数据的仓库就称为数据库
在我们的项目中我们使用users目录保存若干的obj文件,每个文件保存一个用户信息,
这个users就可以称为是一个数据库.只是我们维护数据要么手动,要么写java代码读写操作来进行维护.低效,且不通用.
数据库管理系统Database management system(简称DBMS)
它们都是独立可运行的软件,我们用java写的程序可以连接这些软件让其负责对数据进行维护
常见的DBMS:
oracle
mysql
MariaDB
超级管理员 root,自己设计密码(输两遍)
设置默认字符集utf8
TCP端口:3306
MySQL
MySQLdriver
DB2
SQLServer
如何操作DBMS?
SQL语言:Structured Query Language
Sql注⼊攻击
⽤⼾输⼊信息时,通过输⼊特殊格式的内容来改变sql语句逻辑达到攻击的⽬ 的。
如输⼊⽤⼾名时,输⼊
1' or 1=1 # ,密码任意输⼊就可以登录成功
select * from user where username='1' or 1=1 # ' and password=sha1('1')
解决方法一:
将⽤⼾输⼊的单引号字符替换成单引号的转义符即可。
SQL有执行标准:
SQL92
from 后⾯⽤逗号隔开写多个表名。
where ⼦句中添加表的连接条件。
为了明确字段属哪张表,使⽤“表.字段”形式来引⽤字段。
为了书写⽅便、增加可读性,可以为表起⼀个别名。
SQL语言是操作DBMS的语言,所有的DBMS都支持标准的SQL语言以及自身提供的可扩展SQL
SQL优化
高效、快速 原则是尽可能保证避免索引失效,而导致全表扫描的做法
查询SQL尽量不要使用select *,而是具体字段
很可能不会用到索引,就会造成全表扫描
节省资源、减少网络开销
尽量少使用or
可能会使索引失效,从而全表扫描
使用varchar代替char
按数据内容实际长度存储,存储空间小,可以节省存储空间
尽量使用数值替代字符串类型
查询尽量避免返回大量数据
数据量较多时,尽量使用分页
使用explain分析你SQL执行计划
是否使用了索引及其扫描类型
连接数据库的方式:
1:命令行(控制台)的客户端
show databases;
2:图形化界面的客户端
3:IDEA终极版提供了连接数据库的功能
4:在JAVA中要是用JDBC连接数据库
存储引擎
Server层
与客⼾端建⽴连接、鉴权 Sql 的分析、优化、执⾏ 查询结果的缓存
存储引擎
存储、检索数据
提供数据读写接⼝
show variables like '%engine%';
InnoDB
(默认) 行锁 事务 外键
MyIsam
表锁
Memory
表锁
SQL语言 DDL DML DQL DCL TCL
不严格区分大小写 字符串用单引号
DDL:数据定义语言
DDL:数据定义语言, 用来操作数据库对象 如:表,索引,序列 包含的语法:CREATE,DROP,ALTER
数据库操作
增,建库
CREATE DATABASE 数据库名
CHARSET=[字符集]
创建一个数据库时可以指定该数据库使用的字符集
UTF8(默认)
UTF8MB4
GBK
删,删库
DROP DATABASE 数据库名
if exist
判断
改
ALTER DATABASE 数据库名 CHARSET GBK;
查,查库
SHOW DATABASES
查看DBMS里有多少个数据库?
SHOW CREATE DATABASE 数据库名
查看指定的数据库信息
SHOW VARIABLES LIKE '?'
查看数据库参数
用,用库
USE 数据库名
想要保存数据,那么数据是保存在表中的,而表是归属于某一个数据库的.因此我们在创建
表之前必须要先使用某一个已创建的数据库.
表操作
增,建表
CREATE TABLE 表名( 字段名1 数据类型, 字段名2 数据类型, ... )
字段
约束
约束就是为表中某个字段添加一些限制条件,只有符合条件时才可以对表中记录进行操作。 主键约束,外键约束,唯一性约束,非空约束
主键约束
方式一:字段修饰 PRIMARY KEY
方式二:,PRIMARY KEY(字段名)
等同于添加字段
非空且唯一,自动创建索引
与业务⽆关的⽆意义字段作为主键
⾃动增加的整数。
随机产⽣的字符串。
双主键
两个字段组合不重复。
单个字段中可以有重复值。
两个字段都不能取null 值。
会创建两个字段的组合索引。
primary key(title, version)
alter table tb1 drop primary key;
删除主键
外键约束
foreign key(banji_id) references banji(id);
创建外键,会⾃动创建索引。
删除被引⽤的主键约束,必须先删除外键约束。
被引⽤的主键数据不能删除,若要删除必须先删除⼦数据,或修改⼦数据。
删除主表,必须先删除外键约束,或先删除⼦表。
外键会降低效率
⾼压⼒系统中可以不添加外键约束
非空约束(NOT NULL)
添加了非空约束的字段:每条记录该字段的值都不能为NULL
唯一约束
unique
,unique key(name)
检查约束
,check(条件);
不符合条件的数据插⼊时会报错,提⽰违反了检查约束的条件
AUTO_INCREMENT 自增
MySQL中只能将主键或唯⼀约束字段设置为⾃增,其他字段不能⾃增。
⾃增字段也可以⼿动插⼊指定的值,下⼀个⾃增值也会被⼀起修改
select last_insert_id();
获取新产⽣的⾃增主键值
CHARCATER SET UTF8MB4
单独设置字段的字符集
DEFAULT '默认值'
如果⼀个字段设置了默认值,插⼊数据时该字段可以不给值,会⾃动填⼊默认值。
COMMENT '品牌名称的拼音'
注释,可以帮助开发⼈员理解表的结构和字段的含义。
ENGINE=INNODB
表的引擎
CHARSET=UTF8MB4
表的字符集
CREATE TABLE person( name VARCHAR(30), age INT(3) )
数据库中的数据类型
不同的数据库管理系统, 数据类型不完全一致
数字
整型
unsigned 无符号位
如:TINYINT[-128,127]变[0,255]
参数 m 它只控制显⽰的宽度,不限制整数的取值范围!
没有zerofill 没有任何意义
int(5)
18
18
666666
666666
zerofill
在数字不⾜指定显⽰宽度时在数字上补0,补⾜显⽰宽度 超过显示宽度时也可以显示。
int(5)
18
00018
666666
666666
浮点型
m 指的是整数位和⼩数位总宽度, d 指的是⼩数位的宽度。
默认为6位有效数字
⼩数部分超出宽度时会进⾏四舍五⼊;整数位超出宽度时会报错,⽆法添加数据
定点数
⼩数位的位数通过 d 参数指定, m位数是固定⻓度,超出指定⻓度会四舍五⼊,不⾜指定的⻓度会补0。
decimal 类型可以精确表⽰数字,更适合于⽤来做⾦融运算。
字符串
定长字符串
char(n) 的参数 n 表⽰字符串的⻓度。
char(6)
定⻓6个字符。
超过6个字符,可能出错(严格模式),也可能被截断(⾮严格模式)。
不⾜6个字符,会在后⾯添加空格。
可变字符串
varchar(n) 的参数 n 表⽰字符串的最⼤ ⻓度。
建议 varchar 类型最⻓使⽤255个字符⻓度
varchart(6)
最⻓存储6个字符。
超过6个字符,可能出错(严格模式),也可能被截断(⾮严格模式)。
不⾜6个字符,按字符串⻓度存储
在字符串之前会添加⼀个字节或两个字节来表⽰字符串存储的字节⻓度。
字符串的字节⻓度 <=255 时,会在前⾯添加⼀个字节来表⽰⻓度;
当 >255 时,会在前⾯添加两个字节来表⽰字节⻓度。
text
超过255个字符⻓度就考虑使⽤ text 类型
设置严格模式
set session sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
默认严格模式
show variables like '%sql_mode%';
set session sql_mode='NO_ENGINE_SUBSTITUTION';
设为非严格模式参数
⽇期
data
time
datatime
DEFAULT CURRENT_TIMESTAMP
插⼊时填⼊系统时间作为默认值。
ON UPDATE CURRENT_TIMESTAMP
修改时自动刷新系统时间
timestamp
show variables like 'explicit_defaults_for_timestamp';
ON
OFF
可以隐式自动更新
set session explicit_defaults_for_timestamp=0;
设置隐式更新参数
MySQL中,⼀张表所有字段的字节宽度之和不能超过65535。
表中存在允许 null 值的字段时,需要占⽤⼀个额外字节来表⽰ null 值。 varchar 字节宽度 <=255 时,需要⼀个额外字节表⽰字节⻓度; >255 ,需要2个 额外字节表⽰字节⻓度。 text 类型只占⽤表字节宽度的10个字节。
CREATE TABLE t AS
把查询结果直接存储为⼀张新的表
SELECT 字段 FROM 另一张表名 WHERE 过滤条件
子查询
删,删表
DROP TABLE 表名
IF EXISTS
存在删除
TRUNCATE TABLE student;
截断 先删除,然后按原结构重建表, 截断后该表变成⼀张全新的表,表中存储的数 据会全部被删除,自增id可以从1开始。 无事务,无回滚。
改,改表
1:修改表
RENAME TABLE 原表名 TO 新表名
alter table student engine=myisam charset=gbk;
2:修改表字段结构
ALTER TABLE 表名
1.添加列:
ADD gender VARCHAR(10);
添加新字段(默认添加到末尾)
ADD id INT FIRST;
将新字段放在首位
ADD gender VARCHAR(10) AFTER username;
将新字段指定到某一位置
ADD (age INT,height INT);
批量添加字段
4.删除字段
DROP gender
主键
drop primary key;
外键
drop foreign key xuesheng_ibfk_1;
删除外键约束不会删除该字段的索引,如果索引不再需要,可以⼿动删除索引。
drop index banji_id;
删除索引
唯一
drop index name;
检查
drop constraint tb1_chk_1;
5.修改表中的字段
注意:可以修改表中字段的类型,长度信息.但是尽量不要在表中包含数据后再进行修改,否则可能因为表中现有数据违背该字段修改后的要求导致修改失败.
CHANGE 原字段名 新字段名 新类型**
小改,字段名、长度
CHANGE age age VARCHAR(10);
CHANGE age age VARCHAR(100);
CHANGE age gender VARCHAR(10);
MODIFY name CHART(10) CHARSET GBK AFTER id;
大改,数据类型、字符集、位置
非空
modify name varchar(20);
modify name varchar(20) null;
查,查表
SHOW TABLES
查看当前数据库中已创建的表名
SHOW CREATE TABLE 表名
查看某一张表的建表信息
DESC 表名
查看表的字段结构
视图 veiw
视图具有以下特点: 视图是⼀个 select 查询语句。 视图被存储在MySQL数据库中。 视图不存储数据。 在某些情况下视图可以做增删改操作,但通常我们使⽤视图只做查询操作。 对于客⼾端,从视图查询就⾏从⼀张表查询⼀样。 视图的作⽤有两点: 安全。 简化查询。 对于敏感数据表,如果对⽤⼾是隐藏的,但⽤⼾⼜需要从其中查询某些数据,就可以使⽤视图来暴露这些数据。另外,⼀个复杂的多表连接的查询,通过视图可以简化为单表查询,
创建视图
create or replace view v_film as [ select 查询语句]
删除视图
drop view if exists v_film;
查看视图
show create table v_film;
show create view v_film;
索引
常见的索引类型
hash:底层就是 hash 表。进行查找时,根据 key 调用hash 函数获得对应的 hashcode,根据 hashcode 找到对应的数据行地址,根据地址拿到对应的数据。
B树:B树是一种多路搜索树,n 路搜索树代表每个节点最多有 n 个子节点。每个节点存储 key + 指向下一层节点的指针+ 指向 key 数据记录的地址。查找时,从根结点向下进行查找,直到找到对应的key。
B+树:B+树是b树的变种,主要区别在于:B+树的非叶子节点只存储 key + 指向下一层节点的指针。另外,B+树的叶子节点之间通过指针来连接,构成一个有序链表,因此对整棵树的遍历只需要一次线性遍历叶子结点即可。
explain DQL
性能分析⼯具
type
查询类型: all为全表扫描 ref为使用了索引
system
表中只有⼀⾏数据,这是 const 的⼀种特殊情况。
myisam 引擎⽀持 system ⽅式,但 innodb 不⽀持。
const
使⽤主键或唯⼀索引过滤,只查询⼀⾏数据
eq_ref
在多表连接查询时,使⽤主键或唯⼀索引字段作为外键进⾏连接查询,两张表的数据为⼀对⼀ 关系,这种查询⽅式是 eq_ref 。
explain select * from xuesheng x join lianxi l on x.id=l.xs_id;
ref
使⽤⾮唯⼀索引字段过滤
ref_or_null
使⽤⾮唯⼀索引字段过滤,同时对该字段做空值过滤
explain select * from xuesheng x where banji_id=1 or banji_id is null;
index_merge
使⽤两个索引进⾏过滤查询,会使⽤索引融合优化⽅式
range
使⽤索引进⾏⼤⼩⽐较过滤,或者 between 过滤,或者 in 过滤,会使⽤ range ⽅式
index
全索引扫描会扫描索引中所有数据,但不会回表。只有在使⽤覆盖索引时才可能会使⽤这种⽅式
all
全表扫描是查询性能最差的情况,不能使⽤索引
rows
扫描行数估算
key
标使用的索引名注
create index idx_name on xuesheng(name);
删
drop index ... on ...
alter table ... drop index ...
查
show index
聚簇索引
聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。聚簇索引将索引和数据行放到了一块,找到索引也就找到了数据。因为无需进行回表操作,所以效率很高。
InnoDB 中必然会有,且只会有一个聚簇索引。通常是主键,如果没有主键,则优先选择非空的唯一索引,如果唯一索引也没有,则会创建一个隐藏的row_id 作为聚簇索引。至于为啥会只有一个聚簇索引,其实很简单,因为我们的数据只会存储一份。 而非聚簇索引则将数据存储和索引分开,找到索引后,需要通过对应的地址找到对应的数据行。MyISAM 的索引方式就是非聚簇索引。
覆盖索引和回表
InnoDB 中,对于主键索引,只需要走一遍主键索引的查询就能在叶子节点拿到数据。 而对于普通索引,叶子节点存储的是 key + 主键值,因此需要再走一次主键索引,通过主键索引找到行记录,这就是所谓的回表查询,先定位主键值,再定位行记录。 如果查询语句所要求的字段全部命中了索引,那么就不必再进行回表查询。
索引失效
create index idx_name on xuesheng(name);
隐式或显式类型转换
explain select id,name,email from xuesheng where name=123;
对索引字段进⾏运算
explain select id,name,email from xuesheng where left(name, 1)='张';
like左模糊查询
explain select id,name,email from xuesheng where name like '%张%';
or连接⾮索引字段条件
explain select id,name,email from xuesheng where name='张三' or email='f@f.f';
create index idx_banji_age_name on xuesheng(banji_id,age,name);
多字段组合索引
最左前缀原则
缺少第⼀个字段条件,索引会失效 第二个条件跳过,之后字段的索引会失效。
explain select id,name,email from xuesheng where name='张三' and age=22;
⼤⼩⽐较运算后⾯的索引条件失效
使⽤多字段组合索引时,中间字段如果使⽤⼤⼩⽐较运算,后⾯字段的索引会失效
explain select id,name,email from xuesheng where banji_id=1 and age>20 and name='张三';
DML:数据操作语言
数据操作语言, 是对表中的数据进行操作的语言. 包含:增,删,改。 INSERT, DELECT, UPDATE
增,插入数据
在数据库中,字符串是使用单引号的. 指定的值要与前面指定的类的顺序,类型,个数完全一致 在INSERT语句中未被指定的列在插入数据时,默认插入NULL值。如果未指定的列指定了默认值,则插入指定的默认值 在INSERT语句中字段名可以忽略,若忽略则为全列插入,此时VALUES后指定的值的顺序,类型,个数必须与表中字段一致
INSERT INTO 表名 [(字段1,字段2,...)]
VALUES (字段1的值,字段2的值,...), (字段1的值,字段2的值,...), ...
可一次性插入多条数据
SELECT 字段 FROM 另一张表名 WHERE 过滤条件
将查询结果批量插入表中
删,删除表中数据
DELETE FROM 表名
[WHERE 过滤条件]
WHERE子句中常用的条件
=,<,>,<=,>=,<>(不等于,!=不是所有数据库都支持)
UPDATE person
SET age=29
WHERE age>30
将一个计算表达式的结果作为修改的值使用
SET age=age+1
同时修改多个字段
SET name='李老四',age=55
WHERE name='李四'
- SQL语句中的每个子句之间可以有任意个空格或换行。且SQL语句关键字是不区分大小写的。
- 字符串为单引号且内容是严格区分空格,大小写的。
order by id desc limit 2;
可以先对数据进⾏排序,然后删除前⾯的⼏⾏数据
改,修改表中数据
通常情况下修改表中记录时都要指定WHERE条件,如果不添加WHERE条件则是对表中每一条记录都进行修改!
UPDATE 表名
SET 字段名1=字段值1,字段名2=字段值2,....
[WHERE 过滤条件]
order by id desc limit 2;
标可以先对数据进⾏排序,然后修改前⾯的⼏⾏数据
sakila库
使⽤最⼴泛的⽰例数据库之⼀,也是最好的⽰例数据库之⼀。 最初由 MySQL AB ⽂档团队的前成员 Mike Hillyer 开发。它旨在提供⼀ 个标准模式,可⽤于书籍、教程、⽂章等中的⽰例演⽰。
DQL语言:数据查询语言
DQL用于检索表中数据的语言。 基本语法: SELECT 子句 FROM 子句 JOIN... ON... 子句 WHERE 子句 GROUP BY 子句 HAVING 子句 ORDER BY 子句 一条DQL语句必须包含的两个子句分别为:SELECT子句和FROM子句 SELECT 子句用于选定表中的字段,选定的字段会被包含在查询的结果集中 FROM 子句用于指定查询的表
数据查询
1.未知条件(子查询)? 2.数据来自哪些表(可以同表不同名)? 3.过滤条件来自哪些表? 4.关联关系?
SELECT 字段1,字段2,字段3,...
* 星号表⽰所有的字段。
尽量不要使用!!!
如果⼀张宽表包含很多字段,⽽当前业务只使⽤其中⼏个字段,那么查询所有字段会浪费资源,
另外编写业务代码获取字段数据时也会造成障碍。
SELECT子句中可以仅指定个别字段进行查询
表达式或者函数
在DQL中可以使用表达式或者函数的结果进行查询
在SELECT子句中使用函数或表达式
注意:任何数字与NULL进行运算结果都为NULL
函数
单⾏函数
字符串函数
char_length('a中') :字符数 1+1
length('a中') :字节数 1+3
concat('a','b','cde','fff') :字符串连接,其他数据库可⽤ || 连接字符串
concat_ws(';','abc','def','ggg') :⽤分隔符连接字符串
instr('abcdefgdef','def') :返回第⼀个⼦串的位置,从1开始,找不到返回0
locate('abc', '---abc---abc---abc-') :返回第⼀个⼦串的位置,从1开始,找不到返回0
locate('abc', '---abc---abc---abc-',5) :从指定位置向后找
概要
insert('abcdefghijkl',2, 9, '---') :⽤⼦串取代从2位置开始的9个字符
lower('AdFfLJf') :变为⼩写
upper('AdFfLJf') :变为⼤写
left('AdFfLJf',3) :返回最左边的三个字符
right('AdFfLJf',3) :返回最右边的三个字符
lpad('abc', 8, '*') :左侧填充,指定⻓度⽐源字符串少,相当于left
rpad('abc', 8, '*') :右侧填充,指定⻓度⽐源字符串少,相当于left
trim(' a bc ') :去除两端空格
substring('abcdefghijklmn', 3) :从3位置开始的所有字符
substring('abcdefghijklmn', 3, 6) :从3位置开始的6个字符
repeat('abc', 3) :重复三遍abc
REPLACE('Hello MySQL','My','Your') :⼦串替换
REVERSE('Hello') :翻转字符串
SPACE(10) :返回10个空格
数字函数
ceil(3.94) :天花板,向上取整
floor(3.94) :地板,向下取整
round(673.4974) :四舍五⼊
round(673.4974, 2) :四舍五⼊到⼩数点后两位
round(673.4974, -2) :四舍五⼊到百
truncate(234.39, 1) :舍去⾄⼩数点后1位,必须指定位数
format(391.536, 2) :数字格式化为字符串,###,###.###,四舍五⼊
第⼆个参数为⼩数位数
rand() :随机浮点数,[0,1)
⽇期函数
2008-3-15 23:1:39
NOW() :返回当前的⽇期和时间
CURDATE() :返回当前的⽇期
CURTIME() :返回当前的时间
DATE(日期) :提取⽇期或⽇期/时间表达式的⽇期部分
TIME(日期) :提取⽇期或⽇期/时间表达式的时间部分
EXTRACT(字段 From 日期) :从⽇期中提取指定字段的值
DAYOFWEEK(日期) :返回周⼏
DATE_ADD(日期, INTERVAL 数量 字段) :给⽇期添加指定的时间间隔
DATE_SUB(日期, INTERVAL 数量 字段) :从⽇期减去指定的时间间隔
DATEDIFF(日期1, 日期2) :返回两个⽇期之间的天数
DATE_FORMAT(日期, 格式) :⽤不同的格式显⽰⽇期/时间
%Y-%m-%d %H:%i:%s
%d/%m/%Y
%Y年%m月%d日
LAST_DAY(日期) :返回当⽉最后⼀天
null 值函数
IFNULL(arg1,arg2)
e.g. IFNULL(number,0) null时为0。
通常在NULL值会被忽略的情况下使用
如果arg1
为null返回arg2,一般为0
不为null返回arg1
coalesce(参数1,参数2,参数3...)
返回第⼀个⾮空值
加密函数
md5() :⼀种被⼴泛使⽤的密码散列函数,可以产⽣出⼀个128位的散列值,通常转为32位16进制数字字符。
sha() 或 sha1() :安全散列算法1是⼀种密码散列函数,可以⽣成⼀个被称为消息摘要的160位散列值,通常转为40位16进制数字字符。
分⽀
三项运算函数:
参数1为真时,返回参数 2; 参数1为假时,返回参数3。
if(参数1, 参数2, 参数3)
逻辑条件判断:
条件为布尔类型。
case when 条件1 then .... when 条件2 then .... else ... end
⾮布尔值判断:
字段或者表达式的具体值进⾏判断。
case 字段或表达式 when 值1 then .... when 值2 then .... else ... end
select payment_id, rental_id, amount, case when amount<4 then '低' when amount>=4 and amount<9 then '中' when amount>=9 then '高' end level from payment;
多行函数
多⾏函数对多⾏数据进⾏运算,产生一个结果。 多⾏函数查询时,不能与其他字段⼀起查询,否则MySQL 8中会报错。 多⾏函数会忽略null值的⾏,⽐如count计数,只会对⾮null值进⾏计数。
MIN() 求指定字段在结果集中所有记录里的最小值
MAX() 求指定字段在结果集中所有记录里的最大值
SUM() 求指定字段在结果集中所有记录里值的总和
AVG() 求指定字段在结果集中所有记录里的平均值
COUNT() 不是对字段值的统计,而是对结果集中记录数的统计。统计结果集的记录数
通常使用COUNT(*)来对筛选出来的数据个数求一个总和。
count(字段)
对字段中的值进⾏计数,并会忽略null值。
count(1)
InnoDB中两者没有区别 MyIsam表会将精确的⾏数存储下来,所以 count(*) ⾮常快,⽽ count(1) 只有在 第⼀列⾮空时才与 count(*) 性能相同
相当于在查询结果中添加了⼀个新的字段,其中的值都是1,对这个字段中1 的数量进⾏计数,可以得到所有⾏的计数。
count(*)
别名
别名在SELECT子句中可以被应用于字段上,函数上或表达式上
别名也可以被应用于其他子句中,比如在FROM子句中为表取别名(后面学习关联查询详细介绍)
1.字段名 别名 会出现歧义的情况
name*2 rename
2.字段名 AS 别名
3.字段名 AS '别名'
4.字段名 AS "别名"
DISTINCT 去重修饰,在字段之前
在两个字段的前⾯添加 distinct 关键字,表⽰两个字段组合不重复 一句用一次;
FROM 表名
WHERE 过滤条件
在WHERE子句中使用函数或表达式作为过滤条件 但是,聚合函数不能运用于WHERE字句中? 本质原因是过滤时机不同, WHERE子句中添加的过滤条件是在第一次从表中逐条检索数据时生效的 (从而产生查询结果集)。 而聚合函数的过滤实际应当是在从表中查询出结果集, 并针对该结果集分组统计得出统计结果后再进行过滤。
NOT 非修饰
比较运算符:=,>,>=,<,<=,<>(!=)
MySQL中表⽰不等可以⽤ <> 或 != 运算符,MySQL中它们⽤法时候相同的。 <> 是sql标准语法, != 虽然不是sql标准语法,但某些数据库⽀持这种⽤法。
IN(data1,data2,...)
BETWEEN 小 AND 大
[小, 大]
值(DQL)
LIKE 通配符数据
模糊查询
两个通配符
_ : 一个字符
% : 任意字符
组合示意:
- %X% 字符中含有X的
- %X 字符以X结束
- X% 字符以X开始
- _X _ 在三个字符中第二个字符为X
- %X_ 字符中倒数第二个为X
- X_Y 三个字符中第一个为X,第三个为Y
IS NULL 判断某一字段为NULL值
IS NULL 判断某个字段的值是否为NULL
IS NOT NULL 判断某个字段的是不为NULL
不能直接搭配"="或"<>"来判断NULL值!!!
AND(优先级高会先运算!), OR
GROUP BY 字段名
运行时间晚于WHERE
GROUP BY子句,分组
查啥分啥
GROUP BY子句可以将其所在的DQL语句中的查询结果集上按照指定的字段值相同的记录进行分组,搭配聚合函数可以进行组内统计。
分组是配合统计的,如果SELECT子句中没有聚合函数时,无需使用GROUP BY子句
在SELECT子句中如果包含聚合函数,那么凡是不在聚合函数中的字段都应当出现在GROUP BY子句中
GROUP BY子句中如果出现了多个字段,则是将结果集中指定的这些字段值的组合相同的记录看作一组。
HAVING 过滤条件
对分组多⾏函数结果进⾏过滤
可利用聚合函数的统计结果进行过滤。
分组过滤
ORDER BY 字段1
ORDER BY子句排序
ORDER BY 子句只能是DQL中的最后一个子句(原因是该子句是最后执行的子句)。
字段在前则优先级高
即,多字段排序时:会首先按照第一个字段排序,然后当第一个字段值相同的记录中再按照第二个字段排序以此类推
作用:对结果集按照指定字段值的升序或降序进行排序
(默认)ASC 升序
DESC 降序
,字段2
(默认)ASC 升序
DESC 降序
,字段3
(默认)ASC 升序
DESC 降序
,字段2
(默认)ASC 升序
DESC 降序
,...
LIMIT 跳过的记录数【条目数*(页数-1)】,每页显示的条目数
limit 的⼯作⽅式是先查询出 (offset+count) 条数据,再丢offset之前的数据
分页查询
将一个DQL语句执行后的查询结果集分段查询出来。
当一个查询结果记录数非常多时,通常都采取分页查询的方式来分段分批的查询减少不必要的系统开销。
分页查询在SQL92标准中没有定义。意味着不同的数据库有完全不同的分页写法。
在MySQL中是通过在ORDER BY子句后面追加LIMIT来完成分页的。
每页显示5条,显示第三页?
LIMIT (3-1)*5,5 ==> LIMIT 10,5
每页显示8条,显示第9页?
LIMIT (9-1)*8,8 ==> LIMIT 64,8
深度分⻚问题:
从1000 万数据中查询末尾的10条数据: limit 9999990, 10 ,这样会查询出所有1000万数据, 最后只取尾部的10条数据。
在⼤数据量的情况下如果做深度分⻚会性能低下
子查询(DQL)
子查询 嵌套在其他SQL语句中的一条DQL语句,那个这个DQL就称为是子查询。 子查询常被应用于: DQL中 : 可以基于一个查询结果集进行查询(最常被用于DQL语句) DML中 : 可以基于一个查询结果集进行增删改操作 DDL中 : 可以基于一个查询结果集进行数据对象操作(创建表,视图等) 在DQL语句中使用子查询 子查询在DQL中使用时必须使用"()"括起来
单值条件子查询 最常光顾的客户 select customer_id,max(amount) from payment where amount=( select max(amount) from payment );
单行单列子查询:查询结果集为一行一列,即:只有一个值
单值子查询作为过滤条件,可以搭配:=,>,>=,<,<=,<>使用
多值条件子查询
多行单列子查询:查询结果集为多行一列,即:有多个值
多值查询作为过滤条件,要搭配:IN,ALL,ANY使用
IN
IN等效于"="做等于判断,只不过是判断等于列表其中之一
ALL 所有
ALL和ANY用于">,>=,<,<="使用:
ANY 任意之一
e.g.
>ANY:大于列表之一(大于列表最小的即可)
>ALL: 大于列表所有(大于列表最大的)
<ANY:小于列表之一(小于列表最大的即可)
<ALL: 小于列表所有(小于列表最小的)
多列条件子查询
多行多列子查询:查询结果集为一个表. 常嵌套于DDL
⼦查询的结果为多列结果时,主查询可以⽤多列进⾏匹配过滤
AS
常被应用于DQL语句中的FROM子句中当作一张表看待
被用作DDL语句中将一个结果集当作一张表创建出来
From⼦查询 select max(c) from ( SELECT count(*) c FROM sakila.rental group by customer_id ) t;
from ⼦查询把⼀个查询的结果看做是⼀张表(需要起一个别名),从其中再进⾏查询
字段子查询 SELECT rental_id, customer_id, (select c.first_name from customer c where c.customer_id=r.customer_id) first_name FROM rental r where customer_id=148;
字段⼦查询的查询结果作为主查询中的⼀列
多表连接查询
关联查询
SQL92
SELECT 表名.字段1,表名.字段2,表名.字段3,...
FROM 表名1,表名2,...表名n
别名
WHERE 连接条件1
要求:相同对象,即关联关系通常是建立在主外键等值连接的基础上的
笛卡尔积
缺少连接条件时会产生笛卡尔积,会将A表每条记录与B表每条记录都建立一次连接,产生巨量数据,这通常是一个无意义的结果集,要尽量避免。
数据量计算:
以两张表为例:若A表10条记录,B表10条记录,笛卡尔积会产生两张表数据量乘积的条数。即:10*10等于100条记录
(+)另一张表为主表
AND ...
AND 连接条件(n-1)
AND 过滤条件
方法2;内连接
SQL99 内连接的查询与关联查询一致,区别在于我们将关联关系(连接条件)单独定义在JOIN后面跟的ON子句上。 优点:关联查询时结构清晰,连接条件与过滤条件分开在不同的子句定义。ON子句定义连接条件,WHERE子句定义过滤条件 所谓内连接指的是只有符合连接条件的数据会被查询出来,两 张表中不符合连接条件的数据会被过滤掉,
SELECT 表名.字段1,表名.字段2,表名.字段3,...
FROM 表名
? JOIN 表名2 ON 连接条件1
(主表)primary 主键(pk,1)值 = 外键(fk,n)值 foreign
有明显关联性的字段放到一个主表中
(关系表)将表连在一起
pk1,1=fk1,n AND fk2,m=pk2,1
...
JOIN 表名n ON 连接条件n-1
WHERE 过滤条件
外连接
外连接的作用是将关联查询中不满足关联条件的记录显示在结果集中。
外连接分类:
左外连接 LEFT JOIN
左外连接指的是左侧表中不符合连接条件的数据也会被查询出来,连接后,没有数据的字段是NULL 值
右外连接 RIGHT JOIN
outer
右外连接指的是右侧表中不符合连接条件的数据也会被查询出来,连接后,没有数据的字段是NULL 值
UNION
自连接
同一张表中的一条数据可以对应多条数据。
自连接通常用于保存具有相同属性且存在上下级关系的树状结构数据使用
例如
分类树
联合查询
Union 联合查询可以把多个查询结果集拼接成⼀个结果集 要求字段数量相同。
Union :会将合并结果中的重复数据去除掉。
Union All :两个查询结果完整保留,不会去除重复数据。 (select film_id, title, length from film order by length limit 3) UNION ALL (select film_id, title, length from film where title like '%MOON');
DCL:数据控制语言,通常用来让DBA管理数据库使用
它的语句通过 GRANT 或 REVOKE 实现权限控制,确定单个⽤⼾ 和⽤⼾组对数据库对象的访问。
用户管理
增
create user '程杰'@'localhost'
创建本机⽤⼾,此时创建的用户没有啥权限。
identified
with 指定密码认证⽅式
caching_sha2_password :MySQL 8新的认证⽅式。
mysql_native_password :旧的认证⽅式,可以兼容旧版本的客⼾端⼯具。
by 'lisi';
设置密码,不设置的话会免密登录。
登录(建立新连接)
删
改
密码
alter user '程杰'@'localhost' identified with caching_sha2_password by '123456';
用户名
rename user '程杰'@'localhost' to '程杰'@'%';
查
select user();
查看当前登录用户
查看mysql数据库里的数据
user表
user+host 字段为⽤⼾名。
以 _priv 结尾的字段为权限字段。
authentication_string 字段为密码。
plugin 字段为认证⽅式。
权限管理
增
MySQL8新增的⻆⾊管理
TCL:事务控制语言
事务(Transaction)
访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
实际开发中一个业务功能可能执行的sql语句多条, 有一种策略: 一组sql语句要就全部成功,要就全部失败 这种策略:称为事务 同一个事务下的所有的sql语句,要就全部成功,要就全部失败 dao类一个方法只执行一条sql, 业务类中, 一个方法执行多条sql(调用多个Dao类多个方法), 实际开发中: 事务加在业务层
MySQL的事务操作有三个:
begin :开启事务。
事务开启后执⾏的增删改操作,在数据表中并不真实⽣效,通过下⾯的指令可以让数据真实⽣效,或者丢弃所有改动。
commit :提交事务。
可以使事务中的所有数据改动最终真实⽣效,并结束当前事务。
rollback :回滚事务。
会丢弃事务中的所有数据改动,将数据恢复到事务前的状态,并结束事 务。
undo_log
回滚日志 insert :添加数据没有旧版本数据,在回滚⽇志中不会记录⽇志。 delete :删除数据时,会把被删除的数据记录在回滚⽇志中。 update :修改数据时,会把修改前的旧版本数据记录在回滚⽇志⽂件中。
事务四大特性: ACID
1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
2. 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
3. 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
并发问题
数据访问冲突
脏读。
⼀个事务读取到另⼀个事务未提交的数据
不可重复读。
⼀个事务执⾏过程中,对同⼀条数据查询多次,会得到多个不同的结果。
幻读
⼀个事务添加的数据另⼀个事务看不到。 ⼀个事务删除的数据另⼀个事务仍然可以查询到。
兼顾数据安全和性能,MySQL提供了四种隔离级别:
read uncommited :会引起脏读、不可重复读和幻读。
两个事务并⾏时,⼀个事务可以读取另⼀个事 务未提交的数据,这是最低的隔离级别,会引起脏读、不可重复读和幻读问题
read commited :会引起不可重复读和幻读。
repeatable read (默认):会引起幻读。
serializable :串⾏化可以保证数据安全,但性能低。
Redis
是一款基于内存的,使用K-V结构储存数据的NoSQL非关系型数据库
特征
基于内存
使用内存空间存储数据,读写效率非常高
Redis会自动将数据备份到硬盘中,所以数据不会因为计算机断电而丢失
K-V结构
存入数据需要给数据指定一个唯一名称
通过这个名称来访问数据库
NoSQL
No Operation SQL
Redis的使用完全不涉及SQL语句
非关系型数据库
存入数据没有任何关系,本身也不支持体现数据的关系
关系型数据库
数据库中有多张表,且表于表之间可以存在关联的的数据库
缓存使用原则
1.数据量不能太大
2.使用越频繁,Redis保存这个数据越值得
3.保存在Redis中的数据一般不会是数据库中频繁修改的
缓存淘汰策略
如果Redis服务器的内存已经全满,现在还需要向Redis中保存新的数据,如何操作,就是缓存淘汰策略
noeviction:返回错误**(默认)**
allkeys-random:所有数据中随机删除数据
volatile-random:有过期时间的数据中随机删除数据
随机
volatile-ttl:删除剩余有效时间最少的数据
Time To Live (ttl)
allkeys-lru:所有数据中删除上次使用时间距离现在最久的数据
volatile-lru:有过期时间的数据中删除上次使用时间距离现在最久的数据
Least Recently Used (lru)
上次使用时间距离现在最久
allkeys-lfu:所有数据中删除使用频率最少的
volatile-lfu:有过期时间的数据中删除使用频率最少的
Least Frequently Used (lfu)
使用频率最少
缓存穿透
一个业务请求先查询Redis,Redis没有这个数据 接下来查询数据库,数据库也没有。 一旦发生上面的穿透现象,仍然需要连接数据库,一旦连接数据库,项目的整体效率就会被影响 如果有恶意的请求,高并发的访问数据库中不存在的数据,严重的,当前服务器可能出现宕机的情况
大量执行这种访问,会拖慢数据库
解决方案
对不存在的数据,缓存空值
白名单 可访问的数据id作为偏移量存入bitmaps 访问时检查bitmaps
布隆过滤器
1.针对现有所有数据,生成布隆过滤器,保存在Redis中
2.在业务逻辑层,判断Redis之前先检查这个id是否在布隆过滤器中
3.如果布隆过滤器判断这个id不存在,直接返回
4.如果布隆过滤器判断id存在,在进行后面业务执行
Guava
进行实时监控 监控攻击
缓存击穿
一个业务请求先查询Redis,Redis没有这个数据 接下来查询数据库,数据库有。 这个情况也不是异常情况,因为我们大多数数据都需要设置过期时间,而过期时间到时,这个数据就会从Redis中移除,再有请求查询这个数据,就一定会从数据库中再次同步
缓存雪崩
Redis中保存的数据在短时间内同时到期,出现同一时间大量击穿现象 本应该由Redis反馈的信息,由于雪崩都去访问了Mysql,mysql承担不了,非常可能导致异常
多级缓存架构
nginx缓存
ehcache | guava
redis
自动续期、自动更新数据 即将过期的数据提前更新数据
对过期时间使用随机值 分散过期时间
锁 | 队列
Redis持久化
为了防止Redis的重启对数据库带来额外的压力和数据的丢失,Redis支持了持久化的功能 所谓持久化就是将Redis中保存的数据,以指定方式保存在Redis当前服务器的硬盘上 实际开发中RDB和AOF是可以同时开启的,也可以选择性开启
RDB
Redis Database Backup数据库快照,当前Redis中所有数据转换成二进制的对象,保存在硬盘上
每次备份会生成一个dump.rdb的文件
当Redis断电或宕机后,重新启动时,会从这个文件中恢复数据,获得dump.rdb中所有内容
Redis的配置文件
save 60 5
上面配置中60表示秒
5表示Redis的key被更新的次数
配置效果:1分钟内如果有5个及以上的key被更新,就启动rdb数据库快照程序
bgsave
优点:
因为是整体Redis数据的二进制格式,数据恢复是整体恢复的
缺点:
生成的rdb文件是一个硬盘上的文件,读写效率是较低的
如果突然断电,只能恢复到最后一次生成的rdb中的数据
AOF
Append Only File将Redis运行过的所有命令(日志)备份下来,保存在硬盘上 这样即使Redis断电,我们也可以根据运行过的日志,恢复为断电前的样
Redis的配置文件
appendonly yes
理论上任何运行过的指令都可以恢复 但是实际情况下,Redis非常繁忙时,我们会将日志命令缓存之后,整体发送给备份,减少io次数以提高备份的性能 和对Redis性能的影响 实际开发中,配置一般会采用每秒将日志文件发送一次的策略,断电最多丢失1秒数据
优点:
相对RDB来讲,信息丢失的较少
缺点:
因为保存的是运行的日志,所以占用空间较大
Redis的AOF为减少日志文件的大小,支持AOF rewrite 简单来说就是将日志中无效的语句删除,能够减少占用的空间
官方建议采用RDB+AOP混合模式
使用RDB批量回复数据,使用AOF回复增量数据
(默认开启)aof-use-rdb-preamble yes
Redis高可用
存储原理
一致性哈希算法
HashMap e.g"五槽位散列表"
槽位越多代表元素多的时候,查询性能越高,HashMap默认16个槽
Redis底层保存数据用的也是这样的散列表的结构 Redis将内存划分为16384个区域(类似hash槽) 将数据的key使用CRC16算法计算出一个值,取余16384 得到的结果是0~16383 这样Redis就能非常高效的查找元素了
Redis集群
Redis最小状态是一台服务器 这个服务器的运行状态,直接决定Redis是否可 如果它离线了,整个项目就会无Redis可用 系统会面临崩溃 为了防止这种情况的发生,我们可以准备一台备用机 Redis分布式锁的解决方案。
主从复制
安排一台备用机(slave)实时同步数据, 万一主机宕机可以切换到备用机运行。
备用机没有实质性作用,主机不宕机就跟没有一样,体现不出价值。
读写分离
在master正常工作时,让备用机承担部分读的工作
master宕机后主备机的切换需要人工介入,需要一定的时间
# 清理容器 docker rm -f $(docker ps -aq) --volumes # 启动主服务器 docker run -d --name redis6379 \ --net host \ --restart=always \ redis # 启动两个从服务器 docker run -d --name redis6380 \ --net host \ --restart=always \ redis redis-server --port 6380 \ --slaveof 192.168.64.140 6379 docker run -d --name redis6381 \ --net host \ --restart=always \ redis redis-server --port 6381 \ --slaveof 192.168.64.140 6379 # 查看日志 docker logs redis6379 docker logs redis6380 # 查看三个服务器的角色 docker exec -it redis6379 redis-cli info replication docker exec -it redis6380 redis-cli -p 6380 info replication docker exec -it redis6381 redis-cli -p 6381 info replication # 向主服务器添加数据 docker exec -it redis6379 redis-cli set a aaaaaa exit # 在从服务器查看是否有数据 docker exec -it redis6380 redis-cli -p 6380 get a exit # 主机宕机 docker stop redis6379 # 查看从机日志 docker logs -f redis6380 # 在从机访问数据 docker exec -it redis6380 redis-cli -p 6380 info replication get a set b bbbbbbbbbbbbbbbbb # 手动将redis6380提升成主机 docker exec -it redis6380 redis-cli -p 6380 slaveof no one info replication exit # 手动设置redis6381作为redis6380的从机 docker exec -it redis6381 redis-cli -p 6381 slaveof 192.168.64.140 6380 info replication exit # 启动6379后,也作为从机连接6380 docker restart redis6379 docker exec -it redis6379 redis-cli slaveof 192.168.64.140 6380 info replication exit
哨兵模式
发生故障自动切换的固定策略
哨兵节点每隔一段时间就向所有节点发送请求, 如果正常响应认为该节点正常 如果没有响应,认为该节点出现问题,哨兵能自动切换主备机
但是如果哨兵判断节点状态时发生了误判,那么就会错误将master下线,降低整体运行性能所以要减少哨兵误判的可能性
哨兵集群
将哨兵节点做成集群,由多个哨兵投票决定是否下线某一个节点 哨兵集群中,每个节点都会定时向master和slave发送ping请求 如果ping请求有2个(集群的半数节点)以上的哨兵节点没有收到正常响应,会认为该节点下线
三台服务器监测两台服务器不合理 只有一个节点支持写操作无法满足整体性能要求时,系统性能就会到达瓶颈
# 准备三个哨兵的配置文件 mkdir /opt/sentinel/ cd /opt/sentinel/ cat <<EOF >5000.conf port 5000 sentinel monitor mymaster 192.168.64.140 6380 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1 EOF cat <<EOF >5001.conf port 5001 sentinel monitor mymaster 192.168.64.140 6380 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1 EOF cat <<EOF >5002.conf port 5002 sentinel monitor mymaster 192.168.64.140 6380 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1 EOF # 启动三个哨兵服务 docker run -d --name sentinel5000 \ -v /opt/sentinel/5000.conf:/sentinel.conf \ --net host \ --restart=always \ redis redis-sentinel /sentinel.conf docker run -d --name sentinel5001 \ -v /opt/sentinel/5001.conf:/sentinel.conf \ --net host \ --restart=always \ redis redis-sentinel /sentinel.conf docker run -d --name sentinel5002 \ -v /opt/sentinel/5002.conf:/sentinel.conf \ --net host \ --restart=always \ redis redis-sentinel /sentinel.conf # 通过哨兵查看集群信息 docker exec -it sentinel5000 redis-cli -p 5000 sentinel master mymaster sentinel slaves mymaster sentinel sentinels mymaster # 测试主机宕机,哨兵自动执行主从切换 docker stop redis6380 # 查看哨兵日志 docker logs sentinel5000 # 重新启动6380 docker start redis6380 docker logs sentinel5000
分片集群
这时我们就要部署多个支持写操作的节点,进行分片,来提高程序整体性能 分片就是每个节点负责不同的区域 Redis0~16383号槽, 例如 MasterA负责0~5000 MasterB负责5001~10000 MasterC负责10001~16383 一个key根据CRC16算法只能得到固定的结果,一定在指定的服务器上找到数据
有了这个集群结构,我们就能更加稳定和更加高效的处理业务请求了 为了节省哨兵服务器的成本,有些公司在Redis集群中直接添加哨兵功能,既master/slave节点完成数据读写任务的同时也都互相检测它们的健康状态
自己搜索Redis分布式锁的解决方案(redission)
Jedis客户端分片访问
# 启动三个redis服务,使用 --net=host 方式,让容器直接使用主机的端口:6379,6380,6381 docker run -d --name redis6379 \ --net=host \ --restart=always \ redis docker run -d --name redis6380 \ --net=host \ --restart=always \ redis redis-server --port 6380 docker run -d --name redis6381 \ --net=host \ --restart=always \ redis redis-server --port 6381 # 进入redis,测试数据操作 docker exec -it redis6379 redis-cli set a aaaaaaaaaaaa set b bbbbbbbb keys * get a get b flushall
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
子主题
JedisShardInfo
redis分片信息(常存储于ArrayList中)
ip
端口
HostAndPort
哨兵(常存储于HashSet)
ip
端口
JedisCluster
set
set()
close()
GenericObjectPoolConfig
分片连接池配置
ShardedJedisPool
分片连接池
参数: 配置 分片集合
ShardedJedis getResource
set
添加数据
close
关闭
“一致性哈希”算法
环形结构
2^32 -1个节点
所有物理服务器均匀分散到哈希环上
数据经过哈希运算落到一个节点上,从这个节点顺时针寻找物理服务器节点
Jedis
创建分片连接池
# 进入服务器查看数据 docker exec -it redis6379 redis-cli keys * docker exec -it redis6380 redis-cli -p 6380 keys * docker exec -it redis6381 redis-cli -p 6381 keys *
分布式锁
分布式锁是锁的一种,通常用来跟 JVM 锁做区别。 JVM 锁只能作用于单个 JVM,可以简单理解为就是单台服务器(容器),而对于多台服务器之间,JVM 锁则没法解决,这时候就需要引入分布式锁。
1)Redis 的 set 加锁+lua 脚本解锁方案,至于是不是用守护线程续命可以结合自己的场景去决定,个人建议还是可以使用的。
set key value PX milliseconds NX
key、value:键值对
PX milliseconds:设置键的过期时间为 milliseconds 毫秒。
NX:只在键不存在时,才对键进行设置操作。SET key value NX 效果等同于 SETNX key value。
PX、expireTime 参数则是用于解决没有解锁导致的死锁问题。因为如果没有过期时间,万一程序员写的代码有 bug 导致没有解锁操作,则就出现了死锁,因此该参数起到了一个“兜底”的作用。
NX 参数用于保证在多个线程并发 set 下,只会有1个线程成功,起到了锁的“唯一”性。
为了防止死锁,我们会给分布式锁加一个过期时间,但是万一这个时间到了,我们业务逻辑还没处理完,怎么办?
1、守护线程“续命”:额外起一个线程,定期检查线程是否还持有锁,如果有则延长过期时间。Redisson 里面就实现了这个方案,使用“看门狗”定期检查(每1/3的锁时间检查1次),如果线程还持有锁,则刷新过期时间。
1、线程1首先获取锁成功,将键值对写入 redis 的 master 节点
2、在 redis 将该键值对同步到 slave 节点之前,master 发生了故障
3、redis 触发故障转移,其中一个 slave 升级为新的 master
4、此时新的 master 并不包含线程1写入的键值对,因此线程2尝试获取锁也可以成功拿到锁
5、此时相当于有两个线程获取到了锁,可能会导致各种预期之外的情况发生,例如最常见的脏数据
2、超时回滚:当我们解锁时发现锁已经被其他线程获取了,说明此时我们执行的操作已经是“不安全”的了,此时需要进行回滚,并返回失败。
同时,需要进行告警,人为介入验证数据的正确性,然后找出超时原因,是否需要对超时时间进行优化等等。
Lua 脚本来执行解锁操作
Jedis
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
KEYS[1]:我们要解锁的 key
ARGV[1]:我们加锁时的 value,用于判断当“锁”是否还是我们持有,如果被其他线程持有了,value 就会发生变化。
1)查询当前“锁”是否还是我们持有,因为存在过期时间,所以可能等你想解锁的时候,“锁”已经到期,然后被其他线程获取了,所以我们在解锁前需要先判断自己是否还持有“锁”
2)如果“锁”还是我们持有,则执行解锁操作,也就是删除该键值对,并返回成功;否则,直接返回失败。
Redlock
对多台无关的服务器遍历加锁、解锁,只要多数成功就算成功
Redisson
Redisson是一个类似于Jedis的Redis客户端API,提供了分布式锁的API:
可重入锁
公平锁
联锁
红锁
读写锁
2)Zookeeper 方案
对数据安全性要求比较高
1、创建一个锁目录 /locks,该节点为持久节点
2、想要获取锁的线程都在锁目录下创建一个临时顺序节点
3、获取锁目录下所有子节点,对子节点按节点自增序号从小到大排序
4、判断本节点是不是第一个子节点,如果是,则成功获取锁,开始执行业务逻辑操作;如果不是,则监听自己的上一个节点的删除事件
5、持有锁的线程释放锁,只需删除当前节点即可。
6、当自己监听的节点被删除时,监听事件触发,则回到第3步重新进行判断,直到获取到锁。
Zookeeper 方案的主要问题在于性能不如 Redis 那么好,当申请锁和释放锁的频率较高时,会对集群造成压力,此时集群的稳定性可用性能可能又会遭受挑战。
使用
数据类型
string
字符串,整型,浮点型,布尔 string可以做数字运算 string单个字符串最大存储512M(2^32 - 1)
任何字面值
docker exec -it redis7000 redis-cli -c -p 7000 set key value -------------------------- set a aaaa get key -------------------------- get a append key value 在末尾追加字符串 -------------------------- append a xxxxx strlen key 字符串长度 -------------------------- strlen a setnx key value 键不存在才设置, nx -- not exsits -------------------------- setnx c cccccccccccccccc setnx c xxxxxxxxxxx set key value nx nx -- not exsits -------------------------- set c yyyyyyyyyyyyyy nx incr key 递增,原子操作 ------------------------- set d 1 incr d incr d incr d decr key 递减,原子操作 ------------------------- decr d decr d decr d decr d incrby key step ------------------------ incrby d 10000 decrby key step ------------------------ decrby d 3000 mset key value key value ...... ---------------------------------- mset a aaaa b bbbbb c cccccc d ddddd e eeeee f fffff mset {x}a aaaa {x}b bbbbb {x}c cccccc {x}d ddddd {x}e eeeee {x}f fffff mget key key ...... ---------------------------------- mget a b c d mget {x}a {x}b {x}c {x}d msetnx key value key value ...... 所有的键都不存在才设置,有任意一个键存在全部都失败 ---------------------------------- msetnx {x}g ggggg {x}h hhhhh {x}i iiiii msetnx {x}i iiiii {x}j jjjjj {x}k kkkkk getrange key start end 截取字符串,包含start和end ------------------------------------ set a abcdefghijklmn getrange a 0 3 getrange a 9 -1 # 从下标9开始,到末尾最后一个字符 setrange key start value 替换子串,从start位置开始进行替换 ------------------------------------ setrange a 5 ---- get a expire key seconds 设置键的过期时间 ------------------------- set x xxxxxxxxxxxx expire x 10 ttl key 查看过期时间 ------------------------- ttl x setex key seconds value 添加数据,同时指定过期时间 set + expire ------------------------- setex x 10 xxxxxxxxxxxx ttl x set key value EX|PX 时间 EX以秒为单位设置过期时间 PX以毫秒为单位设置过期时间 ------------------------- set c cccccccc EX 10 ttl c set c cccccccc ex 10 nx getset key value 取旧值,换新值 ------------------------- set a aaaaaaaaaaaa getset a xxxxxxxxxxxxxx get a # 清空数据 get a flushall get b flushall get c flushall lpush/rpush key value value ...... 左侧添加/右侧添加 ------------------------------------- lpush a 1 2 3 rpush a x y z lrange key start end 从左侧按范围取数据 -1表示末尾 ----------------------------------- lrange a 0 -1 lpop/rpop key [弹出的数量] 从左侧或右侧弹出值 ----------------------------------- lpop a rpop a rpop a 2 rpoplpush from_key to_key 右侧弹出,再加入左侧 ----------------------------------- rpush {x}a 1 2 3 4 5 6 7 8 9 lrange {x}a 0 -1 rpoplpush {x}a {x}b rpoplpush {x}a {x}b rpoplpush {x}a {x}b lrange {x}a 0 -1 lrange {x}b 0 -1 rpoplpush {x}a {x}a rpoplpush {x}a {x}a rpoplpush {x}a {x}a lindex key index 按下标取数据 ------------------------------ lindex {x}a 0 lindex {x}a 1 lindex {x}a 2 lindex {x}a 3 llen 列表长度 ------------------------------ llen {x}a linsert key before/after value new_value 在指定的的值的前后添加数据 ------------------------------ linsert {x}a before 5 x lrange {x}a 0 -1 lrem key n value 从左侧找到n个给定的值删掉,n是0表示删除全部指定的值 ------------------------------ lrem {x}a 2 6 lset key index new_value 替换index位置 ------------------------------ lset {x}a 3 / lrange {x}a 0 -1
list
List
# 清空数据 get a flushall get b flushall get c flushall lpush/rpush key value value ...... 左侧添加/右侧添加 ------------------------------------- lpush a 1 2 3 rpush a x y z lrange key start end 从左侧按范围取数据 -1表示末尾 ----------------------------------- lrange a 0 -1 lpop/rpop key [弹出的数量] 从左侧或右侧弹出值 ----------------------------------- lpop a rpop a rpop a 2 rpoplpush from_key to_key 右侧弹出,再加入左侧 ----------------------------------- rpush {x}a 1 2 3 4 5 6 7 8 9 lrange {x}a 0 -1 rpoplpush {x}a {x}b rpoplpush {x}a {x}b rpoplpush {x}a {x}b lrange {x}a 0 -1 lrange {x}b 0 -1 rpoplpush {x}a {x}a rpoplpush {x}a {x}a rpoplpush {x}a {x}a lindex key index 按下标取数据 ------------------------------ lindex {x}a 0 lindex {x}a 1 lindex {x}a 2 lindex {x}a 3 llen 列表长度 ------------------------------ llen {x}a linsert key before/after value new_value 在指定的的值的前后添加数据 ------------------------------ linsert {x}a before 5 x lrange {x}a 0 -1 lrem key n value 从左侧找到n个给定的值删掉,n是0表示删除全部指定的值 ------------------------------ lrem {x}a 2 6 lset key index new_value 替换index位置 ------------------------------ lset {x}a 3 / lrange {x}a 0 -1
set
Set
# 清空数据 get a flushall get b flushall get c flushall sadd key value value ..... ------------------------------- sadd a 11 22 33 44 55 11 22 33 66 77 smembers key 取所有值 ------------------------------- smembers a sismember key value 值是否在Set中存在 ------------------------------- sismember a 33 scard key 元素数量 ------------------------------- scard a srem key value value ...... 删除值 ------------------------------- srem a 33 44 smembers a spop key [count] 随机弹出 ------------------------------- spop a spop a 4 srandmember key n 随机访问n个值(不会删除) ------------------------------- srandmember a 2 smove from to value 把值从一个集合移动到另一个集合 ------------------------------- sadd {x}a 11 22 33 44 55 66 smove {x}a {x}b 55 smembers {x}a smembers {x}b sinter key key ...... 多个Set集合的交集 sunion key key ...... 并集 sdiff key key ...... 差集 ------------------------------- sadd {x}s1 11 22 33 44 sadd {x}s2 22 44 66 88 sinter {x}s1 {x}s2 sunion {x}s1 {x}s2 sdiff {x}s1 {x}s2
hash
Map 多数情况下直接使用redis存放兼职数据 如果多个属性的对象中, 有的属性要频繁修改, 可以考虑使用Hash
对象
zset
有序set
5种基本
bitmap
位图
getspatial(GEO)
地理位置
hyperloglogs
这种数据结构在Redis这种NoSQL型数据库中可以非常省内存的去统计各种计数, 比如注册 IP 数、每日访问 IP 数、页面实时UV、在线用户数,共同好友数等。
高级
基本使用命令
set key value
添加K-V,key重复会覆盖value的数据
del key..
返回结果表示删除了几条数据
flusdb
清空
get key
获取key对应的value, 若key不存在返回(nil)
keys pattern
查找key,如果不存在,则反馈(empty list or set)
pattern 模式:可使用*作为通配符
PFADD、PFCOUNT、PFMERGE
key操作命令
docker rm -f $(docker ps -aq) --volumes redis --help redis -l redis -lx redis -c docker exec -it redis7000 redis-cli -c -p 7000 set a aaaaaaaaaa set b bbbbbbbbbbbbb set c ccccccccccccc set d dddddddddd keys * 查询所有Key exists a Key是否存在 type a 对应的值的数据类型 del d 删除Key,等待从所有主从服务器都删除完成 unlink c 异步删除,主机删除后直接返回,不等待从机删除 expire a 10 设置过期时间(秒) ttl a 查看过期时间
库操作
config get databases 查看数据库数量 select 1 切换库,不支持集群模式 dbsize 当前库中键的数量 flushdb 清空当前库 flushall 清空所有库
Pub-Sub
Redis事务
功能弱 Multi 组队命令序列 类似 mysql 的 begin multi 后可以执行多步数据操作 Exec 执行multi的命令序列 相当于 mysql 的 commit Discard 放弃执行命令序列 相当于回滚
在项目中使用Redis
spring-boot-starter-data-redis
子主题
List<V> range(K key, long start, long end);
org.springframework.data.
.redis.core
RedisTemplate<K, V>
Autowired
设置
setKeySerializer(RedisSerializer<?> serializer)
设置key序列化器
setValueSerializer(RedisSerializer<?> serializer)
设置value序列化器
setConnectionFactory(RedisConnectionFactory connectionFactory)
方法设置参数后,spring可自己传参
设置连接工厂
ValueOperations<K, V> opsForValue
对普通值(对应Redis中的string)进行操作时
ListOperations<K, V> opsForList()
对List(对应Redis中的list)进行操作时
BoundValueOperations<K, V> boundValueOps(K key)
根据key执行下面的操作
Boolean/ Long delete(K key/ Collection<K> keys)
Set<K> keys(K pattern)
在项目正式上线的版本中,禁止调用keys()方法以查找Key!!!也禁止在Redis客户端中使用keys命令!!!
Boolean hasKey(K key)
判断缓存是否存在
StringRedisTemplate
<String, String> RedisTemplate对象在保存数据到Redis时,会将数据进行序列化后保存 这样做,对java对象或类似的数据再Redis中的读写效率是高的,但缺点是不能再redis中对数据进行修改 要想修改,必须从redis中获取后修改属性,在添加\覆盖到Redis中,这样的操作在多线程时就容易产生线程安全问题 我们现在保存的库存数,如果也用redisTemplate保存,高并发时就会产生超卖 解决办法是操作一个能够直接在Redis中对数据进行修改的对象,来保存它的库存数,防止超卖 SpringDataRedis提供了StringRedisTemplate类型,它可以直接操作Redis中的字符串值 使用StringRedisTemplate向Redis保存数据,可以直接保存字符串,没有序列化过程的 它支持使用java代码直接向redis发送修改库存数值的方法,适合当下管理库存的业务需求 最后结合Redis操作数据是单线程的特征,避免线程安全问题,防止超卖
RedisSerializer<T>
序列化器
RedisSerializer<String> string()
RedisSerializer<Object> json()
RedisConnectionFactory
连接工厂
ValueOperations<K, V>
对普通值(对应Redis中的string)进行操作时
void set(K key, V value);
存入
V get(Object key);
ListOperations<K, V>
对List(对应Redis中的list)进行操作时
Long rightPush(K key, V value);
增
Long size(K key);
列表长度
List<V> range(K key, long start, long end);
[start, end]的列表
1. 这2个参数的值都表示元素在列表中的下标
2. 各元素有正向下标,也有反向下标,正向下标是以第1个元素从0开始从左至右递增编号的,反向下标是以最后一个元素从-1开始从右至左递减编号的
3. start元素必须不在end元素的右侧,否则获取不到数据
4. 使用超出界限的下标并不会导致出错,实际获取效果将以可用的界限值为准
BoundValueOperations<K, V>
void set(V var1, long var2, TimeUnit var4);
将数据加入缓存,可设置有效期。 过期清除
参数为, 存入内容,有效期,日期单位
我们测试的代码中建议定义较小的有效期,例如1分钟,在上线的项目中保存时间会长,例如24小时甚至更长
V get();
获取查询结果
script.
RedisScript
RedisConfiguration
需要配置RedisTemplate对象,通过此对象的API来访问Redis!
返回一个redisTemplate对象并存入spring容器中
设置序列化器
设置连接工厂
service
1. 判断redis是否存在,存在的话直接读取即可
2. 没有的话向数据库查询,将得到的数据存到redis中
布隆过滤器
巴顿.布隆于⼀九七零年提出 布隆过滤器能够实现使用较少的空间来判断一个指定的元素是否包含在一个集合中 布隆过滤器并不保存这些数据,所以只能判断是否存在,而并不能取出该元素 常安装与Linux系统中
使用情景
凡是判断一个元素是否在一个集合中的操作,都可以使用它
1. idea中编写代码,一个单词是否包含在正确拼写的词库中(拼写不正确划绿线的提示)
2. 公安系统,根据身份证号\人脸信息,判断该人是否在追逃名单中
3. 爬虫检查一个网址是否被爬取过
常规的检查
一个元素是否在一个集合中的思路是遍历集合,判断元素是否相等
如果数据量太大,每个元素都要生成哈希值来保存,我们也要依靠哈希值来判定是否存在,一般情况下,我们为了保证尽量少的哈希值冲突需要8字节哈希值做保存
long取值范围:-9223372036854775808-----9223372036854775807
5亿条数据 每条8字节计算后结果为需要3.72G内存,随着数据数量增长,占用内存数字可能更大
原理
⼀个很长的⼆进制向量(位数组)
⼀系列随机函数 (哈希)
我们使用3个hash算法,找到布隆过滤器的位置
算法1:semlinker--> 2
算法2:semlinker--> 4
算法3:semlinker--> 6
空间效率和查询效率⾼(又小又快)
有⼀定的误判率(哈希表是精确匹配)
布隆过滤器判断不存在的,一定不在集合中
布隆过滤器判断存在的,有可能不在集合中
分配一个合理大小的内存,内存约节省,误判率越高
n 是已经添加元素的数量;
数据量
k 哈希的次数;
算法个数
m 布隆过滤器的长度(位数的大小)
分配的内存
Pfp计算结果就是误判率
<!-- redis依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
spring: redis: host: 192.168.137.150 port: 6379 password:
RedisBloom
利用redis编写布隆过滤器
使用过程
建库,并使用
创建主表,关系表
业务
添加数据
关联查询
修改
查询
Elasticsearch
elastic:富有弹性的 search:搜索 这样的软件有一个名称全文搜索引擎 它本质就是一个java项目,使用它进行数据的增删改查就是访问这个项目的控制器方法(url路径)
这个软件不再是SpringCloud提供的,它也不针对微服务环境的项目来开发 Elasticsearch和redis\mysql一样,不仅服务于java语言,其它语言也可以使用 它的功能也类似一个数据库,能高效的从大量数据中搜索匹配指定关键字的内容 它也将数据保存在硬盘中
ES的底层技术
ES使用了java的一套名为Lucene的API
这个API提供了全文搜索引擎核心操作的接口,相当于搜索引擎的核心支持,ES是在Lucene的基础上进行了完善,实现了开箱即用的搜索引擎软件 市面上和ES功能类似的软件有 Solr(因功能冗余被淘汰)/MongoDB(大数据)
解决的问题
select * from spu where spu_name like '%鼠标%'
前模糊查询是不能使用索引的,只能是全表的逐行搜索,所以效率非常低
数据库进行模糊查询效率严重低下 所有关系型数据库都有这个缺点(mysql\mariaDB\oracle\DB2等)
测试证明一张千万级别的数据表进行模糊查询需要20秒以上 当前互联网项目要求"三高"的需求下,这样的效率肯定不能接受
Elasticsearch主要是为了解决数据库模糊查询性能低下问题的
ES进行优化之后,从同样数据量的ES中查询相同条件数据,效率能够提高100倍以上
数据库索引
所谓的索引(index)其实就是数据目录 通常情况下,索引是为了提高查询效率的
数据库索引分两大类
聚集索引
就是数据库保存数据的物理顺序依据,默认情况下就是主键id,所以按id查询数据库中的数据效率非常高
非聚集索引
如果想在非主键列上添加索引,就是非聚集索引了
索引面试题
1.创建的索引会占用硬盘空间
2.创建索引之后,对该表进行增删改操作时,会引起索引的更新,所以效率会降低
增删改负面影响
3.对数据库进行批量新增(等操作)时,先删除索引,增加完毕之后再创建
4.不要对数据样本少的列添加索引
男,女
5.模糊查询时,查询条件前模糊的情况,是无法启用索引的
6.每次从数据表中查询的数据的比例越高,索引的效果越低
10个数据找8个没必要索引
运行原理
要想使用ES提高模糊查询效率 首先要将数据库中的数据复制到ES中 在新增数据到ES的过程中,ES可以对指定的列进行分词索引保存在索引库中 形成倒排索引结构
创建一个子项目search
操作ES是对ES发送请求
HTTP Request文件
能够向ES发送请求的文件 这种能够向指定url发送请求的文件格式称之为http client(http 客户端)
###
三个#开头,表示注释,也是分割符
http文件要求每个请求必须以分隔符开始,否则运行就会混乱报错
GET http://localhost:9200
analyzer(分词器)
POST http://localhost:9200/_analyze Content-Type: application/json { "text": "my name is hanmeimei", "analyzer": "standard" }
standard是ES默认的分词器,
analyzer ": " standard 是可以省略的
standard这个分词器只能对英文等西文字符
用空格分隔单词的 ,进行正确分词
但是中文分词不能按空格分,按这个分词器分词,每个字都会形成分词,这样的结果不能满足我们日常的搜索需要
免费的中文分词器词库插件IK
解决中文不能正确分词的问题 实际上要引入一个中文常见词语的词库,分词时按照词库中的词语分词即可 但是词库的容量有限,比较新的网络名词和较新出现的人名是不在词库中的
安装插件之后要重启ES才能生效
ik分词插件的种类
ik_smart
优点:特征是粗略快速的将文字进行分词,占用空间小,查询速度快
缺点:分词的颗粒度大,可能跳过一些重要分词,导致查询结果不全面,查全率低
ik_max_word
优点:特征是详细的文字片段进行分词,查询时查全率高,不容易遗漏数据
缺点:因为分词太过详细,导致有一些无用分词,占用空间较大,查询速度慢
使用ES操作数据
一个数据库性质的软件可以执行增删改查操作
和关系型数据库相比
操作数据不使用sql
数据的结构也不同
ES启动后,ES服务可以创建多个index(索引),index可以理解为数据库中表的概念
一个index可以创建多个保存数据的document(文档),一个document理解为数据库中的一行数据
一个document中可以保存多个属性和属性值,对应数据库中的字段(列)和字段值
index
### 创建 index PUT http://localhost:9200/questions(索引名)
### 删除一个Index DELETE http://localhost:9200/questions(索引名)
### 设置index中的文档属性采用ik分词 POST http://localhost:9200/questions/_mapping Content-Type: application/json { "properties": { "title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_max_word" }, "content": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_max_word" } } }
搜索分词器
document
### questions 中添加文档 POST http://localhost:9200/questions/_create/1 Content-Type: application/json { "id":1, "title":"Java基本数据类型有哪些", "content":"面试时候为啥要问基本类型这么简单问题呀,我们要如何回答呢?" }
1
### 删除questions中的一个文档 DELETE http://localhost:9200/questions/_doc/2
### 更新questions索引中的文档 POST http://localhost:9200/questions/_doc/4/_update Content-Type: application/json { "doc": { "title": "Java线程的run方法和start方法有啥区别" } }
### 查询数据 GET http://localhost:9200/questions/_doc/1
### 搜索 ES POST http://localhost:9200/questions/_search Content-Type: application/json { "query": { "match": {"title": "类型" } } }
默认"_score"排序
位置
次数
比例
...
### 多字段搜索 POST http://localhost:9200/questions/_search Content-Type: application/json { "query": { "bool": { "should/must": [ { "match": { "title": "java类型" }}, { "match": { "content": "java类型"}} ] } } }
should 或
must 且
ELK
是当今业界非常流行的日志采集保存和查询的系统
Logstash
logstash是一款开源的日志采集,处理,输出的软件,每秒可以处理数以万计条数据,可以同时从多个来源采集数据,转换数据,然后将数据输出至自己喜欢的存储库中(官方推荐的存储库为Elasticsearch)
过程
input 将数据源的数据采集到Logstash
filter (非必要)如果需要可以对采集到的数据进行处理
output 将处理好的数据保存到目标(一般就是ES)
为了更高效的实现分词,logstash将所有需要分词的列拼接组合成了一个新列search_text
entity类无需编写分词的属性
USE mall_pms; UPDATE pms_spu SET gmt_modified=NOW() WHERE 1=1;
激活logstash对spu表的监听,并向ES中保存数据
安装配置好相关软件后 logstash会自动监听指定的表(一般指定监听gmt_modified列) 当gmt_modified列值变化时,logstash就会收集变化的行的信息 周期性的向ES进行提交 数据库中变化的数据就会自动同步到ES中了 这样,我们在程序中,就无需编写任何同步ES和数据库的代码
Kibana来负责进行查询和查看结果
SQL Server
数据类型
函数
字符串
concat( , )
合并
CONVERT(varchar(20),ADVICE_ID)+CONVERT(varchar(20),idadd)
格式转换
时间
数字
identity(a,b),ab均为正整数,a表示开始数,b表示增幅,就像identity(1,1)意思就是该列自动增长,由1开始每次增加是1
DQL
select 字段名
into DocATEMP
将数据备份到某一个表中
from openrowset( 'OraOLEDB.Oracle', 'orcl'; 'system'; 'Yahua3585668', 'select 护理单元代码 as NURSE_UNIT_CODE, 患者ID as PATIENT_ID, '''' as DEPT_ID, 医嘱ID as ADVICE_ID, 医嘱时间 as ADVICE_TIME, 医嘱类型 as ADVICE_TYPE, 下嘱医生 as ADVICE_DOCTOR, 医嘱内容 as ADVICE_TEXT from system.ZLYH_医嘱信息 where to_char(医嘱时间,''yyyy-mm-dd'')>= to_char(sysdate-1,''yyyy-mm-dd'')')
跨库获取数据
主题
oracle
用户口令
scott
密码
数据库
dos
sqlplus
nolog
不登陆
scott/tiger
用户名/密码
sys/orcl as sysdba
管理员登陆
sqlplus
命令窗口
PL
操作Oracle的程序
sql>
指令代码
conn scott/tiger
用户名/密码连接
conn sys/orcl as sysdba/SYSOPER
管理员 sys system
show user
DESC 表名
非sql
disconnect
断开
clear screen
exit
sql窗口
用户
alter user scott account lock;
alter user scott account unlock;
管理员解锁
CREATE USER RXXT IDENTIFIED BY "rxxt“
创建用户
数据库实例
表
增
删
-- 截断表
没办法回滚,删大数据量的快
truncate table myemp;
改
关联更新
update emp a set deptno=(select b.deptno from dept b where b.deptno=a.deptno) where exists (select 1 from dept b where b.deptno=a.deptno )
约束
查
select * from tab;
tab 查全表
x sysdate x dual
当前日期 dual虚拟表
函数
数值
abs(-100)
绝对值
100
sign(-100),sign(0)
正负1
-1,0
ceil(x)/floor(x)
向上取整
round()
四舍五入
数值
保留位数
负的向左保留
trunc()
截断小数位
数值
截取小数点后几位
mod(,)
取余
数值
除数
字符串
upper/lower('')
转大小写
initcap(ename)
首字母大写
字符串数据
concat('','')/ ||
串连接
字符串数据
nls表达式
instr(,,,)/instrb()
获取目标字符串的(b全角2字符)位置
字符串数据
目标字符串
开始位置
1
出现次数
1
length(ename)=5/lengthb()
字符长度(b全角2字符,其他1字符)
字符串数据
substr(ename,1,3)
截取子串
字符串数据
起始点
负的从后往前
截取长度
replace(ename,'A','a')
转字符
字符串数据
目标
结果
trim('' from '')
删除的字符串
默认空格
旧字符串
日期
sysdate
当前时间
add_months(hiredate, 6)
后几月
日期
月数
months_between(sysdate,hiredate)*12/
间隔月
工龄
当前时间
日期
next_day(add_months(hiredate, 6), '星期一')
某月的日期
next_day(,'')
指定星期的日期
日期
指定的星期日
last_day(hiredate)-2
月末
每月倒数第三天
日期
通用
nvl(comm, 0)
将为null的转换为0,null运算都为null
字段
替换数据
decode(,,,)
条件判断
字段
判断原始数据
结果替换数据
n
默认
转换
to_char(hiredate, 'mm')
数据转字符
数据
字符形式
日期形式
fm 去零
y 年
m 月
d 日
数值
99,999
标注
$99,999
美元位数
L99,999
本地
0000
定长
to_number('123')
字符转数字
to_date('1999-06-17','yyyy-mm-dd')
将字符串转换为日期
算数运算
列函数
聚合函数,与分组绑定
rownum <= 3
取前三行数据
oracle 正则表达式
它们使用POSIX 正则表达式代替了老的百分号(%)和通配符(_)字符
regexp_like(,)
regexp_like(source_string,pattern[match_parameter])
REGEXP_INSTR :与INSTR的功能相似
regexp_instr(source_string,pattern[,position[,occurrence[,return_option[,match_parameter]]]])
REGEXP_SUBSTR :与SUBSTR的功能相似SS
regexp_substr(source_string,pattern[,position[,occurrence[,match_parameter]]])
REGEXP_REPLACE :与REPLACE的功能相似
regexp_replace(source_string,pattern[,replace_string[,position[,occurrence[,match_parameter]]]])
regexp_count(source_char,pattern[,position[,match_param]])
单行函数,分别计算每一行。
POSIX 正则表达式由标准的元字符(metacharacters)所构成:
\转义符
\num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。
(), (?:), (?=), [] 圆括号和方括号
'( )' 标记一个子表达式的开始和结束位置。
'[]' 标记一个中括号表达式。
*, +, ?, {n}, {n,}, {n,m} 限定符
'{m,n}' 一个精确地出现次数范围,m=<出现次数<=n,'{m}'表示出现m次,'{m,}'表示至少出现m次。
'.' 匹配除换行符之外的任何单字符。
'?' 匹配前面的子表达式零次或一次。
'+' 匹配前面的子表达式一次或多次。
'*' 匹配前面的子表达式零次或多次。
'|' 指明两项之间的一个选择。例子'^([a-z]+|[0-9]+)$'表示所有小写字母或数字组合成的字符串。
字符簇:
[[:alpha:]] 任何字母。
[[:digit:]] 任何数字。
[[:alnum:]] 任何字母和数字。
[[:space:]] 任何白字符。
[[:upper:]] 任何大写字母。
[[:lower:]] 任何小写字母。
[[:punct:]] 任何标点符号。
[[:xdigit:]] 任何16进制的数字,相当于[0-9a-fA-F]。
各种操作符的运算优先级
^, $, anymetacharacter 位置和顺序
'^' 匹配输入字符串的开始位置,在方括号表达式中使用,此时它表示不接受该字符集合。
'$' 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。
1999
inner join
等值连接
=
非等值连接
cross join
交叉连接:产生笛卡尔积
natural join
两表拥有相同字段自动连接,且不能使用别名
视图
rowid/rownum
序列
索引
概念
数据字典
用户对象
USER_
ALL_
数据库实例对象
DBA_
动态信息
V$_
GV$
同义词
引用对象不需指出对象的持有者 引用对象不需指出它所位于的数据库 为对象提供另一个名字
CREATE PUBLIC SYNONYM EMP FOR SCOTT.EMP;
DROP PUBLIC SYNONYM EMP
dblink
create public database link mylink connect to scott identified by tiger using 'server'
服务名
DROP PUBLIC DATABASE LINK mylink
SELECT * FROM 表名@据库链接名
结构体系
文件结构
内存结构
逻辑结构
数据分析
数据库查询
excel查询
主题