MySql
1. 初识Mysql
关系型数据库(SQL)
MySql, Oracle, SqlServer
利用,表和表 ,行和列关系存储数据
非关系型数据库(NoSQL)
redis
对象存储,通过对象自身属性来决定
1.1 安装MySql
1.2 安装可视化工具
1.3 数据库语言
DDL : 定义
DML :管理
DQL :查询
DCL :控制
2.操作数据库
2.1基本操作
查看数据库
show Databases
创建数据库
CREATE DATABASE IF NOT EXISTS test
删除数据库
DROP DATABASE IF EXISTS test
2.2数据库的列类型
数值
- tinyint 1字节
- smallint 2字节
- mediumint 3字节
- int
- bigint 8字节
- float
- double
- decimal 字符串形式的浮点数(多用于金融领域)
字符串
- char 0~255
- varchar 0~65535
- tinytext 2^8-1
- test 2^16-1
时间
- data 日期
- time 时间
- datetime 日期+时间
- timestamp 时间戳
- year 年份
null
- 未知(避免使用Null运算)
每个表规范至少应有以下几个属性
id 主键
'version' 乐观锁
is_delete 伪删除
gmt_creat 创建时间
gmt_update 修改时间
表字段创建
/*字符串用’‘括起来
AUTO_INCREMENT 自增
语句后面加,
属性名字段用``而不是''
格式
CREATE TABLE IF NOT EXISTS 'student'{
`字段名` 列类型 【属性】 【索引】 【注释】,
。。。。。。
}【表类型】【字符集】【注释】
*/
CREATE TABLE IF NOT EXISTS `student`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` VARCHAR(20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`sex` VARCHAR(2)NOT NULL COMMENT '性别',
`birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
`address` VARCHAR(100) DEFAULT NULL COMMENT '住址',
`email` VARCHAR(20) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=UTF8;
2.3修改和删除表
修改
修改表名
ALTER TABLE [TABLENAME] RENAME AS [TABLENAME]
-- 增加表的字段
ALTER TABLE teacher ADD age INT(4);
-- 字段重命名
ALTER TABLE teacher CHANGE age Age INT(3);
-- 修改约束
ALTER TABLE teacher MODIFY Age VARCHAR(3);
-- 删除字段
ALTER TABLE teacher DROP Age;
注意点:
- 字段名用 ``
- 大小写不敏感
2.4数据库表的类型
INNODB(默认使用)和MYISAM
MYISAM | INNODB | |
---|---|---|
事务支持 | 不支持 | 支持 |
数据行锁定 | 不支持 | 支持 |
外键约束 | 不支持 | 支持 |
全文索引 | 支持 | 不支持 |
表空间大小 | 较小 | 较大,约为2倍 |
- MYISAM:节约空间,操作速度快
- INNODB:安全性高,事务的处理,支持多表多用户操作
3.数据管理
3.1外键
CREATE TABLE IF NOT EXISTS `grade`(
`gradeId` INT(4) NOT NULL AUTO_INCREMENT,
`teacherId` INT(4) ,
`grade` INT(4),
PRIMARY key(`gradeId`),
key `FK_teacherId`(`teacherId`),
CONSTRAINT `FK_teacherId` FOREIGN KEY(`teacherId`) REFERENCES `teacher` (`id`)
)ENGINE=INNODB DEFAULT CHARSET=UTF8
外键可能产生的问题:
- 容易产生死锁
- 维护麻烦
- 影响数据库效率
规范
尽量不使用外键,一切级联在应用层面实现
3.2DML语言
插入语句
insert into 表名(字段1 , 字段2, ......)values(值1, 值2 , ......)
修改语句
update 表名 set 字段名1=值1 , 字段2 = 值2, ...... where 条件1 and(or) 条件2......
条件语句
操作符 | 含义 | 范围 | 结果 |
---|---|---|---|
= | 等于 | 5=6 | false |
<> | 不等于 | 5<>6 | true |
<= | |||
between…and… | 在某个范围内 |
删除
delete from 表名 where 条件
清空一张表
truncate 表名
delete和truncate的区别
- 都能删除数据
- delete不会影响自增,truncate自增会归零
- truncate不会影响事务
4.DQL
查询语言是数据库中最核心,使用最频繁的语句
简单查询
select id AS 学号, name AS 姓名 from student AS 学生 where 条件
对于不是很好的字段名,我们可以在AS后面写别名
去重 distinct
作用查询重复的结果只显示一个
select distinct 字段名 From 表名 where 条件
条件子句
操作符 | 语法 | 描述 |
---|---|---|
and | a and b | 逻辑与 |
or | a or b | 逻辑或 |
not | not a=b | 逻辑非 |
模糊查询
运算符 | 语法 | 描述 |
---|---|---|
IS NULL | A IS NULL | |
IS NOT NULL | ||
LIKE | A LIKE B | |
IN | A IN B |
like 通常结合 ‘%’(代表任意个字符) , ‘_'(代表一个字符)使用
联表查询
在执行完join on之后就会形成一张临时的中间表,然后针对临时表在进行where筛选、之后分组、排序、分页等操作,这样就将连表查询条件和数据过滤条件严格分开,不可以将连表查询条件写在where子句中
--查询学生所属的年级(学号,姓名,年级名称)
SELECT `studentId`,`studentName`, `gradeName`
FROM Student s
LEFT JOIN Grade g
ON s.GradeId = g.GradeId
--查询科目所属年级
SELECT `subjectName` ,`gradeName`
FROM Subject sub
INNER JOIN Grade g
ON sub.gradeId = g.gradeID
--查询参加数据库结构考试的学生信息(学号,姓名,科目,分数)
SELECT `studentId`, `studentName`, `subjectName`,`studentRes`
FROM student s
INNER JOIN result res
ON s.studentId = res.student.Id
INNER JOIN subject sub
ON (res.subjectId = sub.subjectId and subjectName = '数据库结构')
自连接
即自己和自己连接
核心:一张表拆为两张一样的表
categoryId | categoryName |
---|---|
2 | 信息技术 |
3 | 软件开发 |
5 | 美术设计 |
pid | categoryId | ctaegoryName |
---|---|---|
3 | 4 | 数据库 |
2 | 8 | 办公信息 |
3 | 6 | WEB开发 |
5· | 7 | PS技术 |
操作:查询父类对应的子类信息
select a.categoryName AS ‘父栏目’, b.categoryName AS ‘子栏目’ where a.categoryId = b.pid
分页和排序
排序
语法:ORDER BY 要排序的属性 ASC(升序) DESC(降序)
分页
语法:LIMIT 起始值,页面大小
--查询成绩排名前十且分数大于80的学生信息(学生姓名,课程名,分数)
SELECT studentName, subjectName, result
FROM student s
INNER JOIN result res
ON (s.studentId = res.studentId and res.result >= 80)
ORDER BY res.result DESC
LIMIT 0,10
子查询
--查询数据库的所有考试结果(学号,学生姓名,科目名称,成绩),降序排序
SELECT studentId, studentName, subjectName, result
FROM student s
INNER JOIN result res
ON s.studentId = res.studentId
INNER JOIN subject sub
ON (res.subjectId = sub.subjectId and sub.subjectName = '数据库')
ORDER BY studentID DESC
小结:
![](https://cdn.jsdelivr.net/gh/1717000160/Anime4K@master/results/Demo20200516191514.png)
5.Mysql函数
常用函数
-- 数学运算
ABS(-1) -- 绝对值
CEILING(1.2) -- 向上去取整
FLOOR(1.6) -- 向下取整
RAND() -- 返回一个0~1的随机数
-- 字符串函数
CHAR_LENGTH('') -- 返回字符串长度
CONCAT(‘’ ,‘’ ,‘‘) -- 拼接字符串
LOWER(’‘) -- 小写字母
UPPER(’‘) -- 大写字母
INSTR(’‘,’‘) -- 返回第一次出现子串的索引
REPLACE(’‘, ’‘,’‘ ) -- 替换指定字符串
REVERSE(’‘) -- 翻转字符串
--时间和日期
CURRENT_DATE() -- 返回当前日期
NOW() -- 当前时间
聚合函数
函数名 | 描述 |
---|---|
COUNT() | 计数 |
SUM() | 求和 |
AVG() | 求平均值 |
MAX() | 求最大值 |
SELECT COUNT(`Name`) FROM country -- 忽略NULL
SELECT COUNT(*) FROM country -- 不忽略NULL,本质是计算行数
SELECT COUNT(1) FROM country -- 和COUNT(*)基本没有差别
-- 执行效率上:
-- 列名为主键,count(列名)会比count(1)快
-- 列名不为主键,count(1)会比count(列名)快
-- 如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*)
-- 如果有主键,则 select count(主键)的执行效率是最优的
-- 如果表只有一个字段,则 select count(*)最优。
MD5加密
MD5不可逆,但相同的密码的MD5值相同
pwd = MD5(pwd)
6.事务
6.1什么是事务
事务是指是程序中一系列严密的逻辑操作,而且所有操作必须全部成功完成,否则在每个操作中所作的所有更改都会被撤消。可以通俗理解为:就是把多件事情当做一件事情来处理,好比大家同在一条船上,要活一起活,要完一起完 。
6.2事务的四个特性
原子性
要么都成功,要么都失败
操作这些指令时,要么全部执行成功,要么全部不执行。只要其中一个指令执行失败,所有的指令都执行失败,数据进行回滚,回到执行指令前的数据状态。
一致性
事务前后数据完整性一致
举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。
隔离性
隔离性是当多个用户并发访问数据库时,比如同时操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性
事务一经提交,数据就要可持久化(直接更新到内存中)
模拟事务
USE shop
CREATE TABLE `account`(
`id` INT(3) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL,
`money` DECIMAL(9,2) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO account(`name`,`money`)
VALUES ('A', 2000.00), ('B', 10000.00)
-- 模拟转账
set autocommit = 0; -- 关闭自动提交
START TRANSACTION; -- 开启一个事务
UPDATE account SET money=money-500 WHERE `name`='A';
UPDATE account SET money=money+500 WHERE `name`='B';
COMMIT; -- 提交
ROLLBACK; -- 回滚
SET autocommit = 1; -- 开启自动提交
6.3常见数据读取问题
脏读
读取未提交的数据
A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。
不可重复读
前后多次读取,数据内容不一致
幻读
前后多次读取,数据条目不一致
6.4事务的隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(未授权读取、读未提交)、Read committed(授权读取、读提交)、Repeatable read(可重复读取)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。
- Read uncommitted(未授权读取、读未提交):
如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。这样就避免了更新丢失,却可能出现脏读。也就是说事务B读取到了事务A未提交的数据。 - Read committed(授权读取、读提交):
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。 - Repeatable read(可重复读取):
可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,即使第二个事务对数据进行修改,第一个事务两次读到的的数据是一样的。这样就发生了在一个事务内两次读到的数据是一样的,因此称为是可重复读。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。这样避免了不可重复读取和脏读,但是有时可能出现幻象读。(读取数据的事务)这可以通过“共享读锁”和“排他写锁”实现。 - Serializable(序列化):
提供严格的事务隔离。**它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。**如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。
不可重复读的重点是修改:
同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
幻读的重点在于新增或者删除:
同样的条件, 第1次和第2次读出来的记录数不一样
从控制的角度来看, 两者的区别就比较大
对于前者, 只需要锁住满足条件的记录
对于后者, 要锁住满足条件及其相近的记录
6.5乐观锁与悲观锁
虽然数据库的隔离级别可以解决大多数问题,但是灵活度较差,为此又提出了悲观锁和乐观锁的概念。
乐观锁
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。
使用乐观锁就不需要借助数据库的锁机制
我们在更新之前,先查询一下库存表中当前库存数(quantity),然后在做update的时候,以库存数作为一个修改条件。当我们提交更新的时候,判断数据库表对应记录的当前库存数与第一次取出来的库存数进行比对,如果数据库表当前库存数与第一次取出来的库存数相等,则予以更新,否则认为是过期数据。
乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现ABA问题,因为版本号只会增加不会减少。(解决ABA问题)
悲观锁
当我们要对一个数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制
悲观锁主要是共享锁或排他锁
- 共享锁又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
- 排他锁又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据行读取和修改。
总结:
- 乐观锁并未真正加锁,效率高。一旦锁的粒度掌握不好,更新失败的概率就会比较高,容易发生业务失败。
- 悲观锁依赖数据库锁,效率低。更新失败的概率比较低。
7.索引
索引(index)是帮助MySQL高效获取数据的数据结构
7.1索引的分类
- 主键索引(PRIMARY KEY)
- 唯一的标识,主键不可重复,只能有一个列作为主键
- 唯一索引(UNIQUE KEY)
- 你面重复的列出现,可以有多个唯一索引
- 常规索引(KEY/INDEX)
- 全文索引(FullText)
7.2索引原则
- 索引不是越多越好
- 不要对经常变动的数据加索引
- 小数据量的表不需要加索引
- 索引一般加在查询的字段上
7.2索引的数据结构
推荐博客:
http://blog.codinglabs.org/articles/theory-of-mysql-index.html
8.权限管理和备份
用户权限管理
常用语法
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码'
CREATE USER mrh IDENTIFIED BY '991024'
-- 修改密码(为当前用户)
SET PASSWORD = PASSWORD('123456')
-- 修改指定用户密码
SET PASSWORD FOR mrh = PASSWORD('123456')
-- 重命名用户
RENAME USER mrh TO mrh2
-- 用户授权 ALL PRIVILEGES(所有权限,不包括授予别人权限)
GRANT ALL PRIVILEGES ON *.* TO mrh
-- 查看权限
SHOW GRANTS FOR mrh
SHOW GRANTS FOR root@localhost
-- 撤销权限
REVOKE ALL PRIVILEGES ON *.* FROM mrh
-- 删除用户
DROP USER mrh
数据备份
控制台语句:
数据导出:
mysqldump -hlocalhost -uroot -p991024 shop account >D:/a.sql
mysqldump -h主机 -u用户名 -p密码 数据库 表1 表2 表3 >路径
数据导入:
source 备份文件
9.数据库设计
糟糕的数据库设计
- 数据冗余,浪费空间
- 操作麻烦异常,难以维护
- 性能差
良好的数据库
- 节省空间
- 保证数据库的完整性
- 方便开发
设计数据库举例:
- 搜集信息,分析需求
- 标识实体(落地需求到每个字段)
- 明确实体间的关系
三大范式
关于函数依赖
第一范式
原子性:保证每一列不可分
第二范式
前提:满足第一范式
消除非主属性对主码的部分函数依赖
每张表描述一件事
第三范式
前提:满足第二范式
消除传递依赖
10.JDBC
10.1数据库驱动
程序通过数据库驱动和数据库连接
不同的数据库驱动具体操作不同,为了同一操作规范,使用JDBC
JDBC位于数据库驱动和程序之间,用于同一操作的规范
PreparedStatement对象
先写sql,不执行
防止sql注入的本质:将传入的参数全部当作字符,如果存在转义字符直接忽略
数据库连接池
数据库连接–执行–释放
连接–释放十分浪费资源
池化技术:准备一些预先的资源,有需求就直接连接预先准备好的
最小连接数
最大连接数
等待超时
编写连接池:实现一个接口
开源数据源实现
DBCP
C3P0
Druid(阿里巴巴)