【数据库】SQL语法
https://blog.csdn/weixin_42915286/article/details/85339284
【数据库】数据库原理
https://blog.csdn/weixin_42915286/article/details/83028265
安装路径:
/usr/local/mysql
MySQL
MySQL是什么?
My是MySQL的联合创始人 - Monty Widenius 的女儿的名字。MySQL是My和SQL的组合,这就是MySQL命名的由来。
MySQL的官方网址: http://www.mysql/ ;
MySQL的社区版本下载地址为: http://dev.mysql/downloads/mysql/
MySQL是一个数据库管理系统,也是一个关系数据库。 它是由Oracle支持的开源软件。这意味着任何一个人都可以使用MySQL而不用支付一毛钱。 另外,如果需要,还可以更改其源代码或进行二次开发以满足您的需要。
即使MySQL是开源软件,但是可以从Oracle购买商业许可证版本,以获得高级支持服务(特殊企业用户需要)。
与其他数据库软件(如Oracle数据库或Microsoft SQL Server)相比,MySQL非常容易学习和掌握。
MySQL可以在各种平台上运行UNIX,Linux,Windows等。可以将其安装在服务器甚至桌面系统上。
此外,MySQL是可靠,可扩展和快速的。如果您开发网站或Web应用程序,MySQL是一个不错的选择(强烈建议使用)。
MySQL是LAMP堆栈的重要组成部分,包括Linux,Apache,MySQL和PHP。
————————————————————
SQL语言共分为四大类:
数据【查询】语言【DQL】 (Data Query Language)
数据【操纵】语言【DML】 (Data Manipulation Language)
数据【定义】语言【DDL】 (Data Definition Language)
数据【控制】语言【DCL】 (Data Control Language)
-
数据查询语言 DQL
数据查询语言DQL基本结构是由SELECT子句,FROM子句,WHERE
子句组成的查询块:
SELECT <字段名表>
FROM <表或视图名>
WHERE <查询条件> -
数据操纵语言 DML
数据操纵语言DML主要有三种形式:
- 插入:INSERT
- 更新:UPDATE
- 删除:DELETE
- 数据定义语言 DDL
(作用:建立数据库对象)
数据定义语言DDL用来创建数据库中的各种对象:表、视图、索引、同义词、聚簇等如:
CREATE TABLE/VIEW/INDEX/SYN/CLUSTER
| | | | |
表 视图 索引 同义词 簇
DDL操作是隐性提交的!不能rollback
- 数据控制语言 DCL
数据控制语言DCL用来授予或回收访问数据库的某种特权,并控制
数据库操纵事务发生的时间及效果,对数据库实行监视等。如:
-
GRANT:授权。
-
ROLLBACK [WORK] TO [SAVEPOINT]:回退到某一点。
回滚—ROLLBACK
回滚命令使数据库状态回到上次最后提交的状态。其格式为:
SQL>ROLLBACK; -
COMMIT [WORK]:提交。
在数据库的插入、删除和修改操作时,只有当事务在提交到数据
库时才算完成。在事务提交前,只有操作数据库的这个人才能有权看
到所做的事情,别人只有在最后提交完成后才可以看到。
提交数据有三种类型:显式提交、隐式提交及自动提交。下面分
别说明这三种类型。
(1) 显式提交
用COMMIT命令直接完成的提交为显式提交。其格式为:
SQL>COMMIT;
(2) 隐式提交
用SQL命令间接完成的提交为隐式提交。这些命令是:
ALTER,AUDIT,COMMENT,CONNECT,CREATE,DISCONNECT,DROP,
EXIT,GRANT,NOAUDIT,QUIT,REVOKE,RENAME。
(3) 自动提交
若把AUTOCOMMIT设置为ON,则在插入、修改、删除语句执行后,
系统将自动进行提交,这就是自动提交。其格式为:
SQL>SET AUTOCOMMIT ON;
————————————————————
————————————————————
提问:数据库三范式?
1NF:关系模式R的所有属性都不能再分解成更基本的数据单位;
2NF:满足1NF,且R所有非主属性都完全依赖与R的每一个候选关键属性;
3NF:满足1NF和2NF,X是R的仁义属性集,若X非传递依赖于R的任意一个候选关键字;
提问:一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
答:6
一般情况下,我们创建的表的类型是InnoDB:
如果新增一条记录(不重启mysql的情况下),这条记录的id是8;
但是如果【重启】MySQL的话,这条记录的ID是6。
因为InnoDB表只把自增主键的最大ID记录到内存中,所以重启数据库或者对表OPTIMIZE操作,都会使最大ID丢失。
但是,如果我们使用表的类型是MylSAM;
那么这条记录的ID就是8。因为MylSAM表会把自增主键的最大ID记录到数据文件里面,重启MYSQL后,自增主键的最大ID也不会丢失。
提问:如何获取当前数据库版本??
mysql>select version();
提问:ACID 是什么?
1.Atomicity 原子性
原子性,指的是整个事务是一个独立的单元,是不可分割的工作单位,要么操作成功,要么操作不成功,事务必须要保持和系统处于一致的状态,只有使据库中所有的操作执行成功,才算整个事务成功;事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。
2.Consistency 一致性
指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。例如对银行转帐事务,不管事务成功还是失败,应该保证事务结束后ACCOUNTS表中Tom和Jack的存款总额为2000元。
3.Isolation 隔离性
指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。
4.Durability 持久性
指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。
提问:char 和 varchar 的区别是什么?
char:定长,效率高,一般用于固定长度的表单提交数据存储 ;
例如:身份证号,手机号,电话,密码等
varchar:不定长,效率偏低
存数据时的区别
char定义的是固定长度,长度范围为0-255,存储时,如果字符数没有达到定义的位数,会在后面用空格补全存入数据库中,在上例中,name实际存储在数据中的数据为’zejin ‘;
varchar是变长长度,长度范围为0-65535,存储时,如果字符没有达到定义的位数,也不会在后面补空格,在上例subject字段中,实际存储在数据中的数据为’zejin’,当然还有一或两个字节来描述该字节长度;
取数据时的区别
数据库取char的数据时,会把后面的空格全部丢弃掉;
而数据库在取varchar数据时,尾部空格会保留;
提问:float 和 double 的区别是什么?
double精度高,有效数字16位,float精度7位。但double消耗内存是float的两倍,double的运算速度比float慢得多;
float数值类型用于表示单精度浮点数值,而double数值类型用于表示双精度浮点数值,float和double都是浮点型,而decimal是定点型;
MySQL 浮点型和定点型可以用类型名称后加(M,D)来表示,M表示该值的总共长度,D表示小数点后面的长度,M和D又称为精度和标度,如float(7,4)的 可显示为-999.9999,MySQL保存值时进行四舍五入,如果插入999.00009,则结果为999.0001。
FLOAT和DOUBLE在不指 定精度时,默认会按照实际的精度来显示,而DECIMAL在不指定精度时,默认整数为10,小数为0。
提问:mysql 的内连接、左连接、右连接有什么区别??
1.内连接,显示两个表中有联系的所有数据;
2.左连接,以左表为参照,显示所有数据;
3.右连接,以右表为参照,显示所有数据;
提问:mysql 索引是怎么实现的?
索引是一个【排序】的列表,在这个列表中存储着【索引的值】和包含这个值的数据【所在行】的【物理地址】;
在数据十分庞大的时候,索引可以大大加快查询的速度;这是因为使用索引后可以【不用扫描全表】来定位某行的数据,而是先通过【索引表】找到该行数据对应的【物理地址】然后访问相应的数据;
优势:
可以快速检索,减少I/O次数,加快检索速度;根据索引分组和排序,可以加快分组和排序;
劣势:
索引【本身也是表】,因此会占用【存储空间】,一般来说,索引表占用的空间的数据表的【1.5倍】;
索引表的【维护和创建】需要时间成本,这个成本随着数据量增大而增大;构建索引会降低数据表的修改操作(增删改)的效率,因为在修改数据表的同时还需要修改索引表;
索引的分类
常见的索引类型有:主键索引、唯一索引、普通索引、全文索引、组合索引
1、主键索引:即主索引,根据主键pk_clolum(length)建立索引,不允许重复,不允许空值;
2、唯一索引:用来建立索引的列的值必须是唯一的,允许空值
3、普通索引:用表中的普通列构建的索引,没有任何限制
4、全文索引:用大文本对象的列构建的索引(下一部分会讲解)
5、组合索引:用多个列组合构建的索引,这多个列中的值不允许有空值
提问:什么时候要使用索引?
【主键】自动建立唯一索引;
经常作为查询条件在WHERE
或者ORDER BY
语句中出现的列要建立索引;
作为【排序的列】要建立索引;
查询中【与其他表关联】的字段,外键关系建立索引
【高并发】条件下倾向【组合索引】;
用于【聚合函数的列】可以建立索引:例如使用了max(column_1)
或者count(column_1)
时的column_1
就需要建立索引
-
什么时候不要使用索引?
【经常增删改】的列不要建立索引;
有【大量重复的列】不建立索引;
【表记录太少】不要建立索引。只有当数据库里已经有了足够多的测试数据时,它的性能测试结果才有实际参考价值。如果在测试数据库里只有几百条数据记录,它们往往在执行完第一条查询命令之后就被全部加载到内存里,这将使后续的查询命令都执行得非常快–不管有没有使用索引。只有当数据库里的记录超过了1000
条、数据总量也超过了MySQL服务器上的内存总量时,数据库的性能测试结果才有意义。 -
索引失效的情况:
在【组合索引】中不能有列的值为NULL
,如果有,那么这一列对组合索引就是无效的。
在一个SELECT
语句中,【索引只能使用一次】:如果在WHERE
中使用了,那么在ORDER BY
中就不要用了。
LIKE
操作中,'%aaa%'
不会使用索引,也就是索引会失效,但是‘aaa%’
可以使用索引。
在索引的列上使用表达式或者函数会使索引失效,
例如:select * from users where YEAR(adddate)<2007
,将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成:select * from users where adddate<’2007-01-01′
。
其它通配符同样,也就是说,在查询条件中使用【正则表达式】时,只有在搜索模板的【第一个字符不是通配符】的情况下才能使用索引。
在查询条件中使用不等于
,包括<
符号、>
符号和!=
会导致【索引失效】。特别的是如果对主键索引使用!=
则不会使索引失效,如果对主键索引或者整数类型的索引使用<
符号或者>
符号不会使索引失效。
在查询条件中使用IS NULL
或者IS NOT NULL
会导致索引失效。
字符串【不加单引号】会导致索引失效:更准确的说是类型不一致会导致失效,比如字段email
是字符串类型的,使用WHERE email=99999
则会导致失败,应该改为WHERE email='99999'
。
在【查询条件】中使用OR
连接多个条件会导致索引失效,除非OR链接的每个条件都加上索引,这时应该改为两次查询,然后用UNION ALL
连接起来。
如果【排序的字段】使用了索引,那么【select
的字段也要是索引字段】,否则【索引失效】;
特别的是如果排序的是主键索引则select *
也不会导致索引失效。
【尽量不要包括多列排序】,如果一定要,最好为这队列构建【组合索引】;
提问:怎么验证 mysql 的索引是否满足需求?
在select
语句前加上explain
,运行,查看key那一列有没有用到所用的索引;
explain
显示了MySQL如何使用索引来处理select
语句以及连接表
possible_keys
:显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句
key
: 实际使用的索引。如果为NULL
,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USE INDEX(indexname)
来强制使用一个索引或者用IGNORE INDEX(indexname)
来强制MYSQL忽略索引;
提问:说一下数据库的事务隔离?
未提交读(Read Uncommitted):
允许脏读,也就是可能读取到其他会话中未提交事务修改的数据;
提交读(Read Committed):
只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读);
可重复读(Repeated Read):
可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读;
串行读(Serializable):
完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞;
① 脏读:
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
② 不可重复读:
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
④ 幻读:
第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
提问:说一下 mysql 常用的引擎?
Server version: 8.0.15中:
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
更改默认引擎:
在配置文件my.ini
中的[mysqld]
下面加入default-storage-engine=INNODB
a.Innodb引擎
MySQL 8.0的默认引擎,他提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
它本身实际上是基于Mysql后台的完整的系统。Mysql运行的时候,Innodb会在内存中建立缓冲池,用于缓冲数据和索引。
但是,该引擎是不支持全文搜索的。同时,启动也比较的慢,它是不会保存表的行数的。当进行Select count(*) from table
指令的时候,需要进行扫描全表。
所以当需要使用数据库的事务时,该引擎就是首选。由于锁的粒度小,写操作是不会锁定全表的。所以在并发度较高的场景下使用会提升效率的。
b.MyIASM引擎
不提供事务的支持,也不支持行级锁和外键;
因此当执行Insert
和Update
操作时,即执行写操作的时候需要锁定这个表。所以会导致效率会降低。
不过和Innodb不同的是,MyIASM引擎是保存了表的行数,于是当进行Select count(*) from table
语句时,可以直接的读取已经保存的值而不需要进行扫描全表。
所以, 如果表的读操作远远多于写操作时,并且不需要事务的支持的。可以将MyIASM作为数据库引擎的首先。
提问:mysql 的行锁和表锁?
表级锁:
每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
行级锁:
每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;
提问:乐观锁和悲观锁?
白话:
有人想去足浴,【乐观锁】认为XX技师应该在上班,于是走到店门口再问前台(乐观锁非数据库自带,需自己去实现):XX技师在吗?
【悲观锁】认为XX技师应该不在,在家就用自己的电话(数据库本身就有悲观锁)问:确认XX技师在吗?
总结:
1.悲观锁使用了排他锁(写锁),当程序独占锁时,其他程序就连查询都是不允许的,导致吞吐较低。如果在查询较多的情况下,可使用乐观锁。
2.乐观锁更新有可能会失败,甚至是更新几次都失败,这是有风险的。所以如果写入较频繁,对吞吐要求不高,可使用悲观锁。
读多的时候,即冲突真的很少发生的时候,用乐观锁,省去了锁的开销,加大了系统整个吞吐量;如果经常产生冲突,上层应用会不断retry,这样反而降低了性能,所以适合用悲观锁;
也就是一句话:【读频繁用乐观锁,写频繁用悲观锁】。
乐观锁
乐观锁假设认为数据一般情况下不会造成冲突,每次拿数据(读)的时候都认为别人不会修改,所以不会上锁,在数据进行【提交/更新】时,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何处理或者程序自动去重试;
【场景】:【写少读多,提高系统吞吐量】;(读多)
【样例】:数据库乐观锁、缓存乐观锁、write_condition
机制;
乐观锁不是数据库自带的,需要我们自己去实现。
乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。
通常实现是这样的:
在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。
悲观锁
悲观锁对数据被外界修改持保守态度(悲观),每次拿数据(读)的时候都认为别人会修改,因此在整个数据处理过程(读)时,将数据处于锁定状态,往往依靠数据库提供的锁机制实现;
【场景】:【写多读少,保证数据安全】;(写多)
【样例】:行锁、页锁、表锁、共享锁(读锁)、【排他锁】(写锁);
与乐观锁相对应的就是悲观锁了。悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行【每次操作】时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized
很相似,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。
说到这里,由悲观锁涉及到的另外两个锁概念就出来了,它们就是共享锁与排它锁。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。
共享锁:select * from table lock in share mode;
排他锁:select * from table for update;
应用场景 案例:
某商品,用户购买后库存数应-1,而某两个或多个用户同时购买,此时三个执行程序均同时读得库存为n,之后进行了一些操作,最后将均执行update table set 库存数=n-1,那么,很显然这是错误的。
解决:
1.使用悲观锁(其实说白了也就是排他锁)
|–程序A在查询库存数时使用排他锁(select * from table where id=10 for update)
|–然后进行后续的操作,包括更新库存数,最后提交事务。
|–程序B在查询库存数时,如果A还未释放排他锁,它将等待。
|–程序C同B……
2.使用乐观锁(靠表设计和代码来实现)
|–一般是在该商品表添加version版本字段或者timestamp时间戳字段
|–程序A查询后,执行更新变成了:
update table set num=num-1 where id=10 and version=23
这样,保证了修改的数据是和它查询出来的数据是一致的,而其他执行程序未进行修改。当然,如果更新失败,表示在更新操作之前,有其他执行程序已经更新了该库存数,那么就可以尝试重试来保证更新成功。为了尽可能避免更新失败,可以合理调整重试次
数(阿里巴巴开发手册规定重试次数不低于三次)。
提问:mysql 问题排查方法?
事物级别
select @@global.tx_isolation;
输出数据当前状态(返回最近一次死锁场景,等等)
可用于排查死锁问题,锁定行数等问题
SHOW ENGINE INNODB STATUS ;
查询事务信息
(观察事务启动时间,判断是否为最近的创建的)
select * from information_schema.INNODB_TRX;
查询数据库锁等待信息
(如果存在数据表示当前存在所等待情况)
select * from information_schema.INNODB_LOCK_WAITS;
手动杀掉某个进程
(来源于select * from information_schema.INNODB_TRX;)
kill trx_mysql_thread_id;
https://blog.csdn/weixin_34220179/article/details/88160163
提问:?
Myf
我们需要重点关注[mysqld]
Spring Boot 配置MySQL:
spring:
datasource:
driver: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: XXXX
GMT%2B8
= +8
东八区
MySQL版本问题
MySQL版本问题看似简单,其实非常重要,面试中也常问到,我们必须了解每个版本的区别;
本人使用的是Server version: 8.0.15
面试常见问题有:
-
1.工作中使用的是什么版本的MySQL?为什么用这个版本?
“ 公司要我用哪个我就用哪个 ” 是大忌;其实考察的是常见版本有哪些?优缺点? -
2.如何决定是否要对MySQL进行升级,如何升级?
考察是否对MySQL有过升级,升级操作需要非常谨慎,不能为升级而升级,需要大量测试; -
3.最新的MySQL版本是什么?他的什么特性最吸引你?
考察对MySQL社区的关注度,以及快速学习能力;(新版本也许不用马上使用,但必须有个大致了解) -
MySQL常见的发行版
1.官方社区版本(ORACLE):
https://dev.mysql/downloads/mysql/
2.Percona MySQL:(Percona是一家MySQL咨询公司,MySQL最初的开发者)
与官方兼容,可提供了很多官方版没有的功能:InnoDB工具、审计日志等等,他对官方版进行了优化,从他提供的数据来看,Percona版性能要优于官方版;但由于Percona版是从官方版基础上二次开发,所以最新版始终会落后一拍;
https://www.percona/downloads/Percona-Server-LATEST/
3.MariaDB :(Maria是MySQL最初的开发者,被ORACLE收购后,成立了MariaDB公司)
算法有些许不同,不能保证与MySQL完全兼容,只能说大多数功能是兼容的;
把数据从MySQL转移到MariaDB是比较容易的;
https://downloads.mariadb/
- 如何决定是否要对MySQL进行升级,如何升级?
升级前要考虑什么?
- 升级可以给业务带来的好处;
(1).是否可以解决【业务】上某一方面的痛点?
比如老版本主从延迟十分明显(甚至几个小时),比如从5.6升到5.7时,由于5.7增强了多线程复制,所以relay日志时支持更好并发性,大幅度减小主从延迟时间【升级是有意义的】
又比如,使用数据库用了很多JSON类型,8.0之前对JSON类型是全部复制,8.0支持针对修改:只复制JSON中被修改的部分,升级8.0后提高了性能;
(2).是否可以解决【运维】上某一方面的痛点?
是否修正了老版本的BUG?如8.0升级了可分式功能…
- 升级可能对业务带来的影响;
(1).对原业务程序的【支持】是否有影响?
比如:不同版本需要使用不同JDBC;MySQL 5.5升到5.6时,老版本JDBC无法在新版本MySQL上运行;或者正常的SQL语句在新版本中不被通过;所以升级前需要不断的测试;
(2).对原业务程序的【性能】是否有影响?
-
数据库升级方案的制定;
-
升级失败的回滚方案;
MySQL用户管理类问题
常见问题:
- 一.如何在给定场景下【为某用户授权】?
如:用户A用服务器B对位于C服务器上某个表进行读写操作,如何对用户A进行授权?
考察的是:MySQL用户定义的方式,常见的MySQL权限; - 二.如何保证【数据库帐号】的【安全】?
如果数据产生泄漏,对公司带来的损失不可估量,账号安全性非常重要!
考察的是:数据库账号授权策略与规范,数据库账号密码策略与强度设定;
(MySQL 8.0中对这方面有加强) - 三.如何把【数据库账号】从一个实例【迁移】到另一个实例?
此情况常出现在 数据库版本升级,或者 数据库分库分表;
考察的是:如何备份和迁移数据库账号;
————————————————
一.如何在给定场景下【为某用户授权】?
知识点:
1.如何定义MySQL数据库账号
(仅仅有账号还是不够,当然要有权限)
2.MySQL常用的用户权限
(常用的用户权限?)
3.如何根据特定环境为用户授权
1.如何定义MySQL数据库账号
(1).【用户名@可访问控制列表
】
@可访问控制列表:这一部分限制了用户只能从哪些服务器上访问;
- 1.
%
代表可以从所有外部主机访问;(非常不安全) - 2.
192.168.1.%
代表只能从192.168.1
网段对数据库进行访问; - 3.
localhost:DB
服务器本地访问;
一个工作中容易忽视的地方:
如果一个用户名有多个不同访问控制列表的账号,MySQL会使用…匹配的账号;
在客户端登录账号时,不需要提供@
之后这一部分;
这一部分会由MySQL客户端根据发起访问的服务器自动识别;
(2).使用【Create user
】命令建立用户(5.7版本后)
5.7后建议Create user
命令建立用户后,再单独授权;
若不熟悉Create user
命令,可在在mysql中获得语法帮助:
mysql>\h create user
2.MySQL常用的用户权限
想获得MySQL所有权限列表?
mysql>show privileges;
3.如何根据特定环境为用户授权
- 遵循最小权限原则;
- 使用
Grant
命令对user@ip
用户授权;
grant select,insert,update,delete on db.tb user@ip;
(给用户授予在db.tb表上的select,insert,update,delete权限)
收回命令?revoke delete on db.tb from user@ip;
(收回用户在db.tb表上的delete权限)
————————————————
二.如何保证【数据库帐号】的【安全】?
知识点:
1.数据库账号授权策略与规范
2.数据库账号密码策略与强度设定
1.数据库账号授权策略与规范
2.数据库账号密码策略与强度设定
【流程规范】
(1).最小权限原则(借助外部软件控制)
只给用户最小的权限,不能给一般用户授予服务器的控制权限;
尽量不对外网IP开通数据库权限;(一定要这样的话,要用SSL加密)
不能用独立百分号建立数据库账号;
(2).密码强度策略(借助外部软件控制)
即,密码复杂度;
建议:16位包括大小写,数字,字符串;
(若有特殊字符,需要考虑特殊字符在指定程序中是否有其他含义,或者无法识别)
(3).密码过期原则(MySQL本身可控)
(4).限制历史密码重用原则(MySQL本身可控)(MySQL 8.0引入)
此两项一般一起配合使用;
密码过期后要重新设置密码;
新密码一方面要符合密码强度的要求,另一方面还要符合历史密码重用配置要求;
8.0中可以设置:用户不能使用n以内次数的密码;
这两项配置要小心:
除非程序支持【当发现密码不可用后】,重新自动启动【在配置中心获取新密码】这个功能,否则不要轻易配置密码的过期策略;
实验:
mysql> \h create user
password_option: {
PASSWORD EXPIRE [DEFAULT | NEVER | INTERVAL N DAY] // 密码过期时间
| PASSWORD HISTORY {DEFAULT | N} // 历史密码重用次数N
| PASSWORD REUSE INTERVAL {DEFAULT | N DAY} // 禁止重用 N 天
| PASSWORD REQUIRE CURRENT [DEFAULT | OPTIONAL]
}
建立测试账号:test@‘localhost’ (只能从本地访问)
密码:12345678
密码策略:password history 1 (若修改新密码,不能与上1次的密码相同)
(若设计成新密码不能与上5次的密码相同,数字写5即可)
mysql> create user test@'localhost' identified by '12345678' password history 1;
Query OK, 0 rows affected (0.03 sec)
【查看是否存在这个用户】
mysql> select * from mysql.user where user = 'test'\G
*************************** 1. row ***************************
Host: localhost
User: test
Select_priv: N
Insert_priv: N
Update_priv: N
Delete_priv: N
Create_priv: N
Drop_priv: N
Reload_priv: N
Shutdown_priv: N
Process_priv: N
File_priv: N
Grant_priv: N
References_priv: N
Index_priv: N
Alter_priv: N
Show_db_priv: N
Super_priv: N
Create_tmp_table_priv: N
Lock_tables_priv: N
Execute_priv: N
Repl_slave_priv: N
Repl_client_priv: N
Create_view_priv: N
Show_view_priv: N
Create_routine_priv: N
Alter_routine_priv: N
Create_user_priv: N
Event_priv: N
Trigger_priv: N
Create_tablespace_priv: N
ssl_type:
ssl_cipher:
x509_issuer:
x509_subject:
max_questions: 0
max_updates: 0
max_connections: 0
max_user_connections: 0
plugin: mysql_native_password
authentication_string: *84AAC12F54AB666ECFC2A83C676908C8BBC381B1
password_expired: N
password_last_changed: 2019-06-13 23:29:36
password_lifetime: NULL
account_locked: N
Create_role_priv: N
Drop_role_priv: N
Password_reuse_history: 1
Password_reuse_time: NULL
Password_require_current: NULL
User_attributes: NULL
1 row in set (0.00 sec)
其中:Password_reuse_history: 1
意思即新密码不能与上1次的密码相同;
这是我们还没有为test账户授权!
我们先试一试,用这个账户登录MySQL
$mysql -u test -p
Enter password: ......
Welcome to the MySQL monitor…
查看下他下面的数据库?
$show databases;
±---------------------------+
| Database
±---------------------------+
| information_schema |
±----------------------------+
此information_schema
是所有人都能看到的;
等于说没赋予权限时,他无法查看任何数据库;
把此用户的 【密码设置成过期】
我们新开一个窗口,登录root账号:
mysql>alter user test@'localhost' password expire;
Query OK, 0 rows affected (0.01 sec)
返回test账户;
先exit出账户,再登录(顺利),然后查看数据库?
mysql>show databases;
ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
提示必须reset password
【重设密码】:
mysql> alter user user() identified by '12345678';
(user()
函数:获取当前账户)
ERROR 3638 (HY000): Cannot use these credentials for ‘test@localhost’ because they contradict the password history policy
因为设置了password reuse history:1,当我用到上1次设置的密码,就会报错;(contradict the password history policy)
那就改一个没用过的密码:
alter user user() identified by’123456’;
Query OK, 0 rows affected (0.01 sec)
————————————————
三.如何把【数据库账号】从一个实例【迁移】到另一个实例?
解决思路:
新旧数据库版本是否一致?
到处用户建立及授权语句:
pt-show-grants u=root,p=xxxxxxxx,h=localhost
MySQL服务器配置类问题
目录:
- 分析一个group by语句的异常原因;
- 如何比较系统运行配置和配置文件中配置是否一致?
- 举几个MySQL中关键性能参数;
——————————————————————————————
分析一个group by语句的异常原因;
知识点只有一个:
SQL_MODE
的作用
SQL_MODE
配置MySQL处理SQL_MODE的方式
SQL_MODE的本质是让MySQL的语法更接近ANSI标准,
-
1.set命令 动态变量修改(覆盖写入):
set [session/global/persist] sql_mode= '参数'
(session
:本线程生效)
(global
:全局;修改后的值重启后会丢失)
(persist
:全局;修改的值重启后不会丢失,因其比global多了持久性,会在MySQL目录下生成mysqld-autof
文件保存修改后的修改值) -
2.配置文件中配置
(页面搜索:生成myf配置文件)
[mysqld]
sql_mode=xxxx
———————————————
常见的SQL_MODE参数目录:
宽松模式(ANSI)之所以这么叫,是因为他对数据的校验不严格,虽然会对insert数据进行校验,但当数据不满足要求时,会自动修改其格式以达到合适的格式,依然会输出内容(只是可能输出的不是我们想要的结果),另外会给出warning;
ANSI部分(宽松模式)
可统一用set SQL_MODE='ansi';
处理;
only_full_group_by
ansi_quotes
real_as_float
pipes as concat
———————————————
如果我们希望MySQL对于法检测更加严格,可以使用严格模式(TRADITIONAL),这个名字时相对宽松模式而言的;
当 insert update 出现少值或者无效值时,会进行特殊处理(语法错误);
TRADITIONAL部分(严格模式)
可统一用set SQL_MODE='traditional';
处理;
strict_trans_tables
strict_all_tables
(当前 / 全局)
no_auto_create_user
no_zero_in_date
no_engine_substitution
———————————————
only_full_group_by
对于group by聚合操作,如果出现在select中的列、having或者order by子句的非聚合列,没有在group by中出现,那么这个SQL语法检查会报错;
【查看现时的SQL_MODE参数】
mysql> show variables like 'sql_mode';
+---------------+------------------------+
| Variable_name | Value |
+---------------+------------------------+
| sql_mode | NO_ENGINE_SUBSTITUTION |
+---------------+------------------------+
NO_ENGINE_SUBSTITUTION
代表当指定的存储引擎不可用时报错(之后有介绍);
【添加SQL_MODEL参数】
mysql> set session sql_mode='NO_ENGINE_SUBSTITUTION,only_full_group_by';
因为每一次set动态添加参数都是【覆盖】操作,为保留之前的参数,每次写语句都要带上原有参数;
回到问题:
【若SQL_MODE中没有添加only_full_group_by
参数】
mysql> select product_id,warehouse_id,sum(count)as cnt
-> from stock
-> group by product_id;
返回的表格如同上图,没有达到我们的预期;
【若SQL_MODE中添加了only_full_group_by
参数】
再写同样的select语句时,会报错:
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘test.stock.warehouse_id’ which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
mysql> set session sql_mode=‘NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY’;
语句应该改为:
mysql> select product_id,warehouse_id,sum(count)as cnt
-> from stock
-> group by product_id,warehouse_id;
返回了我们期望的结果:
+------------+--------------+------+
| product_id | warehouse_id | cnt |
+------------+--------------+------+
| 2030 | 1 | 10 |
| 2030 | 2 | 15 |
| 2030 | 3 | 25 |
| 2040 | 1 | 30 |
| 2040 | 2 | 15 |
| 2040 | 3 | 20 |
+------------+--------------+------+
———————————————
ansi_quotes
禁止用双引号来引用字符串;
【没有使用ansi_quotes
时】
mysql> "this is a String";
+------------------+
| this is a String |
+------------------+
| this is a String |
+------------------+
【使用ansi_quotes
后】
mysql> select "this is String";
ERROR 1054 (42S22): Unknown column ‘this is String’ in ‘field list’
修改为正确代码(单引号):
mysql> select 'this is String';
+----------------+
| this is String |
+----------------+
| this is String |
+----------------+
———————————————
real_as_float
Real作为Float的同义词;
real
用在建表语句中,写在列名后,表示该列的Type为double
(MySQL默认情况下);
real_as_float
使用后,real
表示的Type会改为float
;
【未使用real_as_float
时】
新建一个名为t
的表,有一个名为id
的列,其Type为double
;
mysql>create table t(id real);
【查看该表的写操作语句】
mysql> show create table t\G
*************************** 1. row ***************************
Table: t
Create Table: CREATE TABLE "t" (
"id" double DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
id
列的Type确实为double
;
【使用real_as_float
后】
mysql>set session sql_mode='real_as_float';
删掉老表t;
mysql> drop table t;
再建一个表t,使用real
表示列id
的Type;
mysql> create table t(id real);
【查看该表的写操作语句】
mysql>show create table t\G
*************************** 1. row ***************************
Table: t
Create Table: CREATE TABLE `t` (
`id` float DEFAULT NULL
...
达到了我们的目的;
———————————————
pipes as concat
将||
视为字符串的连接操作符,而不是` 或 运算符;
【concat
函数】
都知道concat
函数表示连接两个字符串;
mysql> select concat('hello','aloha');
+-------------------------+
| concat('hello','aloha') |
+-------------------------+
| helloaloha |
+-------------------------+
【未使用pipes as concat
时,使用||
连接两个字符串】
mysql> select'hello' || 'aloha';
+--------------------+
| 'hello' || 'aloha' |
+--------------------+
| 0 |
+--------------------+
1 row in set, 2 warnings (0.01 sec)
查看这两个warning:
mysql>show warnings;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'hello' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'aloha' |
+---------+------+-------------------------------------------+
原来MySQL看到||
,把这两个字符串转换成了Double值进行了计算,未达到我们的目的;
【使用pipes as concat
后,使用||
连接两个字符串】
mysql>set session sql_mode='pipes_as_concat';
mysql> select 'hello' || 'aloha';
+--------------------+
| 'hello' || 'aloha' |
+--------------------+
| helloaloha |
+--------------------+
这样,||
也达到了concat
函数的功效,达到了我们的目的;
———————————————
———————————————
———————————————
strict_trans_tables
在当前存储引擎上启动严格模式;
strict_all_tables
在所有存储引擎上启动严格模式;
drop旧表t,新建一个表t(id列的Type为int
):
mysql>drop table t;
mysql> create table t(id int);
【宽松模式下(SQL_MODE为ansi
时),往int列里插入String值】
设置为宽松模式:
mysql> set sql_mode='ansi';
查看表的写操作,id的Type确实为int:
mysql>show create table t\G
Create Table: CREATE TABLE "t" (
"id" int(11) DEFAULT NULL
往内插入一个String值:
mysql> insert into t
->values('haha');
Query OK, 1 row affected, 1 warning (0.00 sec)
查看warning:
mysql>show warnings;
+---------+------+----------------------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------------------+
| Warning | 1366 | Incorrect integer value: 'haha' for column 'id' at row 1 |
+---------+------+----------------------------------------------------------+
查看表数据:
mysql> select * from t;
+------+
| id |
+------+
| 0 |
+------+
原来宽松模式把不符合int期望的String值转换成了0,且成功输出,只是多了个warning;
【严格模式下,往int列里插入String值】
mysql> set sql_mode='strict_trans_tables';
mysql>insert into t values('haha');
ERROR 1366 (HY000): Incorrect integer value: 'haha' for column 'id' at row 1
报错,输出失败;
这种情况下,宽松模式和严格模式区别是:是否成功输出,和是否有警告;
———————————————
error-for_division_by_zero
不允许0作为除数(比如2/0
)
【未使用error-for_division_by_zero
时(宽松模式下)】
mysql>set sql_mode='ansi';
mysql> select 1/0;
+------+
| 1/0 |
+------+
| NULL |
+------+
成功输出,结果为NULL,无警告;
【使用error-for_division_by_zero
时(严格模式下)】
mysql>set sql_mode='strict_trans_tables','error-for_division_by_zero';
mysql> select 1/0;
+------+
| 1/0 |
+------+
| NULL |
+------+
1 row in set,1 warning (0.00 sec)
成功输出,结果为NULL,有警告;
———————————————
no_auto_create_user
在用户不存在时,不允许使用grant
语句自动建立用户;
(MySQL 8.0后已经默认支持)
———————————————
no_zero_in_date
no_zero_date
日期数据内年月日不能为0(区别是有一种年可以为0)
no_auto_create_user
———————————————
no_engine_substitution
当指定的存储引擎不可用时报错;
宽松模式:搜索引擎不可用/不存在时,自动用默认可用的搜索引擎替代;
严格模式:搜索引擎不可用/不存在时,语句报错;
——————————————————————————————
如何比较系统运行配置和配置文件中配置是否一致?
提问:为什么【系统运行配置】和【配置文件的配置】会出现差异?
因为使用过set命令来配置动态参数;
数据库开发中,初始时期与后期各种参数都需要不断进行调整(因为初始时参数较小,而后期发现小参数已远远无法满足业务需求);而业务已经运行过一段时间,不可能重写数据库;我们就需要使用set命令配置动态参数;
知识点:
1.使用set
命令配置动态参数;
2.使用pt-config-diff
工具比较配置文件;
———————————————
1.使用set
命令配置动态参数;
set [session | @@session.] system_var_name = expr
set [global | @@global.] system_var_namer = expr
(修改数据会丢失)set [persist | @@global.] system_var_namer = expr
(8.0新特征,修改数据持久化)
(8.0之前,若配置全局变量,除了修改global值,还要同时修改myf
中的参数值,重启后修改的数据才不会丢失,保证了【系统运行配置】和【配置文件中配置】一致)
(8.0之后,有了新特征persist,也是配置全局变量,但还会在MySQL目录下生成mysqld-autof
文件保存修改后的修改值,重启后数据会保存)
比如,wait_timeout
参数:
设置MySQL线程的空闲时间,当连接时间超过此参数,MySQL会主动断开该连接,节省资源;
【查看参数:wait_timeout】
mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout | 28800 |
+---------------+-------+
【使用global
修改参数:wait_timeout】
mysql> set global wait_timeout=300;
Query OK, 0 rows affected (0.00 sec)
【查看global
修改后的参数value】
mysql> show variables like'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout | 28800 |
+---------------+-------+
果然global修改的参数没有持久化;
【查看和持久化修改参数:max_connections
】
mysql>show variables like'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 151 |
+-----------------+-------+
mysql>set persist max_connections = 1000;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 1000 |
+-----------------+-------+
达到目的,因为修改数据持久化到了mysqld-autof
文件中;
———————————————
2.使用pt-config-diff
工具比较配置文件;
此工具属于percona-toolkit
工具包,是MySQL的三方,需另行下载
https://blog.csdn/weixin_42915286/article/details/82692447
页面搜索:percona-toolkit
验证此工具是否已安装:
pt-summary --version
返:pt-summary 3.0.9
pt-config-diff u=用户名,p=xxxx,h=localhost /etc/myf
(myf
路径根据实际情况修改)
返回:
(node-3
和 /rtc/myf
这两者的basedir
路径其实是一样的,这里做了个重定向)
——————————————————————————————
举几个MySQL中关键性能参数;
分成:服务器配置参数 + 存储引擎参数
服务器配置参数
max_connections
设置MySQL允许访问的最大连接数量;
(若value为1000,表示MySQL最多只能接受1000个用户连接到服务器)
(内存有上限,所以value越小越好,越大的话造成内存溢出的可能性越大)
interactive_timeout
设置【交互连接】的timeout时间;
(只影响sleep状态,其他状态则不受其制约,)
wait_timeout
设置【非交互连接】的timeout时间;
max_allowed_packet
控制MySQL可以接收的数据包的大小;
(若使用主从复制,主从的此参数最好保持一致,若此参数从小于主,很可能造成复制失败)
sync_binlog
表示每写多少次缓存会向磁盘同步一次binlog
日志;
(为提高IO性能;建议设置成1
,每写1次都同步一次binlog
,减少binlog
的丢失)
sort_buffer_size
(针对单个会话操作,因此总量会很大,谨慎设置value大小!)
设置每个会话使用的【排序缓存区】的大小;
(查询时,需要【排序】时才会为缓存区分配内存;若设置10M,启动后会占用10G,容易造成服务器溢出,所以要小心,不能设置太大)
join_buffer_size
(针对单个会话操作,因此总量会很大,谨慎设置value大小!)
设置每个会话所使用的【连接缓冲】的大小;
(如果查询【关联】了多个表,就会在每个表中分配缓冲,会产生多个连接缓冲池,所以也不能设置太大)
read_buffer_size
(针对单个会话操作,因此总量会很大,谨慎设置value大小!)
指定了对一个【MYISAM】进行表扫描时所分配的读缓存池的大小;(value需要为4K的倍数)
这个参数容易被忽视,但是也应该熟悉;
- 1.针对MYISAM表;
但现在已经很少使用MYISAM表了 - 2.针对8.0之前某些操作自动生成的临时表
8.0之前,若需要操作比如【排序、汇总】,MySQL会自动建立【临时表】,临时表就是一个MYISAM表,若value设置得太小,会对临时表产生影响,影响查询性能;
read_rnd_buffer_size
(针对单个会话操作,因此总量会很大,谨慎设置value大小!)
设置控制【索引缓冲区】的大小;
binlog_cache_size
(针对单个会话操作,因此总量会很大,谨慎设置value大小!)
设置每个会话用于【缓存未提交的事物】缓存大小;
——————
【interactive_timeout
】解释
【wait_timeout
】解释
在Terminal使用MySQL时,若一段时间没有操作(sleep状态),再次操作时,常常会报如下错误:
ERROR 2013 (HY000): Lost connection to MySQL server during query
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
此报错信息意味着当前的连接已经断开,需要重新建立连接。
那么,连接的时长是如何确认的?
就与interactive_timeout
和wait_timeout
的设置有关;
他们的参数均默认为28800秒(8个小时)
【交互式连接】即在mysql_real_connect()
函数中使用了CLIENT_INTERACTIVE
选项;
说得直白一点,通过MySQL连接数据库是【交互式连接】,通过JDBC连接数据库是【非交互式连接】;
提问:为什么要同时设置interactive_timeout
,wait_timeout
才会生效?
区分交互与非交互连接,是靠配置参数的不同来确定的;
而不同系统之间的参数不相同,所以一般要同时配置这两个参数,来保证生效;
——————————————————————————————
存储引擎参数
innodb_flush_log_at_trx_commit
(非常重要)
0
:(最不安全)log buffer
每秒一次地写入log file
中(即,【重做日志】频率为1秒),并且log file
的flush
(刷新到磁盘中)操作同时进行;
该模式下在事务提交的时候,不会主动触发写入磁盘的操作;2
:(中等安全)每次事务提交时,MySQL都会把log buffer
的数据写入log file
,但flush
(刷新到磁盘中)操作并不会同时进行;该模式下,MySQL每秒执行一次 flush操作;
宕机时可能丢失1秒事物;1
:【默认值】(最安全)每次事务提交时,MySQL都会把log buffer
的数据写入log file
,并且flush
(刷新到磁盘中);
innodb_buffer_pool_size
设置Innodb
缓存池的大小,应为系统可用内存的75%;
innodb_buffer_pool_instances
Innodb
缓存池的实例个数,每个实例的大小为总缓存池大小/实例个数;
innodb_file_per_table
设置每个表独立使用一个表空间文件;
——————
——————
SQL如何优化 Optimization?
官网中写了:
de.mysql
文档中的Optimization部分:
- 1.人为优化:
explain
格式:explain + SQL语句
比如:explain select * from 表名;
mysql>explain select * from stock;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | stock | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
id:编号
select_type:查询类型
table:表名
partitions:
type:类型
possible_keys:预测用到的索引(比如8个索引里只有5个有用,他认为要用到哪些就返回哪些)
key:实际使用的索引(只用到5个,返回5)
key_len:实际使用索引的长度(若有个varchar(8)
,他返回8)
ref:表和表之间的引用关系
rows:通过索引查到的数据量
filtered:
Extra:额外
例子,用来研究【explain】:
(三张表的思路:课程表,老师表,老师卡表;然后1表和2表以tid作为外键,表2和表3以tcid作为外键)
create table course(
cid int(3),
cname varchar(20),
tid int(3)
);
create table teacher(
tid int(3),
tname varchar(20),
tcid int(3)
);
create table teacherCard(
tcid int(3),
tcdesc varchar(200)
);
insert into course values(1,'java',1);
insert into course values(2,'html',1);
insert into course values(3,'sql',2);
insert into course values(4,'web',3);
insert into teacher values(1,'tz',1);
insert into teacher values(2,'tw',2);
insert into teacher values(3,'tl',3);
insert into teacherCard values(1,'tzdesc');
insert into teacherCard values(2,'twdesc');
insert into teacherCard values(3,'tldesc');
——————————————————————————————————————————
提问1:查询课程编号为2,或教师证编号为3的老师信息;
三表关联:(and后的或者部分要括号括起来)
select t.* from teacher t,course c,teacherCard tc
where t.tid=c.tid and t.tcid=tc.tcid and(c.cid=2 or tc.tcid=3);
+------+-------+------+
| tid | tname | tcid |
+------+-------+------+
| 1 | tz | 1 |
| 3 | tl | 3 |
+------+-------+------+
现在研究下【explain】:在刚刚的SQL语句前加上explain
explain select t.* from teacher t,course c,teacherCard tc
where t.tid=c.tid and t.tcid=tc.tcid and(c.cid=2 or tc.tcid=3);
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | t | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
| 1 | SIMPLE | tc | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 25.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
explain ID
- 【ID:ID值相同时,根据table从上往下依次执行】
意思是:
当ID相同时,表的执行顺序是根据TABLE列从上到下执行(其实是根据ROWS行,从数字小的执行到数字大的)
验证:
表中id对应table列,可以看出底层执行顺序是t - tc - c
;
而这三张表对应的数据行数是3 - 3 - 4,写成这样:t3 - tc3 - c4
;
试试改变t表数据量?往t表中添加3条数据:
insert into teacher values(4,'ta',4);
insert into teacher values(5,'tb',5);
insert into teacher values(6,'tc',6);
再执行一遍explain语句,看看id对应的table变化:
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | tc | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100.00 | NULL |
| 1 | SIMPLE | t | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 25.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
之前的顺序为:t3 - tc3 - c4
最新的顺序为:tc3 - c4 - t6
显而易见,【ID相同的情况下】数据最少的先执行,数据最多的后执行;
原因:遵循了【笛卡尔积】;
【笛卡尔积】:
假设:a2 - b3 - c4:2*3=6 *4 = 24
总量不变,调换顺序后
假设:a4 - b3 - c2:4*3=12 *2 = 24
结果一样,但执行顺序不一样;
因为程序喜欢内存小的操作,6小于12,所以优先执行6这个操作,于是造成了这个顺序;
(先执行内存小的,后执行内存大的)
———————————————————————
- 【ID:ID值不同时?ID值越大越优先查询】(本质:嵌套子查询时,先查内层,再查外层)
验证:
先把Course表删去最后两行,使数据行变成2;
查询教授HTML课老师的描述:
select tc.tcdesc from teacherCard tc,course c,teacher t where c.tid=t.tid and t.tcid=tc.tcid and came='html';
加上explain:
explain select tc.tcdesc from teacherCard tc,course c,teacher t where c.tid=t.tid and t.tcid=tc.tcid and came='html';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
| 1 | SIMPLE | t | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | tc | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
将以上【多表查询】转换为【子查询形式】:
select tc.tcdesc from teacherCard tc where tc.tcid=(select t.tcid from teacher t where t.tid=(select c.tid from course c where came='hmtl'));
加上explain:
explain select tc.tcdesc from teacherCard tc where tc.tcid=(select t.tcid from teacher t where t.tid=(select c.tid from course c where came='hmtl'));
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | PRIMARY | tc | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
| 2 | SUBQUERY | t | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where |
| 3 | SUBQUERY | c | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
ID值不同时,ID值越大越优先查询?
意思是执行顺序是:3 - 2 - 1
(C - T - TC
);
为什么呢?
子查询形式是一层套一层的模式,肯定最先执行最内层,才能一层层往外计;
即,先计算C,再计算T,再计算TC;
———————————————————————
- 又有子查询,又有多表查询,执行顺序是?
explain select t.tname,tc.tcdesc from teacher t,teacherCard tc where t.tcid=tc.tcid and t.tid=(select c.tid from course c where cname='hmtl');
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | PRIMARY | t | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 16.67 | Using where |
| 1 | PRIMARY | tc | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where; Using join buffer (Block Nested Loop) |
| 2 | SUBQUERY | c | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
包含【嵌套子查询】和【多表查询】的explain语句返回的ID中:
两个ID相同,一个ID不同;
判断依据:ID越大越优先;剩下相同ID中,从上往下顺序执行;
先执行最大ID数2:C;剩下两个ID1:先执行T,再执行TC;
——————————————————————————————————————————
explain Select_type
PRIMARY
:包含子查询SQL中的【主查询】(最外层);
SUBQUERY
:包含子查询SQL中的【子查询】(非最外层;)
simple
:简单查询;(不包含【子查询】、【union】)两个条件
derived
:衍生查询;(查询时用到了临时表,解释如下:)
1⃣️ 在from子查询中只有一张表;
2⃣️ 在from子查询中,如果有table union table2,则table1就是derived表;
simple
:简单查询;
mysql>select * from teacher;
mysql>explain select * from teacher;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | teacher | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | NULL |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
(衍生方法前把course表添加两条数据)
derived
:衍生查询(查询时用到了临时表,解释如下:)
1⃣️ 在from子查询中只有一张表;(若from后只有一张表,该表就是衍生表)
mysql>select crame from (select * from course where tid in(1,2))cr;
(临时表就是cr
表,即衍生表)
explain select crame from (select * from course where tid in(1,2))cr;
2⃣️ 在from子查询中,如果有table union table2
,则table1
就是derived表;
改造一下刚刚那个表:
explain select crame from (select * from course where tid = 1 union select * from course where tid = 2)cr;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | NULL |
| 2 | DERIVED | course | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 25.00 | Using where |
| 3 | UNION | course | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 25.00 | Using where |
| NULL | UNION RESULT | <union2,3> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
——————————————————————————————————————————
explain Table
——————————————————————————————————————————
explain Partitions
——————————————————————————————————————————
explain Type
——————————————————————————————————————————
explain Possible_keys
——————————————————————————————————————————
explain Key
——————————————————————————————————————————
explain Key_len
——————————————————————————————————————————
explain Ref
——————————————————————————————————————————
explain Rows
——————————————————————————————————————————
explain Filtered
——————————————————————————————————————————
explain Extra
——————————————————————————————————————————
- select_type:查询类型
- table:表名
- partitions:
- type:类型
- possible_keys:预测用到的索引(比如8个索引里只有5个有用,他认为要用到哪些就返回哪些)
- key:实际使用的索引(只用到5个,返回5)
- key_len:实际使用索引的长度(若有个
varchar(8)
,他返回8) - ref:表和表之间的引用关系
- rows:通过索引查到的数据量
- filtered:
- Extra:额外
——————————————————————————————————————————
- 2.自动优化;
MySQL 主从同步(主从复制)
复制的基本原理
三步:
1.Master将改变日志记录到二进制文件Binlog,这些记录过程叫做二进制日志事件binary log event;
2.Slave将Master的binary log events拷贝到他的中继日志:Relaylog;
3.Slave重做中继日志里的事件,将改变应用到自己的数据库里;
MySQL复制是异步、串行化、有延迟的;
复制的基本原则
每个Slave只有一个Master(有点像Java的单继承);
每个Slave只能有唯一一个服务器ID;
每个Master可以有多个Slave;
复制的最大问题
-
主库宕机后,数据可能丢失;
-
主从同步延迟。
-
提问:为什么MySQL主从复制有延迟?
MySQL的主从复制都是单线程的操作,日志写进binlog,由于binlog是顺序写,所以效率很高。
Slave的SQL Thread线程将主库的操作事件在slave中重放。
IO操作是随即的,不是顺序的,成本高很多。
另一方面,由于SQL Thread也是单线程的,当主库的并发较高时,产生的DML数量超过slave的SQL Thread所能处理的速度,或者当slave中有大型query语句产生了锁等待那么延时就产生了。
当主库的TPS并发较高时,产生的DDL数量超过slave一个sql线程所能承受的范围,那么延时就产生了。
当然还有就是可能与slave的大型query语句产生了锁等待。
服务器硬件太差。
负载过高(master,slave),大量的查询操作。
网络延迟 -
提问:怎么解决数据丢失的问题?
1.半同步复制
从MySQL5.5开始,MySQL已经支持半同步复制了,半同步复制介于异步复制和同步复制之间,主库在执行完事务后不立刻返回结果给客户端,需要等待至少一个从库接收到并写到relay log中才返回结果给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一个TCP/IP往返耗时的延迟。
2.主库配置sync_binlog=1,innodb_flush_log_at_trx_commit=1
sync_binlog的默认值是0,MySQL不会将binlog同步到磁盘,其值表示每写多少binlog同步一次磁盘。
innodb_flush_log_at_trx_commit为1表示每一次事务提交或事务外的指令都需要把日志flush到磁盘。
注意:将以上两个值同时设置为1时,写入性能会受到一定限制,只有对数据安全性要求很高的场景才建议使用,比如涉及到钱的订单支付业务,而且系统I/O能力必须可以支撑!
- 提问:怎么解决复制延迟的问题?
1.优化网络
2.升级Slave硬件配置
3.Slave调整参数,关闭binlog,修改innodb_flush_log_at_trx_commit参数值
4.升级MySQL版本到5.7,使用并行复制
一主一从常见配置
这里讲一般中小型公司的一主一从就够了,不讲集群,毕竟我们是Java工程师,知道一点就够了;
前提:主机Windows(比如…165);从机Linux(比如…167);
要求:
- 1.MySQL版本一定要一致(版本小数点后有差异稍微可接受,但不要差异太多),且后台以服务运行;
一定不要忘记先测试网络是否通,主机:ping ...165
;从机:ping ...167
; - 2.主从都配置在
[mysqld]
节点下,建议都小写;
主机Windows:修改my.ini
;从机Linux:修改myf
;
(1).修改配置文件
—————————————————
Windows:my.ini
就server-id=1
和logbin=本地data路径/mysqlbin
是必填;
[mysqld]
server-id=1
【必填】建议写1代表主机;logbin=本地data路径/mysqlbin
【必填】必须启用二进制文件 binlog(最好写官方文件名mysqlbin);
比如:logbin=/usr/local/mysql/data/mysqlbinlog-err=本地data路径/mysqlerr
[可选]:启用错误日志;主从复制失败时可以记录日志;
比如:logbin=/usr/local/mysql/data/mysqlerrbasedir=""
[可选]:根目录;
就像JavaHome一样;
比如:basedir="/usr/local/mysql"tmpdir=""
[可选]:临时目录;
比如:tmpdir="/usr/local/mysql"datadir="本地路径/data"
[可选]:数据目录;read-only=0
主机读写都可以;binlog-ignore-db=mysql
[可选]:设置不要复制的数据库;(mysql库是出厂默认的库,不要复制)binlog-do-db=需要复制的主数据库名
[可选]:设置要复制的数据库;
【修改完配置文件记得重启MySQL;】
—————————————————
Linux:myf
两个步骤:
1.【必须】从服务器唯一ID(建议写2)
2. [可选]启用主从复制;
[mysqld]
注释掉server-id=1
开启server-id=2
log-bin=mysql-bin
代表启动主从复制,建议不要修改这个名字;
【修改完配置文件记得重启MySQL;】
—————————————————
(2).主从机都关闭防火墙
—————————————————
(3).在Windows主机建立账户并授权Slave
意思为:我不能让所有人来翻看我的数据;
Windows:我要建立一个白名单,允许某人以什么账号什么密码登录我的机器,抄我的数据;
主机在MySQL的命令行中启动:
GRANT REPLICATION SLAVE ON *.* TO '账号' @ '从机IP' IDENTIFIED BY '密码';
还需要刷新:
flush privileges;
查询主机的状态:
show master status;
意思要让从机知道:
你要在mysqlbin.00004
文件(File)的第70166
行(Position)开始复制,除掉mysql
这个库;
把File和Position这两个值抄下来;
—————————————————
(4).在Linux从机配置需要复制的主机
在MySQL命令行输入:
CHANGE MASTER TO MASTER _HOST='主机IP', MASTER_USER='账号', MASTER_PASSWORD='密码', MASTER_LOG_FILE='File名字', MASTER_LOG_POS=Position数字;
启动从机的服务器复制功能:
start slave;
show slave status\G
(\G
是KV键值对的意思,方便阅读)
返回一长串列表,其中有几条重要参数:
Slave_IO_State:Waiting for master to send event
...
Slave_IO_Running:Yes
Slave_SQL_Running:Yes
这两条必须同时为Yes,否则一定失败!!!
都为Yes时,说明主从复制的道通了!!!
—————————————————
(5).此时,主机新建库、新建表、insert,从机都会复制
—————————————————
(6).停止复制服务
注意!如果第一次复制中断,进行第二次复制时,File和Position都要重新获取,因为可能发生变化;
每次重新主从复制都要查:show master status;
停止:在从机
stop slave;
—————————————————
—————————————————
—————————————————
—————————————————
——————————————————————————————————
有延迟:代表会有时间差,不会百分百保证数据完整(可能有丢失);
实战
主:从 为 1比多 ;
从Windows(my.ini
)转移到Linux(mysqlf
)
配置前,为保证无误,先把权限、防火墙等处理;
防火墙:
关闭Windows防火墙:网络设置;
关闭Linux防火墙:service iptables stop
授权:(让Windows和Linux都允许远程连接)(MySQL语句)
grant all privileges on *.* to 'root' @'%' identified by 'root' with grant option;
flush privileges;
my.ini
中:
给Master起一个ID叫1;
给binlog文件配置存放路径,一般放在data
文件夹中;(文件中都是/杠,最好保持一致)后面加上一个新生成的binlog文件(随意取名,比方叫mysql-bin
)
[mysqld]
# ID
server-id=1
# 二进制记录文件
log-bin="D:/MySQL/.../data/mysql-bin"
# 错误记录文件
log-bin="D:/MySQL/.../data/mysql-error"
# 主从同步时,要忽略的数据库
binlog-ignore-db=数据库名
# (可写可不写)主从同步时,要同步的数据库
binlog-do-db=数据库名
比如slave的IP为:192.168.2.128
grant replication slave,reload,super on *.* to 'root' @'192.168.2.%' identified by 'root';
flush privileges;
https://www.bilibili/video/av29072634/?p=28
MySQL三大 列 类型:
MySql三大 列 类型
1.数值型
①整型:tinyint / smallint / mediuint / int / bigint
②小数型:浮点型/定点型
float(M,D),decimal(M,D)
2.字符串型
① char(M)
② varchar(M)
③ text
④ blob
⑤ NCHAR、NVARCHAR、NTEXT
3.日期时间类型
① date
② time
③ datetime
④ year
⑤ timestamp 时间戳
注:除了数值型,其他类型写数值都要加单引号;
(选择原则是既满足存储,又不浪费空间)
1.数值型
①整型:
- Tinyint 占据空间:1个字节;最大:127
- Smallint 占据空间:2个字节;最大:3,2767
- Mediumint 占据空间:3个字节;最大:838,8607
- Int 占据空间:4个字节;最大:21,4748,3647
- Bigint 占据空间:8个字节;
3.日期时间类型:
————————————————————————————————————————————————————————————————————————————————
类型 说明 标准格式 范围
————————————————————————————————————————————————————————————————————————————————
date 日期 YYYY-MM-DD 1000-01-01 到 9999 - 12 - 21
————————————————————————————————————————————————————————————————————————————————
time 时间 HH:MM:SS -838:59:59'到'838:59:59'
————————————————————————————————————————————————————————————————————————————————
datetime 日期+时间 YYYY-MM-DD HH:MM:SS '1000-01-01 00:00:00到 '9999-12-31 23:59:59'
————————————————————————————————————————————————————————————————————————————————
year 年份类型 YYYY和YY(不推荐) 1901 - 2155
————————————————————————————————————————————————————————————————————————————————
DATE 3个字节;允许存0000-00-00
TIME 3个字节;
DATATIME 8个字节;
TIMESTAMP 4个字节;
YEAR 1个字节;允许存0000年
举例操作time类型,在已存在table test3中生成签到列
mysql> alter table test3 add sign time not null default '00:00:00';
-> Query OK
————————————————————————————————————————-
索引 Index
帮助MySQL高效获取数据的数据结构,索引可以大大提高MySQL的检索速度,索引是【数据结构】;
打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。
- 什么情况适合创建索引?
主键自动建立唯一索引;(自动创建)
频繁作为查询条件的字段应该创建索引;
查询中与其他表关联的字段,外键关系建立索引;
单键/组合索引的选择问题,组合索引性价比高;(MySQL会选择认为价值更大的索引使用)
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度;
查询中统计或分组字段;(分组group by
比排序order by
更烧性能,因分组会先内部实现一次排序,即group by
包括了order by
) - 什么情况下不适合建索引?
表记录太少;(几百行都算少)
经常增删改的表或者字段;(电商用户余额需要经常修改)
Where条件利用不到的字段不创建索引;
过滤性不好的不适合创建索引;(过滤性不好:比如性别;过滤性好:有唯一性,如身份证号、手机号)
优点:
通过建立唯一索引或主键索引,保证数据库中每一行数据的唯一性;
大大提高检索的数据的效率,减少表的检索行数;
缺点:
创建索引和维护索引会耗费时间,随着数据量的增加而增加;
索引文件会占用物理空间;
当对表的数据进行增删改查的时候,索引也要动态的维护,这样就会降低数据的维护速度。
(索引就是为了查而生的,增删改的时候会拖累效率)
存储过程
存储过程是一个预编译的SQL语句;
优点是允许模块化的设计,(只需创建一次),以后在该程序中就可以调用多次;
如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。
触发器
触发器是一种特殊的存储过程,主要是通过事件来触发而被执行的;
它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化。可以联级运算;
如,某表上的触发器上包含对另一个表的数据操作,而该操作又会导致该表触发器被触发。
触发器和存储过程的区别?
触:当某类数据操纵DML语句发生时隐式地调用;
存:从一个应用或过程中显示的调用;
触:在触发器体内禁止使用COMMIT和ROLLBACK语句;
存:在过程体内可以使用所有PL/SQL块中都能使用的SQL语句,包括COMMIT和ROLLBACK语句;
触:不能接受参数输入;
存:可以接受参数输入;
TRUNCATE和DELETE的区别?
TRUNCATE :是一个数据定义语言,会被隐式提交,执行后不能回滚;
DELETE是从表中删除一行数据,同时把删除的操作以日志的形式保存,以便将来回滚;
TRUNCATE删除数据后,会立即释放占用的数据空间,数据不能被恢复;
DELETE删除的数据占用空间还在,还可以恢复;
TRUNCATE执行速度比DELETE快;
单列索引:
一个索引只包含一个列;
可以有多个单列索引;
主键索引,唯一索引,普通索引;
组合索引:
一个组合索引包含两个或两个以上的列;
普通索引:
CREATE INDEX 索引名 ON 表名(字段名)
或
ALTER TABLE 表名 ADD INDEX 索引名(字段名)
附:这里的表名(字段名)意思是:表名加上字段名,字段名要用括号括起来;
唯一索引
和普通索引类似,但必须是唯一的,可有空值
CREATE UNIQUE INDEX 索引名 ON 表名(字段名)
ALTER TABLE 表名 ADD UNIQUE (字段名)
主键索引
必须唯一,不可以有空值
创建表时添加 或ALTER TABLE表名 ADD PRIMARY KEY(字段)
(添加主键时就等于添加了一个索引)
组合索引
跟以上都类似,区别是字段定义多个时,用逗号分隔;只能有一个索引名称;
删除索引
DROP INDEX 索引名 ON 表名
explain命令:
显示如何使用索引来处理select语句以及连接表;
——————————————
比如
创建一个简单的表后,若想增加索引?
建表
CREATE TABLE mytable(id int,name varchar(10));
插入索引
(索引名叫name,在mytable表上,在name字段上;)
create index name on mytable(name);
查看表格show create table mytable;
`id` int(11) default null,
`name` varchar(10) default null,
KEY `name` (`name`)
)ENGINE=InnoDB DEFAULT CHARSET-utf8
括号中是字段
删除索引:
drop index name on mytable;
——————————————
又添加一条索引
(索引名叫myname)
alter table mytable add index myname(name);
查看表格show create table mytable;
`id` int(11) default null,
`name` varchar(10) default null,
KEY `myname` (`name`)
)ENGINE=InnoDB DEFAULT CHARSET-utf8
——————————————
添加一条唯一索引
(索引名叫oh)
create unique index oh on myname(name);
查看表格show create table mytable;
`id` int(11) default null,
`name` varchar(10) default null,
UNIQUE KEY `oh` (`name`)
)ENGINE=InnoDB DEFAULT CHARSET-utf8
——————————————
我们试试添加元素,感受下唯一索引的作用
insert into mytable values(1,'sam');
或
alter table mytable add unique (name);
Query OK
在ID为2之处重复添加Sam看看:insert into mytable values(2,'sam');
ERROR:Duplicate entry ‘sam’ for key ‘oh’
唯一索引不允许重复参数值出现;
——————————————
添加一个主键(相当于添加了一个索引)
alter table mytable add primary key(id)
——————————————
创建一个组合索引
(索引名用下划线方式,叫id_name,控制字段为id和name)
create index id_name on mytable(id,name);
Query OK
查看表格show create table mytable;
`id` int(11) default null,
`name` varchar(10) default null,
KEY `id_name` (`id`,`name`)
)ENGINE=InnoDB DEFAULT CHARSET-utf8
于是查询id加name时,速度会更快:
打比方,查询一个字段时,加索引前用时0.73s,添加后查询用时0.00s;
————————————————————————————————————————-
MySQL报错问题
-
ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’ (using password: YES)
密码输入错误; -
-bash: mysql: command not found
因为MySQL默认在/usr/local/bin
目录下寻找路径,而此目录下缺失MySQL导致;
只需建立软链接,把MySQL安装目录,映射到/usr/local/bin
目录下,即可以解决(注意更改实际路径):
cd /usr/local/bin
ln -fs /usr/local/mysql-8.0.15-macos10.14-x86_64/bin/mysql mysql
Can't connect to local MySQL server through socket '/tmp/mysql.sock'
经常碰到这个问题,如果解决不了只好重装MySQL;
我的问题在于:
1.手动新建了myf
,写了socket=/var/lib/mysql/mysql.sock
或者socket=/tmp/mysql.sock
,导致了资源冲突,这是由于不熟悉cnf
的意思而导致的。把新建的myf
删除;(利用查看错误日志的方式:$cd mysql
$cd data
$ls
$sudo cat *.err
)
2.解决了这个问题后,又报了另一个错误,是因为sock
那几个文件有几个是我手动添加的($ls -l
查看文件由谁创建),删除手动添加的那几个;
—————————————————————————————————
附.安装必备:生成myf
配置文件
在Mac OS X 中默认是没有myf 文件,如果需要对MySql 进行定制,拷贝以下目录中任意一个f 文件。
拷贝my-medimf 文件
(注意!如果不熟悉myf的配置,就不要随意添加此文件,可能让资源冲突导致报错)
- 1.)此文件可以使用如下默认的位置,只要放在默认位置,MySQL自动识别(通过deb或者APT源安装的,初始位置在下方列表):
文件名 目的
—————————————————————————————————————————————————————————————————————
/etc/myf 全局选项
/etc/mysql/myf 全局选项
SYSCONFDIR/myf 全局选项
$MYSQL_HOME/myf 服务器特定选项(仅限服务器)
defaults-extra-file 指定的文件 --defaults-extra-file,如果有的话
~/.myf 用户特定选项
~/.myloginf 用户特定的登录路径选项(仅限客户端)
- 2.)范本:
# Example MySQL config file for medium systems.
#
# This is for a system with little memory (32M - 64M) where MySQL plays
# an important part, or systems up to 128M where MySQL is used together with
# other programs (such as a web server)
#
# MySQL programs look for option files in a set of
# locations which depend on the deployment platform.
# You can copy this option file to one of those
# locations. For information about these locations, see:
# http://dev.mysql/doc/mysql/en/option-files.html
#
# In this file, you can use all long options that a program supports.
# If you want to know which options a program supports, run the program
# with the "--help" option.
# The following options will be passed to all MySQL clients
[client]
default-character-set=utf8
#password = your_password
port = 3306
socket = /tmp/mysql.sock
# Here follows entries for some specific programs
# The MySQL server
[mysqld]
sql_mode=NO_ENGINE_SUBSTITUTION
lower_case_table_names=1
character-set-server=utf8
init_connect='SET NAMES utf8
port = 3306
socket = /tmp/mysql.sock
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
character-set-server=utf8
init_connect='SET NAMES utf8'
# Don't listen on a TCP/IP port at all. This can be a security enhancement,
# if all processes that need to connect to mysqld run on the same host.
# All interaction with mysqld must be made via Unix sockets or named pipes.
# Note that using this option without enabling named pipes on Windows
# (via the "enable-named-pipe" option) will render mysqld useless!
#
#skip-networking
# Replication Master Server (default)
# binary logging is required for replication
log-bin=mysql-bin
# binary logging format - mixed recommended
binlog_format=mixed
# required unique id between 1 and 2^32 - 1
# defaults to 1 if master-host is not set
# but will not function as a master if omitted
server-id = 1
# Replication Slave (comment out master section to use this)
#
# To configure this host as a replication slave, you can choose between
# two methods :
#
# 1) Use the CHANGE MASTER TO command (fully described in our manual) -
# the syntax is:
#
# CHANGE MASTER TO MASTER_HOST=<host>, MASTER_PORT=<port>,
# MASTER_USER=<user>, MASTER_PASSWORD=<password> ;
#
# where you replace <host>, <user>, <password> by quoted strings and
# <port> by the master's port number (3306 by default).
#
# Example:
#
# CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306,
# MASTER_USER='joe', MASTER_PASSWORD='secret';
#
# OR
#
# 2) Set the variables below. However, in case you choose this method, then
# start replication for the first time (even unsuccessfully, for example
# if you mistyped the password in master-password and the slave fails to
# connect), the slave will create a master.info file, and any later
# change in this file to the variables' values below will be ignored and
# overridden by the content of the master.info file, unless you shutdown
# the slave server, delete master.info and restart the slaver server.
# For that reason, you may want to leave the lines below untouched
# (commented) and instead use CHANGE MASTER TO (see above)
#
# required unique id between 2 and 2^32 - 1
# (and different from the master)
# defaults to 2 if master-host is set
# but will not function as a slave if omitted
#server-id = 2
#
# The replication master for this slave - required
#master-host = <hostname>
#
# The username the slave will use for authentication when connecting
# to the master - required
#master-user = <username>
#
# The password the slave will authenticate with when connecting to
# the master - required
#master-password = <password>
#
# The port the master is listening on.
# optional - defaults to 3306
#master-port = <port>
#
# binary logging - not required for slaves, but recommended
#log-bin=mysql-bin
# Uncomment the following if you are using InnoDB tables
#innodb_data_home_dir = /usr/local/mysql/data
#innodb_data_file_path = ibdata1:10M:autoextend
#innodb_log_group_home_dir = /usr/local/mysql/data
# You can set .._buffer_pool_size up to 50 - 80 %
# of RAM but beware of setting memory usage too high
#innodb_buffer_pool_size = 16M
#innodb_additional_mem_pool_size = 2M
# Set .._log_file_size to 25 % of buffer pool size
#innodb_log_file_size = 5M
#innodb_log_buffer_size = 8M
#innodb_flush_log_at_trx_commit = 1
#innodb_lock_wait_timeout = 50
[mysqldump]
quick
max_allowed_packet = 16M
[mysql]
no-auto-rehash
# Remove the next comment character if you are not familiar with SQL
#safe-updates
default-character-set=utf8
[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M
[mysqlhotcopy]
interactive-timeout
-
3.)修改文件读写权限
sudo chmod 664 /etc/myf
-
4.)特别注意!
Windows环境下的MySQL数据库的表名默认 不区分大小写;
Linux(含Mac OS)环境下的MySQL数据库的表名默认是 区分大小写 的;
为避免数据库与IDE连接失败,需要在myf
中[mysqld]
下添加:
lower_case_table_names=1
(1为不区分大小写,0为区分大小写)
3.Spring MVC 中 mybatis-generator 的大坑!!!!!!!!
mybatis-generator是为了自动生成mapper和dao的
注意!!!!!
db.driverClassName这一栏!!!
MYSQL8版本的驱动名称是:com.mysql.cj.jdbc.Driver
MYSQL8以下的驱动名称是:com.mysql.jdbc.Driver
我的Mysql版本是8,所以填写了com.mysql.jdbc.Driver后一直报错!!!
应该写!!!com.mysql.cj.jdbc.Driver
————————————————————————————————————————
Sequel Pro
大坑!!!
注:若连接失败,提示MySQL said: Authentication plugin 'caching_sha2_password' cannot be loaded: dlopen(/usr/local/...
;即在链接数据库时不能加载‘caching_sha2_password'
这个插件,也就是不能对身份验证。解决方案:Initialize Database。
- System Preference - mysql - Initialize Database
- √ Use Legacy Password Encryption
- 输入8位密码
再转返Sequel Pro连接。
或者还有一种思路:
最近下载新的MySQL8.0 来使用的时候, 通过sqlyog、或者程序中连接数据库时,提示:Authentication plugin ‘caching_sha2_password’ cannot be loaded 的错误,经查看发现,8.0改变了 身份验证插件 , 打开 my.ini (或者my.cofg) 可以看到变更了 5.7及其以前的方式: mysql_native_password
解决
Authentication plugin ‘caching_sha2_password’ cannot be loaded 的方法,可以往你的连接工具、或者程序应用显示指定身份验证方式,或者直接改为以前的版本方式:
你可以使用如下方式:
这里root的密码改为11111111,使用老版本的身份验证插件方式:
alter user root@localhost identified with mysql_native_password by'你的密码';
安装过两次,这两种方式亲测都有效;
可参考:https://www.jianshu/p/c5276a8dd397
https://blog.csdn/zixiao217/article/details/80156362
——————————————————————————
新建数据库:
——————————————————————————
删除数据库:
Query输入:
drop database dbname
再Run Current;
——————————————————————————
特别注意!!
Sequel Pro中某一数据库信息更改后,想要查看新信息,点击Refresh无用;
要先点击另一数据库,再点回要查看的数据库,才可查看更新。
——————————————————————————
表格的导出和导入:
1.导出:Export As SQL dump
2.导入:Import
先切换到要导入的数据库,没有?就先新建一个
MySQL下载 on Mac
mysql-8.0.15-macos10.14-x86_64
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 8.0.15 MySQL Community Server - GPL
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
MySQL 安装 on Win
安装新版本之前必须完全卸载老版本MySQL
-
1.cmd中输入
sc delete mysql
;或者控制面板中卸载mysql; -
2.regedit删除三项MySQL的项:
HKEY_LOCAL_MACHINE/SYSTEM/ControlSet001/Services/Eventlog/Application/MySQL
HKEY_LOCAL_MACHINE/SYSTEM/ControlSet002/Services/Eventlog/Application/MySQL
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Eventlog/Application/MySQL -
3.手动删除C盘下mysql根目录。
【8.0.12】
1.安装后bin目录下无法找到my.ini
【5.6.41】
2.安装后bin目录下只有my-default.ini,无my.ini
可以拷贝一个my-default.ini修改成my.ini
然后在my.ini里面的[mysqld]下面加:character-set-server=utf8
注意:原文件my-default.ini不要动,不然重新启动mysql会报系统错误
附录
- 操作Table之模版:
use database;
create table stu(
id int primary key auto_increment,
sname varchar(10) not null default ``, //名称未填写时,自动声明留空;
gender char(1) not null default ``,
company varchar(20) not null default ``,
salary decimal(6,2) not null default 0.00, //整数+小数共6位;小数为2位;
fanbu samllint not null default 0 //饭补未填写时,自动声明为0;
)engine myisam charset utf8;
更多推荐
【数据库】MySQL + Sequel Pro + 报错总结
发布评论