高性能MySQL -- 查询性能优化
一般来说一个好的程序:查询优化,索引优化,库表结构要同时进行优化。今天我们来讲一下查询优化。
我们需要对MySQL的架构有基本认知,所以这里贴一张图大家看看:
图片来自于《小林coding》
为什么从查询会慢?
查询的生命周期大概可以按照如下顺序来看:从客户端到服务器,然后在服务器上进行语法解析,生成执行计划,执行,并给客户端返回结果。执行是整个生命周期中最重要的一个阶段,其中包括了大量为了检索数据对存储引擎的调用以及调用后的数据处理,包括排序,分组等。在这些过程中有很多地方需要消耗大量时间,例如:网络,CPU计算,生成统计信息和执行计划,锁等待(互斥等待)。因为查询可能会很慢。
慢查询基础:优化数据访问
我们需要注意两个点:
- 确认应用程序是否在检索大量且不必要的数据。这通常意味着访问了太多的行,但有时候也可能是访问了太多的列。
- 确认MySQL服务器层是否在分析大量不需要的数据行。
是否向数据库请求了不需要的数据
以下是一些典型案例:
- 查询了不需要的数据,没有加limit,但是前端一个分页只能显示一部分,而你把数据全部查了给前端了。
- 多表联接时返回全部列,但是实际上只需要返回你需要的列就可以了,没有必要的全部。
- 总是取出全部的列,每次当你使用
SELECT*
的时候,你都必须思考清楚你是不是真的需要所有列的数据。但是取出全部的列在有的时候也并不是一件坏事,我们可能有缓存机制,取出全部的列可能会对缓存机制有好处,但是你必须知道这样做的代价是什么。 - 查复查询相同的数据:遇到常用的数据我们一定要用缓存存起来。
MySQL是否在扫描额外的记录
在MySQL中有3个最简单的衡量查询开销的指标:
- 响应时间
- 扫描的行数
- 返回的行数
这三个指标会被记录到MySQL的慢日志中。
响应时间
响应时间 = 服务时间 + 排队时间
服务时间是真正执行查询使用了多少时间,排队时间是服务器等待某些资源而没有真正执行查询的时间 – 可能是等待I/O操作完成。
扫描的行数和返回的行数
扫描的行数和返回的行数大多数情况是相同的,但是在做一个联接查询的时候,服务器必须要扫描多行才能生成结果集中的一行。
扫描的行数和访问类型
我们在评估查询开销的时候,有一个语句是非常好的,这个语句叫做EXPLAIN
,EXPLAIN
语句中的type列反应了访问类型。访问类型有很多种
- 全表扫描
- 索引扫描
- 范围扫描
- 唯一索引查询
- 常数引用等
这里列出的速度是从慢到快。
我们来把这些查询类型详细的讲解一下,因为这个很重要:
1. ALL
全表扫描(Full Table Scan), MySQL将遍历全表以找到匹配的行.
mysql> explain select * from film where rating ='G';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | film | NULL | ALL | NULL | NULL | NULL | NULL | 1000 | 20.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
film 表中 rating 字段没有索引.
2. index
全索引扫描(Full Index Scan), index 与 ALL 区别为 index 类型只遍历索引树. MYSQL 遍历整个索引来查找匹配的行.
mysql> explain select title from film;
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| 1 | SIMPLE | film | NULL | index | NULL | idx_title | 514 | NULL | 1000 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
虽然 where 条件中没有用到索引, 但是要取出的列 title 是索引包含的列, 所以只要全扫描 title 索引即可, 直接使用索引树查找数据.
3. range
索引范围扫描, 常见于 ‘<’, ‘<=’, ‘>’, ‘>=’, ‘between’ 等操作符.
mysql> explain select * from film where film_id > 100;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | film | NULL | range | PRIMARY | PRIMARY | 2 | NULL | 900 | 100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
因为 film_id 是索引, 所以只要查找索引的某个范围即可, 通过索引找到具体的数据.
4. ref
使用非唯一性索引或者唯一索引的前缀扫描, 返回匹配某个单独值的记录行.
mysql> explain select * from payment where customer_id = 10;
+----+-------------+---------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | payment | NULL | ref | idx_fk_customer_id | idx_fk_customer_id | 2 | const | 25 | 100.00 | NULL |
+----+-------------+---------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
customer_id 在 payment 表中是非唯一性索引
5. eq_ref
类似ref, 区别就在使用的索引是唯一索引. 在联表查询中使用 primary key 或者 unique key 作为关联条件.
mysql> explain select * from film a left join film_text b on a.film_id = b.film_id;
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+-------------+
| 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 1000 | 100.00 | NULL |
| 1 | SIMPLE | b | NULL | eq_ref | PRIMARY | PRIMARY | 2 | sakila.a.film_id | 1 | 100.00 | Using where |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+-------------+
6. const/system
当 MySQL 对查询某部分进行优化, 并转换为一个常量时, 使用这些类型访问. 如将主键置于 where 列表中, MySQL 就能将该查询转换为一个常量, system 是 const 类型的特例, 当查询的表只有一行的情况下使用 system.
mysql> explain select * from film where film_id = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | film | NULL | const | PRIMARY | PRIMARY | 2 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
7. NULL
MySQL 不用访问表或者索引就直接能到结果.
mysql> explain select 1 from dual where 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
dual是一个虚拟的表, 可以直接忽略.
MySQL有三种方式使用WHERE
:
- 在索引中使用
WHERE
条件来过滤不匹配的记录。这是在存储引擎层完成的。 - 使用索引覆盖,直接从索引中过滤不需要的记录并返回命中结果,这个过程不需要进行回表查询。
- 从数据表中返回数据,然后过滤不满足条件的记录。这是在MySQL服务器层完成的。
如果我们发现查询时需要扫描大量数据但只返回少量数行,那么通常可以尝试下面的技巧去优化它。
- 使用索引覆盖扫描
- 改变库表结构
- 重写复杂查询
重构查询的方法
切分查询
有的时候对于一个大查询,我们需要分而治之,将大查询切分成小查询,每个查询的功能完全一样,只完成一小部分,每次只返回一小部分查询结果。
例如删除旧数据。定期删除大量数据的时候,如果用一个大的语句一次性完成的话,则可能需要一次锁住很多数据,占慢整个事务日志,耗尽系统资源,阻塞很多小但是重要的查询。例如这个:
DELETE FROM messages WHERE created < DATE_SUB(NOW(), INTERVAL 3 MONTH)
可以优化成这个样子:
rows_affected = 0;
do {rows_affected = do_query("DELETE FROM messages WHERE created < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000");// 每次操作完成之后睡一会儿sleep(10000);
} while (row_affected > 0);
分解联接查询
对于联接查询,可以对每一张表进行一次单表查询,然后将结果在应用程序中进行联接。例如下面这个查询:
SELECT * FROM tag JOIN tag_post ON tag_post.tag_id = tag.id JOIN post ON tag_post.post_id = post.id WHERE tag.tag = 'mysql';
可以分解成下面这些查询:
SELECT * FROM tag WHERE = 'mysql';
SELECT * FROM tag_post WHERE tag_id = 1234;
SELECT * FROM post WHERE post.id in (123, 465, 78);
为什么要这样做呢?
- 让缓存效率更高。许多应用程序可以方便的缓存单表查询的结果对象。例如上面查询的tag mysql已经被缓存了,那么应用就可以跳过第一个查询。
- 在查询分解后,执行单个查询可以减少锁的竞争。
- 在应用层做联接可以更容易对数据库进行拆分,更容易做到高性能和可拓展。
- 可以减少冗余记录的访问。在应用层做联接查询,意味着对于某条记录应用只需要查询一次,而在数据库中做联接查询则可能需要重复的访问一部分数据。从这点看,这样的重构可能会减少网络和内存的消耗。
- 查询本身的效率可能会提升。在这个例子中,使用IN()代替联接查询,可以让MySQL按照ID顺序进行查询,这可能比随机的联接要更高效。
查询执行的基础
MySQL的客户端/服务器通信协议
MySQL的客户端和服务器之间的通信协议是半双工的,这意味着在同一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时发生。这样带来的问题就是无法进行流量控制。这里有一个小细节:
多数连接MySQL的库函数都可以获得全部结果集并将结果缓存到内存里,还可以逐行获取需要的数据。默认一般是获得全部结果集并将他们缓存到内存中。MySQL需要等所有的数据全部发送完毕才能释放这条查询所占用的资源。
查询状态
Sleep
线程正在等待客户端发送新的请求。
Query
线程正在执行查询或者正在将结果发送给客户端。
Locked
在MySQL服务器层,该线程正在等待表锁。在存储引擎级别实现的锁,例如InnoDB的行锁,并不会体现在线程状态中。对于MyISAM来说这是一个比较典型的状态,但在其他没有行锁的引擎中也经常会出现。
Analyzing and statistics
线程正在收集存储引擎的统计信息,并生成查询的执行计划。
Copying to tmp table [on disk]
线程正在执行查询,并且将其结果集都复制到一个临时表中,这种状态一般要么是在做GROUP BY操作,要么是文件排序操作,或者是UNION操作。如果这个状态后面还有“on disk”标记,那表示MySQL正在将一个内存临时表放到磁盘上。
Sorting result
线程正在对结果集进行排序。
Sending data
这表示多种情况:线程可能在多个状态之间传送数据,或者在生成结果集,或者在向客户端返回数据。
语法解析器和预处理
MySQL通过关键字将SQL语句进行解析,并生成一颗对应的解析树。这里会检测语法错误,是否使用了错误的关键字,关键字的顺序是否正确,引号是否能够前后匹配等。
然后预处理器检查生成的解析树,检查数据表和数据列是否存在,还是解析名字和别名看看是否有歧义等
下一步预处理器会验证权限。
查询优化器
到这里为止解析树是已经完全合法的了,优化器会将其转换成查询执行计划。一条查询可以有很多种执行方式,最后都返回相同的结果。优化器的作用就是找到最好的执行计划。
MySQL使用基于成本的优化器。它将尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。最初成本的最小单位是随机读取一个4KB数据页的成本,后来引入了一些因子来进行估算。巨复杂!!!虽然查询优化是可能有问题的,但是读者不要为了这个可能产生的问题费尽心思,因为您不一定可以比查询优化器做的更好了。
优化策略可以分成两种:
- 静态优化
- 动态优化
静态优化在第一次完成之后就一直有效,即使使用不同的参数重复执行查询页不会发生变化,可以认为是一种编译时优化。而动态优化跟上下文有关,可以理解为运行时优化。
下面是MySQL能够处理的一些优化类型:
-
重新定义联接表的顺序
-
将外联接转换成内联接
-
使用代数等价变换规则。例如:
(5 = 5 AND b = c) AND a = 5 可以改写成 a > 5
就是类似这样的优化。
-
优化COUNT(), MIN(), MAX()。例如:我想要找某一列的最小值,只需要查询对应B-Tree索引最左端的记录,MySQL可以直接获取第一行记录,最大值就可以直接获取最后一行记录。
-
预估并转换为常数表达式。
-
覆盖索引优化
-
子查询优化
-
提取终止查询。当遇到LIMIT子句的时候,MySQL会自动中止等
-
等值传播。如果两列的值可以通过等式联接,那么MySQL能够把其中一列的WHERE条件
-
列表IN()的比较。列表IN()的比较。在很多数据库系统中,IN()完全等同于多个OR条件的子句,因为这两者是完全等价的。在MySQL中这点是不成立的,MySQL将IN()列表中的数据先进行排序,然后通过二分查找的方式来确定列表中的值是否满足条件,这是一个O(log n)复杂度的操作,等价地转换成OR查询的复杂度为O(n),对于IN()列表中有大量取值的时候,MySQL的处理速度将会更快。
表和索引的统计信息
服务器层有查询优化器,但是数据和索引的统计信息是在存储引擎层实现的。因此MySQL查询执行计划的时候,需要向存储引擎层获取响应的统计信息。常见的统计信息有:
- 每个表或者索引有多少个页面
- 每个表的每个索引的基数是多少,数据行和索引的长度是多少
- 索引的分布信息等
MySQL如何执行联接查询
MySQL认为每一个查询都是联接 – 不仅仅是匹配两张表中对应的查询,而是每一个查询,每一个片段(包括子查询,甚至基于单表的SELECT)都是联接。
对于UNION查询,MySQL先将一系列的单个查询结果放到一个临时表中,然后重新读出临时表中的数据来完成UNION查询。在MySQL概念中,每个查询都是一次联接,所以读取临时表的结果也是一次联接。
MySQL的联接执行策略是:MySQL对任何联接都执行嵌套循环连接操作,即MySQL先在一个表中循环读取出单条数据,然后再嵌套循环到下一个表中寻找匹配的行。依次下去,直到找到所有表中匹配的行为止。最后根据各个表匹配的行,返回查询中需要的各列。
执行计划
我们用一张图来表示MySQL的联接查询执行计划:
联接查询优化器
联接优化器会尝试在所有的联接顺序中选择一个成本最低的来生成执行计划树。如果可能,优化器会遍历每一个表,然后逐个做嵌套循环,计算执行每一棵可能的计划树的成本,最后返回一个最优的执行计划。但是这个代价可能有点大,所以优化器选择使用贪婪搜索的方式查找最优的联接顺序。当需要联接的表超过optimizer_search_depth
的限制的时候,就会选择贪婪搜索模式了。
排序优化
排序是一个成本很高的操作,所以从性能角度考虑,应尽可能避免排序或者尽可能避免对大量数据进行排序。当不能使用索引生成排序结果的时候,MySQL需要自己排序,如果数据量小的话在内存中进行,如果数据量大则需要使用磁盘。如果需要排序的数据量小于排序缓冲区,MySQL使用内存进行快速排序操作。如果内存不够排序,那么MySQL会先将数据分块,对每个独立的块使用快速排序进行排序,并将各个块的排序结果存放在磁盘上,然后将各个排序好的块进行合并。
查询执行引擎
调用存储引擎实现的接口来完成,这些接口就是我们说的handler API的接口。查询中的每一个表都由一个handler的实例表示。如果一个表在查询中出现了三次,服务器就会创建三个handler对象,handler里面存储了表的所有列名,索引统计信息等等。
将结果返回给客户端
MySQL将结果集返回客户端是一个增量、逐步返回的过程。例如,对于关联操作,一旦服务器处理完最后一个关联表,开始生成第一条结果时,MySQL就可以开始向客户端逐步返回结果集了。
MySQL查询优化器的局限性
等值传递
有些时候,等值传递会带来一些意想不到的额外消耗。例如一列上的巨大IN()列表,优化器知道它将等于其他表中的一些列,这是由于WHERE, ON 或者 USING子句使列彼此相等。
优化器通过将列表复制到所有相关表中的相应列来“共享”列表。通过因为各个表新增了过滤条件,所以优化器可以高效地从存储引擎过滤记录。但是如果这个列表非常大,则会导致优化和执行都会变慢。
并行执行
MySQL无法利用多核特定来并行执行查询。
在同一个表中查询和更新
MySQL不允许对一张表同时进行查询和更新。
优化特定类型的查询
优化COUNT()查询
COUNT()有两个不同的作用:
- 统计某个列值的数量,即统计某列值不为NULL的个数。
- 统计行数。
当使用COUNT(*)时,统计的是行数,它会忽略所有的列而直接统计所有的行数。而在括号中指定了一个列的话,则统计的是这个列上值不为NULL的个数。
可以考虑使用索引覆盖扫描或增加汇总表对COUNT()进行优化。
优化LIMIT和OFFSET子句
处理分页会使用到LIMIT,当翻页到非常靠后的页面的时候,偏移量会非常大,这时LIMIT的效率会非常差。例如对于***LIMIT 10000,20***这样的查询,MySql需要查询10020条记录,将前面10000条记录抛弃,只返回最后的20条。这样的代价非常高,如果所有的页面被访问的频率都相同,那么这样的查询平均需要访问半个表的数据。
优化此类分页查询的一个最简单的办法就是尽可能地使用索引覆盖扫描,而不是查询所有的列。然后根据需要与原表做一次关联操作返回所需的列。对于偏移量很大的时候,这样的效率会提升非常大。考虑下面的查询:
SELECT film_id, description FROM sakila.film ORDER BY title LIMIT 50, 5;
如果这个表非常大,那么这个查询最好改写成下面的这样子:
SELECT film.film_id, film.description FROM sakila.film
INNER JOIN
(SELECT film_id FROM sakila.film ORDER BY title LIMIT 50,5) AS lim
USING(film_id);
注意优化中关联的子查询,因为只查询film_id一个列,数据量小,使得一个内存页可以容纳更多的数据,这让MySQL扫描尽可能少的页面。在获取到所需要的所有行之后再与原表进行关联以获得需要的全部列。
LIMIT的优化问题,其实是OFFSET的问题,它会导致MySql扫描大量不需要的行然后再抛弃掉。可以借助书签的思想记录上次取数据的位置,那么下次就可以直接从该书签记录的位置开始扫描,这样就避免了使用OFFSET。可以把主键当做书签使用,例如下面的查询:
SELECT * FROM sakila.rental ORDER BY rental_id DESC LIMIT 20;
假设上面的查询返回的是主键为16049到16030的租借记录,那么下一页查询就可以直接从16030这个点开始:
SELECT * FROM sakila.rental WHERE rental_id < 16030
ORDER BY rental_id DESC LIMIT 20;
该技术的好处是无论翻页到多么后面,其性能都会很好。
此外,也可以用关联到一个冗余表的方式提高LIMIT的性能,冗余表只包含主键列和需要做排序的数据列。
优化UNION查询
MySQL总是通过创建并填充临时表的方式来执行UNION查询,因此很多优化策略在UNION查询中都没法很好地使用。经常需要手工地将WHERE、LIMIT、ORDER BY等子句“下推”到UNION的各个子查询中,以便优化器可以充分利用这些条件进行优化(例如,直接将这些子句冗余地写一份到各个子查询)。
除非确实需要服务器消除重复的行,否则就一定要使用UNION ALL,这一点很重要。如果没有ALL关键字,MySQL会给临时表加上DISTINCT选项,这会导致对整个临时表的数据做唯一性检查。这样做的代价非常高。即使有ALL关键字,MySQL仍然会使用临时表存储结果。事实上,MySQL总是将结果放入临时表,然后再读出,再返回给客户端。
子查询优化
MySql的子查询实现的非常糟糕。最糟糕的一类查询是WHERE条件中包含IN()的子查询语句。
应该尽可能用关联替换子查询,可以提高查询效率。
排序优化
应该尽量让MySql使用索引进行排序。当不能使用索引生成排序结果的时候,MySql需要自己进行排序。如果数据量小于“排序缓冲区”的大小,则MySql使用内存进行“快速排序”操作。如果数据量太大超过“排序缓冲区”的大小,那么MySql只能采用文件排序,而文件排序的算法非常复杂,会消耗很多资源。
无论如何排序都是一个成本很高的操作,所以从性能角度考虑,应尽可能避免排序。所以让MySql根据索引构造排序结果非常的重要。
临时表
上面提到在MySql中,任何一个查询实质上都是一个关联查询。那么对于子查询或UNION查询是如何实现关联操作的呢。
对于UNION查询,MySql先将每一个单表查询结果放到一个临时表中,然后再重新读出临时表数据来完成UNION查询。MySql读取结果临时表和普通表一样,也是采用的关联方式。
当遇到子查询时,先执行子查询并将结果放到一个临时表中,然后再将这个临时表当做一个普通表对待。
MySql的临时表是没有任何索引的,在编写复杂的子查询和关联查询的时候需要注意这一点。
临时表也叫派生表。
用IN()取代OR
在MySql中,IN()先将自己列表中的数据进行排序,然后通过二分查找的方式确定列的值是否在IN()的列表中,这个时间复杂度是O(logn)。如果换成OR操作,则时间复杂度是O(n)。所以,对于IN()的列表中有大量取值的时候,用IN()替换OR操作将会更快。
优化MAX()和MIN()
在MySql中,IN()先将自己列表中的数据进行排序,然后通过二分查找的方式确定列的值是否在IN()的列表中,这个时间复杂度是O(logn)。如果换成OR操作,则时间复杂度是O(n)。所以,对于IN()的列表中有大量取值的时候,用IN()替换OR操作将会更快。
相关文章:

高性能MySQL -- 查询性能优化
一般来说一个好的程序:查询优化,索引优化,库表结构要同时进行优化。今天我们来讲一下查询优化。 我们需要对MySQL的架构有基本认知,所以这里贴一张图大家看看: 图片来自于《小林coding》 为什么从查询会慢࿱…...

Android Binder机制之一(简介)
目录 前言 一、Android 进程间通信方式 二、Binder架构图 三、Binder涉及角色 3.1 Binder驱动 3.2 Binder实体 3.3 Binder引用 3.4 远程服务 3.5 ServiceManager守护进程 四、涉及源码 前言 这是本人第N次看Binder 相关知识了,其实每次看都有新的收获&…...

《SOC芯片研究框架》深度科普,发展趋势、技术特点、产业链一文看懂
片上系统SoC(System on Chip),即在一块芯片上集成一整个信息处理系统,简单来说 SoC芯片是在中央处理器CPU的基础上扩展音视频功能和专用接口的超大规模集成电路,是智能设备的“大脑”。随着半导体工艺的发展࿰…...
WebRTC中的ICE
ICE简介 ICE是用于UDP媒体传输的NAT穿透协议(适当扩展也可以支持TCP),它需要利用STUN和TURN协议来完成工作。 STUN协议提供了获取一个内网地址对应的公网地址映射关系(NAT Binding)的机制,并且提供了它们…...

了解webpack
文章目录一、webpack是什么?二、为什么要使用webpack三、webpack的五个核心概念四、安装webpack提示:以下是本篇文章正文内容,下面案例可供参考 一、webpack是什么? 本质上,webpack 是一个用于现代 JavaScript 应用程…...

NoSQL数据库详细介绍
一、NoSQL发展历史 NoSQL 一词最早出现于 1998 年,是 Carlo Strozzi 开发的一个轻量、开源、不提供 SQL 功能的关系数据库。 2009 年,Last.fm 的 Johan Oskarsson 发起了一次关于分布式开源数据库的讨论,来自 Rackspace 的 Eric Evans 再次…...
【2023】华为OD机试真题Java-题目0210-优秀学员统计
优秀学员统计 题目描述 公司某部门软件教导团正在组织新员工每日打卡学习活动,他们开展这项学习活动已经一个月了,所以想统计下这个月优秀的打卡员工。 每个员工会对应一个id,每天的打卡记录记录当天打卡员工的id集合,一共30天。 请你实现代码帮助统计出打卡次数top5的员…...
2023备战金三银四,Python自动化软件测试面试宝典合集
马上就又到了程序员们躁动不安,蠢蠢欲动的季节~这不,金三银四已然到了家门口,元宵节一过后台就有不少人问我:现在外边大厂面试都问啥想去大厂又怕面试挂面试应该怎么准备测试开发前景如何面试,一个程序员成长之路永恒绕…...
2023年实体店做什么比较好赚钱?
2023年实体店做什么比较好赚钱?未来实体店真正能赚的模型是什么?#百收#狂潮老师#千行#干货分享#商业思维 2023年实体店做什么比较好赚钱?...

SpringSecurity前后端分离(一篇就够了)
SpringSecurity前后端分离 从上至下操作,直接上手SpringSecurity 文章目录SpringSecurity前后端分离1、项目环境maven依赖数据库表2、自定义UserService接口3、屏蔽Spring Security默认重定向登录页面以实现前后端分离功能1、实现登录成功/失败、登出处理逻辑1、表…...

Allegro如何用Label Tune功能自动调整丝印到器件中心
Allegro如何用Label Tune功能自动调整丝印到器件中心 在做PCB设计的时候,调整丝印是比较费时的工作,如果需要把整板的丝印位号调整到器件的中心做装配图使用,Allegro的Label Tune功能支持快速把丝印位号居中到器件中心。 以下图为例,快速把所有丝印位号居中 调整前 调整后…...

Linux(十)线程安全 上
目录 一、概念 二、互斥锁实现互斥 三、条件变量实现同步 银行家算法 生产者与消费者模型 一、概念 概念:在多线程程序中,如果涉及到了对共享资源的操作,则有可能会导致数据二义性,而线程安全就指的是,就算对共享…...

CRM系统能给企业带来什么? CRM系统推荐
什么是CRM系统? CRM系统(又称客户关系管理系统)是一个以客户为核心的管理软件,能有效改善企业与现有客户的关系,且帮助企业寻找新的潜在客户,并赢回以前老客户。 CRM系统能给企业带来什么? C…...

ESP32设备驱动-LED控制器生成PWM信号
LED控制器生成PWM信号 文章目录 LED控制器生成PWM信号1、LED控制器介绍2、软件准备3、硬件准备4、代码实现PWM 是一种在数字引脚上获取类似模拟信号的方法。PWM实际上是一个在高电平和低电平之间切换的方波信号,在 0V 和 3.3V 之间。 当信号为 HIGH 和 LOW 时,这种连续的 HIG…...

秒杀项目之网关服务限流熔断降级分布式事务
目录一、网关服务限流熔断降级二、Seata--分布式事务2.1 分布式事务基础2.1.1 事务2.1.2 本地事务2.1.3 分布式事务2.1.4 分布式事务场景2.2 分布式事务解决方案2.2.1 全局事务可靠消息服务2.2.2 最大努力通知2.2.3 TCC事事务三、Seata介绍四、 Seata实现分布式事务控制4.1 案例…...

OSS(Object Storage Service)进行上传图片,下载图片(详细看文档可以完成操作)
文章目录1.单体前后端项目上传1.上传流程2. BuckName 和EndPoint3. AccessKey 和Access Secret(创建RAM(Resource Access Manage)的子账号,然后可以获得Accesskey和Acess Secret)3.根据创建的子账号分配OSS的所有权限(可以对文件进行上传&…...

4年功能测试经验,裸辞后找不到工作怎么办?
软件测试四年,主要是手动测试(部分自动化测试和性能测试,但是用的是公司内部自动化工具,而且我自动化方面是弱项。) 现在裸辞三个月了,面试机会少而且面试屡屡受挫。总结就是自动化,性能&#…...

类和对象(中)(二)
类和对象(中)(二)1.赋值运算符重载1.1运算符重载1.2赋值运算符重载1.3前置和后置重载2.const成员3.取地址及const取地址操作符重载🌟🌟hello,各位读者大大们你们好呀🌟🌟…...

Hadoop自动安装JDK
目录 1、使用xftp工具 在opt目录下创建install和soft文件 2、使用xftp工具 将压缩包上传到install文件 3、编写shell脚本 3.1、创建目录来放shell脚本 3.2、创建autoinsatll.sh文件并修改权限 3.3、编写autoinsatll.sh 文件 4、 运行 5、测试 1、使用xftp工具 在opt目…...

Springboot+Vue java毕业论文选题管理系统
在分析并得出使用者对程序的功能要求时,就可以进行程序设计了。如图展示的就是管理员功能结构图。 系统实现前端技术:nodejsvueelementui 前端:HTML5,CSS3、JavaScript、VUE 系统分为不同的层次:视图层(vue页面&#…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...

Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...

倒装芯片凸点成型工艺
UBM(Under Bump Metallization)与Bump(焊球)形成工艺流程。我们可以将整张流程图分为三大阶段来理解: 🔧 一、UBM(Under Bump Metallization)工艺流程(黄色区域ÿ…...