Linux磁盘挂载、分区、扩容操作

注:以下操作系统环境为CentOS7

基本概念

在操作前,首先要了解一些基本概念

磁盘

在Linux系统中所有的设备都会以文件的形式存储。设备一般保存在/dev目录下面,以sda、sda1、sda2 …,sdb、sdb1…,hda,hdb。现在的设备一般都是sd命名,以前的很老的硬盘是以ha命名。
sda:第一块硬盘,如果对磁盘进行了分区会有sda1(第一个分区),sda2等。
sdb:第二个硬盘,同样对硬盘分区后有sdb1,sdb2等。

分区

分区的目的就是便于管理,比如在Windows系统我们一般会分C盘,D盘,E盘等。

Linux只能创建4个主分区,如果需要创建更多的分区那么久必须创建逻辑分区,其中逻辑分区需要占用一个主分区。

文件系统

Linux中的文件系统也就是分区类型,在Windows中有NTEF,FAT32等,linux中常见的有Ext2、Ext3、Ext4、Linux swap、proc、sysfs、tmpfs等,可以通过mount命名查看当前已挂载的文件系统。

格式化

在前面创建完分区后有一步是要对分区进行格式化,其实在Windows系统中也是一样,在创建好一个分区后也需要将分区格式化,只有格式化成具体的文件类型才能使用。

挂载

在Windows中分区格式化后就可以使用,但是在Linux系统中必须将分区挂载到具体的路径下才可以。

常用命令

1
2
3
4
lsblk  查看当前磁盘情况
df -lh 查看文件系统情况 -l 查看挂载点
parted -l 会列出文件系统类型
fdisk -l 查看当前未挂载硬盘

挂载新硬盘

挂载一个新硬盘基本思路是:创建分区、创建文件系统、挂载。

一、查看新硬盘

首先,查看硬盘状况:

1
fdisk -l

其中:
如果磁盘下面有类似:Disk /dev/sdc doesn’t contain a valid partition table;或者说磁盘下面没有类似于:sdb1 sdb2 说明该磁盘未挂载

这里假设看到硬盘名为 /dev/sdb

二、创建分区

1
dfisk /dev/sdb

根据提示,依次输入”n”,”p” “1”,两次回车,”wq”
意思就是新建一个主分区(1),大小是整个sdb磁盘,然后写入。

注:上述操作为了简便,只是创建一个主分区。其实一个磁盘最多有四个主分区(包括一个扩展分区),1-4都是主分区,我们也可以把一个分区作为扩展分区(通过df -lh 查看到的system为Extended)

此时磁盘已经分区,但是还没有文件系统,磁盘依然不能用

三、写入系统

1
mkfs.ext4 /dev/sdb

该命令会格式化磁盘并写入文件系统

四、挂载

比如挂载到/data下面

1
2
mkdir /data # 如果存在此步省略
mount /dev/sdb /data

五、设置开机自动挂载

以上只是临时挂载,还需设置为开机自动挂载

1
2
3
4
5
6
vim /etc/fstab


# 然后在内容结尾处增加一行(注意文件类型要对应):

/dev/sdb /data ext4 defaults 0 0

扩容

关于挂载到已有目录

如果你要用来挂载的目录里面并不是空的,那么挂载了文件系统之后,原目录下的东西就会暂时的消失。并不是被覆盖掉,而是暂时的隐藏了起来,等到新分割槽被卸除之后,则原目录原本的内容就会再次出来。

如果要永久挂载已有目录,可以在新硬盘创建文件系统后,先挂载到一个临时目录,然后把要扩展的目录复制到这临时目录,然后删除要扩展的目录,再卸载临时挂载点,重新挂载到要扩展的目录上。举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 比如要扩充 /var

# 在创建好文件系统后 新建临时挂载点 storage
mkdir /storage

# 将/dev/sdb1挂载到/storage下
mount /dev/sdb1 /storage

# 拷贝/var下的所有内容到新的硬盘
cp -pdr /var /storage
# 或在/var 目录下执行:find . -depth -print | cpio - pldvm /temp
# 删除当前/var目录下的内容
rm -rf /var/*
# 重新挂载硬盘到/var目录
umount /dev/sdb1
mount /dev/sdb1 /var

# 过程中若提示磁盘忙,使用fuser找出将正在使用磁盘的程序并结束掉;

fuser -m -v /var
fuser -m -v -i -k /var

扩展

如果扩容比较频繁,那推荐使用LVM管理(最后一个参考链接)

参考

http://www.cnblogs.com/chenmh/p/5096592.html
https://segmentfault.com/a/1190000004585900
https://blog.csdn.net/wzb56_earl/article/details/7580601
https://www.cnblogs.com/sourceforge/p/mount-folder-to-new-disk-in-centos.html
http://www.cnblogs.com/gaojun/archive/2012/08/22/2650229.html

Share Comments

Laravel关联模型中过滤结果为空的结果集(has和with区别)

首先看代码:

1
2
3
4
5
6
$userCoupons = UserCoupons::with(['coupon' => function($query) use($groupId){
return $query->select('id', 'group_id', 'cover', 'group_number', 'group_cover')->where([
'group_id' => $groupId,
]);
}])
// 更多查询省略...

数据结构是三张表用户优惠券表(user_coupons)、优惠券表(coupons),商家表(corps),组优惠券表(group_coupons) (为了方便查看,后两项已去除)

这里我本意想用模型关联查出用户优惠券中属于给定组gourpId的所有数据(如果为空该条数据就不返回)。

但有些结果不是我想要的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
array(20) {
["id"]=>
int(6)
["user_id"]=>
int(1)
["corp_id"]=>
int(1)
["coupon_id"]=>
int(4)
["obtain_time"]=>
int(1539739569)
["receive_time"]=>
int(1539739569)
["status"]=>
int(1)
["expires_time"]=>
int(1540603569)
["is_selling"]=>
int(0)
["from_id"]=>
int(0)
["sell_type"]=>
int(0)
["sell_time"]=>
int(0)
["sell_user_id"]=>
int(0)
["is_compose"]=>
int(0)
["group_cover"]=>
string(0) ""
["is_delete"]=>
int(0)
["score"]=>
int(100)
["created_at"]=>
NULL
["updated_at"]=>
NULL
["coupon"]=>
NULL // 注意返回了coupons为空的数据
}

记录中有的coupon有记录,有的为空。想想也是,with只是用sql的in()实现的所谓预加载。无论怎样主user_coupons的数据都是会列出的。

它会有两条sql查询,第一条查主数据,第二条查关联,这里第二条sql如下:

1
select `id`, `group_id`, `cover`, `group_number`, `group_cover` from `youquan_coupons` where `youquan_coupons`.`id` in (1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 13, 14) and (`group_id` = 1) and `youquan_coupons`.`deleted_at` is null

如果第二条为空,主记录的关联字段就是NULL。

后来看到了Laravel关联的模型的has()方法,has()是基于存在的关联查询,下面我们用whereHas()(一样作用,只是更高级,方便写条件)

这里我们思想是把判断有没有优惠券数据也放在第一次查询逻辑中,所以才能实现筛选空记录。

加上whereHas()后的代码如下

1
2
3
4
5
6
7
$userCoupons = UserCoupons::whereHas('coupon', function($query) use($groupId){
return $query->select('id', 'group_id', 'cover', 'group_number', 'group_cover')->where([
'group_id' => $groupId,
]);
})->with(['coupon' => function($query) use($groupId){
return $query->select('id', 'group_id', 'cover', 'group_number', 'group_cover');
}])-> // ...

看下最终的SQL:

1
select * from `youquan_user_coupons` where exists (select `id`, `group_id`, `cover`, `group_number`, `group_cover` from `youquan_coupons` where `youquan_user_coupons`.`coupon_id` = `youquan_coupons`.`id` and (`group_ids` = 1) and `youquan_coupons`.`deleted_at` is null) and (`status` = 1 and `user_id` = 1)

这里实际上是用exists()筛选存在的记录。然后走下一步的with()查询,因为此时都筛选一遍了,所以with可以去掉条件。

显然区分这两个的作用很重要,尤其是在列表中,不用特意去筛选为空的数据,而且好做分页。

Share Comments

关于JWT(Json Web Token)的思考及使用心得

什么是JWT?

JWT(Json Web Token) 是一个开放的数据交换验证标准rfc7519 ( https://tools.ietf.org/html/rfc7519 ) ,一般用来做轻量级的API鉴权。由于许多API接口设计是遵循无状态的(比如Restful),所以JWT是Cookie Session这一套机制的替代方案。

组成

JWT由三部分组成头部(header)、载荷(payload)、签名(signature)。头部定义类型和加密方式;载荷部分放不是很重要的数据;签名使用定义的加密方式加密base64后的header和payload和一段你自己的加密key。最后的token由base64(header).base64(payload).base64(signatrue)组成。

使用方式

平时需要鉴权的接口需要传这个token,可以post字段提交,但是一般建议放在header头中 ,因为JWT一般配合https使用,这样就万无一失。

为什么安全?

首先token是服务端签发,然后验证时是用同样加密方式把header、payload和key再加密遍 然后看是不是和签名一致 如果不一致就说明token是非法的 这里主要靠的是加密(比如HS256)难以被攻破 至少目前吧 另外不得不说这里的加密对服务器来说是一个开销 这也是JWT的缺点

使用

我很早就听说过JWT 但那时候还没用上 感觉近几年前后端分离思想加速了JWT的使用 MVC前置到前端(VUE、REACT)后端只用提供API API强调无状态 自然而然使用了JWT这套方案

之前做小程序时 没有绝对使用JWT这套方案 我把它简化了下

最近开发的一套项目用的是Laravel做后端提供API服务,所以自然而然使用了JWT,使用的扩展是tymon/jwt-auth 关于这套扩展机制的具体使用可以参考这篇文章 写的很不错:https://laravel-china.org/articles/10885/full-use-of-jwt

项目使用时的具体细节

JWT三个时间概念

JWT有三个时间概念: 过期时间 宽限时间 刷新时间

上面那文章说token过了过期时间是不可刷新的,但其实是可以刷新的,我这边使用时可以(开启了黑名单机制和1min宽限时间) 但是过了刷新时间不能刷新这是肯定的

token刷新

可以借用laravel的中间件实现自动刷新 服务端判断过期了但是在刷新时间内主动刷新一次 并把新的token在Header头中返回给客户端

记住我功能

我感觉定义刷新时间(比如一个月) + token自动刷新机制这一套就是记住我功能

不能主动销毁Token

默认JWT这套方案好像不可以主动剔除用户的,因为服务端不会存token,只是验证。这和session不一样。 但是我感觉可以借用黑名单机制 来判断 中间件中判断该token在剔除黑名单中就销毁token并返回鉴权失败

单点登录

结合缓存比如redis存客户端标识是可以的

多套用户权限机制

比如客户端有用户端和商家端两套用户机制(不同表),可以在auth.php定义两个guard 分别指定对应的model Login逻辑单独写 比如auth(‘userApi’) auth(‘corpApi’) 可以拿到不同的guard 刷新中间件是可以共用的 不过router中的middleware方法可以指定具体的middleware

为什么不同权限体系的刷新中间件可以公用

感觉是jwt-auth实现了这机制 验证token时会根据sub字段判断 我base64decode两个token的payload后发现sub一个是1 一个是2

如果有问题欢迎指正!

Share Comments