SSD在Linux平台的使用优化

ubuntu-nuc

最近装了台Nuc主机来办公,为了性能上了SSD硬盘,今天网上搜罗了一些在Linux平台上SSD的优化配置。以减少硬盘写入,延长SSD的使用寿命,同时也提高linux的性能。

过程中发现Archlinux中的理论资料真不少,编排清晰,强烈推荐。

关闭交换区(swapoff)

Linux上的交换区(swap)和Windows上的虚拟内存大致相同:在硬盘上预分配一个swap区域,系统虚拟内存的总大小为物理内存加上swap区大小。对于内存不足或者部分常驻却不使用的内存,会被换出(swap out)到硬盘上的SWAP区域。过种中会有大量的磁盘写入。优化的第一步,便是减少掉这部分磁盘IO。

这里有两种方式:

第一种是直接使用swapoff -a命令关闭交换区,这样将不会再使用再交换区(打开使用swapon),

第二种方式是通过配置swapness来尽量减少交换区的使用机会。通过sysctl -q vm.swappiness查看默认值是60,调低该值将会减少从内存到磁盘的换出操作。 将这个值设置为0时,表示只有当物理内存使用率达到地100%后,才开始换出到磁盘。

这里我选择了第二种方式,执行sysctl vm.swappiness=0,这只是临时修改。若需要永久修改,可以编辑/etc/sysctl.conf,添加或修改值vm.swappiness=0。需要提一下的是,如果完全swapoff,则linux的休眠功能hibernate将会无法使用。

开启TRIM(与硬盘挂载优化)

TRIM是硬盘写入优化技术:SSD写入的指令可以是基于页(page)的,但对于数据的擦除经常是基于块(block,页的集合),所以写入页时,读取到所处块中有数据,则需要将其移动到缓存中,然后整块擦除,再将原有数据和新写入的数据一同写入到磁盘中,造成不必要的写操作。 而开启Trim后,则可以通过后台GC,在写命令达到之前,预先为准备可写入的空的块。

首先需要检测硬盘是否支持TRIM,可以通过如下命令检测:

hdparm -I /dev/sda | grep TRIM
        * Data Set Management TRIM supported (limit 1 block)
        //如果有类似输出表示支持TRIM

开启TRIM的方式为修改/etc/fstab文件,在主硬盘挂载记录中,修改挂载参数,如

#/etc/fstab
UUID=xxxxxxxxxxx / ext4 discard,noatime,nodiratime,errors=remount-ro 0 1

其中discard的意思便是开启自动TRIM(除自动TRIM外,还可以通过fstrim执行手动的trim操作)

此外,我们加上noatime与nodiratime,可以在读取文件时,避免对文件和目录的最后访问时间做更新操作,达到减少SSD写入操作的目的。

挂载完成需要重启,重启后可以通过mount -l查看是否挂载参数是否添加成功。

利用TMPFS

tmpfs是一种内存文件系统,临时存储,重启后数据便消失,可以将经常用于写的目录,挂载为tmpfs文件系统类型,这样这些目录的读写都变成了内存读写。比如可以将常用的/var/log和/tmp都挂载为tmpfs文件系统(其中/run/shm转认就挂载为tmpfs,可以用过du -l查看所有所有目录的挂载文件系统类型)。

挂载的方式是修改/etc/fstab,按如下格式增加记录:

tmpfs   /tmp            tmpfs   defaults,noatime,mode=1777      0       0
tmpfs   /var/tmp        tmpfs   defaults,noatime,mode=1777      0       0
tmpfs   /var/log        tmpfs   defaults,noatime,mode=1777      0       0

如果需要查看系统日志的话,则最好将/var/log删除掉,仍然挂载到硬盘上。

tmpfs的额度是虚拟分配,即使分配了2G,则实际目录仅使用了1个G。另1G的内存空间也不是被强制战用的,系统仍然可能使用到那1G的内存。

更换IO调度器

操作系统发出的IO指令,是通过一个IO Scheduler调度完成,不同的IO调度器有不同的特点及适用场景,对于HDD,一般是使用最为复杂的CFQ,合并减少寻道时间。而对于SSD来说没有寻道,反而CFQ复杂的调试算法没有了作用。而Noop则为最为简单的有请求合并功能的单队列算法,更适用于SSD。还有一种读写双队列分离的,按预定完成时间排序的Deadline调度器。对于SSD来说,Noop与Deadline都适用,wiki上多推荐使用Noop,而不少评测也证明Deadline性能也非常好。关于IO高度器在更多场景下的选择可以参考这里这里

阅读剩余部分 –

WEB安全:浅谈输入验证

近期负责一个主站系统的Code Review,过程中排查了一些由于输入验证引起的安全漏洞,但遗憾的是遗漏了一个分支功能点,导致线上用户昵称可能存在被篡改的风险。打算总结分享一些安全方面的经常,所以有了这篇博客。

假设所有的用户输入都是攻击性的,所有的用户都具有恶意

如果理解这句话并在开发时注意到这一点,关于输入验证的安全问题已经没有过多需要阐述的了。之前几次考虑过写一篇输入验证相关的文章而没有写,就是觉得这个问题的答案过于简单:"所有用户输入都是恶意".不过这个简单的问题能玩出的花样实在很多,许多同事都中过踩过其中的雷.

一些原则

我不是专职的安全测试,一些关点还是从研发角度出发。如果想了解更详多的WEB安全可以看一下 OWASP 开发指导,包括了WEB开发中会涉及到的安全准则。

界定用户输入

http_log
在一次HTTP请求中,由HTTP协议承载的所有信息都是用户输入。所以HTTP的头和体中的信息都是可以伪造的,比如Get/Post参数,Cookie,Referer,UserAgent等等.
应该假设这其中所有的元素都可能具有攻击性,需要对其做出必要的验证,程序不能以它们为依据,轻意做出与安全资源相关的访问决定.由于在开发面对更多的是GET/POST参数,往往会习惯性忽略header中的其它信息。
 

攻击影响范围
attack_diagram

请求都是基于Http协议,所以对于输入难的边界应该是HTTP请求。而图中所有的黄色方块区域,表示恶意输入可能影响范围,可以大致分为三种:
HTTP响应
除了比较常见对响应体的攻击(如XSS),对于响应头的攻击也是值得重要视的(如CRLF注义)。
浏览器一些安全难都是对服务器所返回的响应头所决定的,比如X-XSS-Protection,Access-Control-Allow-Origin,其中还包含了一些将被用于再次发送的信息,如Cookie,对响应头的保护应该受到重视。
资源访问
数据资源的访问可以包括但不限于数据库(SQL注入),文件系统(未授权访问)和LDAP(注入)等系统中能涉及到的资源。
服务访问
服务访问可以分为内部服务和外部服务,则框架,工具,逻辑代码到容器的漏洞都是攻击的目标。而且这类攻击,往往对服务器本身的影响更加致命。
总结来说主要针对服务器响应,和服务器代码执行的攻击。
 
客户端验证不可靠,但不可或缺
系统防范的边界是HTTP协议请求:唯一和系统进行通信交流的部分.正常操作下HTTP请求是由浏览器代用户发出,但除了浏览器,HTTP请求可以轻意的通过工具伪造发出.浏览器/Javascript的验证不是一道可靠的防线.
但如果客户端能甄别出大多数正常用户的无效请求,避免将其发送到服务端从而减少后端服务器的压力,这是另一个方面的"系统安全".
 

阅读全文

Linux I3窗口管理器调整优化

在Ubuntu用I3wm有相当一段时间了。开发配合Intellij和Vimium的话基本上很少需要用鼠标调整,对工作效率提升很大。昨天618项目刚上线,今天难得清闲又不想看书,就把I3给优化了一下。也顺带记录总结一下。

I3的安装很简单,执行sudo apt-get install i3然后登出,选择使用I3窗口管理重新登陆,就进入I3界面了. I3的文档写的很简单易懂,可以参考官方文档发掘更多适合个人的功能,也可以看一下快捷键说明快速入门一下,这里介绍一些我常用的操作.主要的快捷键盘有四个,掌握就入门。

常用技巧

入门操作与桌面移动

Mod+Enter 启动命令行
Mod+D 启动菜单,命令行模式,输入程序后在当前窗口运行
Mod+Shift+Q 退出当前程序
Mod+Shift+E 退出I3
I3有10个桌面,可以通过Mod(Win)加数字键0~9进行切换,切换非常方便,合理分配好桌面用途,效率会提升很多.

窗口移动与窗口拆分

使用Mod键加J(左)K(上)L(下):(右)可以在窗口间移动,如果是多屏显示器,窗口的移动可以跨越桌面的(A屏幕的最后一个窗口衔接B屏幕的第一个窗口)

窗口布局有三种模式,可以使用Mod + E,S,W快速试一下,就能明白.

使用Mod + F全屏化,效果很不错,某些时段专注某件时的时候可以开启.

Mod + V垂直或Mod + H水平排列子窗口,对于某些用于监控,不需要太多操作的桌面,可以使用,效果如下:

i3_layout

浮动窗口及大小调整

阅读全文

MySQL中的那些坑

MySQL使用中遇到了一些小”坑”和一些不便,无聊来总结一下.

IN子句逻辑问题

这个是在给同事调BUG时发现的,展示之前先初始化一些数据.

create table mysql_pitfalls(
	c1 int,
	c2 varchar(128),
	c3 datetime,
	c4 timestamp
);
-- 插入测试数据
insert into mysql_pitfalls(c1,c2,c3,c4) values(1,'1',now(),now());
insert into mysql_pitfalls(c1,c2,c3,c4) values(2,'2',now(),now());
insert into mysql_pitfalls(c1,c2,c3,c4) values(3,'3',now(),now());
insert into mysql_pitfalls(c1,c2,c3,c4) values(4,'4',now(),now());

下面我们分别执行以下两条SQL

mysql> select * from mysql_pitfalls where c1 in (1,2,3);
+------+------+---------------------+---------------------+
| c1   | c2   | c3                  | c4                  |
+------+------+---------------------+---------------------+
|    1 | 1    | 2015-06-06 19:00:05 | 2015-06-06 19:00:05 |
|    2 | 2    | 2015-06-06 19:00:08 | 2015-06-06 19:00:08 |
|    3 | 3    | 2015-06-06 19:00:11 | 2015-06-06 19:00:11 |
+------+------+---------------------+---------------------+
3 rows in set (0.00 sec)

这条SQL很简单,C1列是数值型的,IN逻辑正确.接下面再看一句有逻辑问题的查询,去IN一个字符串

-- 瞬间就被玩坏了
mysql> select * from mysql_pitfalls where c1 in ('1,2,3');
+------+------+---------------------+---------------------+
| c1   | c2   | c3                  | c4                  |
+------+------+---------------------+---------------------+
|    1 | 1    | 2015-06-06 19:00:05 | 2015-06-06 19:00:05 |
+------+------+---------------------+---------------------+
1 row in set, 1 warning (0.00 sec)

同样是数值型C1列,查询如果IN的条件是一个带逗号的字符串,IN条件会错误命中字符串中第一个逗号之前的数字.虽然这条SQL写错了,但这本身算是一个逻辑错误,明明不相等,IN去处怎么能匹配成功呢.再者,由于错误返回了每一条数据,有时候会麻痹开发和测试,误认为功能没有问题.

PS: 可以试试执行IN (‘1,2,3′,’2,3,4′) , 会发现MySQL会求每一个带逗号字符串的第一个值.

 

时间精度丢失(5.6解决)

MySQL在5.6之前,无论是DATETIME类型或是TIMESTAMP类型都无无法存储毫秒,以至于在对时间有毫秒精度要求的场景下,我直接选用了INT型作为存储时间的类型.

首先展示一下,MySQL提供的时间函数,是能够支持毫秒的

mysql> select microsecond('2015-1-1 12:00:00.3213') microsecond;
+-------------+
| microsecond |
+-------------+
| 321300 |
+-------------+
1 row in set (0.00 sec)

之前在测试表中,分别创建了DATETIME和TIMESTAMP的两个字段,C1和C2,我们使用microsecond函数测试一下
阅读全文

JVM垃圾回收算法

Mark-Sweep 标记清除算法

标记清除算法是最基础的回收算法,回收过程包括“标记”和“清除”两个阶段,过程大概是这样的:

mark_sweep

1:标记阶段时,标识出可回收的引用。

2:清除阶段时,对标记阶段所标识为的对像进行回收。

这个算法存在的最大问题是,通过该算法回收的内存区域是不连续的,会产生大量的无法被利用的内存碎片,而如果找不到连续足够多的空间,就需要触发另一个GC来进去回收。

另外,sweep阶段,当所管理的内存区域很大时(无论是否全部使用),效率也会受到影响。

但是,GC算法只是一些理论算法,而GC回收器则是基于这些算法的具体实现,通过对回收器机制的扩展和加入一些补偿措施,可以规避算法自身的缺陷。例如标记清除算法,当前最稳定高效的tenure代的回收器 ,CMS回收器,便是基于这种算法。CMS通过利用多核/线程,将并发标记阶段、并发清除阶段变为并行,通过通过一些额外的补偿方案实现内存碎片的整理,使其成为很优秀的垃圾回收器。

后续会单独用一篇博客来描述具体垃圾回收器,本文还是继续描述回收算法。需记住加收算法本身缺陷是可以通过具体实现来规避的就行了,并不是基于某种回收算法实现的回收器,就一定有该种算法存在的缺陷,如CMS。


Copying 复制算法

复制算法的出现,是为了解决标记清除所存存的问题。复制算法将容量划分两块区域,其过程如下:

gc_copying
阅读全文

搭建HG/Mercurial服务

公司的HG仓库在北京,每次代码的Push和Pull都非常慢,于是在Dev服务器启动一个HG服务做为境像,并由Jenkins完成与北京仓库的定时/手动同步。

 

代理仓库服务器配置

配置仓库,编辑.hg/hgrc文件,增加以下配置

#关闭ssl认证
[web]
push_ssl = false

#配置该仓库提交时用户名
[ui]
username = CD Mirror

[trusted]
users = <用户>

在仓库目录下,使用HG命令启动仓库服务

sudo hg --config web.allow_push=* serve -p 81

其中 -p是端口,–config web.allow_push=*是指允许所有用户向该仓库push代码
阅读全文

对比MySQL,什么场景MongoDB更适用

MongoDB已经流行了很长一段时间,相对于MySQL,究竟什么场景更需要用MongoDB?下面是一些总结。

更高的写入负载

默认情况下,MongoDB更侧重高数据写入性能,而非事务安全,MongoDB很适合业务系统中有大量“低价值”数据的场景。但是应当避免在高事务安全性的系统中使用MongoDB,除非能从架构设计上保证事务安全。

高可用性

MongoDB的复副集(Master-Slave)配置非常简洁方便,此外,MongoDB可以快速响应的处理单节点故障,自动、安全的完成故障转移。这些特性使得MongoDB能在一个相对不稳定(如云主机)的环境中,保持高可用性。

数据量很大或者未来会变得很大

依赖数据库(MySQL)自身的特性,完成数据的扩展是较困难的事,在MySQL中,当一个单达表到5-10GB时会出现明显的性能降级,此时需要通过数据的水平和垂直拆分、库的拆分完成扩展,使用MySQL通常需要借助驱动层或代理层完成这类需求。而MongoDB内建了多种数据分片的特性,可以很好的适应大数据量的需求。

基于位置的数据查询

MongoDB支持二维空间索引,因此可以快速及精确的从指定位置获取数据。

表结构不明确,且数据在不断变大

在一些传统RDBMS中,增加一个字段会锁住整个数据库/表,或者在执行一个重负载的请求时会明显造成其它请求的性能降级。通常发生在数据表大于1G的时候(当大于1TB时更甚)。 因MongoDB是文档型数据库,为非结构货的文档增加一个新字段是很快速的操作,并且不会影响到已有数据。另外一个好处当业务数据发生变化时,是将不在需要由DBA修改表结构。

没有DBA支持

如果没有专职的DBA,并且准备不使用标准的关系型思想(结构化、连接等)来处理数据,那么MongoDB将会是你的首选。MongoDB对于对像数据的存储非常方便,类可以直接序列化成JSON存储到MongoDB中。 但是需要先了解一些最佳实践,避免当数据变大后,由于文档设计问题而造成的性能缺陷。 阅读剩余部分 –

Java 8与Linux OOM killer

由于想体验新版的Lambda和Stream API,把服务器上的JRE换成了Java 8.而之后网站偶尔出现无法访问,java进程莫名奇妙地退出。怀疑是虚拟机内存溢出,但tomcat与应用的日志都没有崩溃的相关日志输出。

经过几次在线监控,发现java进程占用超过系统50%的内存。首先,网站应该占用不了这么多的内存,虽然我没有用-Xmx指定heap内存大小,但按规范,默认应该分配是物理内存的1/4。其它的Permgen区域,也应该无法使用这么多的内存。

这点我先放了放。猜测linux会不会自动杀死占用内存过多的进程? (没有太多的linux维护经验,也没有系统的学习过linux)。 结果在linux syslog中发现了这段。

#oom killer
kernel: java invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0

按关键字google一下,查到了linux的oom-killer机制:linux会在内存紧张时,杀死占用太多内存的进程,以保证系统正常运行。

查到这个原因后便开始分析内存使用的情况。
由于是在linux上,惯手的内存工具缺失,jmap运行很久也拿不到内存dump(吐个嘲先,我用jmap导出内存dump,一个多小时都没有导出来,而且期间jvm会处于假死状态,网站无法访问)。 就打算用jstat来看下各个段的大致使用情况(主要使用jstat -gcutil/jstat -gccapacity/jmap -heap)。

发现Permgen段使用量为97%,而且已经使用了120多M了 (其实当时jstat中的标记是M:指代Java8中的Metaspace,而Permgen标记应该为P,这犯了个傻,误认为是PermGen了)
阅读全文

重建索引-没那么简单

在ask tom看到一个很精彩的讨论,关于索引的重建,解释了很多问题,值得学习。

索引的“平衡”

贴子中的这句话引起了我的兴趣,因为之前习惯将重建索引安排到数据库定期维护中。

The time lag between index rebuilds should be approximate FOREVER. They would destroy the equilibrium that the system worked so hard to get to.

对于这个结论的解释是:

在对索引列进行反复更新时,这条记录在索引树上的位置也在不断变化移动,在这期间索引会不断的增大:当可移动的空间不够时,数据库会扩展空间(拆分block)。这将造成索引上有许多空闲的blocks(正常情况下这些空闲的Leaf blocks会在freelist上,在需要的时候会被重用),但这并不会造成问题,扩展到一定程度后,即便再插入新的数据,索引树也不需要再继续扩展,因为索引树上空闲空间已经足够多,可以被新数据重复利用了。

引用中的”平衡”就是指这:使用中Freelist上blocks共成的组成的索引。两者比例应该是最适合系统业务运行的(因为是通过实际业务操作一点一点形成的,就像HashMap一样,所占用空间并不能完全被利用,而是通过LoadFactor来控制),或者说是因为系统业务特点而造成了索引离散性,正常情况下这应该是索引的最佳状态

而当重建索引后,所有经过以上过程产生blocks清掉。但随着系统业务模块运行,反复的更新再次导致不断记录在索引的移动,而索引又重新开始尝试扩展到”最佳状态”的过程。

阅读全文