干货6500字全面字讲解 Redis 性能优化点!

Posted 侠梦的开发笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了干货6500字全面字讲解 Redis 性能优化点!相关的知识,希望对你有一定的参考价值。

可以用 MGET key1 key2 合并。这样在实际通讯中,请求数也减少了,延时自然得到好转。

如果不能用 multi-key 指令来合并,比如一个 SET,一个 GET 无法合并。怎么办?

Redis 中有至少这样两个方法能合并多个指令到一个 request 中,一个是 MULTI/EXEC,一个是 script。前者本来是构建 Redis 事务的方法,但确实可以合并多个指令为一个 request,它到通讯过程如下。至于 script,最好利用缓存脚本的 sha1 hash key 来调起脚本,这样通讯量更小。

这样确实更能减少网络传输时间,不是么?但如此以来,就必须要求这个 transaction / script 中涉及的 key 在同一个 node 上,所以要酌情考虑。

如果上面的方法我们都考虑过了,还是没有办法合并多个请求,我们还可以考虑合并多个 responses。比如把 2 个回复信息合并:

这样,理论上可以省去 1 次回复所用的网络传输时间。这就是 pipeline 做的事情。举个 ruby 客户端使用 pipeline 的例子:

require \'redis\'
@redis = Redis.new()
@redis.pipelined do
@redis.get \'key1\'
@redis.set \'key2\' \'some value\'
end
# => [1, 2]

据说,有些语言的客户端,甚至默认就使用 pipeline 来优化延时问题,比如 node_redis。

另外,不是任意多个回复信息都可以放进一个 TCP 包中,如果请求数太多,回复的数据很长(比如 get 一个长字符串),TCP 还是会分包传输,但使用 pipeline,依然可以减少传输次数。

pipeline 和上面的其他方法都不一样的是,它不具有原子性。所以在 cluster 状态下的集群上,实现 pipeline 比那些原子性的方法更有可能。

小结一下:

  1. 使用 unix 进程间通信,如果单机部署
  2. 使用 multi-key 指令合并多个指令,减少请求数,如果有可能的话
  3. 使用 transaction、script 合并 requests 以及 responses
  4. 使用 pipeline 合并 response
的查询挺快的,(在普通笔记本上)扫描 1 百万个 key,只需 40 毫秒(参见:https://redis.io/commands/keys),但几十 ms 对于一个性能要求很高的系统来说,已经不短了,更何况如果有几亿个 key(一台机器完全可能存几亿个 key,比如一个 key 100字节,1 亿个 key 只有 10GB),时间更长。

所以,尽量不要在生产环境的代码使用这些执行很慢的指令,这一点 Redis 的作者在来禁止使用这个耗时的指令。

除了这些耗时的指令,Redis 中 transaction,script,因为可以合并多个 commands 为一个具有原子性的执行过程,所以也可能占用 Redis 很长时间,需要注意。

如果你想找出生产环境使用的「慢指令」,那么可以利用 SLOWLOG GET count 来查看最近的 count 个执行时间很长的指令。至于多长算长,可以通过在 redis.conf 中设置 slowlog-log-slower-than 来定义。

除此之外,在很多地方都没有提到的一个可能的慢指令是 DEL,但 redis.conf 文件的访问。检查的方式是:

  1. 从中随机取出 20 个 keys
  2. 把过期的删掉。
  3. 如果刚刚 20 个 keys 中,有 25% 以上(也就是 5 个以上)都是过期的,Redis 认为,过期的 keys 还挺多的,继续重复步骤 1,直到满足退出条件:某次取出的 keys 中没有那么多过去的 keys。

这里对于性能的影响是,如果真的有很多的 keys 在同一时间过期,那么 Redis 真的会一直循环执行删除,占用主线程。

对此,Redis 作者的这个指令,因为它更容易产生 keys 同时过期的现象。我还见到过一些建议是给 keys 的过期时间设置一个随机波动量。最后,redis.conf 中也给出了一个方法,把 keys 的过期删除操作变为异步的,即,在 redis.conf 中设置 lazyfree-lazy-expire yes

的时间复杂度是 O(log(N)),这比其他数据类型增加一个新元素的操作更复杂,所以要小心使用。
  • 若 Hash 类型的值的 fields 数量有限,它很有可能采用 ziplist 这种结构做存储,而 ziplist 的查询效率可能没有同等字段数量的 hashtable 效率高,在必要时,可以调整 Redis 的存储结构。
  • 除了时间性能上的考虑,有时候我们还需要节省存储空间。比如上面提到的 ziplist 结构,就比 hashtable 结构节省存储空间(Redis Essentials 的作者分别在 hashtable 和 ziplist 结构的 Hash 中插入 500 个 fields,每个 field 和 value 都是一个 15 位左右的字符串,结果是 hashtable 结构使用的空间是 ziplist 的 4 倍。)。但节省空间的数据结构,其算法的复杂度可能很高。所以,这里就需要在具体问题面前做出权衡。欢迎关注公众号:朱小厮的博客,回复:1024,可以领取redis专属资料。

    如何做出更好的权衡?我觉得得深挖 Redis 的存储结构才能让自己安心。这方面的内容我们下次再说。

    以上这三点都是编程层面的考虑,写程序时应该注意啊。下面这几点,也会影响 Redis 的性能,但解决起来,就不只是靠代码层面的调整了,还需要架构和运维上的考虑。

    考虑操作系统和硬件是否影响性能

    Redis 运行的外部环境,也就是操作系统和硬件显然也会影响 Redis 的性能。在官方文档中,就给出了一些例子:

    1. CPU:Intel 多种 CPU 都比 AMD 皓龙系列好
    2. 虚拟化:实体机比虚拟机好,主要是因为部分虚拟机上,硬盘不是本地硬盘,监控软件导致 fork 指令的速度慢(持久化时会用到 fork),尤其是用 Xen 来做虚拟化时。
    3. 内存管理:在 linux 操作系统中,为了让 translation lookaside buffer,即 TLB,能够管理更多内存空间(TLB 只能缓存有限个 page),操作系统把一些 memory page 变得更大,比如 2MB 或者 1GB,而不是通常的 4096 字节,这些大的内存页叫做 huge pages。同时,为了方便程序员使用这些大的内存 page,操作系统中实现了一个 transparent huge pages(THP)机制,使得大内存页对他们来说是透明的,可以像使用正常的内存 page 一样使用他们。但这种机制并不是数据库所需要的,可能是因为 THP 会把内存空间变得紧凑而连续吧,就像mongodb 的文档[11]中明确说的,数据库需要的是稀疏的内存空间,所以请禁掉 THP 功能。Redis 也不例外,但 Redis 官方博客上给出的理由是:使用大内存 page 会使 bgsave 时,fork 的速度变慢;如果 fork 之后,这些内存 page 在原进程中被修改了,他们就需要被复制(即 copy on write),这样的复制会消耗大量的内存(毕竟,人家是 huge pages,复制一份消耗成本很大)。所以,请禁止掉操作系统中的 transparent huge pages 功能。
    4. 交换空间:当一些内存 page 被存储在交换空间文件上,而 Redis 又要请求那些数据,那么操作系统会阻塞 Redis 进程,然后把想要的 page,从交换空间中拿出来,放进内存。这其中涉及整个进程的阻塞,所以可能会造成延时问题,一个解决方法是禁止使用交换空间(Redis Essentials 中如是建议,如果内存空间不足,请用别的方法处理)。
    考虑持久化带来的开销

    Redis 的一项重要功能就是持久化,也就是把数据复制到硬盘上。基于持久化,才有了 Redis 的数据恢复等功能。

    但维护这个持久化的功能,也是有性能开销的。

    首先说,RDB 全量持久化。

    这种持久化方式把 Redis 中的全量数据打包成 rdb 文件放在硬盘上。但是执行 RDB 持久化过程的是原进程 fork 出来一个子进程,而 fork 这个系统调用是需要时间的,根据Redis Lab 6 年前做的实验[12],在一台新型的 AWS EC2 m1.small^13 上,fork 一个内存占用 1GB 的 Redis 进程,需要 700+ 毫秒,而这段时间,redis 是无法处理请求的。

    虽然现在的机器应该都会比那个时候好,但是 fork 的开销也应该考虑吧。为此,要使用合理的 RDB 持久化的时间间隔,不要太频繁

    接下来,我们看另外一种持久化方式:AOF 增量持久化。

    这种持久化方式会把你发到 redis server 的指令以文本的形式保存下来(格式遵循 redis protocol),这个过程中,会调用两个系统调用,一个是 write(2),同步完成,一个是 fsync(2),异步完成。

    这两部都可能是延时问题的原因:

    1. write 可能会因为输出的 buffer 满了,或者 kernal 正在把 buffer 中的数据同步到硬盘,就被阻塞了。
    2. fsync 的作用是确保 write 写入到 aof 文件的数据落到了硬盘上,在一个 7200 转/分的硬盘上可能要延时 20 毫秒左右,消耗还是挺大的。更重要的是,在 fsync 进行的时候,write 可能会被阻塞。

    其中,write 的阻塞貌似只能接受,因为没有更好的方法把数据写到一个文件中了。但对于 fsync,Redis 允许三种配置,选用哪种取决于你对备份及时性和性能的平衡:

    1. always:当把 appendfsync 设置为 always,fsync 会和客户端的指令同步执行,因此最可能造成延时问题,但备份及时性最好。
    2. everysec:每秒钟异步执行一次 fsync,此时 redis 的性能表现会更好,但是 fsync 依然可能阻塞 write,算是一个折中选择。
    3. no:redis 不会主动出发 fsync (并不是永远不 fsync,那是不太可能的),而由 kernel 决定何时 fsync
    使用分布式架构 —— 读写分离、数据分片

    以上,我们都是基于单台,或者单个 Redis 服务进行优化。下面,我们考虑当网站的规模变大时,利用分布式架构来保障 Redis 性能的问题。

    首先说,哪些情况下不得不(或者最好)使用分布式架构:

    1. 数据量很大,单台服务器内存不可能装得下,比如 1 个 T 这种量级
    2. 需要服务高可用
    3. 单台的请求压力过大

    解决这些问题可以采用数据分片或者主从分离,或者两者都用(即,在分片用的 cluster 节点上,也设置主从结构)。

    这样的架构,可以为性能提升加入新的切入点:

    1. 把慢速的指令发到某些从库中执行
    2. 把持久化功能放在一个很少使用的从库上
    3. 把某些大 list 分片

    其中前两条都是根据 Redis 单线程的特性,用其他进程(甚至机器)做性能补充的方法。

    当然,使用分布式架构,也可能对性能有影响,比如请求需要被转发,数据需要被不断复制分发。(待查)

    后话

    其实还有很多东西也影响 Redis 的性能,比如 active rehashing(keys 主表的再哈希,每秒 10 次,关掉它可以提升一点点性能),但是这篇博客已经写的很长了。而且,更重要不是收集已经被别人提出的问题,然后记忆解决方案;而是掌握 Redis 的基本原理,以不变应万变的方式决绝新出现的问题。


    【架构】21个软件架构特点解析


    【PPT】京东金融大数据平台架构


    一次非典型性 Redis 阻塞总结


    支持百亿级别的 Java 分布式日志组件EasyLog


    基于Redis位图实现用户签到功能


    ClickHouse在趣头条中的实战PPT


    点赞是最大的支持 

    Mysql进阶优化篇01——四万字详解数据库性能分析工具(深入全面详细,收藏备用)

    前 言
    🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端
    🍌 专栏简介:mysql进阶,主要讲解mysql数据库进阶知识,包括索引、数据库调优、分库分表等
    🌰 文章简介:本文将介绍数据库优化的步骤、思路、性能分析工具,比如慢查询、EXPLAIN,SHOW PROFILING等,并且对各个工具执行性能分析结果性能参数都有详细的介绍解释、建议收藏备用。
    🍓 相关推荐:

    目录

    1.数据库服务器的优化步骤

    数据库的优化整个流程划分成了 观察(Show status) 和 行动(Action) 两个部分。数据库的优化可以总结为下图。字母 S 的部分代表观察(会使用相应的分析工具),字母 A 代表的部分是行动(对应分析可以采取的行动)。

    可以从图中看到,在整个流程中需要用到很多分析工具:比如慢查询,EXPLAIN,SHOW PROFILING等,这篇文章就会介绍这些数据库性能分析工具。
    简单小结如下:

    可以看到数据库调优的步骤中越往金字塔尖走,其成本越高,效果越差,因此我们在数据库调优的过程中,要重点把握金字塔底部的sql及索引调优数据库表结构调优系统配置参数调优等软件层面的调优。

    2.查看系统性能参数

    可以使用SHOW STATUS语句查询一些数据库服务器的性能参数使用频率
    其语法如下:

    SHOW [GLOBAL][SESSION] STATUES LIKE '参数';
    

    一些常用的性能参数如下:

    Connections:连接MySQL服务器的次数。
    Uptime:MySQL服务器的上线时间。
    Slow_queries:慢查询的次数。
    Innodb_rows_read:Select查询返回的行数
    Innodb_rows_inserted:执行INSERT操作插入的行数
    Innodb_rows_updated:执行UPDATE操作更新的行数
    Innodb_rows_deleted:执行DELETE操作删除的行数
    Com_select:查询操作的次数。
    Com_insert:插入操作的次数。对于批量插入的 INSERT 操作,只累加一次。
    Com_update:更新操作的次数。
    Com_delete:删除操作的次数。

    举几个例子,玩一把。查看mysql的上线时间

    mysql> show status like 'connections';
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | Connections   | 9     |
    +---------------+-------+
    1 row in set (0.01 sec)
    

    看看存储引擎增删改查的行数。

    mysql> show status like  'innodb_rows_%';
    +----------------------+-------+
    | Variable_name        | Value |
    +----------------------+-------+
    | Innodb_rows_deleted  | 0     |
    | Innodb_rows_inserted | 0     |
    | Innodb_rows_read     | 8     |
    | Innodb_rows_updated  | 0     |
    +----------------------+-------+
    4 rows in set (0.00 sec)
    

    3.统计SQL的查询成本:last_query_cost

    先来造一下数据(友情提醒:上一篇文章已经造过,如果您是从上一篇文章跟着阅读过来的,不用重新造了哟。)

    CREATE DATABASE atguigudb1;
    
    USE atguigudb1;
    
    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 //
    
    
    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 //
    
    # 存储过程1:创建插入课程表存储过程
    DELIMITER //
    CREATE PROCEDURE insert_course( max_num INT )
    BEGIN 
    	DECLARE i INT DEFAULT 0; 
    	SET autocommit = 0;   #设置手动提交事务
    	REPEAT  #循环
    	SET i = i + 1;  #赋值
    	INSERT INTO course (course_id, course_name ) VALUES
    	(rand_num(10000,10100),rand_string(6)); 
    	UNTIL i = max_num 
    	END REPEAT; 
    	COMMIT;  #提交事务
    END //
    DELIMITER ;
    
    # 存储过程2:创建插入学生表存储过程
    CREATE PROCEDURE insert_stu( max_num INT )
    BEGIN 
    	DECLARE i INT DEFAULT 0; 
    	SET autocommit = 0;   #设置手动提交事务
    	REPEAT  #循环
    	SET i = i + 1;  #赋值
    	INSERT INTO student_info (course_id, class_id ,student_id ,NAME ) VALUES
    (rand_num(10000,10100),rand_num(10000,10200),rand_num(1,200000),rand_string(6)); 
    	UNTIL i = max_num 
    	END REPEAT; 
    	COMMIT;  #提交事务
    END //
    
    # 插入课程数据
    CALL insert_course(100);
    #插入学生数据
    CALL insert_stu(1000000);
    

    执行查询操作并且查看sql执行成本,Value表示I/O加载的数据页的页数。

    mysql> select * from student_info where id = 900001;
    +--------+------------+--------+-----------+----------+---------------------+
    | id     | student_id | name   | course_id | class_id | create_time         |
    +--------+------------+--------+-----------+----------+---------------------+
    | 900001 |     128284 | jbCKPX |     10080 |    10001 | 2022-05-31 11:01:54 |
    +--------+------------+--------+-----------+----------+---------------------+
    1 row in set (0.00 sec)
    
    mysql> show status like 'last_query_cost';
    +-----------------+----------+
    | Variable_name   | Value    |
    +-----------------+----------+
    | Last_query_cost | 1.000000 |
    +-----------------+----------+
    1 row in set (0.00 sec)
    

    再来个大的。

    mysql>  select * from student_info where id between 900001 and 900100;
    +--------+------------+--------+-----------+----------+---------------------+
    | id     | student_id | name   | course_id | class_id | create_time         |
    +--------+------------+--------+-----------+----------+---------------------+
    | 900001 |     128284 | jbCKPX |     10080 |    10001 | 2022-05-31 11:01:54 |
    // ...
    | 900099 |      45120 | MZOSay |     10081 |    10026 | 2022-05-31 11:01:54 |
    | 900100 |      83397 | lQyTXg |     10034 |    10058 | 2022-05-31 11:01:54 |
    +--------+------------+--------+-----------+----------+---------------------+
    100 rows in set (0.00 sec)
    
    mysql> show status like 'last_query_cost';
    +-----------------+-----------+
    | Variable_name   | Value     |
    +-----------------+-----------+
    | Last_query_cost | 41.136003 |
    +-----------------+-----------+
    1 row in set (0.00 sec)
    

    不知道大家有没有发现,上面的查询页的数量是刚才的 41 倍,但是查询的效率并没有明显的变化,实际上这两个 SQL 查询的时间基本上一样,查询last_query_cost对于比较开销是非常有用的,特别是我们有好几种查询方式可选的时候。

    🎈 SQL查询是一个动态的过程,从页加载的角度,我们可以得到以下两点结论:
    1.位置决定效率:数据库缓冲池>内存>磁盘。
    2.批量决定效率:顺序读取>大于随机读取,有时候批量顺序读取多个页甚至会比随机加载一个页更快。
    在实际生产中,我们可以利用这个特点,把经常用于查询的数据尽量放在缓冲池中,其次我们可以充分利用磁盘的吞吐能力,批量读取数据。

    4.定位执行慢的 SQL:慢查询日志

    慢查询日志用来记录相应时间超过阈值的语句,它可以帮助我们发现那些执行时间特别长的sql语句,以期进行针对性优化。一般mysql的慢查询日志默认关闭,非调优情况不建议开启,避免影响数据库的性能。

    4.1 开启慢查询日志

    1️⃣开启slow_query_log

    查看

    mysql> show variables like '%slow_query_log%';
    +---------------------+------------------------------------------------------+
    | Variable_name       | Value                                                |
    +---------------------+------------------------------------------------------+
    | slow_query_log      | OFF                                                  |
    | slow_query_log_file | D:\\mysql-5.7.26-winx64\\data\\DESKTOP-1PB99O1-slow.log |
    +---------------------+------------------------------------------------------+
    2 rows in set, 1 warning (0.00 sec)
    

    修改,注意这里要加global,因为它是全局系统变量,否则会报错哟。

    mysql> set global slow_query_log='ON';
    Query OK, 0 rows affected (0.02 sec)
    

    再查看。

    mysql> show variables like '%slow_query_log%';
    +---------------------+------------------------------------------------------+
    | Variable_name       | Value                                                |
    +---------------------+------------------------------------------------------+
    | slow_query_log      | ON                                                   |
    | slow_query_log_file | D:\\mysql-5.7.26-winx64\\data\\DESKTOP-1PB99O1-slow.log |
    +---------------------+------------------------------------------------------+
    2 rows in set, 1 warning (0.00 sec)
    
    

    2️⃣修改long_query_time阈值

    查看。

    mysql> show variables like '%long_query_time%';
    +-----------------+-----------+
    | Variable_name   | Value     |
    +-----------------+-----------+
    | long_query_time | 10.000000 |
    +-----------------+-----------+
    1 row in set, 1 warning (0.02 sec)
    

    修改。

    mysql> set global long_query_time = 1;
    Query OK, 0 rows affected (0.00 sec)
    

    再查看。

    mysql> show global variables like '%long_query_time%';
    +-----------------+----------+
    | Variable_name   | Value    |
    +-----------------+----------+
    | long_query_time | 1.000000 |
    +-----------------+----------+
    1 row in set, 1 warning (0.00 sec)
    

    🔊记得要加global,否则默认只在当前会话,不过,即使加global上面的修改还都只是临时的修改,当数据库服务器重启以后,以上修改就会失效。要想永久的生效,需要更改my.cnf文件,然后重启数据库服务器。

    slow_query_log=ON
    slow_query_log_file=/var/lib/mysql/atguigu-slow.log
    long_query_time=3
    log_output=FILE
    

    4.2 案例演示

    1️⃣ 建表

    CREATE TABLE `student` (
    	`id` INT(11) NOT NULL AUTO_INCREMENT,
    	`stuno` INT NOT NULL ,
    	`name` VARCHAR(20) DEFAULT NULL,
    	`age` INT(3) DEFAULT NULL,
    	`classId` INT(11) DEFAULT NULL,
    	PRIMARY KEY (`id`)
    ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    

    2️⃣ 设置参数 log_bin_trust_function_creators

    (第3节已经完成)

    创建函数,假如报错

    This function has none of DETERMINISTIC......
    

    命令开启:允许创建函数设置:

    set global log_bin_trust_function_creators=1;   # 不加global只是当前窗口有效。
    

    3️⃣创建函数

    (第3节已经完成)
    随机产生字符串:

    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 //
    DELIMITER ;
    #测试
    SELECT rand_string(10);
    

    产生随机数值(第3节已经完成):

    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 //
    DELIMITER ;
    #测试:
    SELECT rand_num(10,100);
    

    4️⃣创建存储过程

    DELIMITER //
    CREATE PROCEDURE insert_stu1(  START INT , max_num INT )
    BEGIN 
    DECLARE i INT DEFAULT 0; 
    SET autocommit = 0;   #设置手动提交事务
    REPEAT  #循环
    SET i = i + 1;  #赋值
    INSERT INTO student (stuno, NAME ,age ,classId ) VALUES
    ((START+i),rand_string(6),rand_num(10,100),rand_num(10,1000)); 
    UNTIL i = max_num 
    END REPEAT; 
    COMMIT;  #提交事务
    END //
    DELIMITER ;
    

    步骤5:调用存储过程

    #调用刚刚写好的函数, 4000000条记录,从100001号开始
    mysql> CALL insert_stu1(100001,4000000);
    Query OK, 0 rows affected (10 min 47.03 sec)
    

    注意,这个时间会比较长,请耐心等待几分钟哟。结束后可以查询下是不是插入成功了。

    select count(*) from student;
    

    📘 有一个小细节在这里提下,就是上面查询数据量的语句在存储引擎使用MyISAM时会比使用InnoDB时快很多,这是因为MyISAM存储引擎会有字段专门表示记录数。

    接下来执行一下下面的查询操作,制造慢查询的场景。

    mysql> set  long_query_time = 1;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SELECT * FROM student WHERE stuno = 3455655;
    +---------+---------+--------+------+---------+
    | id      | stuno   | name   | age  | classId |
    +---------+---------+--------+------+---------+
    | 3355654 | 3455655 | QQFFkl |   57 |     904 |
    +---------+---------+--------+------+---------+
    1 row in set (3.47 sec)
    
    mysql> select * from student where name = 'QQFFkl';
    +---------+---------+--------+------+---------+
    | id      | stuno   | name   | age  | classId |
    +---------+---------+--------+------+---------+
    |  143213 |  243214 | qQffkL |   95 |     543 |
    |  225733 |  325734 | qQffkL |   10 |     861 |
    |  280275 |  380276 | QqfFKL |   50 |     118 |
    | 1355465 | 1455466 | QqfFKL |   52 |     195 |
    | 1676763 | 1776764 | qQffkL |   11 |     906 |
    | 1766208 | 1866209 | qqFfKl |   11 |     396 |
    | 1870789 | 1970790 | qqFfKl |   97 |     182 |
    | 2368740 | 2468741 | QQFFkl |   51 |     645 |
    | 2386799 | 2486800 | qQffkL |   11 |     875 |
    | 3170932 | 3270933 | QqfFKL |   50 |      92 |
    | 3355654 | 3455655 | QQFFkl |   57 |     904 |
    | 3966226 | 4066227 | qQffkL |   96 |     629 |
    +---------+---------+--------+------+---------+
    

    查看下慢查询的记录。

    mysql> show status like 'slow_queries';
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | Slow_queries  | 2     |
    +---------------+-------+
    1 row in set (0.00 sec)
    

    🎯补充:在Mysql中,还有另外一个变量min_examined_row_limit用来控制慢查询日志,他的含义是,在查询时,查询时间超过long_query_time 的日志,还要保证查询扫描过的记录数满足min_examined_row_limit才会被记录到慢查询日志。一般它默认是0,我们也一般不会去修改它。

    SHOW VARIABLES like 'min%'
    OK
    时间: 0.002s
    

    以上是关于干货6500字全面字讲解 Redis 性能优化点!的主要内容,如果未能解决你的问题,请参考以下文章

    Mysql进阶优化篇01——四万字详解数据库性能分析工具(深入全面详细,收藏备用)

    独家干货!腾讯T3-3手写8W字Tomcat体系架构,从性能优化到源码底层

    干货分享:7000字总结Redis原理解析,让你从0-1

    干货分享:7000字总结Redis原理解析,让你从0-1

    硬核干货!7600字带你学会 Redis 性能优化点, 建议收藏!

    final 140字评论II