MySQL数据库基础知识及操作

MySQL数据库基础知识及操作

MySQL服务器逻辑架构图

MySql服务器逻辑架构图

问:innodb引擎执行一条select语句的过程?

连接器

第一步,你会先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。连接命令:

mysql -h$ip -P$port -u$user -p

连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。在完成经典的 TCP 握手后,连接器就要开始认证你的身份,这个时候用的就是你输入的用户名和密码。

  • 如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。
  • 如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

这就意味着,一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

长连接和短连接

数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。

建立连接的过程通常是比较复杂的,所以我建议你在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。

mysql执行过程中一些数据绑定在连接对象中,因此全部都用长连接长时间不断开会导致内存占用太多。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。

怎么解决这个问题呢?你可以考虑以下两种方案。

  1. 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
  2. 如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

查询缓存

但是大多数情况下我会建议你不要使用查询缓存,为什么呢?因为查询缓存往往弊大于利。查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。除非你的业务就是有一张静态表,很长时间才会更新一次。比如,一个系统配置表,那这张表上的查询才适合使用查询缓存。

需要注意的是,MySQL 8.0 版本直接将查询缓存的整块功能删掉了,也就是说 8.0 开始彻底没有这个功能了。

分析器

如果没有命中查询缓存,就要开始真正执行语句了。首先,MySQL 需要知道你要做什么,因此需要对 SQL 语句做解析。分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。

优化器

经过了分析器,MySQL 就知道你要做什么了。在开始执行之前,还要先经过优化器的处理。优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。

执行器

执行器MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。

MySQL 整体来看,其实就有两块:一块是 Server 层,它主要做的是 MySQL 功能层面的事情;还有一块是引擎层,负责存储相关的具体事宜

img

存储引擎

常见的有MyISAM和InnoDB。

InnoDB 是支持行锁的,这也是 MyISAM 被 InnoDB 替代的重要原因之一。

事务

MySQL默认采用自动提交(AUTOCOMMIT)模式。也就是说,如果不是显式地开始一个事务,则每个查询都被当做一个事务执行提交操作。

学了很久但是记不住的MYSQL事务以及隔离级别:

https://blog.csdn.net/qq_42240540/article/details/115541067

一、事务的基本要素(ACID)

  1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

   2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。

   3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

   4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

注:MySQL中ACID靠什么保证的?

A:原⼦性由undo log⽇志保证,它记录了需要回滚的⽇志信息,事务回滚时撤销已经执⾏成功的sql

C:⼀致性由其他三⼤特性保证、程序代码要保证业务上的⼀致性

I:隔离性由MVCC来保证

D:持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,宕机的时候可

以从redo log恢复

二、事务的并发问题

  1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

  2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

  3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

  小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

三、MySQL事务隔离级别

事务隔离级别 脏读 不可重复读 幻读
未提交读(read-uncommitted)
提交读(read-committed)
可重复读(repeatable-read)
串行化(serializable)

未提交读:就是指事务不用进行数据提交就可以被其他事务读到。

不可重复读(提交读):事务进行数据提交后才能被其他事务读到。问题是当前事务中存在两次同样的查询之间其他事务对数据修改并提交后当前事务会得到不同的结果,所以叫不可重复读。

可重复读:解决了不可重复读,原理是当前事务要使用数据提交才可见(刷新)其他事务的数据修改。但问题是可能存在幻读,幻读指事务读取某个范围过程中有其他事务插入了新的数据行,该新数据会被当前查询过程读出来;而如果当前事务再读取该范围时并且两次查询中未使用数据提交,则会发现该数据行不见了,就好像出现了幻觉。。(顾名思义得来的把。。。总算是认真理解了下这个意思了。这书讲的也不详细啊)。但InnoDB存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。

可串行化:这是最高的隔离级别,它强制事务都是串行执行的,使之不可能相互冲突,从而解决幻读问题。换言之,它是在每个读的数据行上加上共享锁(读锁)。在这个级别,可能导致大量的超时现象和锁竞争。

注:

对于可重复读,查询只承认在事务启动前就已经提交完成的数据;可重复读的核心就是一致性读(consistent read)

对于读提交,查询只承认在语句启动前就已经提交完成的数据;

参考:https://www.cnblogs.com/wyaokai/p/10921323.html

MySQL中MVCC(多版本控制)的正确打开方式

多版本控制(Multiversion Concurrency Control): 指的是一种提高并发的技术。同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。

最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值。这是一个可以用来增强并发性的强大的技术,因为这样一来的话查询就不用等待另一个事务释放锁。关键:解决了读-写冲突

MySQL中MVCC在 Read Committed 和 Repeatable Read两个隔离级别下工作。

    MySQL的InnoDB存储引擎默认事务隔离级别是RR(可重复读),是通过 "行级锁+MVCC"一起实现的。而 MVCC 的实现依赖:隐藏字段、Read View、Undo log。

实现

​ 使用两个隐藏的列,行版本号row trx_id(最近更新时间),行删除标识(删除时间)。结合undo log日志实现的。

​ InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。也就是说,数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。如图所示,就是一个记录被多个事务连续更新后的状态。

img

图中虚线框里是同一行数据的 4 个版本,当前最新版本是 V4,k 的值是 22,它是被 transaction id 为 25 的事务更新的,因此它的 row trx_id 也是 25。

语句更新会生成 undo log(回滚日志)。那么,undo log 在哪呢?实际上,图 2 中的三个虚线箭头,就是 undo log;而 V1、V2、V3 并不是物理上真实存在的,而是每次需要的时候根据当前版本和 undo log 计算出来的。比如,需要 V2 的时候,就是通过 V4 依次执行 U3、U2 算出来。

简单来说可重复读情况下,一个数据版本,对于一个事务视图来说,除了自己的更新总是可见以外,有三种情况:

  • 版本未提交,不可见;
  • 版本已提交,但是是在视图创建后提交的,不可见;
  • 版本已提交,而且是在视图创建前提交的,可见。

Read View(快照机制):一致性读

​ 当执行select操作是innodb默认会执行快照读,会记录下这次select后的结果,之后select 的时候就会返回这次快照的数据,即使其他事务提交了也不会影响当前select的数据,这就实现了可重复读了。

快照的生成当在第一次执行select的时候,也就是说假设当A开启了事务,然后没有执行任何操作,这时候B insert了一条数据然后commit,这时候A执行 select,那么返回的数据中就会有B添加的那条数据。之后无论再有其他事务commit都没有关系,因为快照已经生成了,后面的select都是根据第一次的快照来的

一致性读依赖mvcc快照,利用事务id递增特性,来做读取数据时历史版本的选择;

当前读规则(更新逻辑)

更新数据时都是先读后写的,而这个读,只能读当前的值也就是最新的值,也被称为当前读。

所以,如果把事务 A 的查询语句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update,也都可以读到版本号是 101 的数据,返回的 k 的值是 3。下面这两个 select 语句,就是分别加了读锁(S 锁,共享锁)和写锁(X 锁,排他锁)。

mysql> select k from t where id=1 lock in share mode;
mysql> select k from t where id=1 for update;

更新行使用冲突情况

​ 如过一个事务A要更新一行的情况下,该行已经被事务B修改过但还未提交,基于当前读规则事务A不确定事务B是否会提交或者回滚,必须等待事务B释放这一行。也就是说事务B的更新过程是对行上了锁的,即常说的行锁。

评论总结:当前读实际上是由行锁来实现的,持有行锁的更新操作才能进行当前读,否则更新操作会阻塞

多版本并发控制与乐观锁CAS思想比较

​ 乐观锁即一般是在数据表中加上一个版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加1。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读到的version值与当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

不同的存储引擎的MVCC实现方式不同,典型的有乐观和悲观两种思想的实现形式。所有认为“并发事务不算大”而采用非加锁的形式来实现“加锁”效果的控制机制我们都认为它是乐观锁。

  • 多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制
  • 乐观并发控制(OCC)是一种用来解决写-写冲突的无锁并发控制

参考:乐观锁和 MVCC 的区别? - 用心阁的回答 - 知乎 https://www.zhihu.com/question/27876575/answer/71836010

总结

InnoDB 的行数据有多个版本,每个数据版本有自己的 row trx_id,每个事务或者语句有自己的一致性视图。普通查询语句是一致性读,一致性读会根据 row trx_id 和一致性视图确定数据版本的可见性。

而读提交的逻辑和可重复读的逻辑类似,它们最主要的区别是:

  • 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;

  • 对于可重复读,查询只承认在事务启动前就已经提交完成的数据;

  • 在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。

  • 对于读提交,查询只承认在语句启动前就已经提交完成的数据;

日志系统

redo log(物理日志)

​ redo log时InnoDB引擎特有的物理日志。记录最近的物理数据做了什么改动

​ 这个写日志的过程其实就是 MySQL 里经常说到的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log里面,并更新内存中的数据,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面。redo log文件如果写满了就循环覆盖掉之前的继续写入。

​ 有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。

​ redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。

主要作用:解决数据库宕机重启丢失数据的问题!

binlog(逻辑日志)

binlog(归档日志)是MySQL的Server层的逻辑日志。Binlog有两种模式,statement 格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有。

binlog 会记录所有的逻辑操作,并且是采用“追加写”的形式。

主要作用:主要用来做复制、数据备份等操作!

这两种日志有以下三点不同:

  1. redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  2. redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
  3. redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

摘自评论:SQL是面向用户的语义化命令,你可以理解为高级编程语言。高级编程语言最终会被执行去完成磁盘上数据的操作。我理解redo log记录的是磁盘上数据的物理变化,binlog记录的是当时所执行的高级编程语言。物理日志与存储引擎相关,逻辑日志可以跨存储引擎。

一条update语句执行的过程

image-20211021211136950

img

总结:InnoDB redo log 写盘,InnoDB 事务进⼊ prepare 状态。 如果前⾯ prepare 成功,binlog 写盘,再继续将事务⽇志持久化到 binlog,如果持久化成功,那么 InnoDB 事务则进⼊ commit 状态(在 redo log ⾥⾯写⼀个 commit 记录)

undo log(回滚日志)

undo log有两个作用:提供回滚和多个行版本控制(MVCC)。

undo log链。结合MVCC章节理解。

参考:

https://database.51cto.com/art/202101/641019.htm

https://www.jianshu.com/p/336e4995b9b8

Innodb逻辑存结构:

innoDB 的数据保存在表空间中,表空间又包含各种段,其中有数据段,索引段,回滚段。InnoDB中数据以B+Tree的数据结构存储的,非叶子节点既是索引,叶子节点既是数据行,回滚段用于存储undoLog,undoLog中记录的就是多版本数据,用于快照读和事务失败后的数据回滚,MySQL在合适的时机会清理undoLog。
————————————————
版权声明:本文为CSDN博主「房姐」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_29531897/article/details/113432180

并发控制之锁

表锁

如何安全地给小表加字段?

行锁

Mysql锁有哪些,如何理解?

按锁粒度分类:

  1. ⾏锁:锁某⾏数据,锁粒度最⼩,并发度⾼

  2. 表锁:锁整张表,锁粒度最⼤,并发度低

  3. 全局锁:锁的是整个数据库实例

还可以分为:

  1. 共享锁:也就是读锁,⼀个事务给某⾏数据加了读锁,其他事务也可以读,但是不能写

  2. 排它锁:也就是写锁,⼀个事务给某⾏数据加了写锁,其他事务不能读,也不能写

还可以分为:

  1. 乐观锁:并不会真正的去锁某⾏记录,⽽是通过⼀个版本号来实现的

  2. 悲观锁:上⾯所的⾏锁、表锁等都是悲观锁

在事务的隔离级别实现中,就需要利⽤锁来解决幻读

显式加锁:

上共享锁(读锁)的写法:lock in share mode,例如:

select` `math ``from` `zje ``where` `math>60 lock in share mode;

上排它锁(写锁)的写法:for update,例如:

select `math `from zje wheremath >60 for update;

参考:https://www.jb51.net/article/193520.htm

表锁

即锁住整张表,此时其它事务无法对当前表进行更新或插入操作。

如果没有索引,update会锁表,如果加了索引,就会锁行

拓展:间隙锁

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内并不存在的记录,叫做间隙

-- 用户A
update user set count=8 where id>2 and id<6

-- 用户B
update user set count=10 where id=5;

如果用户A在进行了上述操作后,事务还未提交,则B无法对2~6之间的记录进行更新或插入记录,会阻塞,当A将事务提交后,B的更新操作会执行。

行锁

顾名思义,行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。即写写冲突。

MySQL的行锁是通过索引加载的,也就是说,行锁是加在索引响应的行上的,要是对应的SQL语句没有走索引,则会全表扫描,行锁则无法实现,取而代之的是表锁,此时其它事务无法对当前表进行更新或插入操作。

两阶段锁

在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。

知道了这个设定,对我们使用事务有什么帮助呢?那就是,如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。

sql操作时什么时候锁表,什么时候锁行?

如果没有索引,update会锁表,如果加了索引,就会锁行

索引

索引基础知识

https://aflyun.blog.csdn.net/article/details/81102957?spm=1001.2101.3001.6650.4&utm_medium=distribute.wap_relevant.none-task-blog-2~default~CTRLIST~default-4.wap_blog_relevant_pic&depth_1-utm_source=distribute.wap_relevant.none-task-blog-2~default~CTRLIST~default-4.wap_blog_relevant_pic

建立索引

修改表方式

-- ALTER TABLE `admin` ADD UNIQUE INDEX `nameIndex` (`name`) USING BTREE ;
ALTER TABLE `admin`
ADD UNIQUE INDEX `loginIndex` (`name`,`PASSWORD`) USING BTREE ;

InnoDB索引

MySQL数据表使用InnoDB作为存储引擎的时候,数据结构就是使用B+树,而表的所有数据存储在主键索引上,也就是通常所说的聚簇索引,也就是每个表都需要有个聚簇索引树。对于InnoDB,主键对应的索引就是聚簇索引,表的所有数据都存储在聚簇索引上。

索引是什么?

索引就是一个数据结构,我们把表中的记录用一个适合高效查找的数据结构来表示,目的就是让查询变得更高效

B+树演变由来:

二叉查找树(缺点:当插入有序时,会生成单支树的清空)——》平衡二叉树(规定了左右子树的高度差不能超过1,如果插入数据导致高度差超过了1则自动进行调整,回复到平衡状态)——》B树(每个结点存储索引值(主键),数据和指针,树变得矮壮)——》(继续优化)——》B+树(非叶子结点存储索引值,指针)

img

B+树相对于B树有几点不同:

  1. 非叶子节点只存储索引值和指针。
  2. 所有叶子节点之间都有一个链指针(双向指针)。
  3. 数据记录都存放在叶子节点中。
  4. 优势:
    • 单一节点存储更多的元素,使得查询的I/O次数更少。由于非叶子结点不存数据,意味着同样的大小的磁盘页可以容纳更多指针,使整颗树变得更加矮壮,便可减少磁盘I/O操作,因为读取每个结点(磁盘块)的数据会执行内存与磁盘块的I/O操作;
    • 所有叶子节点形成有序链表,便于范围查询。在范围查询方面,B+树的优势更加明显。B树的范围查找需要不断依赖中序遍历。首先二分查找到范围下限,在不断通过中序遍历,知道查找到范围的上限即可。整个过程比较耗时。而B+树的范围查找则简单了许多。首先通过二分查找,找到范围下限,然后同过叶子结点的链表顺序遍历,直至找到上限即可,整个过程简单许多,效率也比较高。
    • 所有查询都要查找到叶子节点,查询性能稳定。B树的查找只需找到匹配元素即可,最好情况下查找到根节点,最坏情况下查找到叶子结点,所说性能很不稳定。而B+树每次必须查找到叶子结点,性能稳定。

参考链接:https://www.zhihu.com/question/26113830/answer/908074473

数据库,并发方面,线程池,分布式,多线程

聚簇索引和非聚簇索引

MySQL数据库的索引分为聚集索引和非聚集索引

主键都会自动生成聚簇索引

所有不是聚簇索引的索引都叫非聚簇索引或者辅助索引。

在InnDB存储引擎中,每个辅助索引的每条记录都包含主键,也包含非聚簇索引指定的列。

MySQL使用这个主键值来检索聚簇索引。

因此应该尽可能将主键缩短,否则辅助索引占用空间会更大。

一般来说用自增的整数型列作为主键列。

特征与区别

​ innoDb存储引擎中的聚簇索引表中的数据按主键的顺序存放,它实际上就是按主键构建的一个B+树,叶子节点存放的是数据行记录。聚簇索引只可能是主键,或者是组成唯一键中的所有列都为NOT NULL的第一个唯一索引,或者隐式创建的聚簇索引这三种情况(重点:唯一,非空)

所以数据库中的数据实际上是索引的一部分。由于实际的数据页只能按照一个顺序存放,所以每张表聚簇索引只能有一个。

非聚簇索引的叶子节点中存放的是键值和主键值,所以通过非聚簇索引需要先查找到主键值然后通过聚簇索引查询到具体的数据。非聚集索引并不会影响到数据的存储顺序,所以非聚集索引可以存在多个。

字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

我们每向表中插入一条记录,本质上就是向该表的聚簇索引以及所有二级索引代表的B+树的节点中插入数据。而B+树的每一层中的页都会形成一个双向链表,如果是以页为单位来分配存储空间的话,双向链表相邻的两个页之间的物理位置可能离得非常远

优化建议

  不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白:

  • 为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。
  • 再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

​ InnoDB 表是基于聚簇索引建立的。因此InnoDB 的索引能提供一种非常快速的主键查找性能。不过,它的辅助索引(Secondary Index, 也就是非主键索引)也会包含主键列,所以,如果主键定义的比较大,其他索引也将很大。如果想在表上定义 、很多索引,则争取尽量把主键定义得小一些。InnoDB 不会压缩索引。

  InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树中再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。

参考链接:

MySQL聚簇索引和非聚簇索引的理解_明明如月的技术博客-CSDN博客_mysql聚簇索引和非聚簇索引

MySQL InnoDB数据表缺少主键会怎样 - 知乎 (zhihu.com)

联合索引

最左前缀原则

比如联合索引(a,b,c)的时候可以支持a、(a,b)、(a,b,c) 3种组合进行查找。

b+数是按照从左到右的顺序来建立搜索树的,比如当(a=? and b=? and c=?)这样的数据来检索的时候,b+树会优先比较a列来确定下一步的所搜方向,如果a列相同再依次比较b列和c列,最后得到检索的数据。

又比如当(a=? and c=?)这样的数据来检索时,b+树可以用a列来指定搜索方向,但下一个字段b列的缺失,所以只能把a列的数据找到,然后再匹配c列的数据了,便只能使用索引的第一列了, 这个是非常重要的性质,即索引的最左匹配特性

覆盖索引

回表概念:当我们需要查找某个字段,通过条件走普通索引查找定位到主键后,需要回到主键索引树搜索的过程,我们称为回表。

但是是否可以避免回表通过一次索引查询就得到值呢,答案是使用覆盖索引。如执行的语句是是:

select ID from T where k between 3 and 5

ID是主键,普通索引k的树都会带上主键索引的值,即该ID列是在k这个索引树上的。这就不需要回表。由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

阿里巴巴手册:

【推荐】利用覆盖索引来进行查询操作,避免回表。

说明:如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。

正例:能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效果,用 explain 的结果,extra 列会出现:using index。

如何实现覆盖索引?

常见的方法是:将被查询的字段,建立到联合索引里去。

例如:select id,age,name from user where age = 10;

使用索引覆盖:建组合索引idx_age_name(age,name)即可。

关于explain命令使用与内容参考:https://www.jb51.net/article/180267.htm

总结如下:

  • 联合索引的使用在写where条件的顺序无关,mysql查询分析会进行优化而使用索引。但是减轻查询分析器的压力,最好和索引的从左到右的顺序一致。
  • 使用等值查询,多列同时查询,索引会一直传递并生效。因此等值查询效率最好。
  • 索引查找遵循最左侧原则。但是遇到范围查询列(>、<、between、like)之后的列索引失效只能回表。但5.6之后引入的索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
  • 排序也能使用索引,合理使用索引排序,避免出现file sort。

索引失效问题

  1. 模糊查询:like关键字以%开头
  2. 如果索引了多列,要遵守最左前缀法则(查询按顺序从索引的最左前列开始,并且不跳过索引中的列)。
  3. 对索引的字段使用内部函数,索引会失效(应该建立基于函数的索引)
  4. 在索引列上进行运算操作,索引将失效。
  5. 索引列尽量不要存储null值(在mysql中,含有空值的列很难进行查询优化。因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。
  6. 字符串匹配数据没有加单引号
  7. 范围查询右边的字段索引会失效
  8. 匹配字段数据类型错误

MySQL数据类型相关问题

mysql中int、bigint、smallint 和 tinyint的区别详细介绍

tinyint占一个字节,int占四个字节

然后tinyint(1) 和 tinyint(4) 中的1和4并不表示存储长度,只有字段指定zerofill是有用,无符号和zerofill的时候会填充0,显示成M对应的宽度。如tinyint(4),如果实际值是2,如果列指定了zerofill,查询结果就是0002,左边用0来填充。

MySQL 中的 varchar 和 char 有什么区别

char 是一个定长字段,假如申请了 char(10)的空间,那么无论实际存储多少内容.该字段都占用 10 个字符。

而 varchar 是变长的,也就是说申请的只是最大长度,占用的空间为实际字符长度+1,最后一个字符存储使用了多长的空间.

char(M)类型的数据列里,每个值都占用M个字节,如果某个长度小于M,MySQL就会在它的右边用空格字符补足。(在检索操作中那些填补出来的空格字符将被去掉)

基本理论知识与操作命令

数据库语言

DDL:数据定义语言

DML:数据操纵语言

DCL:数据控制语言

img

mysql windows平台安装

5.7版本安装
https://blog.csdn.net/weixin_43395911/article/details/99702121

MySQL登陆步骤

1.启动管理员模式下的CMD,运行cd /d E:\mysql-8.0.23\bin命令跳转到mysql目录下

2.使用net start mysql命令启动MYSQL服务,net stop mysql命令停止MYSQL服务

3.使用mysql -u root -p命令用户名进行登陆,密码Enter password:123456

4.命令set password='123456';修改密码。命令flush privileges;刷新权限

显示已有数据库:show databases;

切换数据库:use 数据库名称;

显示该数据库下的所有表:show tables;

显示建表语句: show create table 表名;

删除数据库:drop database;

将脚本文件导入数据库中:

1.创建数据库:create database 数据库名称;

2.切换到新建的数据库:use 数据库名称;

3.导入sql脚本,如:source D:/Users/Administrator/桌面/EmpDB.sql;

查看时区:

show variables like'%time_zone';

设置时区:

输入 set global time_zone = '+8:00'; (注意不要漏掉后面的分号)

linux中mysql服务的常用命令

1.查找文件的具体路径   find / -name 文件名

2.重启mysql服务   service mysqld restart

3.停止mysql服务   service mysqld stop

4.启动mysql服务   service mysqld start

5.登录本机mysql数据库   mysql -u root -p   输入密码

6.查看mysql运行状态   service mysqld status

7.查看mysql的运行使用的进程   ps -e |grep mysql

SQL命令笔记

分页limit

https://www.cnblogs.com/xiaoshen666/p/10824117.html

表连接

简单连接,左连接,右连接

  • INNER JOIN:如果表中有至少一个匹配,则返回行(即相等的行返回)
  • LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行
  • RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name=table2.column_name;

group by

group by having

TRUNCATE语句和DELETE语句的区别

1、delete语句,是DML语句,truncate语句通常被认为是DDL语句。

2、delete语句,后面可以跟where子句,通常指定where子句中的条件表达式,只删除满足条件的部分记录。而truncate语句,只能用于删除表中的所有记录

3、truncate语句,删除表中的数据后,向表中添加记录时,自动增加字段的默认初始值重新从1开始,而使用delete语句,删除表中所有记录后,向表中添加记录时,自动增加字段的值,为删除时该字段的最大值加1,也就是在原来的基础上递增。

4、delete语句,每删除一条记录,都会在日志中记录,而使用truncate语句,不会在日志中记录删除的内容,因此,truncate语句的执行效率比delete语句高。
————————————————

delete 和 truncate 仅仅删除表数据,drop 连表数据和表结构一起删除

原文链接:https://blog.csdn.net/nangeali/article/details/73620044

相关知识

Mysql 中 MyISAM 和 InnoDB 的区别有哪些?

  1. InnoDB 支持事务,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;
  2. InnoDB 是聚集索引,MyISAM 是非聚集索引。
  3. InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。
  4. InnoDB 支持外键,而 MyISAM 不支持。

如何选择:

  1. 如果表中绝大多数都只是读查询,可以考虑 MyISAM,如果既有读写也挺频繁,请使用InnoDB。

参考知乎 https://www.zhihu.com/question/20596402/answer/211492971

mysql-connector-java的常用连接配置参数

spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mytest?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

注:useSSL安全连接、useUnicode指定字符的编码、解码格式

解决MySQL8时区问题

方法一:修改java中的时区为东八区

#serverTimezone可以设置为北京时间GMT%2B8、上海时间Asia/Shanghai或者香港时间Hongkong
url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true

方法二:修改MySQL数据库的时区为东八区

// 一:使用命令(优点:不需要重启MySQL服务,缺点:一旦MySQL服务被重启,设置就会消失)
mysql> set time_zone = '+8:00';
mysql> set global time_zone = '+8:00';
// 二:修改my.ini配置文件(优点:永久保存设置,缺点:需重启MySQL服务)
[mysqld]
// 设置默认时区
default-time_zone='+8:00'

原文链接:https://blog.csdn.net/starlemon2016/article/details/90314649

修改mysql root密码报错

原修改命令

mysql> update user set password=password(“新密码”) where user=”用户名”;

执行后报错  ERROR 1054(42S22) Unknown column 'password' in ‘field list’

错误的原因是 5.7版本下的mysql数据库下已经没有password这个字段了,password字段改成了authentication_string

https://www.cnblogs.com/wangbaobao/p/7087032.html

开启MySQL远程访问权限 允许远程连接

实现远程连接(改表法)

use mysql;

update user set host = '%' where user = 'root';

flush privileges;

这样在远端就可以通过root用户访问Mysql.

https://blog.csdn.net/chenlongjs/article/details/86502323?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242

2022/02/22 更

navicate连接报错:client does not support authentication

mysql 8.0貌似需要以下的写法才可以解决

alter user 'root'@'%' identified with mysql_native_password by '123456';

数据库id自增问题

自增是独立于数据表存在的,你可以理解为它是一个单独的序列函数,每次insert的时候去这个函数取一下当前的值。该函数只增不减,永远加1。它只会基于最近一次的序列值上自增(也就是加1)。
所以,如果你删除了数据表中的一行记录,那么这个id就再也不存在了。

数据库中的分布式ID

全局唯一ID

参考:https://zhuanlan.zhihu.com/p/107939861

雪花算法概述

雪花算法生成的ID是纯数字且具有时间顺序的

一、特点(自增、有序、适合分布式场景)

  • 时间位:可以根据时间进行排序,有助于提高查询速度。
  • 机器id位:适用于分布式环境下对多节点的各个节点进行标识,可以具体根据节点数和部署情况设计划分机器位10位长度,如划分5位表示进程位等。
  • 序列号位:是一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号

snowflake算法可以根据项目情况以及自身需要进行一定的修改。

二、总结

分布式唯一ID的方案有很多,雪花算法的组成结构大致分为了无效位、时间位、机器位和序列号位。其特点是自增、有序、纯数字组成查询效率高且不依赖于数据库。适合在分布式的场景中应用,可根据需求调整具体实现细节。

参考:https://developer.51cto.com/art/201909/602525.htm

数据库设计范式:

第一范式:有主键,每一个字段都是不可分割的最小单元

第二范式:满足第一范式,除主键外的所有列都必须完全依赖于主键,而不应该部分依赖;非主属性完全依赖于主属性.(解决方式:拆分成两个表,并增加关系表,关系表中有两个表的外键

第三范式:(解决方式:拆分成两个表,消除传递依赖)

image-20210421211031200

posted @ 2022-03-10 09:13  Y鱼鱼鱼Y  阅读(1381)  评论(0编辑  收藏  举报