1. 查询与索引优化分析
在优化 MySQL 时, 通常需要对数据库进行分析, 常见的分析手段有慢查询日志, profiling 分析, EXPLAIN 分析查询, 以及 show 命令查询系统状态及系统变量, 通过定位分析性能的瓶颈, 才能更好的优化数据库系统的性能
1.1. 性能瓶颈定位
1.1.1. Show 命令
我们可以通过 show 命令查看 MySQL 状态及变量, 找到系统的瓶颈:
Mysql> show status 显示状态信息(扩展 show status like XXX)
Mysql> show variables 显示系统变量(扩展 show variables like XXX)
Mysql> show processlist 显示那些线程正在执行, 包括执行状态是否锁表等
Shell> mysqladmin variables -u username -p password 显示系统变量
Shell> mysqladmin extended-status -u username -p password 显示状态信息
Shell> mysqld verbose help [|more #逐行显示], 查看状态变量及帮助
1.1.2. 慢查询日志
慢查询日志开启方法一:
在配置文件 my.cnf 或 my.ini 中在 [mysqld] 一行下面加入两个配置参数
- log-slow-queries=/data/mysqldata/slow-query.log
- long_query_time=2
注: log-slow-queries 参数为慢查询日志存放的位置, 一般这个目录要有 mysql 的运行帐号的可写权限, 一般都将这个目录设置为 mysql 的数据存放目录;
long_query_time=2 中的 2 表示查询超过两秒才记录;
在 my.cnf 或者 my.ini 中添加 log-queries-not-using-indexes 参数, 表示记录下没有使用索引的查询
- log-slow-queries=/data/mysqldata/slow-query.log
- long_query_time=10
- log-queries-not-using-indexes
慢查询日志开启方法二:
我们可以通过命令行设置变量来即时启动慢日志查询由下图可知慢日志没有打开, slow_launch_time=# 表示如果建立线程花费了比这个值更长的时间, slow_launch_threads 计数器将增加
查看是否开启慢查询日志: show variables like %slow%;
设置慢日志开启
MySQL 后可以查询 long_query_time 的值
为了方便测试, 可以将修改慢查询时间为 5 秒
1.1.3. 慢查询分析 mysqldumpslow
我们可以通过打开 log 文件查看得知哪些 SQL 执行效率低下
从日志中, 可以发现查询时间超过 5 秒的 SQL, 而小于 5 秒的没有出现在此日志中
使用 mysqldumpslow 命令可以非常明确的得到各种我们需要的查询语句, 对 MySQL 查询语句的监控分析优化是 MySQL 优化非常重要的一步开启慢查询日志后, 由于日志记录操作, 在一定程度上会占用 CPU 资源影响 mysql 的性能, 但是可以阶段性开启来定位性能瓶颈
1.1.4. explain 分析查询
使用 EXPLAIN 关键字可以模拟优化器执行 SQL 查询语句, 从而知道 MySQL 是如何处理 SQL 语句的通过 explain 命令可以得到:
表的读取顺序
数据读取操作的操作类型
哪些索引可以使用
哪些索引被实际使用
表之间的引用
每张表有多少行被优化器查询
EXPLAIN 字段:
?Table: 显示这一行的数据是关于哪张表的
?possible_keys: 显示可能应用在这张表中的索引如果为空, 没有可能的索引可以为相关的域从 WHERE 语句中选择一个合适的语句
?key: 实际使用的索引如果为 NULL, 则没有使用索引 MYSQL 很少会选择优化不足的索引, 此时可以在 SELECT 语句中使用 USE INDEX(index)来强制使用一个索引或者用 IGNORE INDEX(index)来强制忽略索引
?key_len: 使用的索引的长度在不损失精确性的情况下, 长度越短越好
?ref: 显示索引的哪一列被使用了, 如果可能的话, 是一个常数
?rows:MySQL 认为必须检索的用来返回请求数据的行数
?type: 这是最重要的字段之一, 显示查询使用了何种类型从最好到最差的连接类型为 systemconsteq_regrefrangeindex 和 ALL
systemconst: 可以将查询的变量转为常量. 如 id=1; id 为 主键或唯一键.
eq_ref: 访问索引, 返回某单一行的数据.(通常在联接时出现, 查询使用的索引为主键或惟一键)
ref: 访问索引, 返回某个值的数据.(可以返回多行) 通常使用 = 时发生
range: 这个连接类型使用索引返回一个范围中的行, 比如使用>或<查找东西, 并且该字段上建有索引时发生的情况(注: 不一定好于 index)
index: 以索引的顺序进行全表扫描, 优点是不用排序, 缺点是还要全表扫描
ALL: 全表扫描, 应该尽量避免
?Extra: 关于 MYSQL 如何解析查询的额外信息, 主要有以下几种
using index: 只用到索引, 可以避免访问表.
using where: 使用到 where 来过虑数据. 不是所有的 where clause 都要显示 using where. 如以 = 方式访问索引.
using tmporary: 用到临时表
using filesort: 用到额外的排序. (当使用 order by v1, 而没用到索引时, 就会使用额外的排序)
range checked for eache record(index map:N): 没有好的索引.
1.1.5. profiling 分析查询
通过慢日志查询可以知道哪些 SQL 语句执行效率低下, 通过 explain 我们可以得知 SQL 语句的具体执行情况, 索引使用等, 还可以结合 show 命令查看执行状态
如果觉得 explain 的信息不够详细, 可以同通过 profiling 命令得到更准确的 SQL 执行消耗系统资源的信息
profiling 默认是关闭的可以通过以下语句查看
打开功能: mysql>set profiling=1; 执行需要测试的 sql 语句:
mysql> show profiles\G; 可以得到被执行的 SQL 语句的时间和 ID
mysql>show profile for query 1; 得到对应 SQL 语句执行的详细信息
测试完毕以后 , 关闭参数: mysql> set profiling=0
2. 索引及查询优化
2.1. 索引的类型
? 普通索引: 这是最基本的索引类型, 没唯一性之类的限制
? 唯一性索引: 和普通索引基本相同, 但所有的索引列值保持唯一性
? 主键: 主键是一种唯一索引, 但必须指定为 PRIMARY KEY
? 全文索引: MYSQL 从 3.23.23 开始支持全文索引和全文检索在 MYSQL 中, 全文索引的索引类型为 FULLTEXT 全文索引可以在 VARCHAR 或者 TEXT 类型的列上创建
大多数 MySQL 索引 (PRIMARY KEYUNIQUEINDEX 和 FULLTEXT) 使用 B 树中存储空间列类型的索引使用 R - 树, MEMORY 表支持 hash 索引
单列索引和多列索引(复合索引)
索引可以是单列索引, 也可以是多列索引对相关的列使用索引是提高 SELECT 操作性能的最佳途径之一
多列索引:
MySQL 可以为多个列创建索引一个索引可以包括 15 个列对于某些列类型, 可以索引列的左前缀, 列的顺序非常重要
多列索引可以视为包含通过连接索引列的值而创建的值的排序的数组一般来说, 即使是限制最严格的单列索引, 它的限制能力也远远低于多列索引
最左前缀
多列索引有一个特点, 即最左前缀 (Leftmost Prefixing) 假如有一个多列索引为 key(firstname lastname age), 当搜索条件是以下各种列的组合和顺序时, MySQL 将使用该多列索引:
- firstname,lastname,age
- firstname,lastname
- firstname
也就是说, 相当于还建立了 key(firstname lastname)和 key(firstname)
索引主要用于下面的操作:
? 快速找出匹配一个 WHERE 子句的行
? 删除行当执行联接时, 从其它表检索行
? 对具体有索引的列 key_col 找出 MAX()或 MIN()值由预处理器进行优化, 检查是否对索引中在 key_col 之前发生所有关键字元素使用了 WHERE key_part_# = constant 在这种情况下, MySQL 为每个 MIN()或 MAX()表达式执行一次关键字查找, 并用常数替换它如果所有表达式替换为常量, 查询立即返回例如:
SELECT MIN(key2), MAX (key2) FROM tb WHERE key1=10;
? 如果对一个可用关键字的最左面的前缀进行了排序或分组(例如, ORDER BY key_part_1,key_part_2), 排序或分组一个表如果所有关键字元素后面有 DESC, 关键字以倒序被读取
? 在一些情况中, 可以对一个查询进行优化以便不用查询数据行即可以检索值如果查询只使用来自某个表的数字型并且构成某些关键字的最左面前缀的列, 为了更快, 可以从索引树检索出值
SELECT key_part3 FROM tb WHERE key_part1=1
有时 MySQL 不使用索引, 即使有可用的索引一种情形是当优化器估计到使用索引将需要 MySQL 访问表中的大部分行时 (在这种情况下, 表扫描可能会更快些) 然而, 如果此类查询使用 LIMIT 只搜索部分行, MySQL 则使用索引, 因为它可以更快地找到几行并在结果中返回例如:
2.2. 合理的建立索引的建议:
(1) 越小的数据类型通常更好: 越小的数据类型通常在磁盘内存和 CPU 缓存中都需要更少的空间, 处理起来更快
(2) 简单的数据类型更好: 整型数据比起字符, 处理开销更小, 因为字符串的比较更复杂在 MySQL 中, 应该用内置的日期和时间数据类型, 而不是用字符串来存储时间; 以及用整型数据类型存储 IP 地址
(3) 尽量避免 NULL: 应该指定列为 NOT NULL, 除非你想存储 NULL 在 MySQL 中, 含有空值的列很难进行查询优化, 因为它们使得索引索引的统计信息以及比较运算更加复杂你应该用 0 一个特殊的值或者一个空串代替空值
2.3. 关于索引和写 SQL 语句应当注意的一些建议
1. 当结果集只有一行数据时使用 LIMIT 1
2. 避免 SELECT *, 始终指定你需要的列
从表中读取越多的数据, 查询会变得更慢他增加了磁盘需要操作的时间, 还是在数据库服务器与 web 服务器是独立分开的情况下你将会经历非常漫长的网络延迟, 仅仅是因为数据不必要的在服务器之间传输
3. 使用连接 (JOIN) 来代替子查询(Sub-Queries)
连接 (JOIN) 之所以更有效率一些, 是因为 MySQL 不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作
4. 使用 ENUMCHAR 而不是 VARCHAR, 使用合理的字段属性长度
5. 尽可能的使用 NOT NULL
6. 固定长度的表会更快
7. 拆分大的 DELETE 或 INSERT 语句
8. 查询的列越小越快
Where 条件
在查询中, WHERE 条件也是一个比较重要的因素, 尽量少并且是合理的 where 条件是很重要的, 尽量在多个条件的时候, 把会提取尽量少数据量的条件放在前面, 减少后一个 where 条件的查询时间
有些 where 条件会导致索引无效:
? where 子句的查询条件里有!=,MySQL 将无法使用索引
? where 子句使用了 Mysql 函数的时候, 索引将无效, 比如: select * from tb where left(name, 4) = xxx
? 使用 LIKE 进行搜索匹配的时候, 这样索引是有效的: select * from tbl1 where name like xxx%, 而 like %xxx% 时索引无效
3. 配置优化
安装 MySQL 后, 配置文件 my.cnf 在 /MySQL 安装目录 / share/mysql 目录中, 该目录中还包含多个配置文件可供参考, 有 my-large.cnf ,my-huge.cnf, my-medium.cnf,my-small.cnf, 分别对应大中小型数据库应用的配置 win 环境下即存在于 MySQL 安装目录中的. ini 文件
下面列出了对性能优化影响较大的主要变量, 主要分为连接请求的变量和缓冲区变量
3.1. 连接请求的变量:
3.1.1. max_connections
MySQL 的最大连接数, 增加该值增加 mysqld 要求的文件描述符的数量如果服务器的并发连接请求量比较大, 建议调高此值, 以增加并行连接数量, 当然这建立在机器能支撑的情况下, 因为如果连接数越多, 介于 MySQL 会为每个连接提供连接缓冲区, 就会开销越多的内存, 所以要适当调整该值, 不能盲目提高设值
数值过小会经常出现 ERROR 1040: Too many connections 错误, 可以过 conn% 通配符查看当前状态的连接数量, 以定夺该值的大小
show variables like max_connections 最大连接数
show status like max_used_connections 响应的连接数
max_used_connections / max_connections * 100% (理想值 85%)
如果 max_used_connections 跟 max_connections 相同 那么就是 max_connections 设置过低或者超过服务器负载上限了, 低于 10% 则设置过大
3.1.2. back_log
MySQL 能暂存的连接数量当主要 MySQL 线程在一个很短时间内得到非常多的连接请求, 这就起作用如果 MySQL 的连接数据达到 max_connections 时, 新来的请求将会被存在堆栈中, 以等待某一连接释放资源, 该堆栈的数量即 back_log, 如果等待连接的数量超过 back_log, 将不被授予连接资源
back_log 值指出在 MySQL 暂时停止回答新请求之前的短时间内有多少个请求可以被存在堆栈中只有如果期望在一个短时间内有很多连接, 你需要增加它, 换句话说, 这值对到来的 TCP/IP 连接的侦听队列的大小
当观察你主机进程列表(mysql> show full processlist), 发现大量 264084 | unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待连接进程时, 就要加大 back_log 的值了
默认数值是 50, 可调优为 128, 对于 Linux 系统设置范围为小于 512 的整数
3.1.3. interactive_timeout
一个交互连接在被服务器在关闭前等待行动的秒数一个交互的客户被定义为对 mysql_real_connect()使用 CLIENT_INTERACTIVE 选项的客户
默认数值是 28800, 可调优为 7200
3.2. 缓冲区变量
3.2.1. key_buffer_size
key_buffer_size 指定索引缓冲区的大小, 它决定索引处理的速度, 尤其是索引读的速度通过检查状态值 Key_read_requests 和 Key_reads, 可以知道 key_buffer_size 设置是否合理比例 key_reads / key_read_requests 应该尽可能的低, 至少是 1:100,1:1000 更好(上述状态值可以使用 SHOW STATUS LIKE key_read% 获得)
key_buffer_size 只对 MyISAM 表起作用即使你不使用 MyISAM 表, 但是内部的临时磁盘表是 MyISAM 表, 也要使用该值可以使用检查状态值 created_tmp_disk_tables 得知详情
举例如下:
- mysql> show variables like key_buffer_size;
- +-++
- | Variable_name | Value |
- +++
- | key_buffer_size | 536870912 |
- + -++
key_buffer_size 为 512MB, 我们再看一下 key_buffer_size 的使用情况:
- mysql> show global status like key_read%;
- ++-+
- | Variable_name | Value |
- ++-+
- | Key_read_requests| 27813678764 |
- | Key_reads| 6798830 |
- ++-+
一共有 27813678764 个索引读取请求, 有 6798830 个请求在内存中没有找到直接从硬盘读取索引, 计算索引未命中缓存的概率:
key_cache_miss_rate =Key_reads / Key_read_requests * 100%, 设置在 1/1000 左右较好
默认配置数值是 8388600(8M), 主机有 4GB 内存, 可以调优值为 268435456(256MB)
3.2.2. query_cache_size
使用查询缓冲, MySQL 将查询结果存放在缓冲区中, 今后对于同样的 SELECT 语句(区分大小写), 将直接从缓冲区中读取结果
通过检查状态值 Qcache_*, 可以知道 query_cache_size 设置是否合理 (上述状态值可以使用 SHOW STATUS LIKE Qcache% 获得) 如果 Qcache_lowmem_prunes 的值非常大, 则表明经常出现缓冲不够的情况, 如果 Qcache_hits 的值也非常大, 则表明查询缓冲使用非常频繁, 此时需要增加缓冲大小; 如果 Qcache_hits 的值不大, 则表明你的查询重复率很低, 这种情况下使用查询缓冲反而会影响效率, 那么可以考虑不用查询缓冲此外, 在 SELECT 语句中加入 SQL_NO_CACHE 可以明确表示不使用查询缓冲
与查询缓冲有关的参数还有 query_cache_typequery_cache_limitquery_cache_min_res_unit
query_cache_type 指定是否使用查询缓冲, 可以设置为 012, 该变量是 SESSION 级的变量
query_cache_limit 指定单个查询能够使用的缓冲区大小, 缺省为 1M
query_cache_min_res_unit 是在 4.1 版本以后引入的, 它指定分配缓冲区空间的最小单位, 缺省为 4K 检查状态值 Qcache_free_blocks, 如果该值非常大, 则表明缓冲区中碎片很多, 这就表明查询结果都比较小, 此时需要减小 query_cache_min_res_unit
举例如下:
- mysql> show global status like qcache%;
- +-++
- | Variable_name | Value |
- +-++
- | Qcache_free_blocks | 22756 |
- | Qcache_free_memory | 76764704 |
- | Qcache_hits | 213028692 |
- | Qcache_inserts | 208894227 |
- | Qcache_lowmem_prunes | 4010916 |
- | Qcache_not_cached| 13385031 |
- | Qcache_queries_in_cache | 43560|
- | Qcache_total_blocks | 111212 |
- +-++
- mysql> show variables like query_cache%;
- +++
- | Variable_name | Value |
- +++
- | query_cache_limit | 2097152 |
- | query_cache_min_res_unit | 4096 |
- | query_cache_size | 203423744 |
- | query_cache_type | ON |
- | query_cache_wlock_invalidate | OFF |
- +++
查询缓存碎片率 = Qcache_free_blocks / Qcache_total_blocks * 100%
如果查询缓存碎片率超过 20%, 可以用 FLUSH QUERY CACHE 整理缓存碎片, 或者试试减小 query_cache_min_res_unit, 如果你的查询都是小数据量的话
查询缓存利用率 = (query_cache_size Qcache_free_memory) / query_cache_size * 100%
查询缓存利用率在 25% 以下的话说明 query_cache_size 设置的过大, 可适当减小; 查询缓存利用率在 80%以上而且 Qcache_lowmem_prunes > 50 的话说明 query_cache_size 可能有点小, 要不就是碎片太多
查询缓存命中率 = (Qcache_hits Qcache_inserts) / Qcache_hits * 100%
示例服务器查询缓存碎片率 = 20.46%, 查询缓存利用率 = 62.26%, 查询缓存命中率 = 1.94%, 命中率很差, 可能写操作比较频繁吧, 而且可能有些碎片
每个连接的缓冲
3.2.3. record_buffer_size
每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区如果你做很多顺序扫描, 你可能想要增加该值
默认数值是 131072(128K), 可改为 16773120 (16M)
3.2.4. read_rnd_buffer_size
随机读缓冲区大小当按任意顺序读取行时(例如, 按照排序顺序), 将分配一个随机读缓存区进行排序查询时, MySQL 会首先扫描一遍该缓冲, 以避免磁盘搜索, 提高查询速度, 如果需要排序大量数据, 可适当调高该值但 MySQL 会为每个客户连接发放该缓冲空间, 所以应尽量适当设置该值, 以避免内存开销过大
一般可设置为 16M
3.2.5. sort_buffer_size
每个需要进行排序的线程分配该大小的一个缓冲区增加这值加速 ORDER BY 或 GROUP BY 操作
默认数值是 2097144(2M), 可改为 16777208 (16M)
3.2.6. join_buffer_size
联合查询操作所能使用的缓冲区大小
record_buffer_size,read_rnd_buffer_size,sort_buffer_size,join_buffer_size 为每个线程独占, 也就是说, 如果有 100 个线程连接, 则占用为 16M*100
3.2.7. table_cache
表高速缓存的大小每当 MySQL 访问一个表时, 如果在表缓冲区中还有空间, 该表就被打开并放入其中, 这样可以更快地访问表内容通过检查峰值时间的状态值 Open_tables 和 Opened_tables, 可以决定是否需要增加 table_cache 的值如果你发现 open_tables 等于 table_cache, 并且 opened_tables 在不断增长, 那么你就需要增加 table_cache 的值了 (上述状态值可以使用 SHOW STATUS LIKE Open%tables 获得) 注意, 不能盲目地把 table_cache 设置成很大的值如果设置得太高, 可能会造成文件描述符不足, 从而造成性能不稳定或者连接失败
1G 内存机器, 推荐值是 128-256 内存在 4GB 左右的服务器该参数可设置为 256M 或 384M
3.2.8. max_heap_table_size
用户可以创建的内存表 (memory table) 的大小这个值用来计算内存表的最大行数值这个变量支持动态改变, 即 set @max_heap_table_size=#
这个变量和 tmp_table_size 一起限制了内部内存表的大小如果某个内部 heap(堆积)表大小超过 tmp_table_size,MySQL 可以根据需要自动将内存中的 heap 表改为基于硬盘的 MyISAM 表
3.2.9. tmp_table_size
通过设置 tmp_table_size 选项来增加一张临时表的大小, 例如做高级 GROUP BY 操作生成的临时表如果调高该值, MySQL 同时将增加 heap 表的大小, 可达到提高联接查询速度的效果, 建议尽量优化查询, 要确保查询过程中生成的临时表在内存中, 避免临时表过大导致生成基于硬盘的 MyISAM 表
- mysql> show global status like created_tmp%;
- +++
- | Variable_name | Value|
- +-++
- | Created_tmp_disk_tables | 21197 |
- | Created_tmp_files| 58|
- | Created_tmp_tables| 1771587 |
- +++
每次创建临时表, Created_tmp_tables 增加, 如果临时表大小超过 tmp_table_size, 则是在磁盘上创建临时表, Created_tmp_disk_tables 也增加, Created_tmp_files 表示 MySQL 服务创建的临时文件文件数, 比较理想的配置是:
Created_tmp_disk_tables / Created_tmp_tables * 100% <= 25% 比如上面的服务器 Created_tmp_disk_tables / Created_tmp_tables * 100% =1.20%, 应该相当好了
默认为 16M, 可调到 64-256 最佳, 线程独占, 太大可能内存不够 I/O 堵塞
3.2.10. thread_cache_size
可以复用的保存在中的线程的数量如果有, 新的线程从缓存中取得, 当断开连接的时候如果有空间, 客户的线置在缓存中如果有很多新的线程, 为了提高性能可以这个变量值
通过比较 Connections 和 Threads_created 状态的变量, 可以看到这个变量的作用
默认值为 110, 可调优为 80
3.2.11. thread_concurrency
推荐设置为服务器 CPU 核数的 2 倍, 例如双核的 CPU, 那么 thread_concurrency 的应该为 4;2 个双核的 cpu, thread_concurrency 的值应为 8 默认为 8
15) wait_timeout
指定一个请求的最大连接时间, 对于 4GB 左右内存的服务器可以设置为 5-10
3.3. 配置 InnoDB 的几个变量
3.3.1. innodb_buffer_pool_size
对于 InnoDB 表来说, innodb_buffer_pool_size 的作用就相当于 key_buffer_size 对于 MyISAM 表的作用一样 InnoDB 使用该参数指定大小的内存来缓冲数据和索引对于单独的 MySQL 数据库服务器, 最大可以把该值设置成物理内存的 80%
根据 MySQL 手册, 对于 2G 内存的机器, 推荐值是 1G(50%)
3.3.2. innodb_flush_log_at_trx_commit
主要控制了 innodb 将 log buffer 中的数据写入日志文件并 flush 磁盘的时间点, 取值分别为 012 三个 0, 表示当事务提交时, 不做日志写入操作, 而是每秒钟将 log buffer 中的数据写入日志文件并 flush 磁盘一次; 1, 则在每秒钟或是每次事物的提交都会引起日志文件写入 flush 磁盘的操作, 确保了事务的 ACID; 设置为 2, 每次事务提交引起写入日志文件的动作, 但每秒钟完成一次 flush 磁盘操作
实际测试发现, 该值对插入数据的速度影响非常大, 设置为 2 时插入 10000 条记录只需要 2 秒, 设置为 0 时只需要 1 秒, 而设置为 1 时则需要 229 秒因此, MySQL 手册也建议尽量将插入操作合并成一个事务, 这样可以大幅提高速度
根据 MySQL 手册, 在允许丢失最近部分事务的危险的前提下, 可以把该值设为 0 或 2
3.3.3. innodb_log_buffer_size
log 缓存大小, 一般为 1-8M, 默认为 1M, 对于较大的事务, 可以增大缓存大小
可设置为 4M 或 8M
3.3.4. innodb_additional_mem_pool_size
该参数指定 InnoDB 用来存储数据字典和其他内部数据结构的内存池大小缺省值是 1M 通常不用太大, 只要够用就行, 应该与表结构的复杂度有关系如果不够用, MySQL 会在错误日志中写入一条警告信息
根据 MySQL 手册, 对于 2G 内存的机器, 推荐值是 20M, 可适当增加
3.3.5. innodb_thread_concurrency=8
推荐设置为 2*(NumCPUs+NumDisks), 默认一般为 8
来源: http://www.bubuko.com/infodetail-2521502.html