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 | tar -zxvf redis-5.0.2.tar.gz |
第二步:编译redis
1 | #进入解压后的tar包执行 |
第三步:移动配置文件到conf目录
1 | cp redis.conf /opt/redis/conf |
额外配置:
- redis开机自启
1 | vim /etc/rc.local |
- 配置数据保存目录
1 | vim /opt/redis/conf/redis.conf |
四、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高级功能
慢查询日志
- 慢查询发生在第三阶段
- 客户端超时不一定是慢查询导致,但慢查询可导致客户端超时
慢查询队列简介
- 先进先出的队列
- 固定长度
- 长度不够时,丢弃最早记录
- 保存在内存中
慢查询设置
-
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 | 清空慢查询队列 |
查询结果示例
Pipeline
一般情况下客户端发送一条命令到Redis,Redis处理结束返回结果,即 N条命令 = N次网络时间 + N次计算时间
PipeLine将命令打包发送给客户端,Redis处理完返回结果,PipLine是一个异步处理方式,并不等待Redis返回。
Pipeline(N条命令) = 1次网络时间 + N次计算时间
1 | /* |
发布订阅
常用命令格式 | 描述 |
---|---|
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 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种 |
|
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
3save 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持久化需要设置appendonly
为yes
AOF文件生成策略
-
always 不丢失数据,每次更新记录数据就进行io操作
-
everysec 可能会丢失1s数据,但io小
-
no 不启用AOF
AOF重写
AOF支持AOF文件重写(从内存中读取的数据,并非读取上次的AOF文件进行重写)。AOF重写可以减少硬盘占用、加速恢复速度。
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 | +------------------------+ |
持久化相关优化
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
2slaveof 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 | ##############从库############## |
主从配置问题
maxmomory不一致导致丢失数据
数据结构参数优化只有优化了主机,从机未配置导致内存不一致,数据错误或丢失
九、Redis Sentinel
- 主观下线:Sentinel根据配置条件,发现redis节点达到故障标准,则此Sentinel认为此redis节点下线
- 客观下线:当Sentinel中认为此redis客观下线的总数达到配置阙值,则认为此节点客观下线
安装Sentinel
配置sentinel.conf文件
1 | #没有开启bind和密码的情况下,保护模式默认被开启。只接受来自环回IPv4和IPv6地址的连接。拒绝外部连接 |
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
数据分布概论
分布方式 | 特点 | 典型产品 |
---|---|---|
哈希分布 | 数据分散度高 | |
Memcache |
Redis Cluster
|
| 键值分布业务无关 |
| 无法顺序访问 |
| 支持批量操作 |
| 顺序分布 | 数据分散度易倾斜 |
BigTable
HBase
|
| 键值分布业务相关 |
| 可顺序访问 |
| 支持批量操作 |
哈希分布方式
节点取余分区
进行取模运算,将余数相等的放入同一节点,简单易操作,增加节点时数据偏移,导致数据的前移达到80%,翻倍扩容可以使数据迁移从80%降到50%
一致性hash分区
为系统中每个节点分配一个token,范围一般在0~2的32次方,这些token构成哈希环。数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的token节点,往往一个节点会对应多个token。加减节点会造成哈希环中部分数据无法命中,需要手动处理或者忽略这些数据,常用于缓存场景
虚拟哈希分区
虚拟分槽使用哈希函数把所有数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。槽数范围远远大于节点数(redisCluster槽的范围是0~16383),每一个节点负责维护一部分槽以及所映射的键值数据
基本架构
-
所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽
-
节点的fail是通过集群中超过半数的master节点检测失效时才生效
-
客户端与redis节点直连,不需要中间proxy层.客户端连接集群中任何一个可用节点即可
-
redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key
安装Cluster
修改redis配置文件
1 | cluster-enable yes |
原生安装
-
启动所有节点
-
执行
redis-cli -p ${port} cluster meet ${ip} ${port}
使节点相遇 -
执行
cluster addslots slot [slot...]
分配槽1
2
3
4
5
6
7
8start=$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 | cluster-enable yes |
集群伸缩
扩展集群
扩展步骤原理
- 对目标节点发送cluster setslot {slot} importing {targetNodeId}命令,让目标节点准备导入槽的数据
- 对源节点发送cluster setslot {slot} migrating {sourceNodeId}命令,让源节点准备槽数据的导出
- 源节点上循环执行cluster getkeyinslot {slot} count 命令,每次获取属于这个槽中键的个数
- 在源节点上执行migrate {sourceIp} {sourcePort} key 0 {timeout}命令,迁移指定的key
- 重复执行步骤3~4直到槽下所有数据完成迁移
- 向集群内所有主节点发送cluster setslot {slot} node {targetNodeId}命令,通知槽分配给目标节点
扩展执行步骤
-
加入集群
执行
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
集群缩容
迁移槽命令和扩展集群的迁移命令相同,迁移完成之后使用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重定向
ask重定向
smart客户端
实现原理:追求性能
- 从集群中选一个可运行节点,使用
cluster slots
初始化槽和节点映射 - 将cluster slots的结果映射到本地缓存,为每个节点创建JedisPool
- 准备执行命令(使用CRC16计算key对应的槽,找到映射节点执行)
1 | public static void main(String[] args) { |
批量操作
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())
方案三:在方案二的基础上将串行取数据改为并行取数据,进一步提高效率
方案四:通过redis自带的hashtag功能,强制一批key分配到某台机器上【不建议,大量数据会造成数据倾斜】
1 | //使用{user}作为key,使key统一 |
故障转移
故障发现
- 通过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过期,重复此操作
策略 | 一致性 | 维护成本 |
---|---|---|
算法剔除 | 最差 | 低 |
被动更新 | 较差 | 低 |
主动更新 | 强 | 高 |
低一致性:最大内存和淘汰策略
高一致性:超时剔除和主动更新结合,最大内存和淘汰策略兜底
缓存穿透&缓存雪崩&无底洞
缓存穿透
特点:
当缓存和数据库中都没有数据的时候,当查询Redis没有数据的时候,会继续查询数据库,数据库也没有数据,当大量查询请求发生或遭到恶意攻击时,这些访问全部透过Redis,并且数据库也没有数据,这种现象称为“缓存穿透”。
解决方案:
- 缓存空对象,storage返回一个空对象,将键存储在缓存层,下次请求此键之间返回空对象
- 需要更多的键,建议设置过期时间
- 缓存层和存储层数据“短期”不一致
- 布隆过滤器
缓存击穿
特点:
当Redis的热点数据key失效时,大量并发查询直接打到数据库,此时数据库负载压力骤增,这种现象称为“缓存击穿”
解决方案:
1.设置key值永不过期
2.使用互斥锁,查到后就回填缓存
缓存雪崩
特点:
缓存雪崩是指在短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。
解决方案:
- 随机设置key过期时间
- 随机延时,让一部分查询先将数据缓存起来
- 设置key值永不过期
无底洞问题
现象:增加节点机器性能没提升反而下降
解决方案参考批量操作
热点key的重建优化
现象:热点key缓存重建过程过长导致浪费了不必要的资源
解决方案:
-
互斥锁【使用redis构建锁机制】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15String 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;
} -
永不过期
- 缓存层面:没有设置过期时间
- 功能层面:为每个value添加逻辑过期时间,但发现超过逻辑过期时间后,会使用单独的线程取构建缓存
方案 | 优点 | 缺点 |
---|---|---|
互斥锁 | 思路简单 | 代码复杂度增加 |
保证一致性 | 存在死锁的风险 | |
永不过期 | 基本杜绝热点key重建问题 | 不保证一致性 |
逻辑过期时间增加维护成本和内存成本 |
十二、Redis布隆过滤器
布隆过滤器的原理
- 首先需要k个hash函数,每个函数可以把key散列成为1个整数
- 初始化时,需要一个长度为n比特的数组,每个比特位初始化为0
- 某个key加入集合时,用k个hash函数计算出k个散列值,并把数组中对应的比特位置为1
- 判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中
优点:不需要存储key,节省空间
缺点:算法判断key在集合中时,有一定的概率key其实不在集合中,且无法删除
google-guava库实现了java版的布隆过滤器
Redis布隆过滤器
使用插件的方式部署
redis4.0之后支持使用插件的方式使用Bloom filters和cuckoo filters,redis4.0之前需手动使用代码的方式编写布隆过滤器,安装步骤参考下列链接
建议在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 | <dependency> |
Api使用
1 | public static void main(String[] args) { |
十三、Redis开发规范
BigKey处理
发现BigKey
debug object {key}
查看指定key的详细信息redis-cli --bigkeys
扫描出BigKey【全表扫描,阻塞,建议从节点本地执行】
BigKey删除
场景:当key非常大时,delete命令执行十分缓慢,会发生阻塞【过期bigkey也是进行删除操作也会阻塞】
redis4.0之后:可以使用unlink
命令进行后台删除,不阻塞前台
生命周期管理
-
使用
OBJECT IDLETIME {key}
查看key的闲置时间 -
过期时间不易集中
命令优化
有遍历需求可以使用hscan
、sscan
、zscan
代替【这些扫描命令在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比值,表示内存碎片率 |
内存消耗划分
-
客户端缓冲区设置规则
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 | #将命令更改为另一个字符串,原命令失效。如果字符串为空则表示禁用此命令 |
附各种数据类型的内部编码:
附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
字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的一个区别。性质完全不同的数据(比如products
和logs
)应该存成两个 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
变成updated
,created
字段变成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": "系统" } }
]
}
}
}'
七、参考链接
(完)
-
Security Group(SG):
- 所有设定默认是拒绝,只可以设定许可规则,拒否ルールは指定できません。
- 特征是不单可以指定CIDR等IP,还可以指定SG
- 安全组是有状态的 — 如果您从实例发送一个请求,则无论入站安全组规则如何,都将允许该请求的响应流量流入。如果是为响应已允许的入站流量,则该响应可以出站,此时可忽略出站规则。セキュリティグループはステートフルです。
-
Access Control List(ACL):
- VPC 自动带有可修改的默认网络 ACL。默认情况下,它允许所有入站和出站 IPv4 流量以及 IPv6 流量 (如果适用)。
- 可以创建自定义网络 ACL 并将其与子网相关联。默认情况下,每个自定义网络 ACL 都拒绝所有入站和出站流量,直至您添加规则。
- VPC 中的每个子网都必须与一个网络 ACL 相关联。如果您没有明确地将子网与网络 ACL 相关联,则子网将自动与默认网络 ACL 关联。
- 可以将网络 ACL 与多个子网关联。但是,一个子网一次只能与一个网络 ACL 关联。当您将一个网络 ACL 与一个子网关联时,将删除之前的关联。
- 网络 ACL 包含规则的编号列表。我们按顺序评估(从编号最小的规则开始)规则,以判断是否允许流量进入或离开任何与网络 ACL 关联的子网。您可以使用的最高规则编号为 32766。我们建议您开始先以增量方式创建规则(例如,以 10 或 100 的增量增加),这样您可以在稍后需要时插入新的规则。
- 网络 ACL 有单独的入站和出站规则,每项规则都或是允许或是拒绝数据流。
- 网络 ACL 没有任何状态,这意味着对允许入站数据流的响应会随着出站数据流规则的变化而改变(反之亦然)。(ネットワーク ACL はステートレスです。許可されているインバウンドトラフィックに対する応答は、アウトバウンドトラフィックのルールに従います(その逆の場合も同様です)。)
- ルール番号。ルールは、最も低い番号のルールから評価されます。ルールがトラフィックに一致すると、それと相反するより高い数値のルールの有無にかかわらず、すぐに適用されます。
- タイプ。トラフィックのタイプ(SSH など)。また、すべてのトラフィックまたはカスタム範囲を指定することもできます。
セキュリティグループはステートフルです。(レスポンスでも明示する必要なしで許可。)
ACLはステートレスです。(レスポンスでも明示する必要ある。)
从源代码安装软件笔记整理
1. 安装C语言开源软件的三个主要步骤
- 解压源代码
- 编译成二进制文件
- 将二进制文件安装到适当的文件夹
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] [目标]
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 | route add -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 | iwconfig wlan0 essid "windsor" |
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 | DEVICE=eth0 |
/etc/network/interfaces (Debian):
1 | auto lo |
7. 网络问题排查
7.1 排查步骤
-
Ping 测试:
1
2ping 172.16.0.1
ping h001.s16.la.net- 如果 IP 能 Ping 通,但主机名失败,可能是 DNS 问题,检查
/etc/resolv.conf
。
- 如果 IP 能 Ping 通,但主机名失败,可能是 DNS 问题,检查
-
局域网连通但无法访问互联网:
- 检查默认网关或路由表:
1
route
- 如果没有默认路由,添加默认网关:
1
route add default gw 172.16.0.1
- 检查默认网关或路由表:
-
网络连接不通:
- 使用
traceroute
或tracepath
查看路径:1
traceroute www.lpi.jp
- 使用
-
网络接口问题:
- 检查网卡状态:
1
ifconfig
- 如果只显示
lo
,启动接口:1
2
3ifup 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。
- 路由表:理解目标网络、网关、子网掩码和接口的关系,确保数据包正确转发。
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 结构:
- PV (Physical Volume):一个硬盘或一个分区作为物理空间。
- PE (Physical Extent):PV 组成 PE。
- VG (Volume Group):PE 组成 VG。
- LV (Logical Volume):VG 内的 PE 分配为 LV。
创建 LVM:
-
准备 LVM 用的分区:
1
2fdisk /dev/sda
fdisk /dev/sdb -
初始化 PV:
1
pvcreate /dev/sda1 /dev/sdb1
-
创建 VG:
1
vgcreate testvg /dev/sda1 /dev/sdb1
-
创建 LV:
1
lvcreate -L 1G -n lv01 testvg
-
创建文件系统并挂载:
1
2mke2fs -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 配置步骤:
-
配置
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> -
启动 iSCSI target 服务:
1
service tgtd start
-
使用
tgtadm
命令查看 target 状态:1
tgtadm --mode target --op show
-
使用
iscsiadm
从 initiator 查询 target:1
iscsiadm -m discovery -t sendtargets -p 192.168.11.3
-
配置认证方式:
修改/etc/iscsi/iscid.conf
:1
2
3node.session.auth.authmethod = CHAP
node.session.auth.username = centuser
node.session.auth.password = password -
登录 iSCSI:
1
iscsiadm -m node --login
登录后,使用
cat /proc/partitions
查看 iSCSI 目标是否添加成功。
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 | cat /etc/mtab # 或 |
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 | 强制卸载 |
强制卸载流程:
fuser -vm /mountpoint
查看占用进程kill -9 PID
结束进程umount -f /mountpoint
3. SWAP 管理
创建SWAP文件
1 | # 创建1GB交换文件 |
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 | fsck -y /dev/sda1 # 自动修复 |
XFS检查:
1 | xfs_repair /dev/sdb1 |
检查时机:
- 异常关机后
- 定期维护时(建议每6个月)
- 出现I/O错误时
5. 高级文件系统特性
ext4 特性管理
转换ext3→ext4:
1 | tune2fs -O extent,uninit_bg,dir_index /dev/sda1 |
调整参数:
1 | tune2fs -c 30 -i 7d /dev/sda1 # 每30次挂载或7天检查 |
XFS 高级功能
查看信息:
1 | xfs_info /dev/sdb1 |
备份恢复:
1 | xfsdump -l 0 -f /backup/root.xfsdump /dev/sda1 |
6. 光盘与加密文件系统
光盘制作
1 | # 创建ISO |
加密文件系统
LUKS加密:
1 | cryptsetup luksFormat /dev/sdb1 |
关闭加密卷:
1 | umount /mnt/secure |
7. 磁盘健康监控
S.M.A.R.T. 监控
1 | smartctl -a /dev/sda # 查看详细信息 |
关键监控指标:
- Reallocated_Sector_Ct
- Current_Pending_Sector
- Temperature_Celsius
8. 故障处理流程
文件系统修复步骤
-
卸载文件系统:
1
umount /dev/sda1
-
进入单用户模式:
1
init 1
-
执行检查修复:
1
fsck -y /dev/sda1
-
严重损坏时:
1
2
3debugfs /dev/sda1
> lsdel # 查看已删除inode
> dump <inode> /tmp/recover_file
应急恢复方案
问题现象 | 解决方案 |
---|---|
无法挂载 | fsck -y /dev/sdX |
Superblock损坏 | e2fsck -b 32768 /dev/sdX |
只读文件系统 | mount -o remount,rw / |
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 | timeout 5 |
常用命令:
1 | grub> root (hd0,0) # 设置根分区 |
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 | boot=/dev/sda |
应用配置:
1 | lilo -v |
3.2 SYSLINUX 系列
类型 | 文件系统 | 用途 |
---|---|---|
SYSLINUX | FAT | USB启动 |
ISOLINUX | ISO9660 | 光盘启动 |
EXTLINUX | ext2/3/4 | 硬盘启动 |
PXELINUX | 网络 | 网络启动 |
PXE启动环境搭建:
- 安装必要服务:
1 | yum install dhcp tftp-server syslinux |
- 配置DHCP:
1 | filename "pxelinux.0"; |
- 准备TFTP目录:
1 | cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/ |
4. 系统恢复方法
4.1 救援模式操作
- 使用安装介质启动
- 进入救援模式
- 挂载原系统:
1 | chroot /mnt/sysimage |
- 修复引导:
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 | vim /etc/init/rc-sysinit.conf |
- 禁用不需要的服务:
1 | chkconfig --list | grep 3:on |
- 使用readahead:
1 | service readahead-early restart |
- SSD优化:
在/etc/fstab添加:
1 | noatime,discard |
1. 内核版本管理
版本号说明
-
2.X.Y 旧版本格式:
- X:偶数表示稳定版,奇数表示开发版
- Y:修订版本号
- 后缀说明:
-rcN
:Release Candidate(候选版本)-gitN
:开发中的Git版本
-
3.0+ 新版本分类:
类型 说明 Prepatch 稳定版前的测试版本 Mainline 包含新特性的主线开发版本 Stable 稳定版本 Longterm 长期支持版本(LTS)
版本查询命令
1 | uname [选项] |
常用选项
选项 | 说明 |
---|---|
-a | 显示全部系统信息 |
-r | 显示内核版本 |
-m | 显示硬件架构 |
其他查询方式
1 | # 查看/proc版本信息 |
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
2alias eth0 e1000
options e1000 speed=1000 duplex=full
3. 内核编译流程
编译步骤
- 准备源代码
- 配置内核:
1
2
3
4make oldconfig # 基于旧配置更新
make menuconfig # 文本界面配置
make xconfig # Qt图形界面配置
make gconfig # GTK图形界面配置 - 编译内核:
1
2
3make # 编译内核和模块
make bzImage # 仅编译内核
make modules # 仅编译模块 - 安装组件:
1
2make modules_install # 安装模块到/lib/modules/
make install # 自动安装内核
手动安装示例
1 | # 复制内核文件 |
其他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 | # 创建initramfs |
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 | SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", |
udevadm 工具
1 | udevadm info [选项] |
选项 | 说明 |
---|---|
-a | 显示父设备属性 |
-n | 指定设备节点路径 |
实时监控
1 | udevadm monitor |