虽然和网络相关,但是它却不是网络协议栈的一部分,这就是netpoll。
它只是一个出入口的处理框架。所谓的网络,它的终端节点就是主机,数据从主机的网卡发出,经过一个出口处理过程,网卡接收到一个数据包,经过一个入口处理过程,这一出一入的过程处理分为两种方式:
1.中断的方式
出口处理过程-数据排入发送队列,读取特定寄存器值,待网卡状态适合发送式,发送,等待发送后的中断通知,继续。
入口处理过程-数据被网卡收到,网卡中断CPU,CPU进而处理数据接收的过程。
网络协议栈下接的就是这种中断的出入口处理方式。
2.poll的方式
出口处理过程-数据排入发送队列,读取特定寄存器值,待网卡状态适合发送式,发送,不等待发送后的中断通知,继续读取寄存器以及根据队列情况权衡是否适合发送。
入口处理过程-数据被网卡收到,等待poll逻辑在适当的时候去主动poll网卡,若有数据,则将其从特定的网卡缓存读出。
Linux netpoll就是利用的这种方式。这种方式完全不依赖中断。
事实总是比说起来更麻烦。
要讲清楚这个有点混乱的主题,不得不引入第三种出入口处理方式,那就是中断和poll结合的方式,这就是NAPI方式。我把三种方式的图示先给出:
a.中断方式
b.poll方式
c.NAPI方式
理解了这个,剩下的就都理解了。至于为何会有NAPI,在本文中只能简单说一句:在高速高带宽网络中,数据包持续到来,每一个包中断一次CPU的话,CPU有点吃不消,反而耽误了CPU处理这些数据包,如果之前的数据包还没有处理完,最好的办法就是将数据包排入一个队列,然后沉默,不要打扰CPU,等CPU空下来的时候,自己去poll这些队列里面的数据包,这就是NAPI。
纯中断的方式我们都很熟悉,也是最直接的方式。然而为何要有纯poll的方式呢?使用纯poll的场合在中断完全不起作用的情况下。举一个例子,系统panic之后。此时中断控制器将可能被disable掉,无论如何,此时的机器已经和外界失联了,然而如果此时必须需要一个方式对外界通告自己的死因的话,这种netpoll的方式就派上用场了,因为它是纯手工的,完全不依赖系统的中断机制。另外一种场合比panic好一些,那就是协议栈故障,如果使用中断或者NAPI的方式,由于它上接的就是协议栈,netif_receive_skb中又没有什么HOOK点,此时使用netpoll可以改变数据包的处理路径,通过一个agent可以实现远程debug。
不要把中断想象的太神秘。它无非也就是一个通知机制,告诉CPU,现在请查询我的状态,该干啥就干啥。事实上,当CPU收到网卡中断的时候,它也不知道该干啥,它只会调用中断处理函数,其内部会去读取一写寄存器的状态,然后才能知道现在该干什么,比如可以发送数据包,比如收到一个数据包等。既然如此,即使在关中断的情形下,如果不依靠中断,CPU择机主动调用一下网卡的中断处理函数,读取一写寄存器的状态,是不是也能知道该干什么呢?答案当然是肯定的了!这就是netpoll的逻辑,它使用两步完成任务:
1.主动调用网卡的中断处理函数,获取当前该发送数据包还是接收到一个数据包;
2.直接hard_xmit数据包或者使用NAPI的接口去poll网卡的数据。
Linux netpoll的总体图示如下:
这个图示中附带了netconsole的原理,没想到他是如此的简单。我记得我曾经写过一个模块,将panic后的信息发到远端,这个是受到了一个xtables-addons模块的启发,起初失败了,但是最后我仔细debug了内核代码后,成功了。在成功的过程中,发现了很多以前不知道的东西。但是现在看看netconsole吧,它什么复杂的东西都不需要,只需要两步:
1.注册一个console,此后内核buffer的信息就会发到这个console;
2.该console下接netpoll的netpoll_send_skb,此后由netpoll逻辑来处理。
即便在panic后,中断已经被关了,甚至中断控制器都关闭的情况下,只要网卡没有进水,数据依然可以收发,它完全不依赖中断和协议栈。这简直太棒了!关于netconsole,我写多少都不如内核的Document来的好:$kernel/Documentation/networking/netconsole.txt.
netpoll是Linux内核中的一种在协议栈不可用或者中断机制异常的情况下与外界通讯的手段,当然它也是一种绕开协议栈的方法。这个位置足够底层,写出来的东西也肯定比基于Netfilter的更好玩。Netfilter是在协议栈的特殊点捕获数据包的,而netpoll却可以在网卡之上直接捕获数据包,它们甚至连协议栈的最底端都到不了。以后,如果想在内核态直接发包,再也不用PACKET套接字从用户态开始了,构造一个数据包,直接通过netpoll接口发出。问题是,它采用手工触发中断处理函数的方式,效率如何待测试。因此这种机制最好还是限制于调试和少量内核审计信息的发送吧。用它做VPN,我觉得悬…
本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-05/117637.htm
索引的代价
一般来说,如果MySQL能够找到方法,利用索引来更快地处理查询,它就会这样做。这意味着,对于大多数情况,如果你没有对表进行索引,就会使性能受到损害。这就是我所描绘的索引优点的美景。但是它有缺点吗?有的,它在时间和空间上都有开销。在实践中,索引的优点的价值一般会超过这些缺点,但是你也应该知道到底有一些什么缺点。
首先,索引加快了检索的速度,但是减慢了插入和删除的速度,同时还减慢了更新被索引的数据列中的值的速度。也就是说,索引减慢了大多数涉及写操作的速度。发生这种现象的原因在于写入一条记录的时候不但需要写入数据行,还需要改变所有的索引。数据表带有的索引越多,需要做出的修改就越多,平均性能的降低程度也就越大。在本文的”高效率载入数据”部分中,我们将更细致地了解这些现象并找出处理方法。
其次,索引会花费磁盘空间,多个索引相应地花费更多的磁盘空间。这可能导致更快地到达数据表的大小限制:
·对于MyISAM表,频繁地索引可能引起索引文件比数据文件更快地达到最大限制。
·对于BDB表,它把数据和索引值一起存储在同一个文件中,添加索引引起这种表更快地达到最大文件限制。
·在InnoDB的共享表空间中分配的所有表都竞争使用相同的公共空间池,因此添加索引会更快地耗尽表空间中的存储。但是,与MyISAM和BDB表使用的文件不同,InnoDB共享表空间并不受操作系统的文件大小限制,因为我们可以把它配置成使用多个文件。只要有额外的磁盘空间,你就可以通过添加新组件来扩展表空间。
使用单独表空间的InnoDB表与BDB表受到的约束是一样的,因为它的数据和索引值都存储在单个文件中。
这些要素的实际含义是:如果你不需要使用特殊的索引帮助查询执行得更快,就不要建立索引。
选择索引
假设你已经知道了建立索引的语法,但是语法不会告诉你数据表应该如何索引。这要求我们考虑数据表的使用方式。这一部分指导你如何识别出用于索引的备选数据列,以及如何最好地建立索引:
用于搜索、排序和分组的索引数据列并不仅仅是用于输出显示的。换句话说,用于索引的最好的备选数据列是那些出现在WHERE子句、join子句、ORDERBY或GROUPBY子句中的列。仅仅出现在SELECT关键字后面的输出数据列列表中的数据列不是很好的备选列:
SELECT
col_a<- 不是备选列
FROM
tbl1LEFT JOIN tbl2
ON tbl1.col_b = tbl2.col_c <-备选列
WHERE
col_d= expr; <- 备选列
当然,显示的数据列与WHERE子句中使用的数据列也可能相同。我们的观点是输出列表中的数据列本质上不是用于索引的很好的备选列。
Join子句或WHERE子句中类似col1=col2形式的表达式中的数据列都是特别好的索引备选列。前面显示的查询中的col_b和col_c就是这样的例子。如果MySQL能够利用联结列来优化查询,它一定会通过减少整表扫描来大幅度减少潜在的表-行组合。
考虑数据列的基数(cardinality)。基数是数据列所包含的不同值的数量。例如,某个数据列包含值1、3、7、4、7、3,那么它的基数就是4。索引的基数相对于数据表行数较高(也就是说,列中包含很多不同的值,重复的值很少)的时候,它的工作效果最好。如果某数据列含有很多不同的年龄,索引会很快地分辨数据行。如果某个数据列用于记录性别(只有”M”和”F”两种值),那么索引的用处就不大。如果值出现的几率几乎相等,那么无论搜索哪个值都可能得到一半的数据行。在这些情况下,最好根本不要使用索引,因为查询优化器发现某个值出现在表的数据行中的百分比很高的时候,它一般会忽略索引,进行全表扫描。惯用的百分比界线是”30%”。现在查询优化器更加复杂,把其它一些因素也考虑进去了,因此这个百分比并不是MySQL决定选择使用扫描还是索引的唯一因素。
索引较短的值。尽可能地使用较小的数据类型。例如,如果MEDIUMINT足够保存你需要存储的值,就不要使用BIGINT数据列。如果你的值不会长于25个字符,就不要使用CHAR(100)。较小的值通过几个方面改善了索引的处理速度:
·较短的值可以更快地进行比较,因此索引的查找速度更快了。
·较小的值导致较小的索引,需要更少的磁盘I/O。
·使用较短的键值的时候,键缓存中的索引块(block)可以保存更多的键值。MySQL可以在内存中一次保持更多的键,在不需要从磁盘读取额外的索引块的情况下,提高键值定位的可能性。
对于InnoDB和BDB等使用聚簇索引(clusteredindex)的存储引擎来说,保持主键(primarykey)短小的优势更突出。聚簇索引中数据行和主键值存储在一起(聚簇在一起)。其它的索引都是次级索引;它们存储主键值和次级索引值。次级索引屈从主键值,它们被用于定位数据行。这暗示主键值都被复制到每个次级索引中,因此如果主键值很长,每个次级索引就需要更多的额外空间。
索引字符串值的前缀(prefixe)。如果你需要索引一个字符串数据列,那么最好在任何适当的情况下都应该指定前缀长度。例如,如果有CHAR(200)数据列,如果前面10个或20个字符都不同,就不要索引整个数据列。索引前面10个或20个字符会节省大量的空间,并且可能使你的查询速度更快。通过索引较短的值,你可以获得那些与比较速度和磁盘I/O节省相关的好处。当然你也需要利用常识。仅仅索引某个数据列的第一个字符串可能用处不大,因为如果这样操作,那么在索引中不会有太多的唯一值。
你可以索引CHAR、VARCHAR、BINARY、VARBINARY、BLOB和TEXT数据列的前缀。
使用最左(leftmost)前缀。建立多列复合索引的时候,你实际上建立了MySQL可以使用的多个索引。复合索引可以作为多个索引使用,因为索引中最左边的列集合都可以用于匹配数据行。这种列集合被称为”最左前缀”(它与索引某个列的前缀不同,那种索引把某个列的前面几个字符作为索引值)。
假设你在表的state、city和zip数据列上建立了复合索引。索引中的数据行按照state/city/zip次序排列,因此它们也会自动地按照state/city和state次序排列。这意味着,即使你在查询中只指定了state值,或者指定state和city值,MySQL也可以使用这个索引。因此,这个索引可以被用于搜索如下所示的数据列组合:
state,city, zip
state, city
state
MySQL不能利用这个索引来搜索没有包含在最左前缀的内容。例如,如果你按照city或zip来搜索,就不会使用到这个索引。如果你搜索给定的state和具体的ZIP代码(索引的1和3列),该索引也是不能用于这种组合值的,尽管MySQL可以利用索引来查找匹配的state从而缩小搜索的范围。
不要过多地索引。不要认为”索引越多,性能越高”,不要对每个数据列都进行索引。我们在前面提到过,每个额外的索引都会花费更多的磁盘空间,并降低写操作的性能。当你修改表的内容的时候,索引就必须被更新,甚至可能重新整理。如果你的索引很少使用或永不使用,你就没有必要减小表的修改操作的速度。此外,为检索操作生成执行计划的时候,MySQL会考虑索引。建立额外的索引会给查询优化器增加更多的工作量。如果索引太多,有可能(未必)出现MySQL选择最优索引失败的情况。维护自己必须的索引可以帮助查询优化器来避免这类错误。
如果你考虑给已经索引过的表添加索引,那么就要考虑你将增加的索引是否是已有的多列索引的最左前缀。如果是这样的,不用增加索引,因为已经有了(例如,如果你在state、city和zip上建立了索引,那么没有必要再增加state的索引)。
让索引类型与你所执行的比较的类型相匹配。在你建立索引的时候,大多数存储引擎会选择它们将使用的索引实现。例如,InnoDB通常使用B树索引。MySQL也使用B树索引,它只在三维数据类型上使用R树索引。但是,MEMORY存储引擎支持散列索引和B树索引,并允许你选择使用哪种索引。为了选择索引类型,需要考虑在索引数据列上将执行的比较操作类型:
·对于散列(hash)索引,会在每个数据列值上应用散列函数。生成的结果散列值存储在索引中,并用于执行查询。散列函数实现的算法类似于为不同的输入值生成不同的散列值。使用散列值的好处是散列值比原始值的比较效率更高。散列索引用于执行=或<=>操作等精确匹配的时候速度非常快。但是对于查询一个值的范围效果就非常差了:
id< 30
weight BETWEEN 100 AND 150
·B树索引可以用于高效率地执行精确的或者基于范围(使用操作<、<=、=、>=、>、<>、!=和BETWEEN)的比较。B树索引也可以用于LIKE模式匹配,前提是该模式以文字串而不是通配符开头。
如果你使用的MEMORY数据表只进行精确值查询,散列索引是很好的选择。这是MEMORY表使用的默认的索引类型,因此你不需要特意指定。如果你希望在MEMORY表上执行基于范围的比较,应该使用B树索引。为了指定这种索引类型,需要给索引定义添加USINGBTREE。例如:
CREATETABLE lookup
(
id INT NOT NULL,
name CHAR(20),
PRIMARYKEY USING BTREE (id)
) ENGINE = MEMORY;
如果你希望执行的语句的类型允许,单个MEMORY表可以同时拥有散列索引和B树索引,即使在同一个数据列上。
有些类型的比较不能使用索引。如果你只是通过把值传递到函数(例如STRCMP())中来执行比较操作,那么对它进行索引就没有价值。服务器必须计算出每个数据行的函数值,它会排除数据列上索引的使用。
使用慢查询(slow-query)日志来识别执行情况较差的查询。这个日志可以帮助你找出从索引中受益的查询。你可以直接查看日志(它是文本文件),或者使用mysqldumpslow工具来统计它的内容。如果某个给定的查询多次出现在”慢查询”日志中,这就是一个线索,某个查询可能没有优化编写。你可以重新编写它,使它运行得更快。你要记住,在评估”慢查询”日志的时候,”慢”是根据实际时间测定的,在负载较大的服务器上”慢查询”日志中出现的查询会多一些。
上一篇查看MySQL索引