MySQL高级

这是根据B站上面周阳老师讲的MySQL高级知识来记得笔记

一、MySQL框架介绍

概述

  • MySQL是一个关系型数据库管理系统,由瑞; 典MySQL AB公司开发,国前属于Oracle公司。
  • MySQL是- -种关联数据库管理系统,将数据保存在不同的表中,而不是将所有数据放在-一个大仓库内, 这样就增加了速度并提高了灵活性。
  • Mysq|是开源的,所以你不需要支付额外的费用。
  • Mysq|支持大型的数据库。可以处理拥有上千万条记录的大型数据库。
  • MySQL使用标准的SQL数据语言形式。
  • Mysq可以允许于多个系统上,并且支持多种语言。这些编程语言包括C、C++、 Python、Java、 Perl、 PHP、 Eiffel、 Ruby和Tcl等 。
  • Mysq|对PHP有:很好的支持,PHP是目前最流行的Web开发语言。
  • MySQL支持大型数据库,支持5000万 条记录的数据仓库,32位系统表文件最大可支持4GB, 64位系统支持最大的表文件为8TB。
  • Mysq|是可以定制的,采用了GPL协议,你可以修改源码来开发自己的Mysql系统。

高级MySQL

完整的mysql优化需要有很深的功底,大公司甚至有专门的DBA来写下述

  • mysq内核
  • sq1优化攻城狮
  • mysq|服务器的优化
  • 各种参数常量设定
  • 查询语句优化
  • 主从复制
  • 软硬件升级
  • 容灾备份
  • sql编程

Linux安装MySQL

Linux的安装包都放在那个opt文件夹下面

二、索引优化分析

1、SQL的性能

性能下降SQL慢,执行时间长,等待时间长

  1. 查询语句写的烂
  2. 索引失效(创建了未使用)
  3. 关联查询太多 join(设计缺陷后者不得已的需求)
  4. 服务器调优以及各个参数的设置 (缓冲,线程数等)

2、常见通用的join查询

1、SQL的执行顺序

  1. 手写

  2. 机读

  3. 总结

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NIPYuQlv-1613796694690)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级---数据库优化.assets\image-20210114104639759.png)]

2、Join图

  1. 内连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JAEAvgkx-1613796694692)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级---数据库优化.assets\image-20210114112033404.png)]

    SELECT <select_list> FROM TableA A INNER JOIN TABLEB B ON A.Key = B.Key
    
  2. 左内连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQifxtG0-1613796694693)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级---数据库优化.assets\image-20210114112130709.png)]

    SELECT <select_list> FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key
    
  3. 右内连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgQuKszJ-1613796694695)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级---数据库优化.assets\image-20210114112142160.png)]

    SELECT <select_list> FROM TABLEA A RIGHT JOIN TableB B ON A.Key = B.Key
    
  4. 左外连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TGz7EzEU-1613796694696)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210114112205067
    .png)]

    SELECT <select_list> FROM TABLEA A LEFT JOIN TABLEB B ON A.Key = B.Key WHERE B.Key IS NULL
    
  5. 右外连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RMWAJhrY-1613796694697)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级---数据库优化.assets\image-20210114112218084.png)]

    SELECT <select_list> FROM TABLEA A RIGHT JOIN TABLEB B A.Key = B.Key WHERE A.Key IS NULL
    
  6. 全连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b2fGNsiB-1613796694698)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级---数据库优化.assets\image-20210114112241606.png)]

    -- mysql不适用此方法
    SELECT <select_list> FROM TableA A FULL OUTER JOIN TableB B ON A.Key = B.Key-- +mysql适用此方法,将左右连接查询使用union连接	起来,将两者重复的剔除掉。
    select * from t_emp a left join t_dept b on a.deptid = b.id 
    union 
    select * from t_emp a right join t_deptb on a.deptid = b.id;
    
  7. 左右外连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-byvs8b5K-1613796694698)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级---数据库优化.assets\image-20210114112254741.png)]

    SELECT <select_list> FROM TableA A FULL OUTEP JOIN TableB B ON A.Key = B.Key WHERE A.Key = B.Key WHERE A.Key IS NULL OR B.Key IS NULL
    
    select  * from t_emp a left join t_dept b on a.deptid = b.id where b.id is null
    union 
    select * from t_emp a right join t_dept b on a.deptid = b.id where a.deptid is null;
    
    

3、建表SQL

4、7中Join

3、索引优化分析

3.1是什么

​ MySQL 官方对索引的定义为: 索引(Index) 是帮助 MySQL 高效获取数据的数据结构。 可以得到索引的本质:
索引是数据结构。可以简单理解为**排好序的快速查找数据结构**

在数据之外, 数据库系统还维护着满足特定查找算法的数据结构, 这些数据结构以某种方式引用(指向) 数据,
这样就可以在这些数据结构上实现高级查找算法。 这种数据结构, 就是索引。

3.2优缺点

3.2.1优势
  • 提高数据检索的效率,降低数据库的IO 成本
  • 通过索引列对数据库进行排序,降低数据库排序成本,降低了CPU消耗。
3.2.2劣势
  • 虽然索引大大提高了检索效率,同时会降低表更新的速度,如对表进行INSERT,UPDATA,DELETE 因为在更新表的时候,MYSQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新带来的键值变化的索引信息。
  • 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的信息,所以索引也是要占用空间的

3.3Mysql索引的分类

3.3.1单值索引

概念:

即一个索引只包含单个列,一个表可以有多个单列索引。

语法:

3.3.2唯一索引

概念:

索引的值必须唯一,但允许有空值

语法:

3.3.3主键索引

概念:

语法:

3.3.4复合索引

概念:

即一个索引包含多个列

语法:

3.3.5基本语法

创建:

-- 添加unique是表示唯一索引   如果有多个列,那么就是符合索引
CREATE [UNIQUE] INDEX indexName ON tableName(columname(length))

ALTER mytable ADD [UNIQUE] INDEX[indexName] ON (columnname(length))

删除:

DROP INDEX [indexName] ON myTable;

查看:

SHOW INDEX FROM table_name\G

3.4mysql索引结构

  • BTree索引(主攻)
  • Hash索引
  • full-text索引
  • R-Tree索引

3.5索引的创建时机

3.5.1适合创建索引的情况
  1. 主键自动建立唯一索引
  2. 频繁作为查询条件的字段应该创建索引
  3. 查询中与其他表关联的字段,外键关系建立索引
  4. 单键/组合索引的选择问题,组合索引性价比更高
  5. 查询中排序的字段,排序字段拖通过索引去访问将大大提高排序速度
  6. 查询中统计或者分组字段
3.5.2不适合创建索引的情况
  1. 表记录太少
  2. 经常增删改的表或者字段
  3. where条件里面用不到的字段不用创建索引
  4. 过滤性不好的不适合创建索引

4、Explain性能分析

4.1概念

使用explain课模拟优化器质性SQL查询语句,从而知道mysql是如何处理你的SQL语句的。分析查询你的查询语句或者表结构的性能瓶颈。

4.2能干嘛

  • 表的读取顺序
  • 数据读取操作的操作类型
  • 那些索引可以使用
  • 那些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被优化器查询

4.3怎么玩

Explain+SQL语句

在这里插入图片描述

4.4名字字段解释

4.4.1:id

表的读取顺序

select 查询的序列号包含一组数字,表示查询中执行select字句或者操作表的顺序。

分为三种情况:

  • id相同,执行顺序由上至下

在这里插入图片描述

  • id不同,如果是子查询,那么id序号会递增,id值越大,优先级越高,越先被执行。

image-20210115134401238.png

  • id有相同的和不同的,如果id相同,他会从上往下执行,如果不同大的优先级高越先执行衍生 = DERIVED;

关注点: id 号每个号码, 表示一趟独立的查询。 一个 sql 的查询趟数越少越好。

4.4.2:select_type

读取表的操作类型

  1. SIMPLE

    简单的SELECT查询不包含子查询或者union查询

  2. PRIMARY

    查询中包含任何复杂的子部分,最外层查询则被标记为PRIMARY

  3. SUBQUERY

    在SELECT或者WHERE 中包含子查询

  4. DERIVED

    在FROM表中包含的子查询被标记为DERIVED(衍生);MYSQL会递归执行这些子查询,把结果放在临时表里面。

  5. UNION

    若第二个SELECT出现在UNIONZ之后,则被标记为UNION;

    若UNIION包含在FROM子句的子查询中,外层SELECT将被标记为DERIVED;

  6. UNION RESULT

    从UNION表中获取结果的SELECT。

4.4.3:table

显示的这一行的数据是关于哪一张表的

4.4.4:type

是查询的访问类型。是较为重要的一个指标,从最好到最差依次是:

system > const > eq_ref > ref > range > index > ALL

一般来说

  1. system

    表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计

  2. const

    表示通过索引一次就能找到了,const用于比较primary key 或者unique索引,因为只匹配一行数据,所以所以很快,如将逐渐置于where列表中,MySQL就能将该查询转换为一个常量。

在这里插入图片描述

  1. eq_ref

    唯一性索引扫描,对于每个多外键,表中只有一条记录与之匹配,常见于主键或者唯一索引扫描。

    在这里插入图片描述

  2. ref

    非唯一索引扫描,返回匹配某个单独的所有行,本质上也是一种索引访问,他返回所有匹配某个单独值的行,然而,他可能会找到多个复合条件的行,所以他应该属于查找和扫描的混合体。

  3. range

    只检索给定范围的行,使用一个索引来选择行,key列显示了使用那个索引,一般就是在你的where语句中出现了between、<、>、in等的查询

    这种范围扫描比全表扫描要好,因为他只需要开始于索引的某一点,而结束与另一点,不用扫描全部索引。

    在这里插入图片描述

  4. index

    FULL INDWEX Scan ,index与ALL的区别为index类型治安遍历索引数,这通常比ALL快,因为索引文件通常比数据文件小,(也就是说虽然all和index收拾读全表,但是index是从索引中读取的,而all是从硬盘中读的);

在这里插入图片描述

  1. all

    遍历全表找到匹配的行

    在这里插入图片描述

4.4.5:possible_keys

理论上的索引

显示可能应用到这张表中的索引,一个或者多个,查询设计到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

4.4.6:key

被用到的索引

实际中使用的索引,如果为null,则没有使用索引,

查询中若使用了覆盖索引,则该索引仅出现在key列表中

4.4.7:key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好key_len显示的值为索引字段的最大可能长度,并非实际使用长度即key_len使根据表定义计算而得,不是通过表内检索出的。key_len 越长,说明索引使用的越充分。

4.4.8:ref

显示索引的那一列被使用了,如果可能 的话,是一个常数,那些列或者常量被用于查找索引列的值。
在这里插入图片描述

查询中与其他表关联的字段,外键关系建立索引;

4.4.9:rows

rows列显示MySQL认为它执行查询时必须检查的行数,越少越好;

根据表统计信息以及索引选用情况,大致估算出所需找到所需要的记录所需要读取的行数。

4.4.10:extra

包含不适合在其他列中显示,但十分重要的其他的额外信息

4.4.10.1 Using filesort

MySQL会对数据使用一个外部的索引进行排序,而不是按照表内的索引顺序进行读取。MySQL中无法使用索引完成的排序操作称为“文件排序”;

4.4.10.2 Using temporary

使用了临时表保存中间结果,MySQL在对查询结果排序的时候使用临时表,常见于排序order by分组查询 group by;

4.4.10.3 Using index

代表相应的select操作中,使用了覆盖索引(Covering index),避免访问了表的数据行,效率不错,如果同时出现 using where ,表宁索引被用来执行索引键值的查找;如果没有同时出现using where ,表名索引只是用来读取数据,而非利用索引来查找。

利用了索引来进行分组和查找。

覆盖索引(Covering index)索引覆盖

理解方式

就是select的数据只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再查读取数据文件,换句话来说 查询列要被所建的索引覆盖

4.4.10.4 Using where

表明使用了where过滤

4.4.10.5 Using join buffer

使用了连接缓存

4.4.10.6 impossible where

wehre的值总为false,不能用来获取任何元组。

4.4.10.7 select table optimized away

在没有 GROUPBY 子句的情况下, 基于索引优化 MIN/MAX 操作或者对于 MyISAM 存储引擎优化 COUNT(*)操
作, 不必等到执行阶段再进行计算, 查询执行计划生成的阶段即完成优化。

4.4.10.8 distinct

优化distinct操作,在找到第一匹配的元组件后即停止找同样值的动作。

5、热身

在这里插入图片描述

6、索引优化

6.1索引分析

单表
建表SQL
CREATE TABLE IF NOT EXISTS `article`(
`id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
`author_id` INT(10) UNSIGNED NOT NULL,
`category_jid` INT(10) UNSIGNED NOT NULL,
`views` INT(10) UNSIGNED NOT NULL,
`comments` INT(10) UNSIGNED NOT NULL,
`title` VARBINARY(255) NOT NULL,
`content` TEXT NOT NULL
);

INSERT INTO `article`(`author_id`,`category_jid`,`views`,`comments`,`title`,`content`) VALUES
(1,1,1,1,'1','1'),
(2,2,2,2,'2','2'),
(1,1,3,3,'3','3');

案例
EXPLAIN SELECT id,author_id FROM article WHERE category_jid = 1
AND comments > 1
ORDER BY
views DESC LIMIT 1;

在这里插入图片描述

问题:出现了using filesort

解决:创建索引

create index index_all on article(category_jid,comments,views);

-- 还是以上错误
-- 删除索引
drop index index_all from article;

-- 重新创建索引
create index inde_two on article(category_jid,views);

在这里插入图片描述

两表
建表SQL
CREATE TABLE IF NOT EXISTS `class`(
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(`id`)
  );
CREATE TABLE IF NOT EXISTS `book`(
`bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`card` INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(`bookid`)
  );
  
INSERT INTO class(card) VALUES(FLOOR(1 + (RANDO * 20)));


左连接查询:建立右表相应的索引

右连接查询:建立左表相应的索引

案例

在这里插入图片描述

三表
建表SQL
案例
Join语句优化
  • 尽可能减少join语句中的NestedLoop的循环总次数,“永远用小结果驱动大结果集”
  • 尽量优化NestedLoop的内循环;
  • 保证
  • join语句中被驱动表上的join条件字段已经被索引
  • 当无法保证被驱动表的join字段被索引并且内存资源充足的前提下,不要太吝啬JoinBuffer的设置

三、索引失效(应该避免)

索引失效的原因

全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
Like百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用;
VAR引号不可丢,SQL高级也不难!

1、全值匹配我最爱

2、最佳左前缀法则

如果索引了多列,要遵守最左前缀法,指的是查询从索引的最左前列开始,并且不跳过索引中的列;带头大哥不能死

3、不在索引上面做任何操作(计算,函数,(自动or手动)转换类型),会导致索引失效而转向全表扫描

在这里插入图片描述

在这里插入图片描述

4、存储引擎不能使用索引中范围条件右边的列

select * from staffs where name = 'z3' and age > 23 and pos = 'dev'
-- 即  age> 23 后面的索引全部都失效了;

在这里插入图片描述

5、尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致,))减少 select *

在这里插入图片描述

6、mysql中在使用不等于(!= 或者 <>)的时候无法使用索引会导致全表扫描

在这里插入图片描述

7、is null 或者 is not null 也无法使用索引

在这里插入图片描述

8、like以通配符开头(%abc...)mysql的索引会失效会变成全表扫描操作

% like 加右边

% like 加右边才会避免索引失效,like是一个范围查询
在这里插入图片描述

问题:

解决like % 字符串 %时索引不被使用的方法???

使用索引覆盖的方法,来进行查询,就是查询的列从索引列中取。要想索引不失效,那么不要查询与索引无关的字段。

9、字符全不加单引号,索引失效

在这里插入图片描述

10、少用 or 使用它的时候会导致索引失效

在这里插入图片描述

四、索引面试题分析

定值范围还是排序,一般 order by 给个范围;

group by 基本上都要进行排序,会有临时表的产生

分组之前必排序

一般性建议

对于单键索引,尽量选择针对当前的query过滤性更好的索引

在选择组合索引的时候,当前query中过滤性最好的字段在索引字段排序中,额日志越靠前越好

在选择组合索引的时候,尽量选择可以能够包括当前query中的where字句中更多字段的索引

尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的。

在这里插入图片描述

五、查询截取分析

1、查询优化

1)、小表驱动大表

永远小表驱动大表(小的数据集驱动大的数据集)

类似嵌套循环 Nested Loop

当B表的数据集必须小于A表的数据集的时候,用in优于exists

当A表的数据集必须小于B表的数据集合的时候,用exists优于in

语法 select * from Table where exists (subquery)

该语法可以解释为:将主查询的数据放到子查询中做条件验证,根据验证结果(True 或者 False)來決定主查询结果是否得以保留

提示

  1. exists (subquery)只返回True或者false,因此子查询中的select * 也可以是 select 1 或者 select X 官方说明是实际执行时会忽略 select清单,因此没有区别。
  2. EXISTS子查询的实际执行过程可能经过了优化而不是我们理解上的逐条对比,如果担忧效率问题,也可以进行实际的校验来确定是否有效率问题。
  3. EXIST子查询往往也可以用条件表达式、其他子查询或者join来替代,何种足有需要具体问题来分析。

在这里插入图片描述

2)order by 关键字优化

mysql两种排序方式:

mysql支持两种排序方式,FileSort和index,index效率高,他指mysql扫描索引本身完成排序,FileSort方式效率低;

order by 使用排序的情况:

Order By 满足两种情况会使用Index排序;

  1. Order By 使用索引最左前列
  2. 使用where子句,与order by子句条件组合满足索引最左前列;
优化:
(1)order by 子句,尽量使用index 排序,避免使用FileSort方式进行排序
(2)尽可能在索引列上面完成排序操作,遵照索引建立的最左前缀法
(3)如果不在索引列上面,FileSort有两种算法:

​ mysql就要启动单路排序,或者双路排序

a、双路排序
b、单路排序
c、结论以及引申出的问题
  1. 由于单路是后出的,总体而言,好于双路
  2. 但是使用单路有问题
    1.
(4)Order By 优化策略
  • 增大 sort_butter_size 参数的设置

    不管用哪种方法,提高这个参数都会提高效率,当然要根据系统的能力去提高,因为这个参数是针对每个进程的 1M - 8M 之间调整的;

  • 增大 max_length_for_sort_data 参数的设置

    mysql使用单路排序的前提是排序字段大小要小于max_length_for_Sort_Data.提高这个参数,会增加用改进算法的概率。但是吐过设的不太高,数据纵容连超出Sort_buffer_size的概率就会增大,明显症状是提高的磁盘I/O活动和低的处理器使用率

  • 减少 select 后面的查询的字段。

    当Query的字段大小综=总和小于max_length_for_sort_Data 而且排序字段不是TEXT|BLOB类型时,会用改进后的算法——单路排序,否则用老算法——多路排序。

    两种爽啊的数据都有肯能超出sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次I\O,但是用单路排序算法的风险会更大一些,所以要提高sort_buffer_size;

(5)小总结
  • MySQL两种排序方式:文件排序或者扫描有序索引排序

  • mysql能为排序与查询使用相同的索引

    order by 能使用索引的最左前缀

    如果where使用的索引最左前缀为常量,则order by 能够使用索引

  • 不能使用索引排序的方式

    • 排序不一致
    • 丢失索引老大
    • 丢失中间索引
    • 不是索引的一部分对于排序来说,多个相等条件也是范围查询

3)group by 查询优化

  • group by使用索引的原则几乎和order by 分规则一致,唯一的区别是group by 即使没有过滤条件用到索引,也可以直接使用索引。
  • group by 的实质是先排序,后进行分组,遵循索引建立的最左前缀法
  • 当无法使用索引列时,增大max_length_for_sort_data参数设置+增大sort_buffer_size的设置
  • where高于having 能写在where限定的条件就不要去having限定了

2、慢查询日志

5.2.1是什么

  1. MySQL的慢查询日志是MySQL提供的一种日志记录, 它用来记录在MySQL中响应时间超过阀值的语句, 具
    体指运行时间超过long_query_time值的SQL, 则会被记录到慢查询日志中。
  2. 具体指运行时间超过long_query_time值的SQL, 则会被记录到慢查询日志中。 long_query_time的默认值为
    10, 意思是运行10秒以上的语句。
  3. 由他来查看哪些SQL超出了我们的最大忍耐时间值, 比如一条sql执行超过5秒钟, 我们就算慢SQL, 希望能
    收集超过5秒的sql, 结合之前explain进行全面分析。

5.2.2怎么玩

5.2.2.1说明
  • 默认情况下,mysql没有开启慢查询日志,需要我们手动来设置=这个参数
  • 当然如果不是性能调优的话,不建议启动该参数,因为开启慢日志查询会或多或少的带来性能上面的影响,慢查询日志支持将日志记录写入文件。
5.2.2.2查看是否开启以及如何开启

默认情况下 slow_query_log的值为off,表示慢查询日志是禁用的,可以通过设置slow_query_log的值来开启;

-- c查看是否开启
show variables like '%slow_query_log%';

在这里插入图片描述

开启:

set global slow_quey_log = 1

使用这个SQL语句开启的慢日志查询功能只对当前数据库生效,如果mysql重启则失效

如果要永久生效,就必须要修改my.cnf配置文件

修改 my.cnf 配置文件,[myid]下面增加或者修改参数

slow_query_log=1

slow_query_log_file=/www/server/data/mysql-slow.log

关于慢日志查询的参数slow_query_log_file ,他指定慢查询日志指定的存放路径,系统会默认给一个省缺的文件host_name-slow.log (如果没有指定参数slow_query_slow_file的话)

5.2.2.3开启后设么样的SQL被记录

这个是有long_query_time的值为10秒,

查看慢查询设定阈值命令

 SHOW VARIABLES LIKE '%long_query_time%'

在这里插入图片描述

设定慢查询阈值

set global long_query_time = 2;

修改以后看不到修改的效果,需要重新打开一个终端窗口,才能看到效果

查看日志中有几条记录

show global status like 'slow_queries';

在这里插入图片描述

可以使用命令修改,也可以在my.cnf中进行修改

[mysqld]
slow_query_log=1
slow_query_log_file=/var/lib/mysql/atguigu-slow.log
long_query_time=3
log_output=FILE

假如运行时间只好等于long_query_time 的情况,并不会被记录下来,也就是说,在mysql源码里是判断大于long_query_time,而非大于等于

5.2.2.5Case
5.2.2.6配置版

5.2.3日志分析工具mysqldumpslow

参数描述备注
-s是表示按照何种方式排序
c访问次数
l锁定时间
r返回记录
t查询时间
al平均锁定时间
ar平均返回记录数
at平均查询时间
-t即为返回前面多少条的数据
-g后边搭配一个正则匹配模式, 大小写不敏感的

六、批量数据脚本

1、插入数据

CREATE TABLE `dept` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`deptName` VARCHAR(30) DEFAULT NULL,
`address` VARCHAR(40) DEFAULT NULL,
ceo INT NULL ,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `emp` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`empno` INT NOT NULL ,
`name` VARCHAR(20) DEFAULT NULL,
`age` INT(3) DEFAULT NULL,
`deptId` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
#CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `t_dept` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2、设置参数

在创建执行函数之前,请先保证log_bin_truse_function_creators 参数为1,即为on开启状态,否则会报错;

在这里插入图片描述

3、编写随机函数

3.1随机产生字符串

DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END $$

如果要删除函数则用:

drop function rand_string;

3.2随机产生部门编号

#用于随机产生多少到多少的编号
DELIMITER $$
CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(from_num +RAND()*(to_num -from_num+1)) ;
RETURN i;
END$$

4、创建存储过程

4.1创建往 emp 表中插入数据的存储过程

DELIMITER $$
CREATE PROCEDURE insert_emp( START INT , max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
#set autocommit =0 把 autocommit 设置成 0
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO emp (empno, NAME ,age ,deptid ) VALUES ((START+i) ,rand_string(6) ,
rand_num(30,50),rand_num(1,10000));
UNTIL i = max_num
END REPEAT;
COMMIT;
END$$


#删除
# DELIMITER ;
# drop PROCEDURE insert_emp;

4.2创建往 dept 表中插入数据的存储过程

执行存储过程, 往 dept 表添加随机数据
DELIMITER $$
CREATE PROCEDURE `insert_dept`( max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO dept ( deptname,address,ceo ) VALUES (rand_string(8),rand_string(10),rand_num(1,500000));
UNTIL i = max_num
END REPEAT;
COMMIT;
END$$

#删除
# DELIMITER ;
# drop PROCEDURE insert_dept;

5、调用存储过程

5.1添加数据到部门表

#执行存储过程, 往 dept 表添加 1 万条数据
DELIMITER ;
CALL insert_dept(10000);

5.2添加数据到员工表

#执行存储过程, 往 emp 表添加 50 万条数据
DELIMITER ;
CALL insert_emp(100000,500000);

6、批量删除某个表上的所有索引

6.1删除索引的存储过程

DELIMITER $$
CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200))
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE ct INT DEFAULT 0;
DECLARE _index VARCHAR(200) DEFAULT '';
DECLARE _cur CURSOR FOR SELECT index_name FROM information_schema.STATISTICS WHERE
table_schema=dbname AND table_name=tablename AND seq_in_index=1 AND index_name <>'PRIMARY' ;
DECLARE CONTINUE HANDLER FOR NOT FOUND set done=2 ;
OPEN _cur;
FETCH _cur INTO _index;
WHILE _index<>'' DO
SET @str = CONCAT("drop index ",_index," on ",tablename );
PREPARE sql_str FROM @str ;
EXECUTE sql_str;
DEALLOCATE PREPARE sql_str;
SET _index='';
FETCH _cur INTO _index;
END WHILE;
CLOSE _cur;
END$$

6.2执行存储过程

CALL proc_drop_index("dbname","tablename");

七、使用 SHOW PROCESSLIST 进行SQL分析

1、是什么

是mysql提供可以用来分析当前会话中语句执行的资源消耗情况,可以用于SQL的调优测量

官网:略

默认情况下:参数处于关闭状态,并保存最近15次的运行结果

2、分析步骤

2.1是否支持

常看当前mysql版本是否支持

show variables like 'profiling';

在这里插入图片描述

2.2开启功能

默认是关闭的,使用前需要开启

set profiling = on;

在这里插入图片描述

2.3运行SQL

2.4查看结果

show profiles

2.5诊断SQL

show profile cpu,block io for query上一步前面的问题SQL数字号码

2.6日常开发需要注意的结论

只要出现这个下面任何一个结果都需要进行优化

  1. converting HEAP to MyISAM查询结果太大,内存都不够用了往磁盘上面搬了。
  2. Creating tmp table 创建临时表
    1. 考别数据到临时表
    2. 用完再删除
  3. Copying to tmp table on disk 把内存中临时表中复制到磁盘中,危险!!!
  4. locked

3、全局查询日志

3.1配置启用

在my.cnf中设置如下

开启

general_log=1

记录日志文件的路径

general_log_file=/path/logfile

输出格式

log_output=FILE

3.2编码启用

命令

set global general_log = 1
set global log_output='TABLE';

此后,你编写的SQL语句将会记录到mysql库里面的general_log表,可以用下面的命令查看

select * from mysql.general_log;

3.3永远不要在生产环境中开启这个功能

八、MySQL中锁的机制

1 概述

1.1 定义

​ 锁是计算机中细条多个进程或者线程并发来访问某一资源的机制

​ 在数据库中,除传统系统资源(如 CPU、RAM、I/O等)的争用以外,数据也是一种提供许多用户共享的资源,如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据并发访问性能的一个重要因素,从这角度来说,锁是对数据库而言尤其重要的,也更加复杂。

1.2 锁的分类

1.2.1 从对数据操作的类型(读/写)分
  1. 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而且还不会相互影响
  2. 写锁(排它锁):当前写操作没有完成前,他会阻断其他写锁和读锁。
1.2.2 从对数据操作的粒度来分
  1. 表锁
  2. 行锁

2、三锁

开销,加锁速度、死锁、粒锁、并发性能只能就具体应用的特点类说那种锁更合适。

2.1 表锁(偏读)

2.1.1 特点

偏向MyISAM储存引擎,开销小,加锁快;无死锁;锁定力度大,发生锁冲突的概率最高,并发度最低;

2.1.2 案例分析
2.1.2.1 建表SQL
create table mylock (
id int not null primary key auto_increment,
name varchar(20) default ''
) engine myisam;

insert into mylock(name) values('a');
insert into mylock(name) values('b');
insert into mylock(name) values('c');
insert into mylock(name) values('d');
insert into mylock(name) values('e');

select * from mylock;

查看锁

show open tables;

给book上写锁,给mylock上读锁

lock table mylock read,book write;
2.1.2.2加读锁

读锁可以共享

为一个表添加一个读锁

lock table staffs read;

添加一个读锁的时候,在终端一可以查询出当前表的信息,在终端二也可以查询去当前表的信息;

如果在终端一查询另一个表的时候,那么就不能查询出来;修改也是修改不了

但是在终端二,修改表的时候,直接成为阻塞状态,那么当在终端一释放这个表的锁,那么修改语句执行成功。

终端一终端二
获得staffs的read锁[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H8h4fest-1613796695920)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120163956974.png)]连接终端
当前终端可以查询出来当前表的信息[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kAf9oNvk-1613796695921)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120164021837.png)]其他终端也可以查询出这个表的信息[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0HFE1uvk-1613796695922)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120164045502.png)]
当前终端不能查询出来其他没有锁定的表的信息[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oSVWmu6i-1613796695924)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120164113641.png)]其他终端可以查询出其他没有锁定的表的信息[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n2EVj2vv-1613796695925)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120164142367.png)]
当前终端插入或者更新锁定的表都会提示错误[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sNKbFUmB-1613796695926)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120164302709.png)]其他终端插入或者更新表会一直等待获得锁[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JwZxuft7-1613796695928)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120164330086.png)]
释放锁[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y7x5BjlB-1613796695929)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120164436983.png)]终端二获得锁,更新数据[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AzuGxPmk-1613796695929)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120164454069.png)]
2.1.2.3加写锁
终端一终端二
终端一添加写锁 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8NhSIl5-1613796695930)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120165535288.png)]连接终端二
当前终端对锁定的表查询+更新+插入操作都可以执行[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5UdnFLDE-1613796695931)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120165804120.png)]当session对锁定表的查询被阻塞,需要等待锁被释放。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6GybsjnV-1613796695932)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120165921214.png)]如果可以,换成不同的id进行测试,因为mysql聪明有缓存,第二次的条件会从缓存中取得,影响锁的演示效果
释放锁[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dF2jHIA3-1613796695932)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120170143688.png)]查询出结果[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NUpWW6eP-1613796695933)(D:\File\学习文件\Typora文档\MySQL高级数据库优化\MySQL高级—数据库优化.assets\image-20210120170200848.png)]
2.1.3 案例结论

简而言之

就是读锁会阻塞下,但是不会堵塞读

而写锁会把读和写都堵塞

2.1.4 表锁分析

如何分析表锁定

可以通过检查 table_waited和Table_locks_immediate状态变量来分析系统上的表锁定

SQL

show status like 'table%'

在这里插入图片描述

这里有两个状态变量记录MySQL内标表记锁定的情况,两个变量的说明情况如下:

Table_locks_immediate:产生表级锁定的次数,表示可以立即获得锁的查询次数,魅力及获取锁值加1

table_locks_Waited:出现表记锁定争用而发生等待的次数(不能立即获取锁的次数,没等待一次锁值加1)此值高则说明存在着教严重的表级锁争用情况。

此外,Myisam的读写锁调度是写优先,这也是mysiam不适合做些为主表的引擎,因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远的阻塞

2.2 行锁(偏写)

2.2.1特点

偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发程度也最高

InnoDB与MyISAM的最大不同有两点;一时支持事务(TRANSACTION);二是采用了行级锁。

2.2.2复习 由于行锁支持事务-
2.2.2.1 事务(Transation),及其ACID属性

事务是由一组SQL语句组成的逻辑处理单元,事务具有以下四个属性,通常称为事务的ACID属性

  1. 原子性(Atomicity):事务是一个原子操作单位,其对数据的修改,要么全部执行,要么全都不执行。
  2. 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态,这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事物结束时,所有的内部数据结构也都必须是正确的。
  3. 隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作的影响的“独立”环境执行,这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
  4. 持久性(Durable):事务完成之后,他对数据的修改是永久性的,及时出现系统故障也能够保持。
2.2.2.2 并发事务处理带来的问题
  1. 更新丢失(Lost Update)

    ​ 当两个或这个多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事物不知道其他事务的存在,就会发生更新丢失问题-----最后的更新覆盖其他事务所做的更新。

    如果在一个程序完成并提交事务之前,另一个程序不能同时访问同一个文件,可以避免此问题。

  2. 脏读(Dirty Reads)

    ​ 一个事务正在对一-条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不-致状态;这时,另-一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做”脏读”。
    ​ 一 句话:事务A读取到了事务B已修改但尚未提交的的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取的数据无效,不符合一致性要求。

  3. 不可重复读(Non-REpeatablel Reads)

    ​ 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被刪除了!这种现象就叫做“不可重复读”。
    ​ 一句话:事务A读取到了事务B已经提交的修改数据,不符合隔离性

  4. 幻读(Phantom Reads)

    ​ 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读

    ​ 一句话:事务A读取到了事务B体提交的新增数据,不符合隔离性。
    ​ 多说一句:幻读和脏读有点类似,
    ​ 脏读是事务B里面修改了数据,
    ​ 幻读是事务B里面新增了数据。

2.2.2.3 事务隔离级别

在这里插入图片描述

2.2.3案例分析
2.2.3.1建表SQL

2.2.3.2行锁基本演示
  1. 把两边的事务提交全部关闭,改为 set autocommit = 0;手动提交
  2. 终端一更新一条数据,不手动提交 commit
  3. 终端二更新同一条数据,被阻塞,只能等待;
  4. 终端一提交更新,终端二阻塞解除,更新正常运行
  5. 当更新两条不同的数据的时候,两个更新互不干扰
2.2.3.3无索引表升级为表锁

当索引失效,会导致一个行锁升级到表锁;

即如果索引失效,事务未提交,那么就会导致另外一个终端对数据无法进行任何操作

2.2.3.4间隙锁的危害
2.2.3.5面试题:如何锁定一行
-- 第一步
begin;
-- 第二步
select * from test_innordb_lock where a = 8 for update;
-- select xxx... for update 锁定某一行之后,其他的操作会被阻塞,知道锁定行的会话提交commit;
-- 第三步
commit;

在这里插入图片描述

在这里插入图片描述

2.2.4案例结论

innodb存储引擎实现了行级锁定,虽然在锁定机制的实现方面所带来的的性能的损耗可能比表级锁定会要更高一些,但是在整体并发处理方面要远远优于MyISAM的表级锁定,当系统并发量较高的时候,Innordb的整体性能和MyISAM相比就会有比较明显的优势了;

但是Innordb的行级锁定也有其脆弱的一面,当我们使用不当的时候,可能会让Innordb的整体性能表现不仅不能比MyISAM高,甚至可能会更差。

2.2.5行锁分析
2.2.5.1如何分析行锁锁定

通过检查InnoDB_row_lock的状态变量来分析系统上的行锁的争夺情况

show status like 'innodb_row_lock%';

在这里插入图片描述

2.2.5.2对各个状态量的说明如下:
  • Innodb_row_lock_current_waits:当前正在等待锁定的数量;
  • Innodb_row_lock_time:从系统启动到现在锁定总时间长度;
  • Innodb_row_lock_time_avg:每次等待花费的平均时间
  • Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花费的时间
  • Innodb_row_lock_waits:系统启动后到现在总共等待的次数;

对于这五个状态变量:比较重要的是

Innodb_row_lock_time_avg(等待的平均时长)

Innodb_row_lock_waits(等待总次数)

Innodb_row_lock_time(等待总时长)这三项

尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划

2.2.6优化建议
  • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能较少检索条件,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度
  • 尽可能降低级别事务隔离

3 页锁

开销和枷锁时间介于表锁和行锁之间,会出现死锁;锁定颗粒度介于表锁和行锁之间,并发度一般。

了解即可

九、主从复制

1、复制原理

  1. slave 会从 master 读取 binlog 来进行数据同步

  2. 三步骤+原理图

在这里插入图片描述

  1. master 将改变记录到二进制日志(binary log)。 这些记录过程叫做二进制日志事件, binary log events;
  2. slave 将 master 的 binary log events 拷贝到它的中继日志(relay log);
  3. slave 重做中继日志中的事件, 将改变应用到自己的数据库中。 MySQL 复制是异步的且串行化的

2、复制原则

(1) 每个 slave 只有一个 master
(2) 每个 slave 只能有一个唯一的服务器 ID
(3) 每个 master 可以有多个 salve

3、复制问题

因为发生多次 IO, 存在延时问题

4、一主一从常见配置

1) mysql 版本一致且后台以服务运行

2) 主从都配置在[mysqld]结点下, 都是小写

3)主机修改 my.ini 配置文件

4)从机修改 my.ini 配置文件

​ 1、[可选]必须从服务器唯一ID

​ 2、[可选]启用二进制文件

5)因为修改过配置文件,所以两个配置文件都需要进行重新启动mysql服务;

6)主机从机都需要关闭防火墙

​ 1)window手动关闭

​ 2)虚拟机关闭防火墙 Service iptables stop

7)在window主机上建立账户并授权save

8)在Lnux从机上配置需要复制的主机

9)主机新建库、新建表、insert记录,从主复制

10)如何停止从服务复制功能;

_lock_time:从系统启动到现在锁定总时间长度;

  • Innodb_row_lock_time_avg:每次等待花费的平均时间
  • Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花费的时间
  • Innodb_row_lock_waits:系统启动后到现在总共等待的次数;

对于这五个状态变量:比较重要的是

Innodb_row_lock_time_avg(等待的平均时长)

Innodb_row_lock_waits(等待总次数)

Innodb_row_lock_time(等待总时长)这三项

尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划

2.2.6优化建议
  • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能较少检索条件,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度
  • 尽可能降低级别事务隔离

3 页锁

开销和枷锁时间介于表锁和行锁之间,会出现死锁;锁定颗粒度介于表锁和行锁之间,并发度一般。

了解即可

九、主从复制

1、复制原理

  1. slave 会从 master 读取 binlog 来进行数据同步

  2. 三步骤+原理图

    [外链图片转存中…(img-hLYdi7AT-1613796694720)]

    1. master 将改变记录到二进制日志(binary log)。 这些记录过程叫做二进制日志事件, binary log events;
    2. slave 将 master 的 binary log events 拷贝到它的中继日志(relay log);
    3. slave 重做中继日志中的事件, 将改变应用到自己的数据库中。 MySQL 复制是异步的且串行化的

2、复制原则

(1) 每个 slave 只有一个 master
(2) 每个 slave 只能有一个唯一的服务器 ID
(3) 每个 master 可以有多个 salve

3、复制问题

因为发生多次 IO, 存在延时问题

4、一主一从常见配置

1) mysql 版本一致且后台以服务运行

2) 主从都配置在[mysqld]结点下, 都是小写

3)主机修改 my.ini 配置文件

4)从机修改 my.ini 配置文件

​ 1、[可选]必须从服务器唯一ID

​ 2、[可选]启用二进制文件

5)因为修改过配置文件,所以两个配置文件都需要进行重新启动mysql服务;

6)主机从机都需要关闭防火墙

​ 1)window手动关闭

​ 2)虚拟机关闭防火墙 Service iptables stop

7)在window主机上建立账户并授权save

8)在Lnux从机上配置需要复制的主机

9)主机新建库、新建表、insert记录,从主复制

10)如何停止从服务复制功能;

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码不能跑我能跑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值