思维导图:https://www.processon.com/view/link/6283c3dce0b34d5ac4197efc

1.操作与落地

1.1.数据库基础知识

1.1.1.范式化设计

1.1.1.1.什么是范式

范式来自英文Normal Form,简称NF。

实际上你可以把它粗略地理解为 一张数据表的表结构所符合的某种设计标准的级别 。就像家里装修买建材,最环保的是E0级,其次是E1级,还有E2级等等

目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式5NF,又称完美范式)。

满足最低要求的范式是第一范式(1NF),在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般来说,数据库只需满足第三范式(3NF)就行了。

1.1.1.2.第一范式(1NF)

定义: 属于第一范式关系的所有属性都不可再分,即数据项不可分。

理解:第一范式强调数据表的原子性,是其他范式的基础。一张表有一个name-age列,这个列具有两个属性,一个name,一个 age,所以不符合第一范式,我们把它拆分成两列name和age,这张表就符合第一范式关系。

你在关系型数据库管理系统(RDBMS),例如SQL Server,Oracle,MySQL中创建数据表的时候,1NF是所有关系型数据库设计的最基本要求。

第一范式详细的要求如下:

1、每一列属性都是不可再分的属性值,确保每一列的原子性;

2、两列的属性相近或相似或一样,尽量合并属性一样的列,确保不产生冗余数据;

3、单一属性的列为基本数据类型构成;

4、设计出来的表都是简单的二维表。

1.1.1.2.第二范式(2NF)

第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。

第二范式(2NF)要求实体的属性完全依赖于主关键字。

以上这张表不符合第二范式(2NF),虽然有主键,但是实体的属性不完全依赖于主关键字。

所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。

设计成两张表,主键分别是id和op_id,这样就符合第二范式(2NF)。

1.1.1.3.第三范式(3NF)

满足第三范式(3NF)必须先满足第二范式(2NF);

第三范式(3NF)要求一个数据库表中不包含已在其它表中包含的非主关键字信息,即数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系。

产品表

这里如果产品ID或产品名称变化会发生什么情况?所以以上不符合第三范式(3NF)

以上订单表就符合第三范式

1.1.2.反范式化设计

完全符合范式化的设计真的完美无缺吗?很明显在实际的业务查询中会大量存在着表的关联查询,而表设计都做成了范式化设计(甚至很高的范式),大量的表关联很多的时候非常影响查询的性能。

反范式化就是违反范式化设计:

1、为了性能和读取效率而适当的违反对数据库设计范式的要求;

2、为了查询的性能,允许存在部分(少量)冗余数据

换句话来说反范式化就是使用空间来换取时间。

1.1.3.范式化和反范式对比

1、范式化的更新操作通常比反范式化要快(字段较少)。

2、当数据较好地范式化时,就只有很少或者没有重复数据,所以只需要修改更少的数据。

3、范式化的表通常更小,所以占据的内存更少。

4、范式化设计的缺点是通常需要关联,稍微复杂一些的查询语句在符合范式的表上都可能需要至少一次关联,也许更多。

5、复杂一些的查询语句也可能使一些索引策略无效。例如,范式化可能将列存放在不同的表中,而这些列如果在一个表中本可以属于同一个索引。

1.1.4.项目中常见的反范式实现

范式化和反范式化的各有优劣,怎么选择最佳的设计?小孩子才做选择,我们全都要!!!

1.1.4.1.缓存与汇总数据

“缓存”来表示存储那些可以比较简单地从其他表获取数据的表。

比如从父表冗余一些数据到子表的。前面我们看到的分类信息放到商品表里面进行冗余存放就是典型的例子。

“汇总”则保存的是使用GROUP BY语句聚合数据的表。

如果需要显示每个用户发了多少消息,可以每次执行一个对用户发送消息进行count的子查询来计算并显示它,也可以在user表用户中建一个消息发送数目的专门列,每当用户发新消息时更新这个值。

在使用缓存表和汇总表时,有个关键点是如何维护缓存表和汇总表中的数据,常用的有两种方式,实时维护数据和定期重建,这个取决于应用程序,不过一般来说,缓存表用实时维护数据更多点,往往在一个事务中同时更新数据本表和缓存表,汇总表则用定期重建更多,使用定时任务对汇总表进行更新。

1.1.4.2.计数器表设计

计数器表在Web应用中很常见。比如网站点击数、用户的朋友数、文件下载次数等。对于高并发下的处理,首先可以创建一张独立的表存储计数器,这样可使计数器表小且快,并且可以使用一些更高级的技巧。

比如假设有一个计数器表,只有一行数据,记录网站的点击次数,网站的每次点击都会导致对计数器进行更新,问题在于,对于任何想要更新这一行的事务来说,这条记录上都有一个全局的互斥锁(mutex)。这会使得这些事务只能串行执行,会严重限制系统的并发能力。

怎么改进呢?可以将计数器保存在多行中,每次随机选择一行进行更新。在具体实现上,可以增加一个槽(slot)字段,然后预先在这张表增加100行或者更多数据,当对计数器更新时,选择一个随机的槽(slot)进行更新即可。

1.1.4.字段数据类型优化

MySQL支持的数据类型非常多,选择正确的数据类型对于获得高性能至关重要。不管存储哪种类型的数据,下面几个简单的原则都有助于做出更好的选择。

1.1.4.1.字段优化基本原则

  • 更小的通常更好

一般情况下,应该尽量使用可以正确存储数据的最小数据类型。更小的数据类型通常更快,因为它们占用更少的磁盘、内存和CPU缓存,并且处理时需要的CPU周期也更少。

比如:是有一个类型既可以用字符串也可以使用整型,优先选择整型。因为字符串牵涉到了字符集及校对规则等。

  • 简单就好

简单数据类型的操作通常需要更少的CPU周期。例如,整型比字符操作代价更低,因为字符集和校对规则(排序规则)使字符比较比整型比较更复杂。比如应该使用MySQL内建的类型而不是字符串来存储日期和时间。

  • 尽量避免NULL

通常情况下最好指定列为NOT NULL,除非真的需要存储NULL值。

如果查询中包含可为NULL的列,对MySQL来说更难优化,因为可为NULL的列使得索引、索引统计和值比较都更复杂。可为NULL的列会使用更多的存储空间,在MySQL里也需要特殊处理。当可为NULL的列被索引时,每个索引记录需要一个额外的字节。

通常把可为NULL的列改为NOT NULL带来的性能提升比较小,所以(调优时)没有必要首先在现有schema中查找并修改掉这种情况,除非确定这会导致问题。但是,如果计划在列上建索引,就应该尽量避免设计成可为NULL的列。

1.1.4.2.Int/整数类型

存储整数,可以使用这几种整数类型:TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT。分别使用8,16,24,32,64位存储空间,也就是1、2、3、4、8个字节。它们可以存储的值的范围请自行计算。

同时整数类型有可选的 UNSIGNED属性,表示不允许负值,这大致可以使正数的上限提高一倍。例如TINYINT UNSIGNED可以存储的范围是0255,而TINYINT的存储范围是-128127。

有符号和无符号类型使用相同的存储空间,并具有相同的性能,因此可以根据实际情况选择合适的类型。

另外integer和int存储及大小没有任何差别,只是为了业务上区分。

MySQL可以为整数类型指定宽度,例如INT(11),对大多数应用这是没有意义的,它不会限制值的合法范围,只是规定了MySQL的一些交互工具(例如MySQL命令行客户端)用来显示字符的个数。对于存储和计算来说,INT(1)和INT(20)是相同的。

MySQL中没有long型,对应的只有bigint

1.1.4.3.实数类型

实数是带有小数部分的数字。MySQL既支持精确类型的存储DECIMAL类型,也支持不精确类型存储FLOAT和 DOUBLE类型(浮点类型)。DECIMAL类型用于存储精确的小数,本质上MySQL是以字符串形式存放的。所以CPU不支持对DECIMAL的直接计算,只是在MySQL中自身实现了DECIMAL的高精度计算。相对而言,CPU直接支持原生浮点计算,所以浮点运算明显更快。

浮点类型在存储同样范围的值时,通常比DECIMAL使用更少的空间。FLOAT使用4个字节存储,DOUBLE占用8个字节,DECIMAL里面存储65个数字,DECIMAL对于列的空间消耗比较大,另外DOUBLE比 FLOAT有更高的精度和更大的范围。

如何选择?
在精度不敏感和需要快速运算的时候,选择FLOAT和 DOUBLE。

应该尽量只在对小数进行精确计算时才使用DECIMAL,例如存储财务或金融数据。

但在数据量比较大的而且要求精度时,可以考虑使用BIGINT代替DECIMAL,将需要存储的货币单位根据小数的位数乘以相应的倍数即可。假设要存储财务数据精确到万分之一分,则可以把所有金额乘以一百万,然后将结果存储在BIGINT里,这样可以同时避免浮点存储计算不精确和 DECIMAL精确计算代价高的问题。

1.1.4.4.字符串类型

MysQL支持多种字符串类型,包括VARCHAR和CHAR类型、BLOB和TEXT类型、ENUM(枚举)和SET类型。

VARCHAR和 CHAR是两种最主要的字符串类型。

VARCHAR

VARCHAR类型用于存储可变长字符串,是最常见的字符串数据类型。它比定长类型更节省空间,因为它仅使用必要的空间(例如,越短的字符串使用越少的空间)。在内部实现上,既然是变长,VARCHAR需要使用1或2个额外字节记录字符串的长度,如果列的最大长度小于或等于255字节,则只使用1个字节表示,否则使用2个字节。

VARCHAR节省了存储空间,所以对性能也有帮助。但是,由于行是变长的,在UPDATE时新值比旧值长时,使行变得比原来更长,这就肯能导致需要做额外的工作。如果一个行占用的空间增长,并且在页内没有更多的空间可以存储,在这种情况下,MyISAM会将行拆成不同的片段存储,InnoDB则需要分裂页来使行可以放进页内。

CHAR

CHAR类型是定长的,MySQL总是根据定义的字符串长度分配足够的空间。当存储CHAR值时,MySQL会删除所有的末尾空格,CHAR值会根据需要采用空格进行填充以方便比较。

CHAR与VARCHAR如何选择?

在CHAR和VARCHAR的选择上,这些情况下使用VARCHAR是合适的:

字符串列的最大长度比平均长度大很多,列的更新很少;使用了像UTF-8这样复杂的字符集,每个字符都使用不同的字节数进行存储。

CHAR适合存储很短的字符串,或者所有值定长或都接近同一个长度。例如,CHAR非常适合存储密码的MD5值,因为这是一个定长的值。对于经常变更的数据,CHAR也比VARCHAR更好,因为定长的CHAR类型不容易产生碎片。

对于非常短的列,CHAR比VARCHAR在存储空间上也更有效率。例如用CHAR( 1)来存储只有Y和N的值,如果采用单字节字符集只需要一个字节,但是VARCHAR(1)却需要两个字节,因为还有一个记录长度的额外字节。

另外,使用VARCHAR(5)和VARCHAR(200)存储’hello’在磁盘空间上开销是一样的。我们随便选择一个就好?应该使用更短的列,为什么?

事实证明有很大的优势。更长的列会消耗更多的内存,因为MySQL通常会分配固定大小的内存块来保存内部值。尤其是使用内存临时表进行排序或操作时会特别糟糕。在利用磁盘临时表进行排序时也同样糟糕。

所以最好的策略是只分配真正需要的空间。

1.1.4.5.BLOB和TEXT类型

BLOB和TEXT都是为存储很大的数据而设计的字符串数据类型,分别采用二进制和字符方式存储。

与其他类型不同,MySQL把每个BLOB和TEXT值当作一个独立的对象处理。存储引擎在存储时通常会做特殊处理。当BLOB和TEXT值太大时,InnoDB会使用专门的“外部”存储区域来进行存储,此时每个值在行内需要1~4个字节存储一个指针,然后在外部存储区域存储实际的值。

BLOB和TEXT家族之间仅有的不同是BLOB类型存储的是二进制数据,没有排序规则或字符集,而 TEXT类型有字符集和排序规则。

使用BLOB和TEXT要慎重:

(1) BLOB和 TEXT 值会引起一些性能问题,所以尽量避免使用BLOB和TEXT类型;

(2)一定要用,建议把BLOB或TEXT 列分离到单独的表中;

(3)在不必要的时候避免检索大型的 BLOB或TEXT值。例如,SELECT *查询就不是很好的想法,除非能够确定作为约束条件的WHERE子句只会找到所需要的数据行。否则,很可能毫无目的地在网络上传输大量的值。建议可以搜索索引列,决定需要的哪些数据行,然后从符合条件的数据行中检索BLOB或 TEXT值;

(4)还可以使用合成的(Synthetic)索引来提高大文本字段(BLOB或TEXT)的查询性能。简单来说,合成索引就是根据大文本字段的内容建立一个散列值,并把这个值存储在单独的数据列中,接下来就可以通过检索散列值找到数据行了。但是,要注意这种技术只能用于精确匹配的查询(散列值对于类似“<”或“>=”等范围搜索操作符是没有用处的)。可以使用MD5函数生成散列值,也可以使用SHA1(或CRC32),或者使用自己的应用程序逻辑来计算散列值。

1.1.4.6.枚举类型

如果表中的字段的取值是固定几个字符串,可以使用枚举列代替常用的字符串类型。

枚举列可以把一些不重复的字符串存储成一个预定义的集合。MySQL在存储枚举时非常紧凑,会根据列表值的数量压缩到一个或者两个字节中,MySQL在内部会将每个值在列表中的位置保存为整数,这样的话可以让表的大小大为缩小。

具体枚举使用见官网地址:https://dev.mysql.com/doc/refman/5.7/en/enum.html

CREATE TABLE
enum_test(e ENUM(' fish', 'apple', 'dog') NOT NULL);INSERT INTO
enum_test(e) VALUES(1),(2),(3);

但是要注意,

1)因为枚举列实际存储为整数,而不是字符串,所以不要使用数字作为ENUM枚举常量,这种双重性很容易导致混乱,例如ENUM( ’ 1’,‘2’,‘3’)。

2)枚举字段是按照内部存储的整数而不是定义的字符串进行排序的,所以尽量按照需要的顺序来定义枚举列。

1.1.4.7.日期和时间类型

MySQL可以使用许多类型来保存日期和时间值,例如YEAR和 DATE以及DATETIME和TIMESTAMP。MySQL能存储的最小时间粒度为秒。

datetime 存储日期范围:1001年~9999年

timestamp 存储日期范围:1970年~2038年,并且跟时区有关系。

如果需要存储比秒更小粒度的日期和时间值怎么办?MySQL目前没有提供合适的数据类型,但是可以使用自己的存储格式:可以使用BIGINT类型存储微秒级别的时间截,或者使用DOUBLE存储秒之后的小数部分。

1.1.5.命名规范

1、可读性原则

数据库、表、字段的命名要遵守可读性原则,尽可能少使用或者不使用缩写。

对象的名字应该能够描述它所表示的对象。例如:表的名称应该能够体现表中存储的数据内容,最好是遵循“业务名称_表的作用”;对于存储过程存储过程应该能够体现存储过程的功能。库名与应用名称尽量一致。

表达是与否概念的字段,应该使用is_xxx的方式命名,数据类型是unsigned tinyint(1表示是,0表示否)。

2、表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
说明:MySQL在Windows下不区分大小写,但在Linux下默认是区分大小写。因此,数据库名、表名、字段名,都不允许出现任何大写字母,避免节外生枝。

3、表名不使用复数名词

4、数据库、表、字段的命名禁用保留字,如desc、range、match之类

6、主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名则为idx_字段名。

在面试过程中涉及到设计表的时,如果命名不规范,必定是一个很大的扣分项。

1.1.6MySql中的索引

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。可以得到索引的本质: 索引是数据结构 。InnoDB存储引擎支持以下几种常见的索引:B+树索引、全文索引、哈希索引,其中比较关键的是B+树索引

那为什么HashMap不适合做数据库索引?

1、hash表只能匹配是否相等,不能实现范围查找;

2、当需要按照索引进行order by时,hash值没办法支持排序;

3、组合索引可以支持部分索引查询,如(a,b,c)的组合索引,查询中只用到了a和b也可以查询的,如果使用hash表,组合索引会将几个字段合并hash,没办法支持部分索引;

4、当数据量很大时,hash冲突的概率也会非常大。

1.1.7.B+Tree

B+树索引就是传统意义上的索引,这是目前关系型数据库系统中查找最常用和最为有效的索引。B+树索引的构造类似于二叉树,根据键值(Key Value)快速找到数据。注意B+树中的B不是代表二叉(binary),而是代表平衡(balance),因为B+树是从最早的平衡二叉树演化而来,但是B+树不是一个二叉树。

在讲二叉树之前,我们必须了解一下二分查找:

二分查找法(binary search) 也称为折半查找法,用来查找一组有序的记录数组中的某一记录。

在以下数组中找到数字48对应的下标

通过3次二分查找 就找到了我们所要的数字,而顺序查找需8次。

对于上面10个数来说,顺序查找平均查找次数为(1+2+3+4+5+6+7+8+9+10)/10=5.5次。而二分查找法为(4+3+2+4+3+1+4+3+2+3)/10=2.9次。在最坏的情况下,顺序查找的次数为10,而二分查找的次数为4。

所以为了索引查找的高效性,我们引入了二叉查找树。

1.1.7.1.二叉树

1.1.7.1.1.树(Tree)

N个结点构成的有限集合。

  • 树中有一个称为”根(Root)”的特殊结点
  • 其余结点可分为M个互不相交的树,称为原来结点的”子树”

1.1.7.1.2.树与非树

1.1.7.1.3.树的一些基本术语

1.1.7.1.4.二叉树

度为2的树(也可称之为阶):(树的度:树中所有结点中最大的度。结点的度:结点的子树个数)

子树有左右顺序之分:

1.1.7.1.5.二叉查找(搜索)树

二叉查找树首先肯定是个二叉树,除此之外还符合以下几点:

  • 左子树的所有的值小于根节点的值
  • 右子树的所有的值大于或等于根节点的值
  • 左、右子树满足以上两点

但是二叉查找树,如果设计不良,完全可以变成一颗极不平衡的二叉查找树:

因此若想最大性能地构造一棵二叉查找树,需要这棵二叉查找树是平衡的,从而引出了新的定义——平衡二叉树,或称为AVL树。

1.1.7.1.6.平衡二叉树(AVL-树)

它是一棵二叉排序树,它的左右两个子树的高度差(平衡因子)的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

目的:使得树的高度最低,因为树查找的效率决定于树的高度

平衡二叉树的查找性能是比较高的,但是维护一棵平衡二叉树的代价是非常大的。通常来说,需要1次或多次左旋和右旋来得到插入、更新和删除后树的平衡性。

具体树的旋转见:算法数据结构体系学习班 马士兵教育官网 - IT职业领路人 (mashibing.com)

章节11-13

1.1.7.2.B+树

B+ 树是从平衡二叉查找树演化而来(但B+树不是二叉树,而是一个多叉查找平衡树)。

下图就是一颗平衡二叉查找树

借助网页工具:Data Structure Visualization (usfca.edu)

现在我们将其改造成 B+ 树

树的阶数表示一个节点最多能有多少个子节点。

每个叶子页(LeafPage)存储了实际的数据,如下图中有的叶子页就存放了3条数据记录,当然可以更多,叶子节点由小到大(有序)串联在一起,叶子页中的数据也是排好序的;

从AVL到B+树的变化可知,如果节点特别多的话,AVL树的高度远远高于B+树。

我们可以归纳出B+树的几个特征:

1、相同节点数量的情况下,B+树高度远低于平衡二叉树;

2、非叶子节点只保存索引信息和下一层节点的指针信息,不保存实际数据记录;

3、每个叶子页(LeafPage)存储了实际的数据,比如上图中每个叶子页就存放了3条数据记录,当然可以更多,叶子节点由小到大(有序)串联在一起,叶子页中的数据也是排好序的;

4、相邻的叶子节点之间用指针相连。

注意:叶子节点中的数据在物理存储上完全可以是无序的,仅仅是在逻辑上有序(通过指针串在一起)。

当然,一棵m阶的B+树完整定义如下:

  • 每个节点最多可以有 m 个元素;

  • 除了根节点外,每个节点最少有 (m/2) 个元素;

  • 如果根节点不是叶节点,那么它最少有 2 个孩子节点;

  • 所有的叶子节点都在同一层;

  • 非叶子节点只存放关键字和指向下一个孩子节点的索引,记录只存放在叶子节点中;

  • 一个有 k 个孩子节点的非叶子节点有(k-1) 个元素,按升序排列;

  • 某个元素的左子树中的元素都比它小,右子树的元素都大于或等于它(二叉排序树的特征);

  • 相邻的叶子节点之间用指针相连。

    以上知识可以不了解,一般面试不会问到B+树的细节(B+树的插入、B+树的删除、B+树的旋转等等),除非面试岗位就是做数据库实现的。
    如果想详细了解,可以找《算法导论》这本书

    面试问得比较多的可能是B树(也可以是B-树)、B*树,以及为什么选用B+树。

    B树也B+树的差别是,B树的非叶子节点也需要存放数据,下图是B树

    而B+树的话,数据只存在叶子节点上,同时相邻的叶子节点有链表的结构

    同时要注意,MySQL中实现的B+树,叶子节点之间的链表是双向链表,这是一个细微的差别。

    另外B* 树的话,与B+树的差别就是在非叶子节点之间,也有相互的指针指向。Oracle中使用的是B*

    那为什么MySQL不用B树而使用B+树呢?

  • 因为B数据每个节点都存储数据,每次查询的数据大小固定,就会造成每次查询返回的数据的条数变少,相同数据规模的情况下B树会增加io次数,而B+树,则数据量较小,一次可以返回多条记录,io次数较少

  • 范围查询B+树明显优于B树

1.1.8.MySQL与B+树

为什么关系型数据库都选择了B+树,这个和磁盘的特性有着非常大的关系。

为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存,这个称之为预读。

预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页,页大小通常为4k。

按照磁盘的这种性质,如果是一个页存放一个B+树的节点,自然是可以存放很多的数据的,比如InnoDB里,默认定义的B+树的节点大小是16KB,这就是说,假如一个Key是8个字节,那么一个节点可以存放大约1000个Key,意味着B+树可以有1000个分叉。同时InnoDB每一次磁盘I/O,读取的都是 16KB的整数倍的数据。也就是说InnoDB在节点的读写上是可以充分利用磁盘顺序IO的高速读写特性。

同时按照B+树逻辑结构来说,在叶子节点一层,所有记录的主键按照从小到大的顺序排列,并且形成了一个双向链表。同一层的非叶子节点也互相串联,形成了一个双向链表。那么在实际读写的时候,很大的概率相邻的节点会放在相邻的页上,又可以充分利用磁盘顺序IO的高速读写特性。

所以我们对MySQL优化的一大方向就是 尽可能的多让数据顺序读写,少让数据随机读写

磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),一般来说,磁盘的顺序读的效率是随机读的40到400倍都有可能,顺序写是随机写的10到100倍。

1.1.9.B+树的作用总结

  • 在磁盘设备上,通过B+树可以有效的存储数据;
  • 所有记录都存储在叶子节点上,非叶子(non-leaf)存储索引(keys)信息;而且记录按照索引列的值由小到大排好了序。
  • B+树含有非常高的扇出(fanout),通常超过100,在查找一个记录时,可以有效的减少IO操作;

*扇出:是每个索引节点(Non-LeafPage)指向每个叶子节点(LeafPage)的指针;

*扇出数 = 索引节点(Non-LeafPage)可存储的最大关键字个数 + 1

1.2.MySQL中的索引

InnoDB存储引擎支持以下几种常见的索引:B+树索引、全文索引、哈希索引,其中比较关键的是B+树索引

1.2.1.B+树索引

InnoDB中的索引自然也是按照B+树来组织的,前面我们说过B+树的叶子节点用来放数据的,但是放什么数据呢?索引自然是要放的,因为B+树的作用本来就是就是为了快速检索数据而提出的一种数据结构,不放索引放什么呢?但是数据库中的表,数据才是我们真正需要的数据,索引只是辅助数据,甚至于一个表可以没有自定义索引。InnoDB中的数据到底是如何组织的?

1.2.1.1.聚集索引/聚簇索引

InnoDB中使用了聚集索引,就是将表的主键用来构造一棵B+树,并且将整张表的行记录数据存放在该B+树的叶子节点中。也就是所谓的索引即数据,数据即索引。由于聚集索引是利用表的主键构建的,所以每张表只能拥有一个聚集索引。

聚集索引的叶子节点就是数据页。换句话说,数据页上存放的是完整的每行记录。因此聚集索引的一个优点就是:通过过聚集索引能获取完整的整行数据。另一个优点是:对于主键的排序查找和范围查找速度非常快。

如果我们没有定义主键呢?MySQL会使用唯一性索引,没有唯一性索引,MySQL也会创建一个隐含列RowID来做主键,然后用这个主键来建立聚集索引。

1.2.1.2.辅助索引/二级索引

聚簇索引只能在搜索条件是主键值时才能发挥作用,因为B+树中的数据都是按照主键进行排序的。

如果我们想以别的列作为搜索条件怎么办?我们一般会建立多个索引,这些索引被称为辅助索引/二级索引。

(每建立一个索引,就有一颗B+树

对于辅助索引(Secondary Index,也称二级索引、非聚集索引),叶子节点并不包含行记录的全部数据。叶子节点除了包含键值以外,每个叶子节点中的索引行中还包含了一个书签( bookmark)。该书签用来告诉InnoDB存储引擎哪里可以找到与索引相对应的行数据。因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。

比如辅助索引index(node),那么叶子节点中包含的数据就包括了(note和主键)。

1.2.1.3.回表

辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后再通过主键索引(聚集索引)来找到一个完整的行记录。这个过程也被称为 回表 。也就是根据辅助索引的值查询一条完整的用户记录需要使用到2棵B+树----一次辅助索引,一次聚集索引。

为什么我们还需要一次回表操作呢?直接把完整的用户记录放到辅助索引d的叶子节点不就好了么?如果把完整的用户记录放到叶子节点是可以不用回表,但是太占地方了,相当于每建立一棵B+树都需要把所有的用户记录再都拷贝一遍,这就有点太浪费存储空间了。而且每次对数据的变化要在所有包含数据的索引中全部都修改一次,性能也非常低下。

很明显,回表的记录越少,性能提升就越高,需要回表的记录越多,使用二级索引的性能就越低,甚至让某些查询宁愿使用全表扫描也不使用二级索引。

那什么时候采用全表扫描的方式,什么时候使用采用二级索引 + 回表的方式去执行查询呢?这个就是查询优化器做的工作,查询优化器会事先对表中的记录计算一些统计数据,然后再利用这些统计数据根据查询的条件来计算一下需要回表的记录数,需要回表的记录数越多,就越倾向于使用全表扫描,反之倾向于使用二级索引 + 回表的方式。

1.2.1.4.联合索引/复合索引

前面我们对索引的描述,隐含了一个条件,那就是构建索引的字段只有一个,但实践工作中构建索引的完全可以是多个字段。所以,将表上的多个列组合起来进行索引我们称之为联合索引或者复合索引,比如index(a,b)就是将a,b两个列组合起来构成一个索引。

千万要注意一点,建立联合索引只会建立1棵B+树,多个列分别建立索引会分别以每个列则建立B+树,有几个列就有几个B+树,比如,index(note)、index(b),就分别对note,b两个列各构建了一个索引。

而如果是index(note,b)在索引构建上,包含了两个意思:

1、先把各个记录按照note列进行排序。

2、在记录的note列相同的情况下,采用b列进行排序

从原理可知,为什么有最佳左前缀法则,就是这个道理

1.2.1.5.覆盖索引

既然多个列可以组合起来构建为联合索引,那么辅助索引自然也可以由多个列组成。

InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录(回表)。使用覆盖索引的一个好处是辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作。所以记住,覆盖索引并不是索引类型的一种。

1.2.2.哈希索引

InnoDB存储引擎除了我们前面所说的各种索引,还有一种自适应哈希索引,我们知道B+树的查找次数,取决于B+树的高度,在生产环境中,B+树的高度一般为3、4层,故需要3、4次的IO查询。

所以在InnoDB存储引擎内部自己去监控索引表,如果监控到某个索引经常用,那么就认为是热数据,然后内部自己创建一个hash索引,称之为自适应哈希索引( Adaptive Hash Index,AHI),创建以后,如果下次又查询到这个索引,那么直接通过hash算法推导出记录的地址,直接一次就能查到数据,比重复去B+tree索引中查询三四次节点的效率高了不少。

InnoDB存储引擎使用的哈希函数采用除法散列方式,其冲突机制采用链表方式。注意,对于自适应哈希索引仅是数据库自身创建并使用的,我们并不能对其进行干预。

show engine innodb status

哈希索引只能用来搜索等值的查询,如 SELECT* FROM table WHERE index co=xxx。而对于其他查找类型,如范围查找,是不能使用哈希索引的,

因此这里出现了non-hash searches/s的情况。通过 hash searches: non- hash searches可以大概了解使用哈希索引后的效率。

innodb_adaptive_hash_index来考虑是禁用或启动此特性,默认AHI为开启状态。

1.2.3.全文索引

什么是全文检索(Full-Text Search)?它是将存储于数据库中的整本书或整篇文章中的任意内容信息查找出来的技术。它可以根据需要获得全文中有关章、节、段、句、词等信息,也可以进行各种统计和分析。我们比较熟知的Elasticsearch、Solr等就是全文检索引擎,底层都是基于Apache Lucene的。

举个例子,现在我们要保存唐宋诗词,数据库中我们们会怎么设计?诗词表我们可能的设计如下:

朝代 作者 诗词年代 标题 诗词全文
李白 静夜思 床前明月光,疑是地上霜。 举头望明月,低头思故乡。
李清照 如梦令 常记溪亭日暮,沉醉不知归路,兴尽晚回舟,误入藕花深处。争渡,争渡,惊起一滩鸥鹭。
…. …. …. …….

要根据朝代或者作者寻找诗,都很简单,比如“select 诗词全文 from 诗词表 where作者=‘李白’”,如果数据很多,查询速度很慢,怎么办?我们可以在对应的查询字段上建立索引加速查询。

但是如果我们现在有个需求:要求找到包含“望”字的诗词怎么办?用

“select 诗词全文 from 诗词表 where诗词全文 like‘%望%’”,这个意味着要扫描库中的诗词全文字段,逐条比对,找出所有包含关键词“望”字的记录,。基本上,数据库中一般的SQL优化手段都是用不上的。数量少,大概性能还能接受,如果数据量稍微大点,就完全无法接受了,更何况在互联网这种海量数据的情况下呢?怎么解决这个问题呢,用倒排索引。

倒排索引就是,将文档中包含的关键字全部提取处理,然后再将关键字和文档之间的对应关系保存起来,最后再对关键字本身做索引排序。用户在检索某一个关键字是,先对关键字的索引进行查找,再通过关键字与文档的对应关系找到所在文档。

于是我们可以这么保存

序号 关键字 蜀道难 静夜思 春台望 鹤冲天
1

如果查哪个诗词中包含上,怎么办,上述的表格可以继续填入新的记录

序号 关键字 蜀道难 静夜思 春台望 鹤冲天
1
2

从InnoDB 1.2.x版本开始,InnoDB存储引擎开始支持全文检索,对应的MySQL版本是5.6.x系列。不过MySQL从设计之初就是关系型数据库,存储引擎虽然支持全文检索,整体架构上对全文检索支持并不好而且限制很多,比如每张表只能有一个全文检索的索引,不支持没有单词界定符( delimiter)的语言,如中文、日语、韩语等。

所以MySQL中的全文索引功能比较弱鸡,了解即可。

1.2.4.索引在查询中的使用

索引在查询中的作用到底是什么?在我们的查询中发挥着什么样的作用呢?

请记住:

1、 一个索引就是一个B+树,索引让我们的查询可以快速定位和扫描到我们需要的数据记录上,加快查询的速度 。

2、一个select查询语句在执行过程中一般最多能使用一个二级索引,即使在where条件中用了多个二级索引。

1.2.4.高性能的索引创建策略

正确地创建和使用索引是实现高性能查询的基础。前面我们已经了解了索引相关的数据结构,各种类型的索引及其对应的优缺点。现在我们一起来看看如何真正地发挥这些索引的优势。

1.2.4.1.索引列的类型尽量小

我们在定义表结构的时候要显式的指定列的类型,以整数类型为例,有TTNYINT、NEDUMNT、INT、BIGTNT这么几种,它们占用的存储空间依次递增,我们这里所说的类型大小指的就是该类型表示的数据范围的大小。能表示的整数范围当然也是依次递增,如果我们想要对某个整数列建立索引的话,在表示的整数范围允许的情况下,尽量让索引列使用较小的类型,比如我们能使用INT就不要使用BIGINT,能使用NEDIUMINT就不要使用INT,这是因为数据类型越小,在查询时进行的比较操作越快(CPU层次)数据类型越小,索引占用的存储空间就越少,在一个数据页内就可以放下更多的记录,从而减少磁盘/0带来的性能损耗,也就意味着可以把更多的数据页缓存在内存中,从而加快读写效率。

这个建议对于表的主键来说更加适用,因为不仅是聚簇索引中会存储主键值,其他所有的二级索引的节点处都会存储一份记录的主键值,如果主键适用更小的数据类型,也就意味着节省更多的存储空间和更高效的I/0。

1.2.4.2.索引的选择性

创建索引应该选择选择性/离散性高的列。索引的选择性/离散性是指,不重复的索引值(也称为基数,cardinality)和数据表的记录总数(N)的比值,范围从1/N到1之间。索引的选择性越高则查询效率越高,因为选择性高的索引可以让MySQL在查找时过滤掉更多的行。唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

很差的索引选择性就是列中的数据重复度很高,比如性别字段,不考虑政治正确的情况下,只有两者可能,男或女。那么我们在查询时,即使使用这个索引,从概率的角度来说,依然可能查出一半的数据出来。

哪列做为索引字段最好?当然是姓名字段,因为里面的数据没有任何重复,性别字段是最不适合做索引的,因为数据的重复度非常高。

怎么算索引的选择性/离散性?比如person这个表:

SELECT count(DISTINCT name)/count() FROM person;
SELECT count(DISTINCT sex)/count(
) FROM person;
SELECT count(DISTINCT age)/count() FROM person;
SELECT count(DISTINCT area)/count(
) FROM person;

1.2.4.3.前缀索引

针对blob、text、很长的varchar字段,mysql不支持索引他们的全部长度,需建立前缀索引。

语法:Alter table tableName add key/index (column(X))

**缺点:**前缀索引是一种能使索引更小、更快的有效办法,但另一方面也有其缺点MySQL无法使用前缀索引做ORDER BY和GROUP BY,也无法使用前缀索引做覆盖扫描。

有时候后缀索引 (suffix
index)也有用途(例如,找到某个域名的所有电子邮件地址)。MySQL原生并不支持反向索引,但是可以把字符串反转后存储,并基于此建立前缀索引。可以通过触发器或者应用程序自行处理来维护索引。

案例:

首先找到最常见的值的列表:

SELECT COUNT(DISTINCT LEFT(order_note,3))/COUNT(*) AS sel3,
COUNT(DISTINCT LEFT(order_note,4))/COUNT(*)AS sel4,
COUNT(DISTINCT LEFT(order_note,5))/COUNT(*) AS sel5,
COUNT(DISTINCT LEFT(order_note, 6))/COUNT(*) As sel6,
COUNT(DISTINCT LEFT(order_note, 7))/COUNT(*) As sel7,
COUNT(DISTINCT LEFT(order_note, 8))/COUNT(*) As sel8,
COUNT(DISTINCT LEFT(order_note, 9))/COUNT(*) As sel9,
COUNT(DISTINCT LEFT(order_note, 10))/COUNT(*) As sel10,
COUNT(DISTINCT LEFT(order_note, 11))/COUNT(*) As sel11,
COUNT(DISTINCT LEFT(order_note, 12))/COUNT(*) As sel12,
COUNT(DISTINCT LEFT(order_note, 13))/COUNT(*) As sel13,
COUNT(DISTINCT LEFT(order_note, 14))/COUNT(*) As sel14,
COUNT(DISTINCT LEFT(order_note, 15))/COUNT(*) As sel15,
COUNT(DISTINCT order_note)/COUNT(*) As total
FROM order_exp;

可以看见,从第10个开始选择性的增加值很高,随着前缀字符的越来越多,选择度也在不断上升,但是增长到第15时,已经和第14没太大差别了,选择性提升的幅度已经很小了,都非常接近整个列的选择性了。

那么针对这个字段做前缀索引的话,从第13到第15都是不错的选择

在上面的示例中,已经找到了合适的前缀长度,如何创建前缀索引:

ALTER TABLE order_exp ADD KEY (order_note(14));

建立前缀索引后查询语句并不需要更改:

select * from order_exp where order_note = ‘xxxx’ ;

前缀索引是一种能使索引更小、更快的有效办法,但另一方面也有其缺点MySQL无法使用前缀索引做ORDER BY和GROUP BY,也无法使用前缀索引做覆盖扫描。

有时候后缀索引 (suffix index)也有用途(例如,找到某个域名的所有电子邮件地址)。MySQL原生并不支持反向索引,但是可以把字符串反转后存储,并基于此建立前缀索引。可以通过触发器或者应用程序自行处理来维护索引。

1.2.4.4.只为用于搜索、排序或分组的列创建索引

也就是说,只为出现在WHERE 子句中的列、连接子句中的连接列创建索引,而出现在查询列表中的列一般就没必要建立索引了,除非是需要使用覆盖索引;又或者为出现在ORDER BY或GROUP BY子句中的列创建索引,这句话什么意思呢?比如:

搜索

select order_note from … and …

只为 条件中的列建立索引即可

排序

SELECT * FROM order_exp ORDER BY insert_time, order_status,expire_time;

查询的结果集需要先按照insert_time值排序,如果记录的insert_time值相同,则需要按照order_status来排序,如果order_status的值相同,则需要按照expire_time排序。回顾一下联合索引的存储结构,u_idx_day_status索引本身就是按照上述规则排好序的,所以直接从索引中提取数据,然后进行回表操作取出该索引中不包含的列就好了。

1.2.4.5.多列索引

很多人对多列索引的理解都不够。一个常见的错误就是,为每个列创建独立的索引,或者按照错误的顺序创建多列索引。

我们遇到的最容易引起困惑的问题就是索引列的顺序。正确的顺序依赖于使用该索引的查询,并且同时需要考虑如何更好地满足排序和分组的需要。反复强调过,在一个多列B-Tree索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列,等等。所以,索引可以按照升序或者降序进行扫描,以满足精确符合列顺序的ORDER BY、GROUP BY和DISTINCT等子句的查询需求。

所以多列索引的列顺序至关重要。对于如何选择索引的列顺序有一个经验法则:将选择性最高的列放到索引最前列。当不需要考虑排序和分组时,将选择性最高的列放在前面通常是很好的。这时候索引的作用只是用于优化WHERE条件的查找。在这种情况下,这样设计的索引确实能够最快地过滤出需要的行,对于在WHERE子句中只使用了索引部分前缀列的查询来说选择性也更高。

然而,性能不只是依赖于索引列的选择性,也和查询条件的有关。可能需要根据那些运行频率最高的查询来调整索引列的顺序,比如排序和分组,让这种情况下索引的选择性最高。

同时,在优化性能的时候,可能需要使用相同的列但顺序不同的索引来满足不同类型的查询需求。

1.2.4.6.三星索引

三星索引概念

对于一个查询而言,一个三星索引,可能是其最好的索引。

满足的条件如下:

  • 索引将相关的记录放到一起则获得一星 (比重27%)
  • 如果索引中的数据顺序和查找中的排列顺序一致则获得二星(排序星) (比重27%)
  • 如果索引中的列包含了查询中需要的全部列则获得三星(宽索引星) (比重50%)

这三颗星,哪颗最重要?第三颗星。因为将一个列排除在索引之外可能会导致很多磁盘随机读(回表操作)。第一和第二颗星重要性差不多,可以理解为第三颗星比重是50%,第一颗星为27%,第二颗星为23%,所以在大部分的情况下,会先考虑第一颗星,但会根据业务情况调整这两颗星的优先度。

一星:

一星的意思就是:如果一个查询相关的索引行是相邻的或者至少相距足够靠近的话,必须扫描的索引片宽度就会缩至最短,也就是说,让索引片尽量变窄,也就是我们所说的索引的扫描范围越小越好。

二星(排序星)

在满足一星的情况下,当查询需要排序,group by、 order by,如果查询所需的顺序与索引是一致的(索引本身是有序的),是不是就可以不用再另外排序了,一般来说排序可是影响性能的关键因素。

三星(宽索引星)

在满足了二星的情况下,如果索引中所包含了这个查询所需的所有列(包括 where 子句和 select 子句中所需的列,也就是覆盖索引),这样一来,查询就不再需要回表了,减少了查询的步骤和IO请求次数,性能几乎可以提升一倍。

1.2.4.6.设计三星索引实战

现在有表,SQL如下

CREATE TABLE customer (cno INT,lname VARCHAR (10),fname VARCHAR (10),sex INT,weight INT,city VARCHAR (10)
);CREATE INDEX idx_cust ON customer (city, lname, fname, cno);

对于下面的SQL而言,这是个三星索引

select cno,fname from customer where lname=’xx’ and city =’yy’ order by fname;

来评估下:

第一颗星:所有等值谓词的列,是组合索引的开头的列,可以把索引片缩得很窄,符合。

根据之前讲过的联合索引,我们是知道条件已经把搜索范围搜到很窄了

第二颗星:order by的fname字段在组合索引中且是索引自动排序好的,符合。

第三颗星:select中的cno字段、fname字段在组合索引中存在,符合。

现在有表,SQL如下:

CREATE TABLE `test` (`id` INT (11) NOT NULL AUTO_INCREMENT,`user_name` VARCHAR (100) DEFAULT NULL,`sex` INT (11) DEFAULT NULL,`age` INT (11) DEFAULT NULL,`c_date` datetime DEFAULT NULL,PRIMARY KEY (`id`),) ENGINE = INNODB AUTO_INCREMENT = 12 DEFAULT CHARSET = utf8;

SQL语句如下:

select user_name,sex,age from test where user_name like 'test%'  and sex =1 ORDER BY age

如果我们建立索引(user_name,sex,age):

第三颗星,满足

第一颗星,满足

第二颗星,不满足,user_name 采用了范围匹配,sex 是过滤列,此时age 列无法保证有序的。

上述我们看到,此时索引(user_name,sex,age)并不能满足三星索引中的第二颗星(排序)。

于是我们改改,建立索引(sex, age,user_name):

第一颗星,不满足,只可以匹配到sex,sex选择性很差,意味着是一个宽索引片(同时因为age也会导致排序有问题)

第二颗星,满足,等值sex 的情况下,age是有序的,

第三颗星,满足,select查询的列都在索引列中,

对于索引(sex,age,user_name)我们可以看到,此时无法满足第一颗星,窄索引片的需求。

以上2个索引,都是无法同时满足三星索引设计中的三个需求的,我们只能尽力满足2个。而在多数情况下,能够满足2颗星,已经能缩小很大的查询范围了,具体最终要保留那一颗星(排序星 or 窄索引片星),这个就需要看查询者自己的着重点了,无法给出标准答案。1.3.MySQL性能调优

1.3.MySQL调优

1.3.1.MySQL调优金字塔

很明显从图上可以看出,越往上走,难度越来越高,收益却是越来越小的。

比如硬件和OS调优,需要对硬件和OS有着非常深刻的了解,仅仅就磁盘一项来说,一般非DBA能想到的调整就是SSD盘比用机械硬盘更好。DBA级别考虑的至少包括了,使用什么样的磁盘阵列(RAID)级别、是否可以分散磁盘IO、是否使用裸设备存放数据,使用哪种文件系统(目前比较推荐的是XFS),操作系统的磁盘调度算法选择,是否需要调整操作系统文件管理方面比如atime属性等等。

所以在进行优化时,首先需要关注和优化的应该是架构,如果架构不合理,即使是DBA能做的事情其实是也是比较有限的。

对于架构调优,在系统设计时首先需要充分考虑业务的实际情况,是否可以把不适合数据库做的事情放到数据仓库、搜索引擎或者缓存中去做;然后考虑写的并发量有多大,是否需要采用分布式;最后考虑读的压力是否很大,是否需要读写分离。对于核心应用或者金融类的应用,需要额外考虑数据安全因素,数据是否不允许丢失。

对于MySQL调优,需要确认业务表结构设计是否合理,SQL语句优化是否足够,该添加的索引是否都添加了,是否可以剔除多余的索引等等

所以本章我们重点关注MySQL方面的调优,特别是索引。SQL/索引调优要求对业务和数据流非常清楚。在阿里巴巴内部,有三分之二的DBA是业务DBA,从业务需求讨论到表结构审核、SQL语句审核、上线、索引更新、版本迭代升级,甚至哪些数据应该放到非关系型数据库中,哪些数据放到数据仓库、搜索引擎或者缓存中,都需要这些DBA跟踪和复审。他们甚至可以称为数据架构师(Data Architecher)。

1.3.2.查询性能优化

前面的章节我们知道如何设计最优的库表结构、如何建立最好的索引,这些对于高性能来说是必不可少的。但这些还不够—还需要合理的设计查询。如果查询写得很糟糕,即使库表结构再合理、索引再合适,也无法实现高性能。

MySQL-数据库基础与索引解析相关推荐

  1. Linux Mysql 数据库基础

    目录 Linux Mysql 数据库基础 一.数据库 数据库简介 数据库的分类 数据库的发展史 数据库系统发展阶段 DBMS 数据库管理系统 数据库管理系统的优点 数据库管理系统的基本功能 数据库系统 ...

  2. php大牛额城战笔记,PHP语言大牛开发笔记(8)——MySQL数据库基础回顾[2]

    本文主要向大家介绍了PHP语言大牛开发笔记(8)--MySQL数据库基础回顾[2],通过具体的实例向大家展示,希望对大家学习php语言有所帮助. 一.数据表 为了确保数据的完整性和一致性,在创建表时指 ...

  3. 1.0 MySQL数据库基础知识

    MySQL数据库基础知识 MYSQL介绍 MySQL分支版本的发展 MySQL. Oracle. SQLServer的市场区别 MYSQL数据库使用上的结构 MYSQL体系架构图 MYSQL体系架构- ...

  4. linux下mysql数据库基础及客户端命令详解

    linux下mysql数据库基础及客户端命令详解 1.mysql数据库存储引擎: SHOW ENGINES;   #查看mysql支持的存储引擎 常见有如下两个存储引擎: MyISAM:每表三个文件: ...

  5. mysql数据库基础语句讲解

    mysql数据库基础讲解 一.数据库客户端命令 二.数据库基础sql语句 三.数据表基础语句 四.数据的增删改查(重点) 切记sql语句之后一定要加 ; 一.数据库客户端命令 1.mysql: mys ...

  6. (19)一篇掌握MySQL数据库基础下 基本操作(外键约束、建表原则、多表查询、子查询)

    MySQL数据库基础下 一.修改表--添加外键约束 二.多表之间的建表原则 1.建数据库原则:通常情况下,一个项目/应用建一个数据库 2.多表之间的表原则: (1)一对多:分类和商品 (一个分类对应多 ...

  7. 【笔记】——MySQL数据库——基础知识-——快速回顾——(加深印象篇)

    文章目录 前言 一.MySQL是什么? 二.数据库的常见概念 三.数据库存储数据的特点 四.SQL语言的分类 1.)DML 2.)DDL 3.)DCL 五.数据库的基本操作 5.1.)创建数据库 5. ...

  8. MySQL数据库基础(一)

    MySQL数据库基础 一.数据库的基本概念 1.1数据的时代 1.2 数据库的发展史 1.2.1 文件管理系统的缺点 1.3 DBMS 数据库管理系统 1.4 数据库管理系统的优点 1.5数据库管理系 ...

  9. Mysql数据库基础入门教程

    Mysql数据库基础入门教程 课程链接:https://www.bilibili.com/video/BV1Qb411x7Yc?p=1 2022/1/22start 一.数据库简介 1.什么是数据库? ...

  10. MySQL数据库基础02 韩顺平 自学笔记

    MySQL数据库基础02 sql表查询增强 分页查询 分组函数和分组子句 数据分组的总结 多表查询 问题的引出 多表查询的练习 自连接 子查询 子查询当作临时表使用 在多行子查询中使用all操作符 在 ...

最新文章

  1. java 时间的封装类
  2. Python调用MySQL模块初试
  3. Ubuntu断电重启后黑屏左上角光标闪烁,分辨率低解决办法
  4. java 多线程跑数据_java——多线程的实现方式、三种办法解决线程赛跑、多线程数据同步(synchronized)、死锁...
  5. web app 自适应方案总结 弹性布局之rem
  6. 创业赚钱 卖货 做项目如何最大化保证成功?
  7. linux串口环形缓冲区,能不能讲解下串口环形缓冲区的概念?
  8. Linux 常用名利总结
  9. wndows make images
  10. C# 电子白板软件开发
  11. VassistX番茄助手使用技巧
  12. 微信小程序怎么设置服务器上,如何为微信小程序设置服务器地址?-微信小程序服务器诗...
  13. 万用表的灵活使用,不仅只有基本使用方法!
  14. JAVA学习导图、思维导图
  15. c语言 数据类型作业 答案,C语言-数据类型习题及答案
  16. ios第三方支付流程
  17. EXCEL 删除表格内的空格和空白字符
  18. 安全产品的核心逻辑-杀毒软件
  19. iOS如何转让 App 给公司或个人
  20. 百度贴吧测试部门实习生电话面试

热门文章

  1. 2023武汉工程大学计算机考研信息汇总
  2. 转载:电线电缆的横截面积与载流量的关系
  3. 桥接模式NAT模式 详解
  4. html个人自我介绍,网站上如何填写个人自我介绍.doc
  5. JSON.stringify() 的使用、toJSON 方法 以及 JSON.parse 方法
  6. caxa齿轮零件图_利用CAXA在CATIA中绘制渐开线齿轮
  7. 【转】互联网金融产品的设计思考【腾讯实践干货】
  8. 辐射发射(辐射干扰)试验(30MHz~1GHz)(2.1)
  9. W-L学习打卡(7.8)
  10. 贷款行业 | 大数据精准获客 ,破除流量困局