图片 10

O硬盘交互,资源等待之PAGEIOLATCH

一.概念

  在介绍财富等待PAGEIOLATCH在此之前,先来领会下从实例等第来深入分析的各类能源等待的dmv视图sys.dm_os_wait_stats。它是回去实践的线程所遭遇的兼具等待的连带音讯,该视图是从一个事实上等级来剖判的种种等待,它回顾200七体系型的守候,要求关爱的席卷PageIoLatch(磁盘I/O读写的等候时间卡塔 尔(阿拉伯语:قطر‎,LCK_xx(锁的等候时间卡塔 尔(阿拉伯语:قطر‎,WriteLog(日志写入等待卡塔尔,PageLatch(页上闩锁卡塔尔Cxpacket(并行等待卡塔尔等甚至别的财富等待排前的。 

  1.  上面依据总耗费时间排序来察看,这里分析的等候的wait_type 不饱含以下

SELECT  wait_type ,
        waiting_tasks_count,
        signal_wait_time_ms ,
        wait_time_ms,
        max_wait_time_ms
FROM    sys.dm_os_wait_stats
WHERE   wait_time_ms > 0
        AND wait_type NOT IN ( 'CLR_SEMAPHORE', 'CLR_AUTO_EVENT',
                               'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE',
                               'SLEEP_TASK', 'SLEEP_SYSTEMTASK',
                               'SQLTRACE_BUFFER_FLUSH', 'WAITFOR',
                               'LOGMGR_QUEUE', 'CHECKPOINT_QUEUE',
                               'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',
                               'BROKER_TO_FLUSH', 'BROKER_TASK_STOP',
                               'CLR_MANUAL_EVENT',
                               'DISPATCHER_QUEUE_SEMAPHORE',
                               'FT_IFTS_SCHEDULER_IDLE_WAIT',
                               'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN',
                               'SQLTRACE_INCREMENTAL_FLUSH_SLEEP' )
ORDER BY signal_wait_time_ms DESC

  下图排行在前的能源等待是第大器晚成须要去关爱剖判:

图片 1

  通过上面的询问就能够找到PAGEIOLATCH_x类型的能源等待,由于是实例级其他总计,想要拿到有意义数据,就必要查阅感兴趣的光阴世距。假设要间隔来解析,不须要重启服务,可透过以下命令来重新设置

DBCC SQLPERF ('sys.dm_os_wait_stats', CLEAR);  

  wait_type:等待类型
  waiting_tasks_count:该等待类型的等候数
  wait_time_ms:该等待类型的总等待时间(包蕴三个历程悬挂状态(Suspend)和可运转境况(Runnable)花费的总时间)
  max_wait_time_ms:该等待类型的最长等待时间
  signal_wait_time_ms:正在等待的线程从收到非确定性信号通告到其发轫运维之间的时差(多个进度可运营情状(Runnable)开支的总时间)
  io等待时间==wait_time_ms – signal_wait_time_ms

一. 概述

 sql server作为关系型数据库,须求张开数据存款和储蓄,
那在运转中就能够没完没了的与硬盘实行读写交互作用。就算读写不能够准确快速的姣好,就能够现出质量难点以致数据库损坏难点。下面讲讲引起I/O的爆发,以致剖析优化。

 

二. PAGEIOLATCH_x

  2.1 什么是Latch

    在sql
server里latch是轻量级锁,分化于lock。latch是用来一只sqlserver的中间对象(同步资源访谈),而lock是用来对于客户对象富含(表,行,索引等)举行同步,轻易归纳:Latch用来珍惜SQL server内部的部分能源(如page卡塔尔的物理访谈,可以感到是多个同盟对象。而lock则重申逻辑访问。比方四个table,正是个逻辑上的概念。关于lock锁那块在”sql server
锁与事务水落石出”中有详细表达。

  2.2 什么是PageIOLatch 

  当查问的数据页假设在Buffer
pool里找到了,则还未其余等待。不然就能够产生一个异步io操作,将页面读入到buffer
pool,没做完在此以前,连接会保持在PageIoLatch_ex(写)或PageIoLatch_sh(读)的等候情状,是Buffer
pool与磁盘之间的守候。它反映了询问磁盘i/o读写的等候时间。
  当sql
server将数据页面从数据文件里读入内部存款和储蓄器时,为了防止其余客户对内部存款和储蓄器里的同三个多少页面进行拜访,sql
server会在内部存款和储蓄器的数目页同上加多少个排它锁latch,而当任务要读取缓存在内部存款和储蓄器里的页面时,会申请一个共享锁,疑似lock相同,latch也会产出拥塞,依照差别的等候财富,等待状态犹如下:PAGEIOLATCH_DT,PAGEIOLATCH_EX,PAGEIOLATCH_KP,PAGEIOLATCH_SH,PAGEIOLATCH_UP。器重关切PAGEIOLATCH_EX(写入)和PAGEIOLATCH_SH(读取)三种等待。

2.1  AGEIOLATCH流程图

  不经常大家分析当前移动客商情形下时,三个风趣的情景是,一时候你开掘有个别SPID被自身梗塞住了(通过sys.sysprocesses了翻看)
为啥会协和等待本人吗? 那些得从SQL server读取页的进程提起。SQL
server从磁盘读取三个page的进程如下:

图片 2

图片 3

  (1):由三个顾客哀求,获取扫描X表,由Worker x去实践。

  (2):在围观进度中找到了它需求的数额页同1:100。

  (3):发面页面1:100并不在内部存款和储蓄器中的数据缓存里。

  (4):sql
server在缓冲池里找到五个方可存放的页面空间,在地方加EX的LATCH锁,制止数据从磁盘里读出来从前,外人也来读取或修正那一个页面。

  (5):worker x发起三个异步i/o诉求,要求从数据文件里读出页面1:100。

  (6):由于是异步i/o(可见为三个task子线程),worker
x可以随着做它上边要做的事务,就是读出内部存款和储蓄器中的页面1:100,读取的动作必要报名一个sh的latch。

  (7):由于worker
x在此以前申请了一个EX的LATCH锁还平昔不自由,所以那个sh的latch将被窒碍住,worker
x被本人窒碍住了,等待的能源便是PAGEIOLATCH_SH。

  最终当异步i/o结束后,系统会文告worker
x,你要的数额已经写入内部存款和储蓄器了。接着EX的LATCH锁释放,worker
x申请获取了sh的latch锁。

计算:首先说worker是三个实行单元,上面有多少个task关联Worker上,
task是运维的小小任务单元,能够这么明白worker产生了第叁个x的task职分,再第5步发起叁个异步i/o要求是第2个task任务。叁个task归于二个worker,worker
x被本身拥塞住了。 关于职分调解通晓查看sql server
任务调节与CPU。

 2.2 具体深入分析

  通过地点精晓到假设磁盘的快慢不可能满足sql
server的内需,它就能够化为三个瓶颈,日常PAGEIOLATCH_SH
从磁盘读数据到内部存款和储蓄器,假使内部存款和储蓄器缺乏大,当有内部存款和储蓄器压力时候它会放出掉缓存数据,数据页就不会在内部存款和储蓄器的多少缓存里,那样内部存款和储蓄器难题就导致了磁盘的瓶颈。PAGEIOLATCH_EX是写入数据,这经常是磁盘的写入速度明显跟不上,与内存未有直接关系。

下边是查询PAGEIOLATCH_x的财富等待时间:

select wait_type,
waiting_tasks_count,
wait_time_ms ,
max_wait_time_ms,
signal_wait_time_ms
from sys.dm_os_wait_stats
where wait_type like 'PAGEIOLATCH%' 
order by wait_type

下边是询问出来的守候新闻:

PageIOLatch_SH
总等待时间是(7166603.0-15891)/1000.0/60.0=119.17分钟,平均耗费时间是(7166603.0-15891)/297813.0=24.01飞秒,最大等待时间是3159秒。

PageIOLatch_EX 总等待时间是(3002776.0-5727)/1000.0/60.0=49.95分钟,   
平均耗费时间是(3002776.0-5727)/317143.0=9.45皮秒,最大等待时间是1914秒。

图片 4

关于I/O磁盘 sys.dm_io_virtual_file_stats 函数也做个参照他事他说加以侦查

SELECT  
       MAX(io_stall_read_ms) AS read_ms,
         MAX(num_of_reads) AS read_count,
       MAX(io_stall_read_ms) / MAX(num_of_reads) AS 'Avg Read ms',
         MAX(io_stall_write_ms) AS write_ms,
        MAX(num_of_writes) AS write_count,
         MAX(io_stall_write_ms) /  MAX(num_of_writes) AS 'Avg Write ms'
FROM    sys.dm_io_virtual_file_stats(null, null)
WHERE   num_of_reads > 0 AND num_of_writes > 0 

图片 5

  总结:PageIOLatch_EX(写入)跟磁盘的写入速度有关联。PageIOLatch_SH(读取)跟内部存款和储蓄器中的数额缓存有提到。透过上边包车型地铁sql总计查询,从等待的时刻上看,并不曾明晰的评估磁盘品质的正经,但能够做评估标准数据,准时重新载入参数,做性能剖判。要规定磁盘的下压力,还索要从windows系统品质监视器方面来深入分析。
关于内部存款和储蓄器原理查看”sql server
内部存款和储蓄器初探“磁盘查看”sql
server I/O硬盘交互作用” 。

二.sql server  首要磁盘读写的行事

  2.1 
从数据文件(.mdf)里, 读入新数据页到内部存款和储蓄器。前页陈说内部存款和储蓄器时咱们清楚,借使想要的数目不在内部存款和储蓄器中时,就能从硬盘的数据文件里以页面为最小单位,读取到内部存储器中,还包罗预读的数额。
当内部存储器中存在,就不会去磁盘读取数据。丰盛的内部存款和储蓄器能够最小化磁盘I/O,因为磁盘的快慢远慢于内部存款和储蓄器。

  2.2  预写日志系统(WAL),向日志文件(.ldf)写入增加和删除改的日志记录。
用来保障数据业务的ACID。

  2.3  Checkpoint 检查点发生时,将脏页数据写入到数据文件
,在sp_configure的recovery interval 调节着sql
server多久进行一遍Checkpoint,
要是平常做Checkpoint,那每趟发生的硬盘写就不会太多,对硬盘冲击不会太大。假诺隔长日子叁遍Checkpoint,不做Checkpoint时品质或然会相当慢,但储存了汪洋的纠正,可能要爆发大量的写,这个时候品质会受影响。在大部据气象下,默许设置是相比较好的,没要求去改正。

  2.4   内部存款和储蓄器不足时,Lazy
Write发生,会将缓冲区中期维订正过的数额页面同步到硬盘的数据文件中。由于内部存款和储蓄器的上空欠缺触发了Lazy
Write, 主动将内部存款和储蓄器中十分久未有动用过的数据页和实施安插清空。Lazy
Write常常不被常常调用。

  2.5   CheckDB, 
索引维护,全文索引,总括音信,备份数据,高可用一块日志等。

 

三. 磁盘读写的连带剖判

  3.1 sys.dm_io_virtual_file_stats  获取数据文件和日志文件的I/O
计算新闻。该函数从sql server
二〇一〇方始,替换动态管理视图fn_virtualfilestats函数。
哪些文件日常要做读num_of_reads,哪些平时要做写num_of_writes,哪些读写平日要等待io_stall_*。为了赢得有意义的数额,须求在短期内对这么些数量开展快速照相,然后将它们同基线数据相比较。

SELECT  DB_NAME(database_id) AS 'Database Name',
        file_id,
        io_stall_read_ms / num_of_reads AS 'Avg Read Transfer/ms',
        io_stall_write_ms / num_of_writes AS 'Avg Write Transfer/ms'
FROM    sys.dm_io_virtual_file_stats(null, null)
WHERE   num_of_reads > 0 AND num_of_writes > 0 

  io_stall_read_ms:顾客等待文件,发出读取所用的总时间(阿秒)。

  io_stall_write: 客商等待在该公文中做到写入所用的总时间微秒。

  图片 6

  3.2  windows 质量计数器:  Avg. Disk Sec/Read
那些流量计是指每秒从磁盘读取数据的平均值

< 10 ms – 非常好
 10 ~ 20 ms 之间- 还可以
 20 ~50 ms 之间- 慢,必要关怀
> 50 ms –严重的 I/O 瓶颈

  3.4  I/O  物理内部存款和储蓄器读取次数最多的前50条

 SELECT TOP 50
 qs.total_physical_reads,qs.execution_count,
 qs.total_physical_reads/qs.execution_count AS [avg I/O],
 qs. creation_time,
 qs.max_elapsed_time,
 qs.min_elapsed_time,
 SUBSTRING(qt.text,qs.statement_start_offset/2,
 (CASE WHEN qs.statement_end_offset=-1
 THEN LEN(CONVERT(NVARCHAR(max),qt.text))*2
 ELSE qs.statement_end_offset END -qs.statement_start_offset)/2) AS query_text,
 qt.dbid,dbname=DB_NAME(qt.dbid),
 qt.objectid,
 qs.sql_handle,
 qs.plan_handle
 from sys.dm_exec_query_stats qs
 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
 ORDER BY qs.total_physical_reads DESC

 3.5 使用sp_spaceused查看表的磁盘空间

  exec sp_spaceused 'table_xx'

图片 7

reserved:保留的上空总数
data:数据运用的空香港中华总商会量
index_size:索引使用空间
Unused: 未用的空间量

 3.6  监测I/0运营状态 STATISTICS IO ON;

在写那篇东西的时候本身亦不是很明白质量基线,到底要检查点什么,dmv要不要检查,perfmon要检查实验那先。

 四  磁盘读写瓶颈的病症

  4.1  errorlog里告知错误 833

  4.2  sys.dm_os_wait_stats 视图里有雅量等候情形PAGEIOLATCH_* 或
WriteLog。当数码在缓冲区里未有找到,连接的等候景况正是PAGEIOLACTH_EX(写)
PAGEIOLATCH_SH(读),然后发起异步操作,将页面读入缓冲区中。像
waiting_tasks_count和wait_time_ms比较高的时候,平时要等待I/O,除在体未来数据文件上以外,还应该有writelog的日记文件上。想要获得有意义数据,必要做基线数据,查看感兴趣的光阴间距。

select wait_type,
waiting_tasks_count,
wait_time_ms ,
max_wait_time_ms,
signal_wait_time_ms
from sys.dm_os_wait_stats
where wait_type like 'PAGEIOLATCH%' 
order by wait_type

  wait_type:等待类型
  waiting_tasks_count:该等待类型的等候数
  wait_time_ms:该等待类型的总等待时间(包罗二个经过悬挂状态(Suspend)和可运维处境(Runnable)花销的总时间)
  max_wait_time_ms:该等待类型的最长等待时间
  signal_wait_time_ms:正在守候的线程从收受非确定性信号布告到其初步运行之间的时差(一个经过可运增势况Runnable开支的总时间)
  i/o等待时间==wait_time_ms – signal_wait_time_ms

进而自身决定,对作者发的《sql server 质量调优》小说内的 perfmon和dmv做二个计算。来树立谐和的性质基线。

   五  优化磁盘I/O

   5.1
数据文件里页面碎片收拾。 当表产生增加和删除改操作时索引都会时有产生碎片(索引叶级的页拆分卡塔 尔(英语:State of Qatar),碎片是指索引上的页不再具备大要三番两回性时,就能够产生碎片。譬喻您询问10条数据,碎片少时,可能只扫描2个页,但零星多时大概要扫描越多页(前边讲索引时在前述)。

   5.2
表格上的目录。比如:提议每个表都包括聚集索引,那是因为数量存款和储蓄分为堆和B-Tree,
按B-Tree空间占用率更加高。 充裕利用索引缩短对I/0的需求。

   5.3
数据文件,日志文件,TempDB文件提出寄放差异物理磁盘,日志文件放写入速度超快的磁盘上,例如RAID 10的分区

        5.4
文件空间处理,设置数据库增进时要按一定大小拉长,而不可能按比例,那样防止二回提升太多或太少所带来的不必要麻烦。建议对一点都不大的数据库设置一遍进步50MB到100MB。下图浮现要是按5%来加强近10G, 假设有四个应用程序在品味插入生机勃勃行,可是从未空间可用。那么数据库可能会初始进步三个近10G,
文件的巩固也许会耗用太长的年月,以致于客商端程序插入查询失败。

  图片 8

       5.5 幸免自动裁减文件,如若设置了此意义,sql
server会每间距半个小时检查文件的施用,要是空闲空间>伍分之一,会自行运营dbcc
shrinkfile 动作。自动缩短线程的会话ID
SPID总是6(以往大概有变) 如下突显自动缩短为False。

   
 图片 9

     图片 10

   5.6 尽管数据库的恢复生机情势是:完整。
就须要定期做日志备份,防止日志文件Infiniti的增加,用于磁盘空间。

    

     

io

在io中我们要小心哪些质量目标呢?

  1. physical
    diskdisk reads/sec   –这几个相应很精通生机勃勃看就就清楚 那个目的是指什么的

  2. physical disk disk writes/sec

豆蔻年华张开小说就看到那2个值,而却有阀值,见到阀值很喜悦,因为不用您去搜聚值了。

• Less than 10 ms = good performance

• Between 10 ms and 20 ms = slow performance

• Between 20 ms and 50 ms = poor performance

• Greater than 50 ms = significant performance
problem.

接下去正是 sys.dm_os_wait_stats
中的多少个wait type

3.
 PAGEIOLATCH_* 

 PAGEIOLATCH_* 系列的wait type 一共有

PAGEIOLATCH_DT   — 破坏,什么是破坏,正是把内部存款和储蓄器中数据页释放掉
PAGEIOLATCH_EX   — x锁,能够怎么知道,就是排他占用那几个锁

PAGEIOLATCH_KP   — 保持,就是保证这一个页不被毁掉
PAGEIOLATCH_NL   — 未有定义,保留
PAGEIOLATCH_SH   — 在读,数据页的时候就分配这些闩

PAGEIOLATCH_UP   — 在改过的时候分配那么些            

依赖onlinebook的表达:在任务等待 I/O 央求中缓冲区的闩锁时产生。闩锁央浼处于“XX”方式。长日子的等候恐怕提醒磁盘子系统现身难点。

讲的直接一点正是系统在io,入读或写的时候分配的。等待io伏乞

4.
ASYNC_IO_COMPLETION

依据onlinebook的讲明:当某职责正在等候 I/O 达成时现身

其一是等待异步io完毕,那么和上边有未有涉及吧?答案是绝非,上边等待的是io读抽出来,只怕写入。这么些是伺机系统的异步io实现是不相符的概念。

5.
IO_COMPLETION

依据onlinebook的分解:在等候 I/O 操作完毕时现身。经常,该等待类型表示非数据页 I/O。数据页 I/O 完结等待展现为 PAGEIOLATCH_* waits。

以此就不解释了说的很清楚了不畏等待非数据页的io实现

6.
WRITELOG

基于onlinebook的演说:等待日志刷新完结时现身。导致日志刷新的宽广操作是检查点和事务提交。

本条也非常的少解释,便是写入日志时候等待的年华。

cpu

7.Processor/
%Privileged Time                          –内核等第的cpu使用率

8.Processor/ %User
提姆e                                   –客户好多倍的cpu使用率

9.Process
(sqlservr.exe)/ %Processor Time    –某些进程的cpu使用率

10.SQLServer:SQL
Statistics/Auto-Param Attempts/sec  
 –试图运转活动参数化次数

11. SQLServer:SQL Statistics/Failed Auto-params/sec       — 自动参数化失利

12. SQLServer:SQL Statistics/Batch Requests/sec      
      — 批管理量

13. SQLServer:SQL Statistics/SQL Compilations/sec    
     — 编写翻译次数

14.  SQLServer:SQL Statistics/SQL Re-Compilations/sec  
 — 反编译次数

15.  SQLServer:Plan Cache/Cache hit Ratio              
             — 实施布置,cache命中率

接下去照旧 wait event的

16.signal_wait_time_ms –从发出时域信号到初叶运营的命宫差,时间花销在守候运维队列中,是风流罗曼蒂克味的cpu等待。

下边代码量化的疑似signal_wait_time_ms占的比重

SELECT SUM(signal_wait_time_ms) AS TotalSignalWaitTime ,

( SUM(CAST(signal_wait_time_ms AS NUMERIC(20, 2)))

/ SUM(CAST(wait_time_ms AS NUMERIC(20, 2))) * 100 )

AS PercentageSignalWaitsOfTotalTime

FROM sys.dm_os_wait_stats

在开立baseline 的时候 完全能够 按那一个sql来获得值。

17.SOS_SCHEDULER_YIELD等待

onlinebook的讲明:在职分自愿为要实践的其他职分生成陈设程序时现身。在该等待时期任务正在等候其量程更新。

全盘看不懂,啥叫量程。

直接的说正是:当查问自动废弃cpu,並且等待苏醒推行,那几个等待就称为SOS_SCHEDULER_YIELD。

18.CXPACKET等待

onlinebook:当尝试联合查询Computer调换迭代器时现身。固然针对该等待类型的争用成为问题时,能够设想减弱并行度。

直白点正是:微机之间的意气风发种合作,日常出未来并发查询,为何?因为独有现身查询才用八个计算机。

接下去是 sys.dm_os_schedulers 

SELECT scheduler_id ,

current_tasks_count ,

runnable_tasks_count

FROM sys.dm_os_schedulers

WHERE scheduler_id < 255

19.根本是查每一种微处理器上的义务数和可运转的职责数。

 

内存

20.SQL Server :Buffer Manager

又相当多卓有功用的计数器都是那 buffer manager 对象上边,可以扶助开掘buffer pool滚筒的标题。

21.buffer cache hit ratio

buffer cache hit ratio日常情形下在oltp中要抢先95%,在olap中要超过十分九。缺憾的是尚未关于那特性能指标相关的分解,和那么些值是何等影响预读机制的。假若这些指标的值有高大的下降那么就认证格外。这么些无法证实内部存款和储蓄器压力和sql server 健康指数。

22.page life expectancy

page life expectancy是页生命周期,也正是贰个数码页在内部存款和储蓄器中的时间。在从前sql
server 二〇〇三 4g的内部存款和储蓄器已经非常大了,sql server buffer
pool的高低是1.6g,假设sql
server 从磁盘上读取1.6g的数量也只要5分钟,但是几天前64g的内部存款和储蓄器是主流,假设从磁盘一下子读取50g的内部存款和储蓄器,会严重的碰撞io。当存在大气的查询扫描表,读入新的数据页,导致生命周期值下落亦非不不荒谬的。那一个值必得长时间的监视来剖析难题。

23.Free Pages

free pages是内部存储器中空页的数据,不要临近于0。这几个值表达查询是或不是在其余查询不是放内部存款和储蓄器的意况下,火速的分配内部存款和储蓄器的重大基于。假诺free pages
少之又少,页生命周期十分的短,而且伴随着空页争用(free
list stalls/sec卡塔尔的动静那么很有极大可能招致内部存款和储蓄器压力。

24.Free list stalls/sec

Free list stalls/sec每秒空页等待的数目,假诺意气风发段时间内都在0以上那么注明恐怕存在内存压力。

25.lazy write/sec

lazy write/sec 正是每秒写入磁盘的次数。借使发生量超大並且生命周期超短,free page 相当少,可是 free list stall/sec 量超级大,那么正是发生内部存款和储蓄器压力了。

SQL Server:memory Manager

SQL Server:memory
Manager对象内对内存的费用和内部存款和储蓄器管理的主题素材提供了很要紧参照他事他说加以考察

26.total server
memory 和 target server memory

那2个流速计代表了当下sql server 使用的一同内部存款和储蓄器和sql server 想要用的内存。假设 target server memory超越了total server memory,也是内部存款和储蓄器压力的要紧标记。sql
server
会降低内存的必要来就好像服务的可用内部存款和储蓄器,只怕经过最大服务器内部存款和储蓄器配置,所以当内存现身压力难题的时候不应有第有时间去查看那2个计数器

28.memory grants outstanding

该值是切实多少进度已经打响的获取了内部存款和储蓄器的授权。在生机勃勃段时间内,业务高峰期,若是该值过低,那么标记可能存在内部存储器压力,极其是 memory grants pending 也相比较高的状态下。

29. memory grants pending

该值是有过少进度正在守候内部存款和储蓄器的授权。假使为非0,那么证明需求调动或许优化负载也许扩充内部存款和储蓄器。

 

结束语

各种供给跟踪的事物本人都简短的表明了一下。关于 wait event
是一齐计数的,在总结的时候须求相减。

像这种类型追踪个一天,设置好频率,就能够得出质量基线了,能够做成Logo,这样经过图形就更易于见到难题了。

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注