MySQL性能优化
- 1、数据结构前言
- 2、常见的数据结构
- 2.1 线性表
- 2.1.1 数组
- 2.1.2 链表
- 2.1.3 栈
- 2.1.4 队列
- 2.2 散列表
- 2.3 树
- 2.3.1 二叉树
- 2.4 图
1、数据结构前言
数据结构(data structure)是计算机存储、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合。“结构”就是指数据元素之间存在的关系,分为逻辑结构和存储结构。 我们讨论的大多是逻辑结构
数据逻辑结构
指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后间关系,而与他们在计算机中的存储位置无关。逻辑结构包括:
1.集合:数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系;
2.线性结构:数据结构中的元素存在一对一的相互关系;
3.树形结构:数据结构中的元素存在一对多的相互关系;
4.图形结构:数据结构中的元素存在多对多的相互关系。
数据物理结构
数据的物理结构是数据结构在计算机中的表示(又称映像),它包括数据元素的机内表示和关系的机内表示。由于具体实现的方法有顺序、链接、索引、散列等多种,所以,一种数据结构可表示成一种或多种存储结构。
数据元素的机内表示(映像方法): 用二进制位(bit)的位串表示数据元素。通常称这种位串为节点(node)。当数据元素有若干个数据项组成时,位串中与各个数据项对应的子位串称为数据域(data field)。因此,节点是数据元素的机内表示(或机内映像)。
关系的机内表示(映像方法):数据元素之间的关系的机内表示可以分为顺序映像和非顺序映像,常用两种存储结构:顺序存储结构和链式存储结构。顺序映像借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。非顺序映像借助指示元素存储位置的指针(pointer)来表示数据元素之间的逻辑关系。
数据存储结构
数据的逻辑结构在计算机存储空间中的存放形式称为数据的物理结构(也称为存储结构)。一般来说,一种数据结构的逻辑结构根据需要可以表示成多种存储结构,常用的存储结构有顺序存储、链式存储、索引存储和哈希存储等。
数据的顺序存储结构的特点是:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系;非顺序存储的特点是:借助指示元素存储地址的指针表示数据元素之间的逻辑关系。
2、常见的数据结构
2.1 线性表
线性表(Linear List)就是数据排成像一条线一样的结构,数据只有前后两个方向
2.1.1 数组
概念
数组(Array)是有限个相同类型的变量所组成的有序集合,数组中的每一个变量被称为元素。数组是
最为简单、最为常用的数据结构。
数组下标从零开始(Why)
存储原理
数组用一组连续的内存空间来存储一组具有相同类型的数据
时间复杂度
读取和更新都是随机访问,所以是O(1)
插入数组扩容的时间复杂度是O(n),插入并移动元素的时间复杂度也是O(n),综合起来插入操作的时间
复杂度是O(n)。
删除操作,只涉及元素的移动,时间复杂度也是O(n)
优缺点
优点:
数组拥有非常高效的随机访问能力,只要给出下标,就可以用常量时间找到对应元素
缺点:
插入和删除元素方面。由于数组元素连续紧密地存储在内存中,插入、删除元素都会导致大量元素被迫
移动,影响效率。 (ArrayList LinkedList )
申请的空间必须是连续的,也就是说即使有空间也可能因为没有足够的连续空间而创建失败
如果超出范围,需要重新申请内存进行存储,原空间就浪费了
应用
数组是基础的数据结构,应用太广泛了,ArrayList、Redis、消息队列等等。
2.1.2 链表
概念
链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
链表中数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元
素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据
域,另一个是存储下一个结点地址的指针域。(百度百科)
常见的链表包括:单链表、双向链表、循环链表
- 单链表
单向链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节
点的指针next - 双向链表
双向链表的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev指针。 - 循环链表
链表的尾节点指向头节点形成一个环,称为循环链表
存储原理
数组在内存中的存储方式是顺序存储(连续存储),链表在内存中的存储方式则是随机存储(链式存
储)。
链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎
片空间。
时间复杂度
查找节点 : O(n)
插入节点:O(1)
更新节点:O(1)
删除节点:O(1)
优缺点
优势
插入、删除、更新效率高
省空间
劣势
查询效率较低,不能随机访问
应用
链表的应用也非常广泛,比如树、图、Redis的列表、LRU算法实现、消息队列等
2.1.3 栈
概念
栈(stack)是一种线性数据结构,栈中的元素只能先入后出(First In Last Out,简称FILO)。
最早进入的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫作栈顶 (top)。
存储原理
栈既可以用数组来实现,也可以用链表来实现
- 数组实现的栈也叫顺序栈或静态栈
- 链表实现的栈也叫做链式栈或动态栈
时间复杂度
入栈和出栈的时间复杂度都是O(1)
支持动态扩容的顺序栈
当数组空间不够时,我们就重新申请一块更大的内存,将原来数组中数据统统拷贝过去。这样就实现了
一个支持动态扩容的数组,通过前面学过的知识,可以得知入栈的时间复杂度是O(n)
应用
函数调用
每进入一个函数,就会将临时变量作为一个栈入栈,当被调用函数执行完成,返回之后,将这个函
数对应的栈帧出栈
浏览器的后退功能
我们使用两个栈,X 和 Y,我们把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈
X 中出栈,并将出栈的数据依次放入栈 Y。当我们点击前进按钮时,我们依次从栈 Y 中取出数据,
放入栈 X 中。当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数
据,那就说明没有页面可以点击前进按钮浏览了
2.1.4 队列
概念
队列(queue)是一种线性数据结构,队列中的元素只能先入先出(First In First Out,简称 FIFO)。
队列的出口端叫作队头(front),队列的入口端叫作队尾(rear)。
存储原理
队列这种数据结构既可以用数组来实现,也可以用链表来实现
用数组实现时,为了入队操作的方便,把队尾位置规定为最后入队元素的下一个位置
用数组实现的队列叫作顺序队列
用链表实现的队列叫作链式队列
时间复杂度
入队和出队都是O(1)
应用
资源池、消息队列、命令队列等等
2.2 散列表
概念
散列表也叫作哈希表(hash table),这种数据结构提供了键(Key)和值(Value)的映射关系。只要
给出一个Key,就可以高效查找到它所匹配的Value,时间复杂度接近于O(1)。
存储原理
哈希函数
散列表在本质上也是一个数组
散列表的Key则是以字符串类型为主的
通过hash函数把Key和数组下标进行转换
作用是把任意长度的输入通过散列算法转换成固定类型、固定长度的散列值
以Java为例:
//数组下标=取key的hashcode模数组的长度后的余数
index = HashCode (Key) % Array.length
int index=Math.abs("Hello".hashCode())%10; (0-9)
这是最简单的计算方式
还有很多hash函数:CRC16、CRC32、siphash 、murmurHash、times 33等
此种Hash计算方式为固定Hash方式,也称为传统Hash
该方式在数组固定时,可以快速检索
但当数组长度变化时,需要重新计算数组下标,此时根据key检索将出现问题
所以说传统Hash法虽然比较简单,但不利于扩展,如果要扩展可以采用一致性Hash法
时间复杂度
写操作: O(1) + O(m) = O(m) m为单链元素个数
读操作:O(1) + O(m) m为单链元素个数
Hash冲突写单链表:O(m)
Hash扩容:O(n) n是数组元素个数 rehash
Hash冲突读单链表:O(m) m为单链元素个数
优缺点
优点:读写快
缺点:哈希表中的元素是没有被排序的、Hash冲突、扩容 重新计算
应用
HashMap
位图:位图就是bitmap的缩写。所谓bitmap,就是用每一位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。在STL中有一个bitset容器,其实就是位图。
2.3 树
概念
树(tree)是n(n≥0)个节点的有限集。
当n=0时,称为空树。在任意一个非空树中,有如下特点。
有且仅有一个特定的称为根的节点。
当n>1时,其余节点可分为m(m>0)个互不相交的有限集
每一个集合本身又是一个树,并称为根的子树。
2.3.1 二叉树
概念
二叉树(binary tree)是树的一种特殊形式。二叉,顾名思义,这种树的每个节点最多有2个孩子节
点。注意,这里是最多有2个,也可能只有1个,或者没有孩子节点。
- 满二叉树
一个二叉树的所有非叶子节点都存在左右孩子,并且所有叶子节点都在同一层级上,那么这个树就
是满二叉树 - 完全二叉树
对一个有n个节点的二叉树,按层级顺序编号,则所有节点的编号为从1到n。如果这个树所有节点和同
样深度的满二叉树的编号为从1到n的节点位置相同,则这个二叉树为完全二叉树
满二叉树要求所有分支都是满的;而完全二叉树只需保证最后一个节点之前的节点都齐全即可
存储
二叉树属于逻辑结构,可以使用链表和数组进行存储。
- 链式存储
二叉树的每一个节点包含3部分
存储数据的data变量
指向左孩子的left指针
指向右孩子的right指针 - 数组存储
使用数组存储时,会按照层级顺序把二叉树的节点放到数组中对应的位置上。
如果某一个节点的左孩子或右孩子空缺,则数组的相应位置也空出来
寻址方式:
一个父节点的下标是n,那么它的左孩子节点下标就是2×n+1、右孩子节点下标就是2*(n+1)
对于一个稀疏的二叉树(孩子不满)来说,用数组表示法是非常浪费空间的
所以二叉树一般用链表存储实现。(二叉堆除外)
时间复杂度
二叉查找树的插入和查找时间复杂度为:O(logn)
极端情况下二叉查找树退化成链表,时间复杂度为O(n),所以需要平衡二叉查找树。
应用
非线性数据:菜单,组织结构、家谱等等
线性数据:二叉查找树
二叉查找树是有序的,我们只需要中序遍历,就可以在 O(n) 的时间复杂度内,输出有序的数据序列。
二叉查找树的性能非常稳定,扩容很方便(链表实现)
2.4 图
概念
图(Graph),是一种复杂的非线性表结构。
图中的元素我们就叫做顶点(vertex)
图中的一个顶点可以与任意其他顶点建立连接关系。我们把这种建立的关系叫做边(edge)
跟顶点相连接的边的条数叫做度(degree)
图这种结构有很广泛的应用,比如社交网络,电子地图,多对多的关系就可以用图来表示。
边有方向的图叫做有向图,比如A点到B点的直线距离,微信的添加好友是双向的
边无方向的图叫无向图,比如网络拓扑图
带权图(weighted graph)。在带权图中,每条边都有一个权重(weight),我们可以通过这个权重
来表示 一些可度量的值
存储原理
图最直观的一种存储方法就是,邻接矩阵(Adjacency Matrix)。
邻接矩阵的底层是一个二维数组
-
无向图:如果顶点 i 与顶点 j 之间有边,我们就将 A[i][j]和 A[j][i]标记为 1
-
有向图
如果顶点 i 到顶点 j 之间,有一条箭头从顶点 i 指向顶点 j 的边,那我们就将 A[i][j]标记为 1。同理,如
果有一条箭头从顶点 j 指向顶点 i 的边,我们就将 A[j][i]标记为 1
-
带权图
数组中就存储相应的权重
时间复杂度
邻接表
每个顶点都需要被访问一次,时间复杂度是 O(V);相连的顶点(也就是每条边)也都要被访问一次,加
起来就是 O(E)。因此整体时间复杂度就是 O(V+E)。
邻接矩阵
V 个顶点,每次都要检查每个顶点与其他顶点是否有联系,因此时间复杂度是 O( V²)。
应用
广度优先的搜索可以同时从起始点和终点开始进行,称之为双端 BFS。这种算法往往可以大大地提高搜
索的效率。
社交网络可以用图来表示。这个问题就非常适合用图的广度优先搜索算法来解决,因为广度优先搜索是
层层往外推进的。首先,遍历与起始顶点最近的一层顶点,也就是用户的一度好友,然后再遍历与用户
距离的边数为 2 的顶点,也就是二度好友关系,以及与用户距离的边数为 3 的顶点,也就是三度好友关
系。