MySQL索引以及优化解决方案

这篇具有很好参考价值的文章主要介绍了MySQL索引以及优化解决方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、数据库索引

1.1、索引介绍

1、索引介绍

“索引是帮助MySQL高效获取数据的数据结构”

例如字典里的目录,索引的目的就是为了让你能够快速查找数据

2、索引优势

  • 提高数据检索的效率,降低数据库的IO成本
  • 通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗

3、索引劣势

索引实际上也是一张表,保存了主键和索引的字段,并且指向实体表的记录,所以索引也是需要占用空间的。在索引大大提高查询速度的同时,却会降低表的更新速度,在对表进行数据增删改查的同时,MySQL不仅要更新数据,还要保存索引文件。

1.2、索引数据结构

1、与B+Tree相关的几种二叉树

  • 二叉树

每个节点至多只有两颗子树,左子树和右子树,次序不能颠倒。遍历是对树的一种最基本的运算,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次。有前序、中序、后序遍历。

  • 二叉查找树

在满足二叉树的条件下,左子树的节点值小于根的节点值,右子树的节点值大于根的节点值。

虽然可以排序,但是会出现极端情况,如当依次递增添加数据时,会一直在右边叠加。二叉搜索树甚至会变成顺序查找而不是二分查找。

  • 平衡二叉树 AVL树

符合二叉查找树的条件,必须满足任何节点的两个子树的高度之差的绝对值不超过1。平衡二叉树的查询速度很快,但是维护一颗平衡二叉树的代价非常大,通常来说,需要1次或多次左旋和右旋来得到插入、更新和删除后树的平衡性。同时,随着平衡二叉树的数据的增多,高度会越来越高,大概1000条数据就有9 - 10层,那也就是说可能找一个数据需要9 -10次 IO。

  • B-tree和B+tree

为了解决平衡二叉树高度过高导致的io问题,在每一个节点上多放些元素,从而降低平衡二叉树的高度,减少磁盘io。

B-tree :

多路搜索树,一颗m阶的B-Tree有这样的特性:每个节点最多有m个孩子,若根节点不是叶子节点,则至少有两个孩子。除了根节点和叶子节点外,其他每个节点至少有Ceil(m/2)个孩子(ceil向上取整)。 B-tree 关键字(索引)和数据(除索引外的其他列数据)是放在一起的,没有存储冗余关键字(索引),同时通过指针指向孩子节点,关键字左边的孩子节点都比关键字小,关键字右边的孩子节点都比关键字大。

B-tree 通过多路搜索的方式大大的降低了树的高度,大大减少了查找一个数据的磁盘 IO,比如我要查找6这个元素的信息,只需要3次磁盘 IO 就能找到想要的数据。

B+tree(MySQL的索引底层数据结构):

B+树是B-树的变体,也是一种多路搜索树,其定义基本与 B-tree 相同,存在以下几点不同之处:

非叶子结点的子树指针与关键字个数相同非叶子节点只存储关键字信息(即非叶子节点只存储索引,不存储除索引外的其他字段信息)。所有叶子节点之间都有指针相连,指向下一个叶子结点。

看到 B+tree 对比 B-tree 所有数据都存储在叶子节点中,非叶子结点只存储冗余索引,同时叶子结点之间使用双向循环链表链接

为什么MySQL使用B+树而不是B-树?

进一步降低树的高度,非叶子结点可以存储更多的索引,叶子节点间双向循环链表提高区间访问的性能,方便范围查找数据

2、Hash表

为什么MySQL大部分情况下使用的是B+树而不使用hash表?

因为hash表是对索引的 key 进行一次 Hash 计算就可以定位出数据存储的位置。仅能满足 “=” 和 “IN”,不支持范围查询。同时数据库大数据存储时容易产生Hash冲突,所以通常使用B+tree

3、hash索引和B+树索引的使用范围

  • 关系型数据库中,索引大多采用B+树来作为存储结构,而全文搜索引擎的索引则主要采用hash的存储结构
  • 等值查询的情况,hash索引有绝对优势,只需要经过一次算法即可找到响应的键值(键值唯一)。
  • 对于范围查询,不能用hash索引,hash索引仅满足“=”、“in”查询;
  • hash索引无法被用来进行数据的排序操作
  • 对于组合索引,hash索引也无法利用

4、 索引数据结构

  • Btree

会将(key,value)封装成节点,然后插在二叉树里。key一般是这条数据的值,value是这个值所在的物理地址。

如果没有索引,要查找一条数据,需要一条一条的遍历所有数据;而有了索引后,只需要不断的和根节点的key比较,判断在左节点还是有节点,会比之前那样遍历效率高很多

二叉树数据结构的弊端:当极端情况下,数据递增插入时,会一直向右插入,形成链表,查询效率会降低

  • MySQL常用的索引数据结构有BTree索引(Myisam普通索引),B+Tree索引(Innodb普通索引),Hash索引(memory存储索引)等

1.3、使用场景

5、索引使用场景

哪些情况需要创建索引?

  • 主键自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引(where后面的语句)
  • 查询中与其他表关联的字段,外键关系建立索引
  • 多字段查询下倾向创建组合索引
  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
  • 查询中统计或者分组字段

哪些情况不推荐创建索引?

  • 表记录太少没必要
  • 经常增删改的表,因为建立索引会降低更新表的速度
  • where条件里用不到的字段不建立索引

6、索引分类

  • 主键索引

表中的列设定为主键后,数据库会自动建立主键索引;

单独创建和删除主键索引语法:

创建: alter table 表名 add primary key(字段);

删除:alter table 表名 drop primary key;

  • 唯一索引

表中的列创建了唯一约束时,数据库会自动建立唯一索引

单独创建和删除唯一索引语法:

创建:alter table 表名 add unique 索引名(字段);

删除:drop index 索引名 on 表名;

  • 单值索引

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

建表时可随表一起建立单值索引

单独创建和删除:

创建:alter table 表名 add index 索引名(字段);

删除:drop index 索引名 on 表名

  • 复合索引

即一个索引包含多个列

建表时可随表一起建立复合索引

单独创建和删除复合索引:

创建:create index 索引名 on 表名(字段1,字段2);

删除:drop index 索引名 on 表名

#随表建立索引,主键索引,唯一索引,单值索引,复合索引
create table customer(
 id int(10),
 customer_no varchar(20),
 customer_name Varchar(20),
 primary key(id),
 unique idx_customer_no(customer_no),
 key idx_customer_name(customer_name),
 key idex_customer_no_name(customer_no,customer_name)
 

)

#创建主键索引
alter table customer add primary key(id);
#删除
alter Table customer drop primary key;

#创建唯一索引
alter table customer add unique idx_customer_no(customer_no);
drop index idx_customer_no on customer;

测试发现,建立索引和没建立索引,查询速度显著提升。

7、Explain

使用Explain关键字可以模拟优化器执行SQL语句。 explain+SQL语句

重要字段名

  • id

select查询的序列号,表示查询中执行select子句或操作表的顺序

id相同时,执行顺序由上向下

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

id相同和不同都存在时,id相同的可以理解为一组,从上往下顺序执行,所有组中,id值越高优先执行

  • select_type

常见的值有

SIMPLE:简单的select查询,查询中不包含子查询或者UNION

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

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

SUBQUERY:在SELECT或WHERE列表中包含了子查询

  • type

从最好到最差: system>const>eq_ref>ref>range>index>all 。一般来说,最好保证查询能达到range级别,最好能达到ref

System:表只有一行列表,系统表。这是const类型的特例,平时不会出现

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

eq_ref:唯一性索引扫描,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。联表查询

ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质也是一种索引访问。

range:只检索给定范围的行,使用一个索引来选择行。between,<,>,in 等范围扫描索引。不用扫描全部索引,比all好

index:与all区别是index类型只遍历索引树,比all’快。都是读全表,但是index从索引中读取,all从硬盘读

all:遍历全表

  • prossible_keys

显示可能应用在这张表的索引。(理论上)

  • key

查询中实际使用的索引,为null则没使用索引

  • ref

显示索引的哪一列被使用了,哪些列或常量被用于查找索引列上的值

  • rows

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

  • extra

一些常见的重要的额外信息

1.4、索引优化

8、查询优化

8.1、索引失效

  • 要遵循最佳左前缀法则:如果索引了多列,要遵循最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列
  • 不能在索引列上做任何计算、函数等操作,否则会导致索引失效而转向全表扫描。
  • 不能使用索引中范围条件右边的列
  • 使用不等于时无法使用索引
  • is null 可以使用索引,但是is not null 无法使用索引
  • like以通配符开头会使索引失效导致全表扫描
  • 字符串不加单引号索引会失效
  • 使用or连接时索引失效,使用and会自动调整顺序为最左前列
create table students(
	id int primary key AUTO_INCREMENT comment "主键id",
	sname varchar (24)comment "学生姓名",
	age int comment "年龄",
	score int comment "分数",
	time timestamp comment "入学时间"
	);
	
	INSERT INTO students(sname,age,score,time) VALUES ("小明",22,89,now());
	INSERT INTO students(sname,age,score,time) VALUES ("小红",22,89,now());
	INSERT INTO students(sname,age,score,time) VALUES ("小绿",22,89,now());
	INSERT INTO students(sname,age,score,time) VALUES ("小黑",22,89,now());
	
	-- 加复合索引
	ALTER table students add index idx_sname_age_score(sname,age,score);
	
	-- 索引失效情况
EXPLAIN	SELECT * FROM students where sname="小明"AND age = 22 AND score = 89;
-- 索引遵循最左前缀法则,指的是查询从索引的最左前列开始并且不能跳过中间的列。下面的例子,没有从最左列sname开始,所以结果并没有应用上索引,是all,索引失效

-- 不从最左边索引开始,索引失效
EXPLAIN	SELECT * FROM students where  age = 22 AND score = 89;
-- 跳过中间索引,虽然用上了索引,但是key_len低,效率低。使用到了sname,失效score
EXPLAIN	SELECT * FROM students where  sname="小明" AND score = 89;
	
	-- 在索引上加函数处理,索引失效
	EXPLAIN SELECT * from students WHERE LEFT(sname,2) = "小明";
	
	-- 使用索引中范围条件右边的列导致索引失效,age后面的索引才会失效
	EXPLAIN SELECT * from students WHERE sname="小明" AND age >20;
	
	-- 不等于,索引失效,导致全表扫描
	EXPLAIN SELECT * FROM students WHERE sname !="小明";
	-- is not null 索引失效 
	explain select * from students where sname is null;
	explain select * from students where sname is not null;
	
	-- like以通配符开头,导致索引失效,全盘扫描。但是如果是“明%”这种通配符在后面的是可以使用索引的
	explain select * from students where sname like "%明";
	
	-- 字符串不加单引号会导致索引失效
	explain select * from students where sname = 123;

建议:

  • 对于单值索引,尽量选择针对当前查询字段过滤性(检索频率跟高)更好的索引;
  • 对于组合索引,当前where查询中过滤性更好的字段在索引字段顺序中位置越靠前越好
  • 对于组合索引,尽量选择能够包含在当前查询中 where字句中更多字段的索引;
  • 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的

8.2、索引优化

排序优化

  • 尽量避免使用 Using FileSort方式排序
  • order by 语句能使用索引最左前缀或使用where子句与order by 字句条件组合满足索引最左前列
  • where子句中如果出现索引范围查询会导致 order by 索引失效。对于排序来说,多个相等条件也是范围查询
  • 如果where使用索引的最左前缀定义为常量,则order by能使用索引

单表查询优化

-- 单表查询优化
CREATE TABLE IF NOT EXISTS article(
	id INT(10) PRIMARY KEY AUTO_INCREMENT,
	author_id INT(10) NOT NULL ,
	category_id INT (10) NOT NULL,
	views INT(10) NOT NULL,
	comments INT(10) NOT NULL,
	title VARBINARY(255)NOT NULL,
	content TEXT NOT NULL
	);
	
	INSERT INTO article (author_id,category_id,views,comments,title,content) VALUES 
	(1,1,1,1,'1','1'),
	(2,2,2,2,'2','2'),
	(1,1,3,3,'3','3');
	
	
	#1.查询category_id 为1的,且comments大于1 的情况下,views最多的id和author_id的信息
	explain select id,author_id
	from article
	where category_id = 1 and comments >1 ORDER BY views desc limit 1;
	
	#2.优化--建立索引
	alter table article add index idx_ccv(category_id,comments,views);
	#3.再次测试,因为comments是范围查询,所以order by后面的views索引失效,排序还是用的using filesort
		explain select id,author_id
	from article
	where category_id = 1 and comments >1 ORDER BY views desc limit 1;
	
	#4.再次优化,重新创建索引,可以把comments不加索引,只给category_id和views复合索引
	drop index idx_ccv on article;
	alter table article add index idx_cv(category_id,views);
	
# 再次测试  .用到了索引,type是ref,索引都生效了。
	explain select id,author_id
	from article
	where category_id = 1 and comments >1 ORDER BY views desc limit 1;

关联查询优化

内连接时,mysql会自动把小结果集(数据量小的)选为驱动表,所以大表的字段最好加上索引。左外连接时,左表会全表扫描(要得到左表的每一个数据),所以右边大表字段最好加上索引,同理右外连接也是一样。我们最好保证被驱动表上的字段建立了索引。

	create table if not EXISTS class(
	id int (10) AUTO_INCREMENT,
	card int (10),
	primary key (id)
	);
	
	create table if not EXISTS book(
	bookid int (10) AUTO_INCREMENT,
	card int (10),
	primary key (bookid)
	);
	
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO class(card) VALUES (FLOOR(1+(RAND()*20)));
	
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	INSERT INTO book(card) VALUES (FLOOR(1+(RAND()*20)));
	
	
	# 1.联表查询 
	explain select *
	from class 
	left join book
	on class.card = book.card;
	
	# 优化。左外连接(一般会把数据量小的放在连接表,也就是驱动表,数据量大的是被连接表,可以建立索引优化),左边表会全表扫描无法避免,可以给右表加上索引
	# 建立索引
	alter table book add index idx_card(card);
	
	#再测试。
		explain select *
	from class 
	left join book
	on class.card = book.card;

分组优化

create table students(
	id int primary key AUTO_INCREMENT comment "主键id",
	sname varchar (24)comment "学生姓名",
	age int comment "年龄",
	score int comment "分数",
	time timestamp comment "入学时间"
	);
	
	INSERT INTO students(sname,age,score,time) VALUES ("小明",22,100,now());
	INSERT INTO students(sname,age,score,time) VALUES ("小红",23,80,now());
	INSERT INTO students(sname,age,score,time) VALUES ("小绿",24,80,now());
	INSERT INTO students(sname,age,score,time) VALUES ("小黑",23,70,now());
	
	-- 分组优化
	-- 把学生表里所有的名字分组。using filesort
	explain select count(*),sname from students GROUP BY sname;
	-- 优化  using index
	alter table students add index idx_sas(sname,age,score);
		explain select count(*),sname from students GROUP BY sname;
		
		-- 范围查询使score索引失效,还是有usingfilesort
		explain select count(*),sname 
		from students 
		where sname="小明" and age > 22
		GROUP BY score;

9、慢查询日志文章来源地址https://www.toymoban.com/news/detail-481354.html

  • MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过 long_query_time值的SQL,则会被记录到慢查询日志中。可以由它来查看哪些SQL超出了我们最大忍耐时间值。
  • 注意:非调优场景下,一般不建议启动改参数,慢查询日志支持将日志记录写入文件,开启慢查询日志会带来一定的性能影响。
	-- 慢查询日志
		-- 查看是否开启
		show variables like '%slow_query_log%';
		-- 开启
		set global slow_query_log = 1;
		-- 设置时间 s
		set global long_query_time = 3;
		-- 查看设置的时间
		show variables like 'long_query_time%';

到了这里,关于MySQL索引以及优化解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 外部navicat无法连接mysql数据库的问题原因及解决方案

    问题起因是这样:在linux操作中的docker中部署了一个数据库,数据库启动之后,端口也映射了(创建容器时用 -p 30036:3306进行的映射),但是在外不想使用navicat连接时,怎么都连不上,本人遇到的问题如下 一、端口虽然映射了,但是服务器上的30036端口并未对外开放,因此要先开

    2024年02月07日
    浏览(56)
  • 忘记密码不用愁【linux下 MySQL数据库忘记密码解决方案】

    前言 : 在日常的开发中我们有可能忘记了我们自己设置的密码,譬如说Mysql数据库的密码,不过不要担心,小编整理了2个方法带给大家。 查看初始化密码进行登录: 查看mysql的初始密码 在root@localhost后面的就是mysql初始的密码,以上图为例 初始密码则为:ukehBfivW1 直接跳过

    2024年02月10日
    浏览(57)
  • MySql数据库5.7升级到8.1遇到的问题与解决方案

    Oracle MySql安全漏洞CVE-2023-22056等漏洞爆出来后,准备对系统的MYSQL数据库进行升级,由5.7升级到8.1,本文主要介绍下升级过程中的几个问题。 通过navicat导出数据库的结构和数据,以便升级后恢复到新版本。 下载地址:MySQL :: Download MySQL Community Server 因为系统服务是通过wamp提供

    2024年02月15日
    浏览(37)
  • 使用Docker构建的MySQL主从架构:高可用性数据库解决方案

    MySQL主从架构,我们已经在vmware虚拟机上实践过了,接下来我们一起探讨在docker中如何使用MySQL主从架构。 🏠个人主页:我是沐风晓月 🧑个人简介:大家好,我是沐风晓月,阿里云社区博客专家😉😉 💕 座右铭: 先努力成长自己,再帮助更多的人 ,一起加油进步🍺🍺🍺

    2024年02月08日
    浏览(89)
  • Unity连接数据库mysql全过程+可能遇到的问题与解决方案

    目录 一、具备条件 二、unity连接mysql 三、问题总结 1. Mysql安装完成         安装完成后需要查看mysql的版本,打开终端(管理者身份运行),输入以下语句登录mysql,查看MySQL版本; 可以看到我下载的版本是 5.7.38; 2. MySQL Connector/NET下载 目的:为了搭建unity连接mysql的环境

    2024年02月03日
    浏览(66)
  • MacBook Pro(M1芯片)安装mysql以及一些的问题解决方案

    设备芯片及系统版本 1 安装包下载  官方下载网址: MySQL https://www.mysql.com 第一步 进入官网后点击DOWNLOADS。 第二步 在页面向下找到如图,点击进入。 第三步 选择此项。   第四步  选择版本与下载(M1芯片选择arm64版本)。 2 安装过程   第一步 下载好安装包后双击打开   第

    2024年02月14日
    浏览(51)
  • Redis缓存设计与性能优化【缓存和数据库不一致问题,解决方案:1.加过期时间这样可以一段时间后自动刷新 2.分布式的读写锁】

    在大并发下,同时操作数据库与缓存会存在数据不一致性问题 1、双写不一致情况 2、读写并发不一致 解决方案: 1、对于并发几率很小的数据(如个人维度的订单数据、用户数据等),这种几乎不用考虑这个问题,很少会发生缓存不一致, 可以给缓存数据加上过期时间,每隔一

    2024年04月13日
    浏览(54)
  • MySQL(二)索引原理以及优化

    MySQL(一)基本架构、SQL语句操作、试图 MySQL(二)索引原理以及优化 MySQL(三)SQL优化、Buffer pool、Change buffer MySQL(四)事务原理及分析 MySQL(五)缓存策略 MySQL(六)主从复制 数据库三范式 MySQL数据库是用来保存海量数据的,但是海量数据涉及到一个快速查找问题,怎么

    2024年02月16日
    浏览(49)
  • Elasticsearch解决不能修改索引、字段问题解决方案

    问题1: 由于es索引不能删除,不能修改,在不影响原数据的情况下,并且生产服务不停机的情况下,怎么修改索引,并保留原索引内的数据? 基于kibanna的dev Tools执行参数,淘汰postman,kibanna会有提示 1、原来索引起别名job 2、重建索引,数据迁移,默认是同步执行大数据量太

    2024年02月12日
    浏览(45)
  • 【MySQL数据库 | 第十七篇】索引以及索引结构介绍

    目录 前言: 索引简介:  索引结构:           二叉树索引结构         Tree(普通二叉树)         B-Tree(多路平衡查找树)         B+Tree          哈希索引数据结构 总结: 在实际生活中,我们对SQL语句进行优化实际上有很大一部分都是对索引进行优化,因此对索引

    2024年02月09日
    浏览(76)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包