Redis使用详解

分類 后端, Redis

Redis使用详解

一、NoSQL简介

NoSQL分类

  • 键值(Key-Value)存储数据库:这一类数据库主要会使用到一个hash表,如Redis、Oracle BDB
  • 列存储数据库:通常是用来应对分布式存储的海量数据,键仍然存在但是他们指向了多个列,如HBase、Riak
  • 文档型数据库:该类型的数据模型是版本化的文档,比如JSON,允许之间进行嵌套,如MongoDB

非关系型数据库特点

  • 数据模型比较简单

  • 对于数据库性能要求较高

  • 不需要高度的数据一致性

二、Redis简介

以key-value形式存储,不一定遵循传统数据库的一些基本要求(非关系的、分布式的、开源的、水平可扩展的)

优点:对数据高并发读写

​ 对海量数据的高效率存储和访问

​ 对数据的可扩展性和高可用性

缺点:无法做太复杂的关系模型

Redis单线程:指处理我们的网络请求的时候只有一个线程来处理【文件刷盘等用的是多线程】

Redis单线程的好处

  • Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽,单线程实现比较简单

  • 单线程避免了不必要的上下文切换和竞争条件以及加锁释放锁操作

  • 使用多路I/O复用模型,非阻塞IO

三、Redis的安装

第一步:准备工作【解压tar包,创建Redis相关目录】

1
2
3
4
tar -zxvf redis-5.0.2.tar.gz 
mkdir /opt/redis
mkdir /opt/redis/conf
mkdir /opt/redis/data

第二步:编译redis

1
2
3
4
5
#进入解压后的tar包执行
make
#执行结束之后进入src目录
cd src
make install PREFIX=/opt/redis

第三步:移动配置文件到conf目录

1
cp redis.conf /opt/redis/conf

额外配置:

  • redis开机自启
1
2
3
vim /etc/rc.local
#加入
/opt/redis/bin/redis-server /opt/redis/conf/redis.conf
  • 配置数据保存目录
1
2
3
vim /opt/redis/conf/redis.conf
#修改
dir ./ ---> dir /opt/redis/data/

四、Redis基本通用命令

命令 说明
KEYS [pattern] 查找出匹配的key【生成环境禁止使用,数据量太大阻塞生成环境】
DBSIZE 统计key总数【使用的是redis的内部计数,并不是全部扫描,生产可用】
EXISTS key [key …] 检查key是否存在【存在返回1,不存在返回0】
DEL key [key…] 删除key【成功删除返回1,不存在此key返回0】
EXPIRE key seconds 设置过期时间
TTL key 查看剩余的过期时间【-2代表已不存在,-1代表永不过期】
PERSIST key 取消key的过期设置
TYPE key 查询key的类型

五、Redis数据类型及其使用

注意:redis操作下标都是闭区间的

字符串【String】

String的值类型可以为字符类型、数字类型、bit类型

String类型是包含很多中类型的特殊类型,并且是二进制安全的。比如序列化的对象进行存储,比如一张图片进行二进制存储,比如一个简单地字符串,数值等等。

使用场景:缓存、计数器、分布式锁等

常用命令格式 描述
{ GET key },{ MGET key [key …] } 得到String
{ SET key value },{ MSET key value [key value …] } 设置String
{ SETNX key value },{ MSETNX key value [key value …] } 如果不存在则设置
SET key value XX 如果存在则设置
SETEX key second value 设置过期时间
{ INCR key },{ INCRBY key increment } 自增操作
{ DECR key },{ DECRBY key decrement } 自减操作
GETSET key value 得到就值设置新值
APPEND key value 追加字符串
STRLEN key 得到字符串长度,内部存有计数
GETRANGE key start end 得到指定长度的value
SETRANGE key offset value 设置指定偏移量字符串内容

哈希【Hash】

使用场景:存储具有一定结构化的数据

常用命令格式 描述
HGET key field 得到key对应field的value
HSET key field value 设置key对应field的value
HDEL key field 删除key对应field的value
HEXISTS key field 判断key的field是否存在
HLEN key 获取指定key的filed数量【内部计数,生成环境可用】
HMGET key field [field …] 批量获得hash的field对应的value
HMSET key field value [field value …] 批量设置hash的field和value
HINCRBY key field increment 增加指定increment的对应key的field
HGETALL key 获取key对应所有field和value【生成环境慎用】
HVALS key 返回key对应的所有value【生成环境慎用】
HKEYS key 返回key对应的所有field【生成环境慎用】
HSETNX key field value 不存在此key对应的field则设置

列表【List】

列表为有序、可重复结构。可指定位置插入和删除、也可从左右插入和弹出(模拟栈结构)

常用命令格式 描述
**LPUSH RPUSH** key value [value …]
LINSERT key BEFORE AFTER value newValue
**LPOP RPOP** key
LREM key count value 根据count值,从列表删除所有等于value的值【时间复杂度O(N)】
【count>0,从左到右删除count个】
【count<0,从右到坐删除count个】
【count=0,删除所有value相等的值】
LTRIM key start end 按照索引范围保留list,删除大链表有用【时间复杂度O(N)】
LRANGR key start end 获取列表指定索引范围内的值,数值为负则从右往左取值【时间复杂度O(N)】
LINDEX key index 获取列表指定索引的值,数值为负则从右取值【时间复杂度O(N)】
LLEN key 获取list长度【内部计数,生成环境可用】
LSET key index newValue 设置指定位置的值【时间复杂度O(N)】
**BLPOP RLPOP** key timeout

使用技巧:

  • LPUSH + LPOP = Stack
  • LPUSH + RPOP = Queue
  • LPUSH + LTRIM = Capped Collection【固定容量集合】
  • LPUSH + BRPOP = Block Queue

集合【Set】

Set无序、无重复、有集合间操作。

常用命令格式 描述
SADD key element [member …] 向集合key添加元素
SREM key element 删除集合key中的element元素
SCARD key 查询集合元素的个数【内部计数,生成环境可用】
SISMEMBER key element 集合中是否存在element元素
SRANDMEMBER key 随机得到一个元素
SMEMBERS key 获取集合所有元素【慎用】
SPOP key 随机弹出一个元素
{ SDIFF key [key …] }、{ SDIFFSTORE destination key [key …] } 返回/存储一个集合的全部成员,该集合是所有给定集合之间的差集
{ SINTER key [key …] }、{ SINTERSTORE destination key [key …] } 返回/存储一个集合的全部成员,该集合是所有给定集合的交集
{ SUNION key [key …] }、{ SUNIONSTORE destination key [key …] } 返回/存储一个集合的全部成员,该集合是所有给定集合的并集

使用技巧

  • SADD = Tagging

  • SPOP/SRANDMEMBER = Random Item

  • SADD + SINTER = Social Graph【共同关注、有着相同兴趣等】

有序集合【ZSet】

ZSet有序、无重复、包含分值与元素,有集合间操作。

使用场景:排行榜

常用命令格式 描述
ZADD key score member [score member …] 向集合添加元素
ZREM key member [member …] 移除集合中的元素
ZSCORE key member 得到元素的分数
ZINCRBY key increment member 增长元素的分数
ZCARD key 获得集合中元素的个数
ZRANK key member 成员按分值递减(从小到大)排列的排名
ZRANGE key start stop [WITHSCORES] 按score值递增(从小到大)排序,WITHSCORES返回分数
ZRANGEBYSCORE key max min [WITHSCORES] 返回分数范围内数据,按score值递增(从小到大)排序,WITHSCORES返回分数
ZCOUNT key min max 统计得到分数在min和max之间的元素个数
{ ZREMRANGEBYRANK key start stop }、{ ZREMRANGEBYSCORE key min max} 按照排名/分数范围删除元素
ZREVRANK key member 成员按分值递减(从大到小)排列的排名
ZREVRANGE key start stop [WITHSCORES] 按score值递增(从大到小)排序,WITHSCORES返回分数
ZREVRANGEBYSCORE key max min [WITHSCORES] 返回分数范围内数据,按score值递增(从大到小)排序,WITHSCORES返回分数

六、Redis高级功能

慢查询日志

Redis-Life-Cycle

  • 慢查询发生在第三阶段
  • 客户端超时不一定是慢查询导致,但慢查询可导致客户端超时

慢查询队列简介

  • 先进先出的队列
  • 固定长度
  • 长度不够时,丢弃最早记录
  • 保存在内存中

慢查询设置

  • slow-max-len【慢查询队列长度,默认128】

  • slowlog-log-slower-than【慢查询的阙值(微秒,1000000 微秒=1 秒),默认10000(10毫秒),建议为1000(1毫秒)】

更改慢查询参数建议使用CONFIG SET parameter value方式,而不是更改redis.conf文件重启redis

慢查询命令

常用命令格式 描述
slowlog get n 获取慢查询队列一条记录
slowlog len 获取慢查询队列长度
slowlog reset 清空慢查询队列

查询结果示例

Redis-Slow-Examples

Pipeline

Redis-Process

一般情况下客户端发送一条命令到Redis,Redis处理结束返回结果,即 N条命令 = N次网络时间 + N次计算时间

PipeLine将命令打包发送给客户端,Redis处理完返回结果,PipLine是一个异步处理方式,并不等待Redis返回。

Pipeline(N条命令) = 1次网络时间 + N次计算时间

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
43
/*
* Jest执行参考
*/
long oldPipeline = Instant.now().toEpochMilli();
for (int i = 0; i < 100; i++) {
Pipeline pipeline = jedis.pipelined();
for (int j = 0; j < 100; j++) {
pipeline.sadd("S" + i * 100 + j, j + "");
}
//pipeline.sync();异步不接受返回结果
pipeline.syncAndReturnAll(); //异步接受返回结果
}
long nowPipeline = Instant.now().toEpochMilli();
System.out.println(nowPipeline - oldPipeline);

/**
* Spring Data Redis参考
*/
// 批量插入
redisTemplate.executePipelined(new RedisCallback<Void>() {
@Override
public Void doInRedis(RedisConnection connection) throws DataAccessException {
String keyPrefix = "pipeline-";
for (int i = 0; i < 10000; i++) {
String key = keyPrefix + i;
connection.set(key.getBytes(), String.valueOf(i).getBytes());
}
}
});

// 批量获取
List<Object> data = redisTemplate.executePipelined(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
String keyPrefix = "pipeline-";
for (int i = 0; i < 10000; i++) {
String key = keyPrefix + i;
connection.get(key.getBytes());
}
}
});

data.stream().forEach(System.out::println);

发布订阅

常用命令格式 描述
PUBLISH channel message 将信息 message 发送到指定的频道 channel
SUBSCRIBE channel [channel …] 订阅给定的一个或多个频道的信息
PSUBSCRIBE pattern [pattern …] 订阅一个或多个符合给定pattern的频道
UNSUBSCRIBE [channel [channel …]] 取消订阅

Bitmap

常用命令格式 描述
SETBIT key offset value 对 key所储存的字符串值,设置或清除指定偏移量上的位(bit)
GETBIT key offset 对 key所储存的字符串值,获取指定偏移量上的位(bit)
BITCOUNT key [start] [end] 计算给定字符串中,被设置为 1的比特位的数量
BITOP operation destkey key [key …] 对一个或多个保存二进制位的字符串 key进行位元操作,并将结果保存到 destkey上
operation 可以是 ANDORNOTXOR 这四种操作中的任意一种
BITPOS key bit [start] [end] 返回位图中第一个值为 bit的二进制位的位置

HyperLogLog

实质:用String类型实现,不能取出具体值,有错误率

作用:极小的空间实现独立数量统计

常用命令格式 描述
PFADD key element [element …] 将任意数量的元素添加到指定的HyperLogLog
PFCOUNT key [key …] 计算HyperLogLog有多少值
PFMERGE destkey sourcekey [sourcekey …] 将多个HyperLogLog合并为一个HyperLogLog

Geo

Geo(地理信息定位):存储经纬度,计算两地距离,范围计算等

Geo是使用ZSet实现

常用命令格式 描述
GEOADD key longitude latitude member [longitude latitude member …] longitude:经度,latitude:维度,member:标识
GEOPOS key member [member …] 返回经纬度
GEODIST key member1 member2 [unit] 返回两个给定位置之间的距离
GEORADIUS key longitude latitude radius m km
ZREM key member 删除成员

七、Redis持久化

RDB【Redis Database】

​ RDB是Redis用来进行持久化的一种方式,是把当前内存中的数据集快照写入磁盘,也就是 Snapshot 快照(数据库中所有键值对数据)。恢复时是将快照文件直接读到内存里。

触发机制

  • save

    手动触发,同步命令,会阻塞线程

  • bgsave

    手动触发,fork出一个子进程,异步命令,不会阻塞线程【阻塞仅仅会发生在fork出子进程的阶段】

  • 自动

    1
    2
    3
    save 900 1            #900秒改变1个就生成rdb文件
    save 300 10 #300秒改变10个就生成rdb文件
    save 60 10000 #60秒改变10000个就生成rdb文件

    一般情况下建议关闭自动策略

  • 全量复制

    从节点执行全量复制操作的时候,主节点会自动触发bgsave命令生存rdb文件并发送给从节点

  • debug reload

    在执行debug reload重新加载redis的时候,也会自动触发bgsave

  • shutdown

    默认情况下执行shutdown命令,如果没有开启AOF持久化功能,就会自动执行bgsave

RDB配置参数

命令格式 描述
rdbcompression 压缩RDB文件,默认yes
rdbchecksum RDB文件是否进行校验,默认yes
dbfilename dump.rdb RDB文件名【可使用dump-端口号.rdb区分不同的redis实例】
dir ./ RDB文件存储的目录
stop-writes-on-bgsave-error bgsave出现错误时是否停止写入,默认yes

AOF【Append-Only File】

​ AOF是一个追加写入的日志文件从而实现持久化的方式,生成的AOF文件是可识别的纯文本文件。Redis默认使用RDB持久,开启AOF持久化需要设置appendonlyyes

AOF文件生成策略

  • always 不丢失数据,每次更新记录数据就进行io操作

  • everysec 可能会丢失1s数据,但io小

  • no 不启用AOF

AOF重写

​ AOF支持AOF文件重写(从内存中读取的数据,并非读取上次的AOF文件进行重写)。AOF重写可以减少硬盘占用、加速恢复速度。

Aof-Rewrite

AOF配置参数

命令格式 描述
appendonly 是否开启AOF,默认no
appendfilename 生成AOF文件明【可使用appendonly-端口号.aof区分不同的redis实例】
appendfsync 刷盘策略,默认everysecond
dir 保存文件的目录,默认./
no-appendfsync-on-rewrite AOF重写过程中是否禁止append操作,默认no允许append
auto-aof-rewrite-min-size 进行AOF时文件最小尺寸,默认64mb
auto-aof-rewrite-percentage 下次进行AOF操作时的增量,默认100
aof-load-truncated AOF文件结尾不完整,Redis重启忽略不完整记录,默认yes

命令

  • bgrewriteaof 手动进行AOF重写操作
  • aof_current_size 查看AOF当前尺寸

redis-cli info Persistence可以查看统计信息aof_current_size【当前AOF文件大小】和aof_base_size【上次重写AOF文件大小】

RDB与AOF比较

命令 RDB AOF
启动优先级
体积
恢复
数据安全性 丢数据 根据策略决定
轻重

混合持久化

Redis 4.0 开始支持 rdb 和 aof 的混合持久化(默认开启),这样做的好处是可以结合 rdb 和 aof 的优点, 快速加载同时避免丢失过多的数据。缺点 aof 里面的 rdb 部分就是压缩格式不再是 aof 格式,可读性差。

  • 配置开启混合持久化aof-use-rdb-preamble yes

  • 命令开启混合持久化config set aof-use-rdb-preamble yes

开启混合持久化时,aof rewrite 的时候就直接把 rdb 的内容写到 aof 文件开头。aof 文件内容会变成如下

1
2
3
4
5
6
7
8
9
10
11
12
13
+------------------------+
| |
| |
| RDB |
| FORMAT |
| |
| |
| |
+------------------------+
| |
| AOF |
| FORMAT |
+------------------------+

持久化相关优化

fork操作优化

  • 控制redis实例最大可用内存:maxmemory

  • 合理配置Linux内存分配策略:vm.overcommit_momory=1(默认0,当内存少时fork阻塞不进行)

  • 降低fork频率:例如放宽AOF重写自动触发时机,不必要的全量复制

子进程

  • cpu

    开销:RDB和AOF文件生成,属于CPU密集型

    优化:不做CPU绑定,不和CPU密集型服务部署

  • 内存

    开销:fork内存开销,使用了linux的copy-on-write【父进程未发生改变的内存页,不进行copy-write】

    优化:echo never > /sys/kernel/mm/transparent_hugepage/enabled【不分配大内存页】

  • 硬盘

    开销:AOF和RDB文件写入,可以结合iostat,iotop分析

    优化:不和高硬盘负载服务部署一起,no-appendfsync-on-rewrite设置为yes

八、Redis主从复制原理和优化

  • 一个master可以有多个slave
  • 一个slave只能有一个master
  • 数据流向是单向的,master到slave

主从实现两种方式

  • 在从机上执行slaveof masterIp masterPort,此命令是异步。slaveof no one结束从属关系。

  • 修改redis配置文件

    1
    2
    slaveof masterIp masterPort               #配置主从
    slave-read-only yes #从节点只读

主从状态查看info replication

全量复制和部分复制

redis4后使用psync2实现复制使redis重启也可使用部分同步,还为解决在主库故障时候从库切换为主库时候使用部分同步机制。redis从库默认开启复制积压缓冲区功能,以便从库故障切换变化master后,其他落后该从库可以从缓冲区中获取缺少的命令。该过程的实现通过两组replid、offset替换原来的master runid和offset变量实现:

  • 第一组:master_replid和master_repl_offset

    如果redis是主实例,则表示为自己的replid和复制偏移量; 如果redis是从实例,则表示为自己主实例的replid1和同步主实例的复制偏移量。

  • 第二组:master_replid2和second_repl_offset

    无论主从,都表示自己上次主实例repid1和复制偏移量;用于兄弟实例或级联复制,主库故障切换psync

判断是否使用部分复制条件:如果从库提供的master_replid与master的replid不同,且与master的replid2不同,或同步速度快于master; 就必须进行全量复制,否则执行部分复制。

以下常见的主从切换都可以使用部分复制:

  • 一主一从发生切换,A->B 切换变成 B->A
  • 一主多从发生切换,兄弟节点变成父子节点时
  • 级别复制发生切换, A->B->C 切换变成 B->C->A

全量复制开销

  • bgsave时间
  • RDB文件网络时间
  • 从节点清空数据时间
  • 从节点加载RDB时间
  • 可能的AOF重写时间

当从库与主库断开时间过长导致自己的偏移量不在master_repl_offset允许的范围之内,会触发全量复制

主从相关参数配置

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
##############从库##############

slaveof <masterip> <masterport>
#设置该数据库为其他数据库的从数据库

masterauth <master-password>
#主从复制中,设置连接master服务器的密码(前提master启用了认证)

slave-serve-stale-data yes
# 当从库同主库失去连接或者复制正在进行,从库有两种运行方式:
# 1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续相应客户端的请求
# 2) 如果slave-serve-stale-data设置为no,除了INFO和SLAVOF命令之外的任何请求都会返回一个错误"SYNC with master in progress"

slave-priority 100
#当主库发生宕机时候,哨兵会选择优先级最高的一个称为主库,从库优先级配置默认100,数值越小优先级越高

slave-read-only yes
#从节点是否只读;默认yes只读,为了保持数据一致性,应保持默认


##############主库##############

repl-disable-tcp-nodelay no
#在slave和master同步后(发送psync/sync),后续的同步是否设置成TCP_NODELAY假如设置成yes,则redis会合并小的TCP包从而节省带宽,但会增加同步延迟(40ms),造成master与slave数据不一致假如设置成no,则redis master会立即发送同步数据,没有延迟
#前者关注性能,后者关注一致性

repl-ping-slave-period 10
#从库会按照一个时间间隔向主库发送PING命令来判断主服务器是否在线,默认是10秒

repl-backlog-size 1mb
#复制积压缓冲区大小设置

repl-backlog-ttl 3600
#master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为秒。

min-slaves-to-write 3
min-slaves-max-lag 10
#设置某个时间断内,如果从库数量小于该某个值则不允许主机进行写操作,以上参数表示10秒内如果主库的从节点小于3个,则主库不接受写请求,min-slaves-to-write 0代表关闭此功能。

主从配置问题

maxmomory不一致导致丢失数据

数据结构参数优化只有优化了主机,从机未配置导致内存不一致,数据错误或丢失

九、Redis Sentinel

  • 主观下线:Sentinel根据配置条件,发现redis节点达到故障标准,则此Sentinel认为此redis节点下线
  • 客观下线:当Sentinel中认为此redis客观下线的总数达到配置阙值,则认为此节点客观下线

安装Sentinel

配置sentinel.conf文件

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
#没有开启bind和密码的情况下,保护模式默认被开启。只接受来自环回IPv4和IPv6地址的连接。拒绝外部连接
bind 127.0.0.1 192.168.1.1
protected-mode no

#端口
port 26379

#是否守护进程模式运行
daemonize no

#pid以及日志文件位置
pidfile /var/run/redis-sentinel.pid
logfile ""

#工作目录
dir /tmp

#监控的master
#mymaster指此主从组的名称【Sentinel可以监控多个主从组】
#最后一个数字代码多少个sentinel主观认为此master宕机为客观宕机事实
sentinel monitor mymaster 127.0.0.1 6379 2

#当多少毫秒master不返回ping结果即认为主观宕机
sentinel down-after-milliseconds mymaster 30000

#master重新选举之后slave并发复制master数据的并发量
sentinel parallel-syncs mymaster 1

#故障转移超时时间,默认为3分钟
sentinel failover-timeout mymaster 180000

#当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会调用这个脚本
#如果脚本以“1”退出,则稍后重试执行(最多可执行10次),脚本的最长运行时间为60秒
#sentinel notification-script mymaster /var/redis/notify.sh

#当master因failover而发生改变,这个脚本将被调用,通知相关的客户端关于master地址已经发生改变的信息
#sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

#不允许使用SENTINEL SET设置notification-script和client-reconfig-script
sentinel deny-scripts-reconfig yes

Sentinel三个定时任务

  • 每10秒每个sentinel对master和slave执行info
    • 发现slave
    • 确认主从关系
  • 每2秒每个sentinel通过master节点的channel交换信息(pub/sub模式)
    • 通过__sentinel__:hello频道交互
    • 交互各节点的“看法”及自身信息
  • 每1秒每个sentinel对其他sentinel和redis节点执行ping
    • 心跳检查、失败依据

Master选举过程

第一步:Sentinel选举出leader

原因:只需要一个Sentinel完成故障转移

选举:通过sentinel is-master-down-by-addr命令都希望自己成为领导者

  • 每个做主观下线的Sentinel节点向其它Sentinel节点发送命令,要求它给自己投票
  • 收到命令的Sentinel节点如果没有同意其它Sentinel节点发送的命令,则同意投票否则拒绝
  • 如果该Sentinel节点发现自己的票数已经超过Sentinel半数,那么它将成为leader
  • 如果此过程未选出leader则等待一段时间继续选举

第二步:故障转移选举Master

  • 从slave节点中选出一个“合适”的节点作为新的master节点

    • 选择slave-prority最高的slave节点,如果存在则返回【一般不修改】
    • 选择复制偏移量最大的slave节点,如果存在则返回
    • 选择runId最小的slave节点
  • 对上面的slave节点执行slaveof no one命令让其成为master节点

  • 向剩余的slave节点发送slaveof命令,让它们成为master节点的slave节点,复制规则和paraller-sync参数有关

  • 更新对原来master节点配置为slave,并保存对其“关注”,当其恢复后命令它去复制新的master节点

手动下线master机器sentinel failover <masterName>

十、Redis Cluster

数据分布概论

Data-Distribution

分布方式 特点 典型产品
哈希分布 数据分散度高
Memcache

Redis Cluster

|
| 键值分布业务无关 |
| 无法顺序访问 |
| 支持批量操作 |
| 顺序分布 | 数据分散度易倾斜 |

BigTable

HBase

|
| 键值分布业务相关 |
| 可顺序访问 |
| 支持批量操作 |

哈希分布方式

节点取余分区

Delivery-Partition

进行取模运算,将余数相等的放入同一节点,简单易操作,增加节点时数据偏移,导致数据的前移达到80%,翻倍扩容可以使数据迁移从80%降到50%

一致性hash分区

Consistency-Hash

为系统中每个节点分配一个token,范围一般在0~2的32次方,这些token构成哈希环。数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的token节点,往往一个节点会对应多个token。加减节点会造成哈希环中部分数据无法命中,需要手动处理或者忽略这些数据,常用于缓存场景

虚拟哈希分区

Slot-Partition

虚拟分槽使用哈希函数把所有数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。槽数范围远远大于节点数(redisCluster槽的范围是0~16383),每一个节点负责维护一部分槽以及所映射的键值数据

基本架构

cluster-architecture

  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽

  • 节点的fail是通过集群中超过半数的master节点检测失效时才生效

  • 客户端与redis节点直连,不需要中间proxy层.客户端连接集群中任何一个可用节点即可

  • redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key

安装Cluster

修改redis配置文件

1
2
cluster-enable yes
cluster-config-file nodes-${port}.conf

原生安装

  • 启动所有节点

  • 执行redis-cli -p ${port} cluster meet ${ip} ${port}使节点相遇

  • 执行cluster addslots slot [slot...]分配槽

    1
    2
    3
    4
    5
    6
    7
    8
    start=$1
    end=$2
    port=$3
    for slot in `seq ${start} ${end}`
    do
    echo "slot:${slot}"
    /opt/redis/bin/redis-cli -p ${port} cluster addslots ${slot}
    done
  • 执行redis-cli -p ${port} cluster replicate ${nodeId}执行主从分配

集群命令安装

  • redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

    –cluster-replicas代表集群的每个主节点的从节点个数

    ruby安装已废弃

cluster配置参数

1
2
3
4
cluster-enable yes
cluster-config-file nodes-${port}.conf
cluster-node-timeout 15000
cluster-require-full-coverage yes #必须集群所有节点能提供服务才提供服务

集群伸缩

扩展集群

扩展步骤原理

  1. 对目标节点发送cluster setslot {slot} importing {targetNodeId}命令,让目标节点准备导入槽的数据
  2. 对源节点发送cluster setslot {slot} migrating {sourceNodeId}命令,让源节点准备槽数据的导出
  3. 源节点上循环执行cluster getkeyinslot {slot} count 命令,每次获取属于这个槽中键的个数
  4. 在源节点上执行migrate {sourceIp} {sourcePort} key 0 {timeout}命令,迁移指定的key
  5. 重复执行步骤3~4直到槽下所有数据完成迁移
  6. 向集群内所有主节点发送cluster setslot {slot} node {targetNodeId}命令,通知槽分配给目标节点

Cluster-Expend

扩展执行步骤

  • 加入集群

    执行redis-cli -p ${port} cluster meet ${ip} ${port}将节点加入集群

  • 设置主从关系

    redis-cli -p ${port} cluster replicate ${nodeId}设置主从关系

  • 任意节点执行迁移槽命令,后续过程根据提示进行

    redis-cli --cluster reshard {ip}:{port}

  • 查看节点分配情况

    redis-cli -p {prot} cluster nodes | grep master

集群缩容

Cluster-Shrink

迁移槽命令和扩展集群的迁移命令相同,迁移完成之后使用redis-cli cluster forget {downNodeId}下线节点

  • 迁移数据

    redis-cli --cluster reshard {ip}:{port} --cluster-from {sourceNodeId} --cluster-to {targetNodeId} --cluster-slots {slotsNum}

  • 下线节点

    redis-cli --cluster del-node {ip}:{port} {shutdownNodeId}

客户端路由

使用cluster keyslot ${key}可查看key对应的hash值

moved重定向

Cluster-Move

ask重定向

Cluster-Ask

smart客户端

实现原理:追求性能

  1. 从集群中选一个可运行节点,使用cluster slots初始化槽和节点映射
  2. 将cluster slots的结果映射到本地缓存,为每个节点创建JedisPool
  3. 准备执行命令(使用CRC16计算key对应的槽,找到映射节点执行)

Smart-Client

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
Set<HostAndPort> nodes = new HashSet<HostAndPort>();
nodes.add(new HostAndPort("192.168.1.158", 7000));
nodes.add(new HostAndPort("192.168.1.158", 7001));
nodes.add(new HostAndPort("192.168.1.158", 7002));
nodes.add(new HostAndPort("192.168.1.158", 7003));
nodes.add(new HostAndPort("192.168.1.158", 7004));
nodes.add(new HostAndPort("192.168.1.158", 7005));
JedisCluster jedisCluster = new JedisCluster(nodes);
jedisCluster.set("hello", "cluster");
System.out.println(jedisCluster.get("hello"));
jedisCluster.close();
}

批量操作

Redis主要提供了以下几种批量操作方式:

  • 批量get/set(multi get/set)
  • 管道(pipelining)
  • 事务(transaction)
  • 基于事务的管道(transaction in pipelining)

批量操作必须key在同一个槽,导致以上用法异常苛刻

方案一:传统的串行IO操作,也就说n个key,分n次串行操作来获取key,复杂度是o(n)

方案二:将mget操作(n个key),利用已知的hash函数算出key对应的节点,这样就可以得到一个这样的关系:Map<node, somekeys>,也就是每个节点对应的一些keys,这样将之前的o(n)的效率降低到o(node.size())

MuliCmd-Serial-Io

方案三:在方案二的基础上将串行取数据改为并行取数据,进一步提高效率

MuliCmd-parell-Io

方案四:通过redis自带的hashtag功能,强制一批key分配到某台机器上【不建议,大量数据会造成数据倾斜】

1
2
3
//使用{user}作为key,使key统一
jedisCluster.mset("{user}1001","zhangsan","{user}1002","lisi","{user}1003","wangwu");
List<String> users = jedisCluster.mget("{user}1001","{user}1002","{user}1003");

故障转移

故障发现

  • 通过ping/pong信息实现故障发现,当半数以上持有槽的主节点都标记某节点主观下线则为客观下线【向集群广播下线节点的fail消息】
  • 客观下线发送通知故障节点的从节点触发故障转义流程

故障恢复

  • 资格审查
    • 每个从节点检查与故障主节点的断线时间
    • 超过cluster-node-timeout * cluster-slave-validity-factor取消资格
    • cluster-slave-validity-factor默认是10
  • 准备选举时间
    • 最接近主节点的偏移量的从节点率先发起选举,稍后其他从节点发起选举
  • 选举投票
    • 收集票数大于N/2+1即为选举成功
  • 替换主节点
    • 当前从节点取消复制变为主节点(slave no one)
    • 执行clusterDelSlot撤销故障主节点负责的槽,并执行clusterAddSlot把这些槽分配给自己
    • 向集群广播自己的pong消息,表明已经替换了故障从节点

集群运维问题

集群完整性

cluster-require-full-coverage yes默认为yes

  • 集群中16384个槽全部可用:保证集群完整性
  • 节点故障或者正在故障转移,集群不可使用

大多数情况下业务无法容忍,建议cluster-require-full-coverage设置为no

PubSub广播

任意节点发布消息所有节点都会订阅到消息,消耗带宽较多。JedisCluster只会订阅任意一个节点

数据倾斜

造成的原因:

  • 节点槽分配不均
  • 不同槽对应的键值数量差异较大【可能存在hashTag】
  • 包含bigkey
  • 内存相关配置不一致

使用redis-cli --cluster info {ip}:{port}可以查看key、slot分布情况

使用redis-cli --cluster rebalance {ip}:{port}进行数据平衡【慎用】

从机读写问题

在集群模式下从节点不接受任何读写请求

  • 命令会重定向到负责槽的主节点
  • readonly命令可以读取【连接级别】

数据迁移

官方工具不只能从单机向集群迁移,不支持断点续传,不支持在线迁移,单线程影响速度,不建议使用官方工具

十一、缓存设计与优化

缓存使用的成本

  • 数据不一致:缓存层和数据层有时间窗口不一致,和更新策略有关
  • 代码维护成本:多了一层缓存逻辑
  • 运维成本:例如Redis Cluster

缓存更新策略

redis里面存储的过期时间,都是绝对时间点,所以如果两台机器时钟不同步,那么超过的数据会全部删除。

  • slaves不会独立删除数据,而是等待master给它发送删除指令的时候,再删除数据
  • 如果slave当选为master的时候,会先淘汰keys,然后再成为master

  • 设置maxmemory-policy值指定算法

    更新策略 解释
    volatile-lru 过期的键使用LRU策略剔除,没有可删除对象则退回到noeviction
    allkeys-lru 所有键均使用LRU策略剔除,直到腾出足够空间
    volatile-lfu 过期的键使用LFU策略剔除,没有可删除对象则退回到noeviction
    allkeys-lfu 所有键均使用LFU策略剔除,直到腾出足够空间
    volatile-random 过期的键使用随机策略剔除,没有可删除对象则退回到noeviction
    allkeys-random 所有键均使用随机策略剔除,直到腾出足够空间
    volatile-ttl 剔除TTL最小的键,没有可删除对象则退回到noeviction
    noeviction 默认策略,不做任何事,返回写错误

    LRU【Least Recently Used】最近最少被使用

    LFU【Least Frequently Used】最不常用

  • 被动更新

    当客户端方位key的时候,主动检测这些key是否过期,过期就删除

  • 主动更新

    每秒检测10次以下操作,测试随机的20个keys进行相关过期检测,删除所有的过期的keys,如果有多于25%的keys过期,重复此操作

策略 一致性 维护成本
算法剔除 最差
被动更新 较差
主动更新

低一致性:最大内存和淘汰策略

高一致性:超时剔除和主动更新结合,最大内存和淘汰策略兜底

缓存穿透&缓存雪崩&无底洞

缓存穿透

Cache-Miss

特点:

当缓存和数据库中都没有数据的时候,当查询Redis没有数据的时候,会继续查询数据库,数据库也没有数据,当大量查询请求发生或遭到恶意攻击时,这些访问全部透过Redis,并且数据库也没有数据,这种现象称为“缓存穿透”。

解决方案:

  1. 缓存空对象,storage返回一个空对象,将键存储在缓存层,下次请求此键之间返回空对象
    • 需要更多的键,建议设置过期时间
    • 缓存层和存储层数据“短期”不一致
  2. 布隆过滤器

缓存击穿

特点:

当Redis的热点数据key失效时,大量并发查询直接打到数据库,此时数据库负载压力骤增,这种现象称为“缓存击穿”

hotkey-expir

解决方案:

1.设置key值永不过期

2.使用互斥锁,查到后就回填缓存

缓存雪崩

特点:

缓存雪崩是指在短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。

crash

解决方案:

  • 随机设置key过期时间
  • 随机延时,让一部分查询先将数据缓存起来
  • 设置key值永不过期

无底洞问题

现象:增加节点机器性能没提升反而下降

Cache-Node-Add

解决方案参考批量操作

热点key的重建优化

Hot-Key

现象:热点key缓存重建过程过长导致浪费了不必要的资源

解决方案:

  1. 互斥锁【使用redis构建锁机制】

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    String get(String key) {
    String value = redis.get(key);
    if(value == null) {
    String mutexKey = "mutex:key:"+key;
    if(redis.SetParams.setParams().ex(180).nx()) {
    value = db.get(key);
    redis.set(key,value);
    redis.delete(mutexKey);
    }else {
    Thread.sleep(1000);
    get(key);
    }
    }
    return value;
    }
  2. 永不过期

    • 缓存层面:没有设置过期时间
    • 功能层面:为每个value添加逻辑过期时间,但发现超过逻辑过期时间后,会使用单独的线程取构建缓存
方案 优点 缺点
互斥锁 思路简单 代码复杂度增加
保证一致性 存在死锁的风险
永不过期 基本杜绝热点key重建问题 不保证一致性
逻辑过期时间增加维护成本和内存成本

十二、Redis布隆过滤器

布隆过滤器的原理

Bloom-Filter

  1. 首先需要k个hash函数,每个函数可以把key散列成为1个整数
  2. 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0
  3. 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1
  4. 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中

优点:不需要存储key,节省空间

缺点:算法判断key在集合中时,有一定的概率key其实不在集合中,且无法删除

google-guava库实现了java版的布隆过滤器

Redis布隆过滤器

使用插件的方式部署

redis4.0之后支持使用插件的方式使用Bloom filters和cuckoo filters,redis4.0之前需手动使用代码的方式编写布隆过滤器,安装步骤参考下列链接

GitHub地址 使用文档

建议在conf配置文件中配置,不使用redis-server --loadmodule /path/to/rebloom.so启动

1
loadmodule /path/to/rebloom.so

布隆过滤器命令

命令 说明
BF.RESERVE {key} {error_rate} {size} 创建布隆过滤器,error_rate为错误率,size为预期数据大小
BF.ADD {key} {item} 添加item到指定布隆过滤器
BF.MADD {key} {item} [item …] 批量添加item到指定布隆过滤器
BF.EXISTS {key} {item} 判断item是否存在与指定布隆过滤器
BF.MEXISTS {key} {item} [item …] 批量判断item是否存在与指定布隆过滤器

JavaAPI

导入maven依赖

1
2
3
4
5
<dependency>
<groupId>com.redislabs</groupId>
<artifactId>jrebloom</artifactId>
<version>1.1.0</version>
</dependency>

Api使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
JedisPool jedisPool = new JedisPool("192.168.1.158", 6379);
Client client = new Client(jedisPool);
client.createFilter("main6", 10000, 0.000001);
//批量插入数据
for (int i = 0; i <100; i++) {
String values[] = new String[100];
for (int j = 0; j < 100; j++) {
String value = "item" + (i * 100 +j);
values[j] = value;
}
client.addMulti("main6", values);
}
//判断是否存在
System.out.println(client.exists("main6", "item1"));
System.out.println(client.exists("main6", "a"));
jedisPool.close();
}

十三、Redis开发规范

BigKey处理

发现BigKey

  • debug object {key} 查看指定key的详细信息
  • redis-cli --bigkeys 扫描出BigKey【全表扫描,阻塞,建议从节点本地执行】

BigKey删除

场景:当key非常大时,delete命令执行十分缓慢,会发生阻塞【过期bigkey也是进行删除操作也会阻塞】

redis4.0之后:可以使用unlink命令进行后台删除,不阻塞前台

生命周期管理

  • 使用OBJECT IDLETIME {key}查看key的闲置时间

  • 过期时间不易集中

命令优化

有遍历需求可以使用hscansscanzscan代替【这些扫描命令在field较少时COUNT参数不会生效

必要情况下使用monitor命令监控,注意时间不要过长

Java客户端优化

参数名 含义 默认值 建议
testWhileIdle 是否开启空闲资源检测 false true
timeBetweenEvictionRunsMillis 空闲资源检测周期 -1 自选,也可使用JedisPoolConfig中的默认值
minEvictiableIdleTimeMillis 资源池中资源最小空闲时间 30分钟 自选,也可使用JedisPoolConfig中的默认值
numTestsPerEvictionRun 做空闲资源检测每次的采样数 3 自选,如果设置为-1则为全部做空闲检测

maxIdle需要设置为接近maxTotal

预估maxTotal方法的例子:

一次命令时间平均耗时1ms,一个连接QPS大约1000,业务期忘的QPS时50000,理论上maxTotal=50000/1000=50

十四、内存管理

内存消耗

内存统计

执行info memory命令可以查看内存信息

主要属性名 属性说明
used_memory 实际存储数据的内存总量
used_memory_rss redis进程占用的总物理内存
maxmemory 最大内存
maxmemory_policy 内存剔除策略
mem_fragmentation_ratio used_memory_rss/used_memory比值,表示内存碎片率

内存消耗划分

Memory-Used

  • 客户端缓冲区设置规则

    client-output-buffer-limit

    :客户端类型,分为三种

    ​ (a)normal:普通客户端

    ​ (b)slave:用从节点用于复制,伪裝成客户端

    ​ ©pubsub:发布订阅客户端 :如客户使用的输出冲区大于hardlimit客户端会被立即关闭

    :如果客户端使用的输出缓冲区超过了 并且持续了,客户会被立即关闭

    • 普通客户端默认:client-output-buffer-limit normal 0 0 0
    • salve客户端默认:client-output-buffer-limit slave 256mb 64mb 60
    • pubsub客户端默认:client-output-buffer-limit slave 32mb 8mb 60
  • 复制缓冲区:用于slave和master断开重连时不进行全量复制保存偏移数据使用

    默认为repl-backlog-size 1mb

  • AOF缓冲区:AOF重写期间,AOF缓冲区,没有容量限制

内存回收策略

删除过期值

  • 惰性删除:访问key->expired dict->del key【先在过期表中找,发现过期删除key,返回null】
  • 定时删除:每秒运行10次,采样删除

超过maxmemory使用maxmemory-policy进行控制,参见缓存更新策略

十五、开发运维事项

Linux内核优化

vm.overcommit_memory

Redis建议vm.overcommit_memory = 1(影响fork操作)

立即生效:

永久生效:vm.overcommit_memory = 1写入到/etc/sysctl.conf文件中

含义
0 表示内核将检查是否有足够可用的内存。如果有足够可用的内存,内存申请通过,否则内存申请失败,并返回错误给进程
1 表示内核允许超量使用内存直到用完为止
2 表示内存绝不过量使用,即整个系统内存不能超过swap+50%的RAM值

swappiness

策略
0 Linux3.5及以上:宁愿OOM Killer也不用swap
Linux3.5及以下:宁愿swap也不用OOM Killer
1 Linux3.5及以上:宁愿swap也不用OOM Killer
60 默认值
100 操作系统会主动使用swap

建议:Linux3.5以上vm.swappiness = 1,否则vm.swappiness = 0

立即生效:echo {bestValue} > /proc/sys/vm/swappiness

永久生效:vm.swappiness = {bestValue} 写入到/etc/sysctl.conf

THP(Transparent huge page)

建议禁用,Centos7在/sys/kernel/mm/transparent_hugepage/enabled下设置为never即可

THP为大内存页时fork子线程时Copy-On-Write可能造成延迟

ulimit

建议将Redis启动用户的文件句柄限制调成10032,限制文件etc/security/limits

TCP backlog

建议将/proc/sys/net/core/somaxconn系统TCP backlog的限制设置为与Redis一样默认511

Redis安全问题

在配置文件中配置

1
2
#将命令更改为另一个字符串,原命令失效。如果字符串为空则表示禁用此命令
rename-command {cmd} {str}

附各种数据类型的内部编码:

Data-Encoding

附Redis云平台CacheCloud【一键部署、监控、运维、数据迁移工具等】 GitHub项目地址

留言與分享

全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选。

它可以快速地储存、搜索和分析海量数据。维基百科、Stack Overflow、Github 都采用它。

Elastic 的底层是开源库 Lucene。但是,你没法直接用 Lucene,必须自己写代码去调用它的接口。Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。

本文从零开始,讲解如何使用 Elastic 搭建自己的全文搜索引擎。每一步都有详细的说明,大家跟着做就能学会。

一、安装

Elastic 需要 Java 8 环境。如果你的机器还没安装 Java,可以参考这篇文章,注意要保证环境变量JAVA_HOME正确设置。

安装完 Java,就可以跟着官方文档安装 Elastic。直接下载压缩包比较简单。

1
2
3
4

$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.1.zip
$ unzip elasticsearch-5.5.1.zip
$ cd elasticsearch-5.5.1/

接着,进入解压后的目录,运行下面的命令,启动 Elastic。

1
2

$ ./bin/elasticsearch

如果这时报错"max virtual memory areas vm.maxmapcount [65530] is too low",要运行下面的命令。

1
2

$ sudo sysctl -w vm.max_map_count=262144

如果一切正常,Elastic 就会在默认的9200端口运行。这时,打开另一个命令行窗口,请求该端口,会得到说明信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

$ curl localhost:9200

{
"name" : "atntrTf",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "tf9250XhQ6ee4h7YI11anA",
"version" : {
"number" : "5.5.1",
"build_hash" : "19c13d0",
"build_date" : "2017-07-18T20:44:24.823Z",
"build_snapshot" : false,
"lucene_version" : "6.6.0"
},
"tagline" : "You Know, for Search"
}

上面代码中,请求9200端口,Elastic 返回一个 JSON 对象,包含当前节点、集群、版本等信息。

按下 Ctrl + C,Elastic 就会停止运行。

默认情况下,Elastic 只允许本机访问,如果需要远程访问,可以修改 Elastic 安装目录的config/elasticsearch.yml文件,去掉network.host的注释,将它的值改成0.0.0.0,然后重新启动 Elastic。

1
2

network.host: 0.0.0.0

上面代码中,设成0.0.0.0让任何人都可以访问。线上服务不要这样设置,要设成具体的 IP。

二、基本概念

2.1 Node 与 Cluster

Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。

单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。

2.2 Index

Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。

所以,Elastic 数据管理的顶层单位就叫做 Index(索引)。它是单个数据库的同义词。每个 Index (即数据库)的名字必须是小写。

下面的命令可以查看当前节点的所有 Index。

1
2

$ curl -X GET 'http://localhost:9200/_cat/indices?v'

2.3 Document

Index 里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。

Document 使用 JSON 格式表示,下面是一个例子。

1
2
3
4
5
6

{
"user": "张三",
"title": "工程师",
"desc": "数据库管理"
}

同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

2.4 Type

Document 可以分组,比如weather这个 Index 里面,可以按城市分组(北京和上海),也可以按气候分组(晴天和雨天)。这种分组就叫做 Type,它是虚拟的逻辑分组,用来过滤 Document。

不同的 Type 应该有相似的结构(schema),举例来说,id字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的一个区别。性质完全不同的数据(比如productslogs)应该存成两个 Index,而不是一个 Index 里面的两个 Type(虽然可以做到)。

下面的命令可以列出每个 Index 所包含的 Type。

1
2

$ curl 'localhost:9200/_mapping?pretty=true'

根据规划,Elastic 6.x 版只允许每个 Index 包含一个 Type,7.x 版将会彻底移除 Type。

三、新建和删除 Index

新建 Index,可以直接向 Elastic 服务器发出 PUT 请求。下面的例子是新建一个名叫weather的 Index。

1
2

$ curl -X PUT 'localhost:9200/weather'

服务器返回一个 JSON 对象,里面的acknowledged字段表示操作成功。

1
2
3
4
5

{
"acknowledged":true,
"shards_acknowledged":true
}

然后,我们发出 DELETE 请求,删除这个 Index。

1
2

$ curl -X DELETE 'localhost:9200/weather'

四、中文分词设置

首先,安装中文分词插件。这里使用的是 ik,也可以考虑其他插件(比如 smartcn)。

1
2

$ ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.5.1/elasticsearch-analysis-ik-5.5.1.zip

上面代码安装的是5.5.1版的插件,与 Elastic 5.5.1 配合使用。

接着,重新启动 Elastic,就会自动加载这个新安装的插件。

然后,新建一个 Index,指定需要分词的字段。这一步根据数据结构而异,下面的命令只针对本文。基本上,凡是需要搜索的中文字段,都要单独设置一下。

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

$ curl -X PUT 'localhost:9200/accounts' -d '
{
"mappings": {
"person": {
"properties": {
"user": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
}'

上面代码中,首先新建一个名称为accounts的 Index,里面有一个名称为person的 Type。person有三个字段。

  • user
  • title
  • desc

这三个字段都是中文,而且类型都是文本(text),所以需要指定中文分词器,不能使用默认的英文分词器。

Elastic 的分词器称为 analyzer。我们对每个字段指定分词器。

1
2
3
4
5
6

"user": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}

上面代码中,analyzer是字段文本的分词器,search_analyzer是搜索词的分词器。ik_max_word分词器是插件ik提供的,可以对文本进行最大数量的分词。

五、数据操作

5.1 新增记录

向指定的 /Index/Type 发送 PUT 请求,就可以在 Index 里面新增一条记录。比如,向/accounts/person发送请求,就可以新增一条人员记录。

1
2
3
4
5
6
7

$ curl -X PUT 'localhost:9200/accounts/person/1' -d '
{
"user": "张三",
"title": "工程师",
"desc": "数据库管理"
}'

服务器返回的 JSON 对象,会给出 Index、Type、Id、Version 等信息。

1
2
3
4
5
6
7
8
9
10

{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_version":1,
"result":"created",
"_shards":{"total":2,"successful":1,"failed":0},
"created":true
}

如果你仔细看,会发现请求路径是/accounts/person/1,最后的1是该条记录的 Id。它不一定是数字,任意字符串(比如abc)都可以。

新增记录的时候,也可以不指定 Id,这时要改成 POST 请求。

1
2
3
4
5
6
7

$ curl -X POST 'localhost:9200/accounts/person' -d '
{
"user": "李四",
"title": "工程师",
"desc": "系统管理"
}'

上面代码中,向/accounts/person发出一个 POST 请求,添加一个记录。这时,服务器返回的 JSON 对象里面,_id字段就是一个随机字符串。

1
2
3
4
5
6
7
8
9
10

{
"_index":"accounts",
"_type":"person",
"_id":"AV3qGfrC6jMbsbXb6k1p",
"_version":1,
"result":"created",
"_shards":{"total":2,"successful":1,"failed":0},
"created":true
}

注意,如果没有先创建 Index(这个例子是accounts),直接执行上面的命令,Elastic 也不会报错,而是直接生成指定的 Index。所以,打字的时候要小心,不要写错 Index 的名称。

5.2 查看记录

/Index/Type/Id发出 GET 请求,就可以查看这条记录。

1
2

$ curl 'localhost:9200/accounts/person/1?pretty=true'

上面代码请求查看/accounts/person/1这条记录,URL 的参数pretty=true表示以易读的格式返回。

返回的数据中,found字段表示查询成功,_source字段返回原始记录。

1
2
3
4
5
6
7
8
9
10
11
12
13

{
"_index" : "accounts",
"_type" : "person",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理"
}
}

如果 Id 不正确,就查不到数据,found字段就是false

1
2
3
4
5
6
7
8
9

$ curl 'localhost:9200/weather/beijing/abc?pretty=true'

{
"_index" : "accounts",
"_type" : "person",
"_id" : "abc",
"found" : false
}

5.3 删除记录

删除记录就是发出 DELETE 请求。

1
2

$ curl -X DELETE 'localhost:9200/accounts/person/1'

这里先不要删除这条记录,后面还要用到。

5.4 更新记录

更新记录就是使用 PUT 请求,重新发送一次数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

$ curl -X PUT 'localhost:9200/accounts/person/1' -d '
{
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理,软件开发"
}'

{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_version":2,
"result":"updated",
"_shards":{"total":2,"successful":1,"failed":0},
"created":false
}

上面代码中,我们将原始数据从"数据库管理"改成"数据库管理,软件开发"。 返回结果里面,有几个字段发生了变化。

1
2
3
4

"_version" : 2,
"result" : "updated",
"created" : false

可以看到,记录的 Id 没变,但是版本(version)从1变成2,操作类型(result)从created变成updatedcreated字段变成false,因为这次不是新建记录。

六、数据查询

6.1 返回所有记录

使用 GET 方法,直接请求/Index/Type/_search,就会返回所有记录。

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

$ curl 'localhost:9200/accounts/person/_search'

{
"took":2,
"timed_out":false,
"_shards":{"total":5,"successful":5,"failed":0},
"hits":{
"total":2,
"max_score":1.0,
"hits":[
{
"_index":"accounts",
"_type":"person",
"_id":"AV3qGfrC6jMbsbXb6k1p",
"_score":1.0,
"_source": {
"user": "李四",
"title": "工程师",
"desc": "系统管理"
}
},
{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_score":1.0,
"_source": {
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理,软件开发"
}
}
]
}
}

上面代码中,返回结果的 took字段表示该操作的耗时(单位为毫秒),timed_out字段表示是否超时,hits字段表示命中的记录,里面子字段的含义如下。

  • total:返回记录数,本例是2条。
  • max_score:最高的匹配程度,本例是1.0
  • hits:返回的记录组成的数组。

返回的记录中,每条记录都有一个_score字段,表示匹配的程序,默认是按照这个字段降序排列。

6.2 全文搜索

Elastic 的查询非常特别,使用自己的查询语法,要求 GET 请求带有数据体。

1
2
3
4
5

$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query" : { "match" : { "desc" : "软件" }}
}'

上面代码使用 Match 查询,指定的匹配条件是desc字段里面包含"软件"这个词。返回结果如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

{
"took":3,
"timed_out":false,
"_shards":{"total":5,"successful":5,"failed":0},
"hits":{
"total":1,
"max_score":0.28582606,
"hits":[
{
"_index":"accounts",
"_type":"person",
"_id":"1",
"_score":0.28582606,
"_source": {
"user" : "张三",
"title" : "工程师",
"desc" : "数据库管理,软件开发"
}
}
]
}
}

Elastic 默认一次返回10条结果,可以通过size字段改变这个设置。

1
2
3
4
5
6

$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query" : { "match" : { "desc" : "管理" }},
"size": 1
}'

上面代码指定,每次只返回一条结果。

还可以通过from字段,指定位移。

1
2
3
4
5
6
7

$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query" : { "match" : { "desc" : "管理" }},
"from": 1,
"size": 1
}'

上面代码指定,从位置1开始(默认是从位置0开始),只返回一条结果。

6.3 逻辑运算

如果有多个搜索关键字, Elastic 认为它们是or关系。

1
2
3
4
5

$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query" : { "match" : { "desc" : "软件 系统" }}
}'

上面代码搜索的是软件 or 系统

如果要执行多个关键词的and搜索,必须使用布尔查询

1
2
3
4
5
6
7
8
9
10
11
12

$ curl 'localhost:9200/accounts/person/_search' -d '
{
"query": {
"bool": {
"must": [
{ "match": { "desc": "软件" } },
{ "match": { "desc": "系统" } }
]
}
}
}'

七、参考链接

(完)

留言與分享

VPC

分類 后端, AWS
  1. Security Group(SG):

    1. 所有设定默认是拒绝,只可以设定许可规则,拒否ルールは指定できません。
    2. 特征是不单可以指定CIDR等IP,还可以指定SG
    3. 安全组是有状态的 — 如果您从实例发送一个请求,则无论入站安全组规则如何,都将允许该请求的响应流量流入。如果是为响应已允许的入站流量,则该响应可以出站,此时可忽略出站规则。セキュリティグループはステートフルです。
  2. Access Control List(ACL):

    1. VPC 自动带有可修改的默认网络 ACL。默认情况下,它允许所有入站和出站 IPv4 流量以及 IPv6 流量 (如果适用)。
    2. 可以创建自定义网络 ACL 并将其与子网相关联。默认情况下,每个自定义网络 ACL 都拒绝所有入站和出站流量,直至您添加规则。
    3. VPC 中的每个子网都必须与一个网络 ACL 相关联。如果您没有明确地将子网与网络 ACL 相关联,则子网将自动与默认网络 ACL 关联。
    4. 可以将网络 ACL 与多个子网关联。但是,一个子网一次只能与一个网络 ACL 关联。当您将一个网络 ACL 与一个子网关联时,将删除之前的关联。
    5. 网络 ACL 包含规则的编号列表。我们按顺序评估(从编号最小的规则开始)规则,以判断是否允许流量进入或离开任何与网络 ACL 关联的子网。您可以使用的最高规则编号为 32766。我们建议您开始先以增量方式创建规则(例如,以 10 或 100 的增量增加),这样您可以在稍后需要时插入新的规则。
    6. 网络 ACL 有单独的入站和出站规则,每项规则都或是允许或是拒绝数据流。
    7. 网络 ACL 没有任何状态,这意味着对允许入站数据流的响应会随着出站数据流规则的变化而改变(反之亦然)。(ネットワーク ACL はステートレスです。許可されているインバウンドトラフィックに対する応答は、アウトバウンドトラフィックのルールに従います(その逆の場合も同様です)。)
    8. ルール番号。ルールは、最も低い番号のルールから評価されます。ルールがトラフィックに一致すると、それと相反するより高い数値のルールの有無にかかわらず、すぐに適用されます。
    9. タイプ。トラフィックのタイプ(SSH など)。また、すべてのトラフィックまたはカスタム範囲を指定することもできます。

セキュリティグループはステートフルです。(レスポンスでも明示する必要なしで許可。)
ACLはステートレスです。(レスポンスでも明示する必要ある。)

留言與分享

17.系统维护

分類 后端, linux

从源代码安装软件笔记整理

1. 安装C语言开源软件的三个主要步骤

  1. 解压源代码
  2. 编译成二进制文件
  3. 将二进制文件安装到适当的文件夹

2. 解压源代码

常用解压命令

命令格式 说明
gunzip [文件名] 解压.gz文件
gzip [option] [文件名] 压缩/解压文件
bunzip [文件名] 解压.bz2文件
bzip [option] [文件名] 压缩/解压文件

gzip/bzip 选项说明

选项 说明
-d 解压压缩文件
-c 解压压缩文件到标准输出

3. patch命令

用于应用补丁文件

选项 说明
-d 文件夹 移动到指定文件夹后处理
-p 修正patch文件内的path:
-p0: 不修正path
-p1: 删除最初的’/’
-p2: 删除下一级文件夹
patch 应用补丁
-C 测试(不实际应用补丁)
-R 取消补丁,恢复到补丁前状态

4. 生成Makefile

运行config脚本根据环境配置config文件:

config脚本主要选项

选项 说明
--help 显示帮助说明
--prefix=文件夹 指定安装目标的top路径

5. 编译和安装

make命令

根据当前文件夹内的Makefile编译源代码

基本格式:

make [option] [目标]

留言與分享

16.网络

分類 后端, linux

1. 网络设备管理

1.1 网络接口命名规则

接口名称 说明
eth0 第一个 Ethernet 接口
eth1 第二个 Ethernet 接口
ppp0 第一个 PPP 接口
lo Loopback 接口

1.2 网络设备识别命令

1
dmesg | grep eth  # 查看内核识别的网络设备

1.3 网络接口配置命令

1
ifconfig [-a] [IF名] [参数]
参数 说明
-a 显示所有接口
IF名 指定接口名称
IP地址 设置 IP 地址
netmask 设置子网掩码
up 激活接口(等价于 ifup
down 停止接口(等价于 ifdown

示例:

1
ifconfig eth0 192.168.120.27 netmask 255.255.255.0 up

1.4 NIC 组合与虚拟网络接口

  • NIC 组合:允许将多个物理以太网适配器分组为虚拟网络适配器,提高性能和容错能力。
  • MAC 地址设置:在 “交换机独立” 模式下,团队会将 MAC 地址设置为出站流量。
  • IP 别名:在同一个网卡上分配不同 IP 地址。
    1
    ifconfig eth0:0 192.168.0.1 netmask 255.255.255.0 up

2. ARP 协议管理

2.1 ARP 协议简介

ARP (Address Resolution Protocol) 用于将 IP 地址解析为 MAC 地址。

2.2 ARP 命令

1
arp [option]
选项 说明
-a [host] 显示指定主机或全部 ARP 表
-f 文件名 从文件读取主机:MAC 对应表
-n 显示 IP 地址而非主机名
-d [host] 删除指定主机的 ARP 记录
-i [IF名] 指定接口
-s [host] [MAC地址] 添加主机和 MAC 地址的对应关系

示例:

1
arp -s h001.example.com 00:01:02:03:04:05

2.3 ARP 监视工具

  • arpwatch:监视网络中的 ARP 活动。

3. 网络管理命令

3.1 Ping 命令

1
ping [host名或IP] [option]
选项 说明
-n 不解析主机名,显示 IP 地址
-c 次数 发送指定次数的 ICMP 包
-i 间隔 按指定间隔发送 ICMP 包

示例:

1
ping -c 4 192.168.1.1

3.2 Traceroute 命令

1
traceroute [option] [host名或IP]
选项 说明
-i [IF] 指定接口
-n 显示 IP 地址而非主机名

3.3 Tcpdump 命令

1
tcpdump [option] [条件式]
选项 说明
-i IF名 指定监视的接口
-s 比特数 指定从包中提取的比特数
-X 显示十六进制和 ASCII 文本
-n 不解析 ARP 地址
-l 缓存标准输出
-t 不显示时间
-v 显示详细信息

条件式:

  • port:指定端口

示例:

1
tcpdump -nli eth0 port 53

3.4 Wireshark

  • 图形化网络监视工具。

3.5 Netstat 命令

1
netstat [option]
选项 说明
-a 显示所有 socket
-c 每秒刷新
-i 显示接口状态
-l 只显示监听状态的端口
-n 显示地址和端口
-p 显示 PID 和进程
-r 显示路由表
-t 显示 TCP 连接
-u 显示 UDP 连接

3.6 IP 命令

1
ip [option] 操作对象 [命令] [设备]
选项 说明
-s 显示状态
-r 解析名称

操作对象:

  • link:接口详细信息
  • addr:IP 地址
  • route:路由表
  • neigh:ARP 缓存

命令:

  • show:显示指定对象
  • add:设置指定对象的参数

示例:

1
ip link show eth0

4. 路由设置

4.1 Route 命令

1
route [option]
选项 说明
-n 不解析主机名
-F 显示内核路由表
-C 显示内核路由缓存

路由表项目:

  • Destination:目标网络或主机
  • Gateway:网关
  • Genmask:目标子网掩码
  • Flags:路由状态(U:有效, H:目标主机, G:使用网关, !:无效)
  • Metric:到达目标的距离
  • Ref:路由引用数
  • Use:路径引用次数
  • Iface:使用的接口

示例:

1
2
3
route add -net 192.168.100.0 netmask 255.255.255.0 gw 172.16.0.1
route add default gw 192.168.0.50
route del -net 192.168.100.0 netmask 255.255.255.0 gw 172.16.0.1

5. 无线网络设置

5.1 无线规格与安全标准

规格 频率 传送速度
IEEE 802.11a 5.2GHz 54Mbps
IEEE 802.11b 2.4GHz 11Mbps
IEEE 802.11g 2.4GHz 54Mbps
IEEE 802.11n 2.4GHz/5GHz 600Mbps
IEEE 802.11ac 5GHz 433Mbps~7Gbps

安全标准:

  • WEP:使用 RC4 算法,存在漏洞。
  • WPA:多种加密技术(TKIP, AES),用户认证由 IEEE 802.1x 等实现。WPA-AES 比 WPA-TKIP 更安全。

5.2 无线接口状态确认

1
iwconfig [wlanN] [essid ESSID] [key [s:]WEP key]

示例:

1
2
iwconfig wlan0 essid "windsor"
iwconfig wlan0 key s:abc12345

5.3 获取无线接口信息

1
iwlist [无线IF] [参数]
参数 说明
scanning [essid ID] 显示可接入的热点列表
channel 显示可设置的频道
rate 显示传送速度

6. 高级网络设置

6.1 网络相关文件

文件 说明
/etc/hostname 记录主机名
/etc/hosts 记录主机名、别名和 IP 的对应关系
/etc/networks 记录网络和 IP 的对应关系
/etc/nsswitch.conf 规定信息查找顺序
/etc/resolv.conf 记录 DNS 服务器和域名
/etc/sysconfig/network 网络功能、主机名、默认网关等设置
/etc/sysconfig/network-scripts/ 存放网络接口设置文件
/etc/network/interfaces Debian 系统的网络接口设置文件

6.2 配置文件示例

/etc/sysconfig/network-scripts/ifcfg-eth0:

1
2
3
4
5
6
7
DEVICE=eth0
BOOTPROTO=static
IPADDR=192.168.0.6
NETMASK=255.255.255.0
NETWORK=192.168.0.0
BROADCAST=192.168.0.255
ONBOOT=yes

/etc/network/interfaces (Debian):

1
2
3
4
5
6
7
8
9
auto lo
iface lo inet loopback
allow-hotplug eth0
iface eth0 inet static
address 192.168.0.6
netmask 255.255.255.0
gateway 192.168.0.1
dns-domain example.com
dns-nameservers 192.168.1.1

7. 网络问题排查

7.1 排查步骤

  1. Ping 测试

    1
    2
    ping 172.16.0.1
    ping h001.s16.la.net
    • 如果 IP 能 Ping 通,但主机名失败,可能是 DNS 问题,检查 /etc/resolv.conf
  2. 局域网连通但无法访问互联网

    • 检查默认网关或路由表:
      1
      route
    • 如果没有默认路由,添加默认网关:
      1
      route add default gw 172.16.0.1
  3. 网络连接不通

    • 使用 traceroutetracepath 查看路径:
      1
      traceroute www.lpi.jp
  4. 网络接口问题

    • 检查网卡状态:
      1
      ifconfig
    • 如果只显示 lo,启动接口:
      1
      2
      3
      ifup eth0
      # 或
      ifconfig eth0 up

7.2 需要确认的配置文件

文件 说明
/etc/resolv.conf DNS 服务器设置
/etc/sysconfig/network-scripts/* 网络接口设置(RHEL/CentOS)
/etc/network/interfaces 网络接口设置(Debian)
/etc/hosts.allow, /etc/hosts.deny 连接控制

补充说明

  • IP 别名:允许在同一个网卡上分配多个 IP 地址,常用于服务器提供多种服务。
  • ARP 缓存:存储 IP 地址与 MAC 地址的映射关系,可通过 arp -d 手动清除缓存。
  • 无线安全:建议使用 WPA2 或 WPA3 加密方式,避免使用不安全的 WEP。
  • 路由表:理解目标网络、网关、子网掩码和接口的关系,确保数据包正确转发。

留言與分享

15.高度的存储管理

分類 后端, linux

1. RAID (冗余磁盘阵列)

RAID(Redundant Arrays of Inexpensive Disks)是一种将多个硬盘组合以提高性能或数据冗余的技术。

Linux 上的 RAID

在 Linux 上应用 RAID,有软件 RAID 和硬件 RAID 之分。通常,硬件 RAID 会被视作 SCSI 设备。

RAID 各个等级:

  • RAID 0 (条带化)

    • 多个硬盘组合成一个大磁盘,没有备份,提供较好的性能但没有容灾能力。
    • 示例:三台 100GB 硬盘组成一个 300GB 的 RAID 0。
  • RAID 1 (镜像)

    • 多台硬盘写入相同数据,提供冗余备份。
    • 示例:三个 100GB 硬盘组成一个 100GB 的 RAID 1。
  • RAID 4

    • 至少需要 3 台硬盘,其中一台用作备份分区。性能瓶颈在于备份分区。
    • 示例:三个 100GB 硬盘组成一个 200GB 的 RAID 4。
  • RAID 5

    • 使用分布式备份技术,性能较 RAID 4 好,且不需要专门的备份硬盘。
    • 示例:三个 100GB 硬盘组成一个 200GB 的 RAID 5。
  • 其他 RAID 等级:

    • RAID LINEAR:类似 RAID 0。
    • RAID 0+1 (RAID01):RAID 0 组成的 RAID 1。
    • RAID 10 (RAID+0):RAID 1 组成的 RAID 0。
    • RAID 6:比 RAID 5 多一台备份区。

mdadm 命令控制 RAID 阵列

mdadm [option] [设备名]

常用选项:

选项 描述
-C, --create 创建 RAID 阵列并生成相关设备
-a 自动生成必要的设备文件
-c 指定 Chunk 大小
-l, --level 指定 RAID 级别
-n, --raid-devices 指定 RAID 阵列中的设备数量
-x, --spare-devices 指定备用设备数量
--manage 管理 RAID 阵列
-a, --add 添加设备
-r, --remove 删除设备
-f, --fail 标记设备为失效
--query 查询 RAID 状态
--detail 查看 RAID 详细状态
--stop 停止指定的 RAID 阵列
--stop --scan 停止所有 RAID 阵列

2. LVM (逻辑卷管理)

LVM 是 Linux 下的一种磁盘管理工具,可以灵活地管理硬盘、分区和逻辑卷。

LVM 结构:

  1. PV (Physical Volume):一个硬盘或一个分区作为物理空间。
  2. PE (Physical Extent):PV 组成 PE。
  3. VG (Volume Group):PE 组成 VG。
  4. LV (Logical Volume):VG 内的 PE 分配为 LV。

创建 LVM:

  1. 准备 LVM 用的分区:

    1
    2
    fdisk /dev/sda
    fdisk /dev/sdb
  2. 初始化 PV:

    1
    pvcreate /dev/sda1 /dev/sdb1
  3. 创建 VG:

    1
    vgcreate testvg /dev/sda1 /dev/sdb1
  4. 创建 LV:

    1
    lvcreate -L 1G -n lv01 testvg
  5. 创建文件系统并挂载:

    1
    2
    mke2fs -j /dev/testvg/lv01
    mount /dev/testvg/lv01 /mnt

管理 LVM:

命令 说明
pvdisplay 显示物理 PV 信息
pvscan 显示 PV 状态
pvmove 移动 PV 中的 PE
pvremove 删除 PV
vgextend 扩展 VG
vgreduce 缩小 VG
vgdisplay 显示 VG 详情
lvdisplay 显示 LV 详情
lvextend 扩展 LV
lvreduce 缩小 LV
lvremove 删除 LV
lvcreate -s 创建快照

3. 访问记忆装置

设备文件说明:

设备文件 说明
/dev/hda IDE 主硬盘
/dev/hdb IDE 从硬盘
/dev/sda 第一个 SCSI/STAT/USB 硬盘
/dev/sdb 第二个 SCSI/STAT/USB 硬盘
/dev/sr0 第一个 SCSI/STAT/USB CD/DVD
/dev/st0 第一个 SCSI 磁带设备

硬盘管理:

Linux 将 IDE 以外的硬盘识别为 SCSI 设备。硬盘的数据传送模式有 PIO(Programmed I/O)和 DMA(Direct Memory Access)模式。通常使用 DMA 模式。

检查 DMA 模式:

1
hdparm -i /dev/sda

硬盘控制:

1
hdparm -d1 /dev/sda  # 启用 DMA 模式

使用 SCSI/STAT/USB:

1
sdparm [option] [设备文件]

4. iSCSI (Internet Small Computer System Interface)

iSCSI 是基于 TCP/IP 的 SCSI 协议,可以使远程存储设备像本地设备一样使用。

iSCSI 配置步骤:

  1. 配置 targets.conf 文件:

    1
    2
    3
    4
    5
    6
    <target iqn.2014.01.com.example.lpic:target0>
    backing-store /dev/sdb2
    initiator-address 192.168.11.2
    initiator-address 192.168.11.4
    incominguser centuser password
    </target>
  2. 启动 iSCSI target 服务:

    1
    service tgtd start
  3. 使用 tgtadm 命令查看 target 状态:

    1
    tgtadm --mode target --op show
  4. 使用 iscsiadm 从 initiator 查询 target:

    1
    iscsiadm -m discovery -t sendtargets -p 192.168.11.3
  5. 配置认证方式:
    修改 /etc/iscsi/iscid.conf

    1
    2
    3
    node.session.auth.authmethod = CHAP
    node.session.auth.username = centuser
    node.session.auth.password = password
  6. 登录 iSCSI:

    1
    iscsiadm -m node --login

    登录后,使用 cat /proc/partitions 查看 iSCSI 目标是否添加成功。

留言與分享

14.设备和文件系统

分類 后端, linux

1. 文件系统基础

/etc/fstab 文件格式解析

1
设备/标签/UUID  挂载点  文件系统类型  挂载选项  dump标志  fsck顺序

字段说明:

字段 说明 示例
1 设备标识 /dev/sda1, UUID=xxxx, LABEL=root
2 挂载路径 /, /home, /boot
3 文件系统类型 ext4, xfs, nfs
4 挂载选项 defaults, noatime
5 dump备份标志 0(不备份), 1(备份)
6 fsck检查顺序 0(不检查), 1(优先), 2(次级)

文件系统类型对比

类型 特点 适用场景
ext4 日志式, 最大16TB文件 通用Linux系统
XFS 高性能, 最大8EB文件 大文件处理
Btrfs 写时复制, 快照功能 高级存储需求
FAT32 兼容性好 U盘/移动设备
NTFS Windows兼容 双系统共享分区

常用挂载选项

选项 作用 性能影响
async 异步写入 提升性能
noatime 不更新访问时间 显著提升
nodiratime 不更新目录访问时间 小幅提升
discard TRIM支持(SSD) 延长SSD寿命
data=writeback ext4日志模式 性能最佳

查看支持的文件系统:

1
cat /proc/filesystems

查看已挂载文件系统:

1
2
cat /etc/mtab   # 或
cat /proc/mounts

2. 挂载管理命令

mount 命令详解

1
mount [选项] [设备] [挂载点]

常用选项:

选项 说明
-t 指定文件系统类型
-o 指定挂载选项(多选项用逗号分隔)
-a 挂载/etc/fstab中所有文件系统
–bind 目录绑定挂载
-l 显示标签

示例:

1
mount -t ext4 -o noatime /dev/sdb1 /mnt/data

umount 命令

1
umount [选项] [设备|挂载点]
选项 说明
-a 卸载所有文件系统
-t 按类型卸载
-l 延迟卸载(强制)
-f 强制卸载

强制卸载流程:

  1. fuser -vm /mountpoint 查看占用进程
  2. kill -9 PID 结束进程
  3. umount -f /mountpoint

3. SWAP 管理

创建SWAP文件

1
2
3
4
5
# 创建1GB交换文件
dd if=/dev/zero of=/swapfile bs=1M count=1024
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

SWAP管理命令

命令 功能
swapon -a 启用所有swap
swapon -s 查看swap使用
swapoff -a 禁用所有swap

优化建议:

  • 物理内存<8GB时,SWAP=1.5倍内存
  • 物理内存≥8GB时,SWAP=8GB

4. 文件系统创建与维护

创建工具对比

命令 对应文件系统 特点
mkfs.ext4 ext4 默认日志模式
mkfs.xfs XFS 高性能大文件
mkfs.btrfs Btrfs 高级功能支持

ext4创建示例:

1
mkfs.ext4 -b 4096 -m 1 -L DATA /dev/sdb1

XFS创建示例:

1
mkfs.xfs -f -L BIGDATA /dev/sdc1

文件系统检查

ext系列检查:

1
2
fsck -y /dev/sda1   # 自动修复
e2fsck -f /dev/sda1 # 强制检查

XFS检查:

1
xfs_repair /dev/sdb1

检查时机:

  • 异常关机后
  • 定期维护时(建议每6个月)
  • 出现I/O错误时

5. 高级文件系统特性

ext4 特性管理

转换ext3→ext4:

1
2
tune2fs -O extent,uninit_bg,dir_index /dev/sda1
fsck -pf /dev/sda1

调整参数:

1
2
tune2fs -c 30 -i 7d /dev/sda1  # 每30次挂载或7天检查
tune2fs -m 5 /dev/sda1 # 保留5%空间

XFS 高级功能

查看信息:

1
2
xfs_info /dev/sdb1
xfs_admin -l /dev/sdb1

备份恢复:

1
2
xfsdump -l 0 -f /backup/root.xfsdump /dev/sda1
xfsrestore -f /backup/root.xfsdump /mnt/restore

6. 光盘与加密文件系统

光盘制作

1
2
3
4
5
# 创建ISO
mkisofs -JR -V "BACKUP_2023" -o backup.iso /backup

# 刻录光盘
cdrecord -v dev=1,0,0 speed=24 backup.iso

加密文件系统

LUKS加密:

1
2
3
4
cryptsetup luksFormat /dev/sdb1
cryptsetup luksOpen /dev/sdb1 secret
mkfs.ext4 /dev/mapper/secret
mount /dev/mapper/secret /mnt/secure

关闭加密卷:

1
2
umount /mnt/secure
cryptsetup luksClose secret

7. 磁盘健康监控

S.M.A.R.T. 监控

1
2
3
smartctl -a /dev/sda          # 查看详细信息
smartctl -H /dev/sda # 健康状态
smartctl -t long /dev/sda # 启动长测试

关键监控指标:

  • Reallocated_Sector_Ct
  • Current_Pending_Sector
  • Temperature_Celsius

8. 故障处理流程

文件系统修复步骤

  1. 卸载文件系统

    1
    umount /dev/sda1
  2. 进入单用户模式

    1
    init 1
  3. 执行检查修复

    1
    fsck -y /dev/sda1
  4. 严重损坏时

    1
    2
    3
    debugfs /dev/sda1
    > lsdel # 查看已删除inode
    > dump <inode> /tmp/recover_file

应急恢复方案

问题现象 解决方案
无法挂载 fsck -y /dev/sdX
Superblock损坏 e2fsck -b 32768 /dev/sdX
只读文件系统 mount -o remount,rw /

留言與分享

13.系统启动

分類 后端, linux

1. 系统启动流程

1.1 启动阶段详解

阶段 组件 功能说明
1 BIOS - 硬件自检(POST)
- 读取CMOS设置
- 检测启动设备顺序
- 加载MBR中的BootLoader
2 MBR - 512字节(启动代码+分区表)
- 包含第一阶段BootLoader
- 标识活动分区
3 BootLoader - 阶段1: MBR中的初始代码
- 阶段1.5: 文件系统识别
- 阶段2: 加载内核(/boot/grub/)
4 内核 - 硬件检测与初始化
- 加载驱动模块
- 挂载根文件系统
- 启动init进程(PID=1)
5 init进程 - 根据/etc/inittab初始化系统
- 启动运行级别对应服务
- 生成所有后续进程

1.2 inittab 文件格式

1
id:runlevels:action:process

主要action类型

action 说明
boot 系统启动时执行,不等待完成
bootwait 系统启动时执行,等待完成
ctrlaltdel 捕获Ctrl+Alt+Del组合键
initdefault 设置默认运行级别
once 进入运行级别时执行一次
respawn 进程终止后自动重启
sysinit 系统初始化脚本
wait 进入运行级别时执行并等待

1.3 运行级别管理

标准运行级别

级别 说明
0 关机
1/S 单用户模式
2 多用户无NFS
3 完整多用户模式
4 自定义
5 图形界面
6 重启

服务管理脚本

1
/etc/init.d/service [command]
命令 作用
start 启动服务
stop 停止服务
restart 重启服务
reload 重载配置
status 查看状态

服务自启动管理

RedHat系(chkconfig):

1
chkconfig [选项] 服务名 [on|off]
选项 说明
–list 显示服务状态
–level 指定运行级别
–add 添加服务
–del 删除服务

Debian系(update-rc.d):

1
update-rc.d [选项] 服务名 [commands]
选项 说明
-n 模拟执行
-f 强制操作
remove 删除链接

1.4 Upstart 事件驱动

主要命令:

1
initctl [command] job
命令 功能
start 启动作业
stop 停止作业
restart 重启作业
status 查看状态
list 列出所有作业

配置文件位置:

1
/etc/init/*.conf

2. GRUB 引导加载器

2.1 GRUB Legacy vs GRUB2

特性 GRUB Legacy GRUB2
版本 0.9x 1.9x
配置文件 menu.lst grub.cfg
生成方式 直接编辑 update-grub
分区表示 (hd0,0) hd0,msdos1

2.2 GRUB Legacy 配置

/boot/grub/menu.lst 示例:

1
2
3
4
5
6
timeout 5
default 0
title CentOS
root (hd0,0)
kernel /vmlinuz ro root=/dev/sda1
initrd /initrd.img

常用命令:

1
2
3
grub> root (hd0,0)     # 设置根分区
grub> setup (hd0) # 安装到MBR
grub> cat /grub/menu.lst # 查看配置

2.3 GRUB2 配置

/etc/default/grub 关键参数:

参数 说明
GRUB_DEFAULT 默认启动项
GRUB_TIMEOUT 菜单超时
GRUB_CMDLINE_LINUX 内核参数

生成配置:

1
update-grub

2.4 内核启动参数

参数 作用
root=/dev/sda1 指定根分区
ro/rw 只读/读写挂载
init=/bin/bash 指定初始化程序
single 单用户模式
mem=1024M 限制内存大小

查看当前参数:

1
cat /proc/cmdline

3. 其他引导加载器

3.1 LILO 配置

/etc/lilo.conf 示例:

1
2
3
4
5
6
boot=/dev/sda
map=/boot/map
default=linux
image=/boot/vmlinuz
label=linux
root=/dev/sda1

应用配置:

1
lilo -v

3.2 SYSLINUX 系列

类型 文件系统 用途
SYSLINUX FAT USB启动
ISOLINUX ISO9660 光盘启动
EXTLINUX ext2/3/4 硬盘启动
PXELINUX 网络 网络启动

PXE启动环境搭建:

  1. 安装必要服务:
1
yum install dhcp tftp-server syslinux
  1. 配置DHCP:
1
2
filename "pxelinux.0";
next-server 192.168.1.100;
  1. 准备TFTP目录:
1
2
cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/
mkdir /var/lib/tftpboot/pxelinux.cfg

4. 系统恢复方法

4.1 救援模式操作

  1. 使用安装介质启动
  2. 进入救援模式
  3. 挂载原系统:
1
chroot /mnt/sysimage
  1. 修复引导:
1
grub-install /dev/sda

4.2 常见修复场景

问题 解决方案
MBR损坏 dd if=/usr/share/syslinux/mbr.bin of=/dev/sda
GRUB损坏 grub-install /dev/sda
文件系统错误 fsck -y /dev/sda1
密码重置 init=/bin/bash 启动后修改密码

5. 启动优化技巧

  1. 并行启动:
1
2
3
vim /etc/init/rc-sysinit.conf
# 修改为:
env DEFAULT_RUNLEVEL=2
  1. 禁用不需要的服务:
1
2
chkconfig --list | grep 3:on
chkconfig servicename off
  1. 使用readahead:
1
service readahead-early restart
  1. SSD优化:
    在/etc/fstab添加:
1
noatime,discard

留言與分享

12.内核

分類 后端, linux

1. 内核版本管理

版本号说明

  • 2.X.Y 旧版本格式

    • X:偶数表示稳定版,奇数表示开发版
    • Y:修订版本号
    • 后缀说明:
      • -rcN:Release Candidate(候选版本)
      • -gitN:开发中的Git版本
  • 3.0+ 新版本分类

    类型 说明
    Prepatch 稳定版前的测试版本
    Mainline 包含新特性的主线开发版本
    Stable 稳定版本
    Longterm 长期支持版本(LTS)

版本查询命令

1
uname [选项]

常用选项

选项 说明
-a 显示全部系统信息
-r 显示内核版本
-m 显示硬件架构

其他查询方式

1
2
3
4
5
# 查看/proc版本信息
cat /proc/version

# 通过Makefile查看
head -4 /usr/src/linux/Makefile

2. 内核模块管理

核心命令对比

命令 功能说明 特点
lsmod 显示已加载模块 读取/proc/modules
insmod 加载单个模块 不处理依赖关系
rmmod 卸载模块 需确保无依赖
modprobe 智能加载/卸载模块 自动处理依赖关系
depmod 生成模块依赖关系文件 创建modules.dep

命令详解

lsmod 输出说明

1
Module                  Size  Used by    Dependent modules
  • 显示模块名、大小、引用计数和被哪些模块使用

modinfo 模块信息

1
modinfo [选项] 模块名
选项 说明
-a 显示作者信息
-d 显示描述信息
-l 显示许可证信息
-n 显示模块文件路径

modprobe 高级用法

1
modprobe [选项] 模块名
选项 说明
-a 加载所有匹配模块
-c 显示当前配置
-l 列出所有可用模块
-r 递归卸载模块及依赖
-t 类型 仅操作指定类型模块
–show-depends 显示模块依赖关系

配置文件

  • /etc/modprobe.conf:模块别名和参数配置
    1
    2
    alias eth0 e1000
    options e1000 speed=1000 duplex=full

3. 内核编译流程

编译步骤

  1. 准备源代码
  2. 配置内核
    1
    2
    3
    4
    make oldconfig    # 基于旧配置更新
    make menuconfig # 文本界面配置
    make xconfig # Qt图形界面配置
    make gconfig # GTK图形界面配置
  3. 编译内核
    1
    2
    3
    make           # 编译内核和模块
    make bzImage # 仅编译内核
    make modules # 仅编译模块
  4. 安装组件
    1
    2
    make modules_install  # 安装模块到/lib/modules/
    make install # 自动安装内核

手动安装示例

1
2
3
4
5
6
7
8
9
10
# 复制内核文件
cp arch/x86/boot/bzImage /boot/vmlinuz-2.6.32
cp System.map /boot/System.map-2.6.32

# 创建链接
ln -s /boot/vmlinuz-2.6.32 /boot/vmlinuz
ln -s /boot/System.map-2.6.32 /boot/System.map

# 生成initramfs
mkinitramfs -o /boot/initrd-2.6.32.img 2.6.32

其他make目标

目标 说明
clean 清除编译文件
mrproper 彻底清除(含配置)
rpm-pkg 生成RPM包
deb-pkg 生成Debian包

4. 内核参数调优

/proc/sys 关键参数

路径 说明
kernel/ctrl-alt-del Ctrl+Alt+Del行为
kernel/shmall 共享内存页数上限
kernel/shmmax 共享内存段最大大小
net/ipv4/ip_forward IP转发开关(1/0)
fs/file-max 系统最大文件句柄数

sysctl 工具

1
sysctl [选项] [参数=值]
选项 说明
-a 显示所有可用参数
-p 从/etc/sysctl.conf加载配置
-w 临时修改参数

initramfs 管理

1
2
3
4
5
6
# 创建initramfs
mkinitramfs -o initrd.img 3.16.0-4-amd64

# 解压分析
mkdir initrd && cd initrd
zcat ../initrd.img | cpio -id

5. 内核调试与设备管理

/proc 关键文件

文件 内容说明
/proc/cpuinfo CPU详细信息
/proc/meminfo 内存使用情况
/proc/modules 已加载模块列表
/proc/partitions 分区信息

硬件信息工具

lspci

1
lspci [选项]
选项 说明
-vv 超详细输出
-t 树状显示设备层级

lsusb

1
lsusb [选项]
选项 说明
-v 详细输出设备描述符
-t 树状显示USB设备拓扑

udev 设备管理

规则文件示例

/etc/udev/rules.d/70-persistent-net.rules

1
2
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", 
ATTR{address}=="08:00:27:aa:11:3c", NAME="eth0"

udevadm 工具

1
udevadm info [选项]
选项 说明
-a 显示父设备属性
-n 指定设备节点路径

实时监控

1
udevadm monitor

留言與分享

作者的圖片

Kein Chan

這是獨立全棧工程師Kein Chan的技術博客
分享一些技術教程,命令備忘(cheat-sheet)等


全棧工程師
資深技術顧問
數據科學家
Hit廣島觀光大使


Tokyo/Macau