MySQL体系结构
1,sql 通过数据库连接池
java 成熟的连接池从早起的c3p0 以及发展到目前springboot2默认支持的 HiKariCP
HiKariCP 追求性能,稳定性等方面都具备了 直接优势 监控可以通过其他方式实现
Druid 偏向监控
可以根据项目环境进行选择
2,接受用户的SQL命令,并且返回用户需要查询的结果
3,SQL命令传递到解析器的时候会被解析器验证和解析(权限、语法结构)
4,SQL语句在查询之前会使用查询优化器对查询进行优化
select id,name from user where age = 40;
a、这个select 查询先根据where 语句进行选取,而不是先将表全部查询出来以后再进行age过滤
b、这个select查询先根据id和name进行属性投影,而不是将属性全部取出以后再进行过滤
c、将这两个查询条件联接起来生成最终查询结果
5,缓存
MySQL Server默认打开,但是有些大佬却建议关闭此功能
MySQL Query Cache对性能的影响
消耗
- 读查询开始之前必须检查是否命中缓存。
- 如果读查询可以缓存,那么执行完查询操作后,会查询结果和查询语句写入缓存。
- 当向某个表写入数据的时候,必须将这个表所有的缓存设置为失效,如果缓存空间很大,则消耗也会很大,可能使系统僵死一段时间,因为这个操作是靠全局锁操作来保护的。
- 对InnoDB表,当修改一个表时,设置了缓存失效,但是多版本特性会暂时将这修改对其他事务屏蔽,在这个事务提交之前,所有查询都无法使用缓存,直到这个事务被提交,所以长时间的事务,会大大降低查询缓存的命中
优势
Query Cache的查询,发生在MySQL接收到客户端的查询请求、查询权限验证之后和查询SQL解析之前。也就是说,当MySQL接收到客户端的查询SQL之后,仅仅只需要对其进行相应的权限验证之后,就会通过Query Cache来查找结果,甚至都不需要经过Optimizer模块进行执行计划的分析优化,更不需要发生任何存储引擎的交互。由于Query Cache是基于内存的,直接从内存中返回相应的查询结果,因此减少了大量的磁盘I/O和CPU计算,导致效率非常高
结论
1,当系统是 增删改密集型的时候,建议直接关闭缓存
2,当系统是 查询密集型时候,当然是需要打开缓存,如何设置在3中介绍
3,大部分项目业务都属于 增删改都平均
这个时候涉及到缓存大小的设置
MySQL缓存管理和配置
mysql> show variables like '%query_cache%';
+------------------------------+---------+
| Variable_name | Value |
+------------------------------+---------+
| have_query_cache | YES | --查询缓存是否可用
| query_cache_limit | 1048576 | --可缓存具体查询结果的最大值
| query_cache_min_res_unit | 4096 | --查询缓存分配的最小块的大小(字节)
| query_cache_size | 599040 | --查询缓存的大小
| query_cache_type | ON | --是否支持查询缓存
| query_cache_wlock_invalidate | OFF | --控制当有写锁加在表上的时候,是否先让该表相关的 Query Cache失效
+------------------------------+---------+
6 rows in set (0.02 sec)
have_query_cache
该MySQL Server是否支持Query Cache。
query_cache_limit
MySQL能够缓存的最大查询结果,查询结果大于该值时不会被缓存。默认值是1048576(1MB)如果某个查询的结果超出了这个值,Qcache_not_cached的值会加1,如果某个操作总是超出,可以考虑在SQL中加上SQL_NO_CACHE来避免额外的消耗。
query_cache_min_res_unit
查询缓存分配的最小块的大小(字节)。 默认值是4096(4KB)。当查询进行的时候,MySQL把查询结果保存在qurey cache中,但如果要保存的结果比较大,超过query_cache_min_res_unit的值 ,这时候mysql将一边检索结果,一边进行保存结果,所以,有时候并不是把所有结果全部得到后再进行一次性保存,而是每次分配一块query_cache_min_res_unit大小的内存空间保存结果集,使用完后,接着再分配一个这样的块,如果还不不够,接着再分配一个块,依此类推,也就是说,有可能在一次查询中,mysql要进行多次内存分配的操作。适当的调节query_cache_min_res_unit可以优化内存如果你的查询结果都是一些small result,默认的query_cache_min_res_unit可能会造成大量的内存碎片如果你的查询结果都是一些larger resule,你可以适当的把query_cache_min_res_unit调大
query_cache_size
为缓存查询结果分配的内存的数量,单位是字节,且数值必须是1024的整数倍。默认值是0,即禁用查询缓存。请注意如果设置了该值,即使query_cache_type设置为0也将分配此数量的内存。
mysql> SHOW STATUS LIKE 'Qcache%';
+-------------------------+--------+
| Variable_name | Value |
+-------------------------+--------+
| Qcache_free_blocks | 1 | ----在查询缓存中的闲置块,如果该值比较大,则说明Query Cache中的内存碎片可能比较多。FLUSH QUERY CACHE会对缓存中的碎片进行整理,从而得到一个较大的空闲内存块。
| Qcache_free_memory | 382704 | ----剩余缓存的大小
| Qcache_hits | 198 | ----缓存命中次数
| Qcache_inserts | 131 | ----缓存被插入的次数,也就是查询没有命中的次数。
| Qcache_lowmem_prunes | 0 | ----由于内存低而被删除掉的缓存条数,如果这个数值在不断增长,那么一般是Query Cache的空闲内存不足(通过Qcache_free_memory判断),或者内存碎片较严重(通过Qcache_free_blocks判断)。
| Qcache_not_cached | 169 | ----没有被缓存的条数,有三种情况会导致查询结果不会被缓存:其一,由于query_cache_type的设置;其二,查询不是SELECT语句;其三,使用了now()之类的函数,导致查询语句一直在变化。
| Qcache_queries_in_cache | 128 | ----缓存中有多少条查询语句
| Qcache_total_blocks | 281 | ----总块数
+-------------------------+--------+
8 rows in set (0.00 sec)
Query Cache碎片率Query Cache碎片率 = Qcache_free_blocks / Qcache_total_blocks * 100%
如果Query Cache碎片率超过20%,则可以用FLUSH QUERY CACHE整理内存碎片;如果你的查询都是小数据量的话,可以尝试减小query_cache_min_res_unit。
Query Cache利用率Query Cache利用率 = (query_cache_size - Qcache_free_memory) / query_cache_size * 100%
Query Cache利用率在25%以下的话,说明query_cache_size设置的过大,可适当减小;Query Cache利用率在80%以上,而且Qcache_lowmem_prunes > 50的话,说明query_cache_size可能有点小,或者就是内存碎片太多。
Query Cache命中率
可缓存查询的Query Cache命中率 = Qcache_hits / (Qcache_hits + Qcache_inserts) * 100%
涵盖所有查询的Query Cache命中率 = Qcache_hits / (Qcache_hits + Com_select) * 100%
若命中率在50-70%的范围之内,则表明Query Cache的缓存效率较高。如果命中率明显小于50%,那么建议禁用(将query_cache_type设置为0(OFF))或按需使用(将query_cache_type设置为2(DEMAND))Query Cache,节省的内存可以用作InnoDB的缓冲池。
如何判断Query Cache是空闲内存不足,还是内存碎片太多?如果Qcache_lowmem_prunes值比较大,表示Query Cache的内存空间大小设置太小,需要增大。
如果Qcache_free_blocks值比较大,表示内存碎片较多,需要使用FLUSH QUERY CACHE语句清理内存碎片。
系统变量query_cache_min_res_unit应当设置为多大?query_cache_min_res_unit的计算公式如下所示:
query_cache_min_res_unit = (query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache
其中,一般不建议将Query Cache的大小(也就是query_cache_size系统变量)设置超过256MB
6存储引擎
MyISAM存储引擎
MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。
查询速度1亿数据轻轻松松
MEMORY
MEMORY存储引擎存储数据的位置是内存,因此访问速度最快,但是安全上没有保障。适合于需要快速的访问或临时表
没啥用
BLACKHOLE
黑洞存储引擎,写入的任何数据都会消失
这个数据库不会独立蹲在,用于主从架构当中,分担主节点分发的压力
会把master的二进制日志供下层的slave来读取
而使用blackhole引擎的原因是它不占硬盘空 间,作为一个中转,只负责记日志、传日志
InnoDB存储引擎
mysql 5.5版本以后默认的存储引擎,也是最常用的引擎,支持事务 放到最后也是因为innoDB的内容比较多
数据库事务分为4种
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted(读取未提交内容) | 允许 | 允许 | 允许 |
Read Committed(读取提交内容) | 不允许 | 允许 | 允许 |
Repeatable Read(可重读) | 不允许 | 不允许 | 允许 |
Serializable(可串行化) | 不允许 | 不允许 | 不允许 |
事务的实现
-
1,首先讲数据保存在undo log 当中
1.redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。
2.undo用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录 -
2,其次
InnoDB会给数据库中的每一行增加三个字段来实现mvcc,它们分别是DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID
DB_TRX_ID表示最后一个事务的更新和插入 6字节的事务ID
DB_ROLL_PTR指向当前记录项的undo log信息 7字节的回滚指针
DB_ROW_ID标识插入的新的数据行的id 用来生成默认聚簇索引(聚簇索引,保存的数据在物理磁盘中按顺序保存,这样相关数据保存在一起,提高查询速度)
deleted_bit,删除标记位,删除时设置
译注:
MVCC的全称是“多版本并发控制”。这项技术使得InnoDB的事务隔离级别下执行一致性读操作有了保证,换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值。这是一个可以用来增强并发性的强大的技术,因为这样的一来的话查询就不用等待另一个事务释放锁。这项技术在数据库领域并不是普遍使用的。一些其它的数据库产品,以及mysql其它的存储引擎并不支持它。
其实说白了就是一个乐观锁
索引
-
聚集索引
聚集索引 的叶节点就是数据节点, -
非聚簇索引
非聚簇索引的叶节点仍然是索引节点,并保留一个链接指向对应数据块
MyISAM索引实现
MyISAM 采用的是非聚簇索引
MyISAM索引文件和数据文件是分离的,索引文件的data域保存记录所在页的地址(物理存储位置),通过这些地址来读取页,进而读取被索引的行数据。
InnoDB索引实现
1、聚集索引
InnoDB存储引擎表是索引组织表,即按照主键的顺序存储数据。
与 MyISAM相同的一点是,InnoDB 也采用 B+Tree这种数据结构来实现 B-Tree索引。而很大的区别在于,InnoDB 存储引擎采用“聚集索引”的数据存储方式实现B-Tree索引,
聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,树中的叶子节点存放着表中的行记录数据也就是数据行和相邻的键值紧凑地存储在一起。