MySQL 查询缓存

命中判定

MySQL通过一个hash值引用来获取语句对应的缓存数据集。

hash值受到查询语句,需要查询的数据库和客户端协议版本等的影响。

MySQL在进行缓存命中判定的时候,不会解析语句生成执行计划,而是直接生成hash值并进行命中判定,大大缩短了查询语句的运行时间。查询缓存前只进行一个判断语句是否为SEL开头的大小写不敏感检查。

MySQL对于不确定的数据不会进行缓存,准确的描述是 如果查询语句中包含任何不确定函数,则查询缓存中不可能找到结果

一般来说,自定义函数、存储函数、用户变量、临时表、系统表、包含列级权限的表、子查询和存储过程不能缓存。

绑定变量在5.1版本前不能缓存。

某些数据可以通过提前计算避开限制。

开启缓存的开销有:

  • 读查询前检查是否命中缓存
  • 可以被缓存的读查询进行缓存操作
  • 写操作时设置所有对应缓存失效

缓存的内存使用

由一个40KB左右大小的管理维护数据结构和多个变长数据块组成。

管理维护数据结构记录可用内存空间、已使用内存空间、存储数据表和查询结果映射空间以及存储查询字符串和查询结果空间等内容。

数据块存储了自己的类型、大小、数据和指向前一个和后一个数据块的指针。

数据块的类型:

  • 存储查询结果
  • 存储查询和数据表的映射
  • 存储查询文本

等。

服务器启动时,先初始化查询需要的内存,生成一个完整的空闲块作为内存池,大小是配置的查询缓存大小减去维护元数据的数据结构消耗的空间。

当由查询结果需要缓存时,MySQL从空间块中申请一个大于query_cache_min_res_unit的数据块,同时一般保证这个内存块尽可能小(也可能选择大的)然后填入数据。
若数据块用完,则MySQL会依照上述原则申请一块新的数据块。

若完成查询后内存空间还有剩余,则空余的内存会被释放,并放回空闲内存部分。

缓存查询过程

过程如图

MySQL的查询缓存在写入时可能产生碎片问题。常见于并发。如图

碎片问题

缓存适用情况

只有缓存带来的资源节约大于其本身的资源消耗时才会带来性能提升,取决于具体的服务器压力模型。

理论上可以通过关闭和开启查询缓存并对比系统效率来决定是否开启缓存。

网络消耗为系统主要瓶颈时缓存的意义不大。

复杂的SELECT语句都可以使用查询缓存。如果某些关键的查询能够得到优化,那么降低其他的查询速度也是有意义的,可以通过SQL_CACHE选项进行控制来得到进一步优化。

UPDATE、DELETE和INSERT操作尽可能少。

查询缓存命中率计算公式: Qcache_hits/(Qcache_hits+Com_select)

缓存未命中的可能原因有:

  • 无法缓存(语句结果不确定/结果过大)
  • 从未处理的语句
  • 缓存失效

缓存失效的主要原因有:

  • 缓存碎片
  • 内存不足
  • 数据修改

可以通过Com_*查看数据修改的情况(Com_update,Com_delete等)

通过Qcache_lowmem_prunes来查看内存不足情况。这个参数代表内存不足时删除缓存的数量。

“命中和写入”的比率可以更好的反应查询缓存的效率,即 Qcache_hits/Qcache_inserts
根据经验,通常3:1以上缓存有效
推荐达到10:1。

配置和维护缓存

相关的参数有:

  • query_cache_type:是否打开查询缓存 OFF/ON/DEMAND
  • query_cache_size:缓存总内存空间 1024整数倍
  • query_cache_min_res_unit:分配内存块时的最小单位
  • query_cache_limit:能够缓存的最大查询结果
    • 注意:数据开始生成时就开始缓存,因此当结果返回完毕后MySQL才知道结果是否超出限制,若超出则进行删除缓存操作,严重拖慢性能
  • query_cache_wlock_invalidate:表有锁时是否返回结果

可以通过设置合理的query_cache_min_res_unit减少碎片。

可以通过单个查询的平均缓存大小 (query_cache_size-Qcache_free_memory)/Qcache_queries_in_cache 作为参考。需要考虑结果不均匀的情况。

通过参数Qcache_free_blocks来观察碎片,若Qcache_free_blocks大小接近Qcache_total_blocks/2则说明有严重的碎片问题。

可以使用 FLUSH QUERY CACHE完成碎片整理。

RESET QUERY CACHE是缓存清空,加以区别。

观察Qcache_lowmem_prunes值,若增长过快,则:

  • 很多空闲块:碎片
  • 少空闲块:缓存过小

也有可能是可能缓存不适合工作于目前的应用系统。

InnoDB与查询缓存

4.0版本前,事务处理中查询缓存是禁用的,4.1开始,InnoDB控制事务是否可以使用查询缓存。

事务是否可以访问缓存取决于当前事务ID和数据表上是否有锁。

每一个InnoDB内存数据字典中都保存一个事务ID号,若当前事务小于该事务ID则无法访问查询缓存。

如果表上有锁,则关于该表的任何查询无法访问缓存。

恒成立的事实:

  • 所有大于该表计数器的事务才能使用缓存
  • 对表加锁的事务ID不会被更新为该表的计数器,计数器会被更新成系统事务ID。该事务后续更新操作也无法读取和修改缓存。

通用查询缓存优化

使用多个小表代替一个大表。

采用批量写入。

控制缓存大小,放置服务器僵死,必要时禁用缓存。

通过SQL_CACHE和SQL_NO_CACHE控制单条查询语句缓存。也可以修改会话级的变量query_cache_type。

写密集型应用直接禁用缓存。

读密集型应用根据评估判断是否禁用缓存。

RESTful API 规范

约束

客户-服务器(Client-Server),提供服务的服务器和使用服务的客户需要被隔离对待。

无状态(Stateless),来自客户的每一个请求必须包含服务器处理该请求所需的所有信息。换句话说,服务器端不能存储来自某个客户的某个请求中的信息,并在该客户的其他请求中使用。

可缓存(Cachable),服务器必须让客户知道请求是否可以被缓存。

分层系统(Layered System),服务器和客户之间的通信必须被这样标准化:允许服务器和客户之间的中间层(Ross:代理,网关等)可以代替服务器对客户的请求进行回应,而且这些对客户来说不需要特别支持。

统一接口(Uniform Interface),客户和服务器之间通信的方法必须是统一化的。

  • 资源标识符。每个资源都有各自的标识符。客户端在请求时需要指定该标识符。在 REST 服务中,该标识符通常是 URI。客户端所获取的是资源的表达(representation),通常使用 XML 或 JSON 格式。

  • 通过资源的表达来操纵资源。客户端根据所得到的资源的表达中包含的信息来了解如何操纵资源,比如对资源进行修改或删除。

  • 自描述的消息。每条消息都包含足够的信息来描述如何处理该消息。

  • 超媒体作为应用状态的引擎(HATEOAS)。客户端通过服务器提供的超媒体内容中动态提供的动作来进行状态转换。

支持按需代码(Code-On-Demand,可选),服务器可以提供一些代码或者脚本(Javascrpt,flash,etc)并在客户的运行环境中执行。这条准则是这些准则中唯一不必必须满足的一条。(比如客户可以在客户端下载脚本生成密码访问服务器。)

协议

API与用户的通信协议,总是使用HTTPs协议,确保交互数据的传输安全。

域名

应该尽量将API部署在专用域名之下。

  • https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。

  • https://example.org/api/

API版本控制

路径又称”终点”(endpoint),表示API的具体网址。
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的”集合”(collection),所以API中的名词也应该使用复数。

举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。

  • https://api.example.com/v1/products
  • https://api.example.com/v1/users
  • https://api.example.com/v1/employees

HTTP请求方式

对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面四个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • DELETE(DELETE):从服务器删除资源。

GET /product:列出所有商品。

POST /product:新建一个商品。

GET /product/ID:获取某个指定商品的信息。

PUT /product/ID:更新某个指定商品的信息。

DELETE /product/ID:删除某个商品。

GET /product/ID/purchase:列出某个指定商品的所有投资者。

GET /product/ID/purchase/ID:获取某个指定商品的指定投资者信息。

HTTP头部&HTTP状态码

针对不同的需求和不同的响应,Request应该采用相应的HTTP头部,Response应采用相应的HTTP状态码,方便服务端和用户端作出不同的响应。

  • Authorization 认证报头
  • Cache-Control 缓存报头
  • Content-Type 消息体类型报头

  • 200 OK

  • 400 Bad Request
  • 500 Internal Server Error

黑盒测试

概述

黑盒测试也称功能测试或数据驱动测试,它是在已知产品所应具有的功能,通过测试来检测每个功能是否都能正常使用。

在测试时,把程序看作一个不能打开的黑盒子,在完全不考虑程序内部结构和内部特性的情况下,测试者在程序接口进行测试。

适用范围

  • 是否有不正确或遗漏了的功能

  • 在接口上,能否正确地接受输入数据,能否产生正确地输出信息

  • 访问外部信息是否有错

  • 性能上是否满足要求

  • 界面是否错误,是否不美观

  • 初始化或终止错误

基本类型

通过测试

软件的基本功能能否正常工作

失败测试

为了破坏软件而设计和执行的测试案例

优点

  • 比较简单,不需要了解程序内部的代码及实现

  • 与软件的内部实现无关

  • 从用户角度出发,能很容易的知道用户会用到哪些功能,会遇到哪些问题

  • 基于软件开发文档,所以也能知道软件实现了文档中的哪些功能

  • 在做软件自动化测试时较为方便

缺点

  • 不可能覆盖所有的代码,覆盖率较低,大概只能达到总代码量的30%

  • 自动化测试的复用性较低

方法

等价类划方法

等价类划分是把所有可能的输入数据,即程序的输入域划分成若干部分(子集),然后从每一个子集中选取少数具有代表性的数据作为测试用例。

等价类

等价类是指某个输入域的子集合。在该子集合中,各个输入数据对于揭露程序中的错误都是等效的。

有效等价类

对于程序的规格说明来说是合理的,有意义的输入数据构成的集合。

无效等价类

与有效等价类的定义相反。

步骤

  1. 对每个输入或外部条件进行等价类划分,形成等价类表,为每一等价类规定一个唯一的编号

  2. 设计一测试用例,使其尽可能多地覆盖尚未覆盖的有效等价类,重复这一步骤,直到所有有效等价类均被测试用例所覆盖

  3. 设计一新测试用例,使其只覆盖一个无效等价类,重复这一步骤直到所有无效等价类均被覆盖

例子

设某公司要打印2001~2005年的报表,其中报表日期为6位数字组成,其中,前4位为年份,后两位为月份。

第一步——划分等价类

输入及外部条件 有效等价类 无效等价类
报表日期的类型及长度 6位数字字符① 有非数字字符④
少于6个数字字符⑤
多于6个数字字符⑥
年份范围 在2001~2005之间② 小于2001⑦
大于2005 ⑧
月份范围 在1~12之间③ 小于1⑨
大于12⑩

第二步——有效等价类划分测试用例

对表中编号为①②③的3个有效等价类用一个测试用例覆盖

测试数据 期望结果 覆盖范围
200105 输入有效 等价类①②③

第三步——为每一个无效等价类至少设计一个测试用例

测试数据 期望结果 覆盖范围
001MAY 输入无效 等价类④
20015 输入无效 等价类⑤
2001001 输入无效 等价类⑥
20000 输入无效 等价类⑦
20080 输入无效 等价类⑧
200100 输入无效 等价类⑨
200113 输入无效 等价类⑩

边界值分析法

边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。

通常边界值分析法是作为对等价类划分法的补充,这种情况下,其测试用例来自等价类的边界。

因果图方法

一种适合于描述对于多种条件的组合,相应产生多个动作的形式,适合设计测试用例

因果图方法最终生成的就是判定表,它适合于检查程序输入条件的各种组合情况。

因果图中使用了简单的逻辑符号,以直线联接左右结点。左结点表示输入状态(或称原因),右结点表示输出状态(或称结果)。

ci表示原因,通常置于图的左部;ei表示结果,通常在图的右部。ci和ei均可取值0或1,0表示某状态不出现,1表示某状态出现。

关系

4种符号分别表示了规格说明中向4种因果关系。

  1. 恒等:若ci是1,则ei也是1;否则ei为0

  2. 非:若ci是1,则ei是0;否则ei是1

  3. 或:若c1或c2或c3是1,则ei是1;否则ei为0。“或”可有任意个输入

  4. 与:若c1和c2都是1,则ei为1;否则ei为0。“与”也可有任意个输入

约束

输入状态相互之间还可能存在某些依赖关系,称为约束。

输入条件的约束有以下4类:

  1. E约束(异):a和b中至多有一个可能为1,即a和b不能同时为1

  2. I约束(或):a、b和c中至少有一个必须是1,即 a、b 和c不能同时为0

  3. O约束(唯一);a和b必须有一个,且仅有1个为1

  4. R约束(要求):a是1时,b必须是1,即不可能a是1时b是0

输出条件的约束只有M约束(强制):若结果a是1,则结果b强制为0。

步骤

  1. 分析软件规格说明描述中,那些是原因(即输入条件或输入条件的等价类),那些是结果(即输出条件), 并给每个原因和结果赋予一个标识符

  2. 分析软件规格说明描述中的语义。找出原因与结果之间,原因与原因之间对应的关系。根据这些关系,画出因果图

  3. 由于语法或环境限制,有些原因与原因之间,原因与结果之间的组合情况不不可能出现。 为表明这些特殊情况,在因果图上用一些记号表明约束或限制条件

  4. 把因果图转换为判定表

例子

某软件规格说明书包含这样的要求:第一列字符必须是A或B,第二列字符必须是一个数字,在此情况下进行文件的修改,但如果第一列字符不正确,则给出信息L;如果第二列字符不是数字,则给出信息M。

标识符

原因:

  • 1——第一列字符是A

  • 2——第一列字符是B

  • 3——第二列字符是一数字

结果:

  • 21——修改文件

  • 22 ——给出信息L

  • 23——给出信息M

因果图

11为中间节点;考虑到原因1和原因2不可能同时为1,因此在因果图上施加E约束。

根据因果图建立判定表

1 2 3 4 5 6 7 8
原因(条件) 1 1 1 1 1 0 0 0 0
2 1 1 0 0 1 1 0 0
3 1 0 1 0 1 0 1 0
11 1 1 1 1 0 0
动作(结果) 22 0 0 0 0 1 1
21 1 0 1 0 0 0
23 0 1 0 1 0 1

设计测试用例

1 2 3 4 5 6 7 8
原因(条件) 1 1 1 1 1 0 0 0 0
2 1 1 0 0 1 1 0 0
3 1 0 1 0 1 0 1 0
11 1 1 1 1 0 0
动作(结果) 22 0 0 0 0 1 1
21 1 0 1 0 0 0
23 0 1 0 1 0 1
测试用例 A6 Aa B9 BP C5 HY
A0 A@ B1 B* H4 E%

错误推测法

基于经验和直觉推测程序中所有可能存在的各种错误, 从而有针对性的设计测试用例的方法。

基本思想

列举出程序中所有可能有的错误和容易发生错误的特殊情况,根据他们选择测试用例。

例子

输入数据和输出数据为0的情况;输入表格为空格或输入表格只有一行。

测试一个对线性表(比如数组)进行排序的程序,可推测列出以下几项需要特别测试的情况:

  • 输入的线性表为空表

  • 表中只含有一个元素

  • 输入表中所有元素已排好序

  • 输入表已按逆序排好

  • 输入表中部分或全部元素相同

异度之刃:黄金之国伊拉评价

三天通了黄金之国伊拉这个dlc。感觉实在是太棒了,强力推荐,可以说在我心中是本月最佳游戏了。(隔壁的某播片游戏,同样播片你咋就这么丢人
(以下剧透)
dlc拔高了整个游戏的体验。因为有了前传,异度之刃本篇不再是一个幸运小屁孩嘴炮救世的老套故事,而是有血有肉的一个完整的世界。我对莱克斯的好感简直是蹭蹭的涨。总有一种高桥为了救莱克斯口碑才出个dlc的感觉(笑
本篇的战斗和剧情的割裂,导致天之圣杯和他的主人看起来毫无威力,而全世界对天之圣杯的畏惧和排斥在玩家精分的体验下显得十分滑稽。莱克斯接纳光/焰并战胜反派的那一段就有一种一通嘴炮就能让她们觉醒的荒诞感觉。但是在前传中,天之圣杯造成的灾难和恐惧十分直观,村庄被直接抹除,伊拉王城在熊熊大火中被焚尽,世界最强国伊拉、希亚全境遁入云海,世界都感到震惊。因此,世界上的所有人都惧怕圣杯强大的力量这个设定很容易被玩家所接受。于是,本篇里看起来非常突兀的场景,在dlc里得到了良好的诠释。dlc成功构造了——全世界都排斥、畏惧光,哪怕是曾经持有者,前代英雄也是如此。长期的压抑导致了光/焰的对自身的恐惧和强烈的自毁倾向——这样的设定,因此莱克斯的“力量应当保护所爱之人”的观点和对光/焰力量的完全接纳与包容才显得如此珍贵。说到底,是莱克斯救赎了光/焰,莱克斯作为男主的正当性得到了体现。
除了解释了莱克斯行为的合理性,dlc还提供了一个强烈的对比——阿德尔。本篇里阿德尔只有为数不多的几句耍帅台词,刺客信条一般披风的剪影,无法接纳而将光/焰封印的既成事实,还有光对他的景仰。给我的印象就是一个高冷的酷酷英雄。莱克斯14岁小孩子的幼稚设定在这种对比之下就非常不讨喜。dlc却告诉我这是个二货王子?阿德尔在dlc的设定变成了一个无心政治,梦想是隐居又博爱、受爱戴的私生二货王子。因此阿德尔的结局有两重骂名:作为光的御刃者,阿德尔和其他普通人害怕光的力量带来毁灭,隐藏在心中的恐惧导致阿德尔无法完全发挥与控制光,导致了光的暴走和伊拉的毁灭,反而将过错怪罪于光的力量,将光封印在灵洞中,甚至害怕到将温柔的焰封印沉入海底,作为御刃者不合格;作为广受子民爱戴的王子,结局告诉我阿德尔把光封印以后仅仅收拢了反抗军残部,然后前往群岛隐居。你的子民呢?居然还是被奸诈的王叔收拢成立新国家的?作为政治领袖也不合格。他的英雄形象彻底崩坏了。反观莱克斯,调谐圣杯得到的是完全形态的绿毛,拯救了世界也拯救了绝望的光。开个玩笑,战斗中,阿德尔都是控制不住老婆的输出,打强敌怂的不行;莱克斯都是莽到老婆输出跟不上,打强敌可带劲儿了,“什么天之圣杯,老子打的就是天之圣杯,吼姆拉,你他娘的塞壬机甲呢,给我拉来”(划掉)。总之,通过阿德尔的衬托,莱克斯幼齿的身躯立马伟岸了起来(笑)。
关于新系统,dlc去掉了抽卡(坏文明)环节,主角队开场基本固定,配装很舒服。挂球系统简化,现在三段必杀技的属性都会被挂上属性球,打球更容易了。御刃者下场战斗,带俩异刃,和原来三个异刃相比切换更麻烦,补偿就是人和异刃切换的时候会额外发动一个技能,方便打御刃者素质四连,而且被打掉的血以虚血形式存在,切换人和异刃即可回复。御刃者在场时可以发动一个无限制的特殊技能,例如劳拉可以扣除当前一半的血量刷新所有武技,感觉整套系统就是在鼓励你莽。
最后是一点碎碎念,这样一看本篇的劳拉组太惨了,明明前传里都是大好人,结果本篇里劳拉牌冰棍在和教廷的战斗中连着战舰一起被摧毁,霞(就是后来的芳)被教皇重新调谐,还被做成了食刃种,被真亲手杀死,还只能留下尸体无法进入异刃的轮回,真没有复活劳拉,还在和教皇的决战中阵亡了。完全是bad end啊,我不能接受!还有就是本来想随手写一个空间吐一堆槽的,结果莫名的写出了一坨干巴巴的分析文字,完全不是我玩完之后的感想啊!哪有这么一本正经吐槽的啊!果然玩完当场就要顺手写出来啊,还是说我完全不适合写东西呢。总之感谢看到最后的各位,有感想可以尽情交流。

记一次内网访问外网使用代理出现的apt问题

背景

tx 内网的开发网不允许直接访问外网,而且内网中的 apt 源内容较少,所以使用 apt 等工具管理开源工具时,需要使用代理访问 archive.ubuntu.com 和 security.ubuntu.com 以获取内容和修改PPA源。

问题

  • 通过~/.bashrc设置 http 代理和 https 代理后,source 和重启 bash 都无法使 apt 连接至 archive.ubuntu.com 和 security.ubuntu.com。
  • sudo apt-add-repository 无法正常工作。

原因

  • apt 并不使用系统变量 http_proxy 和 https_proxy ,需修改 /etc/apt/apt.conf 或使用 -o 选项指定 http/https 代理。
  • sudo 命令使用 root 的环境设置,需使用 -E 选项保留当前用户的设置。

方法

  • 使用 sudo apt-get -o Acquire::http::proxy="xxxx"
  • 使用 sudo -E apt-add-repository