Redis 实战之主从复制、高可用、分布式

简介

本节内容基于 CentOS 7.4.1708,Redis 3.2.12 环境实验。

Redis 是一个开源的高性能键值对数据库。

安装:yum install -y redis

特性:

  • 高性能 Key-Value 服务器

  • 多种数据结构

  • 丰富功能

    • 缓存(get|set)

    • 计数器(incre)

    • 消息队列(publish|subcrib)

  • 高可用(v2.8 redis-sentinel)

  • 分布式(v3.0 redis-cluster)

可执行文件:

  • redis-server:服务端

  • redis-cli:客户端

  • redis-benchmark:性能测试工具

  • redis-check-aof:aof修复工具

  • redis-check-dump:rdb修复工具

  • redis-sentinel:sentinel服务端

启动方式:

  • 最简启动:默认配置直接启动redis-server

  • 动态参数启动:命令行指定配置启动redis-server

  • 配置文件启动(推荐):指定配置文件启动redis-server

启动验证:

  • ps -ef|grep redis

  • redis-cli -h locahost -p 6379 ping

由于 redis 是单线程的,推荐在一台多核CPU机器上部署多个 redis 实例充分发挥。

持久化

redis 持久化支持2种:

  1. RDB:快照方式,相当于 MySQL 中的 dump

  2. AOF:写日志方式,相当于 MySQL 中的 binlog,推荐使用

注意:

  1. 当同时开启 RDB 和 AOF 的时候,redis启动的时候会读取 AOF 还原数据。

  2. 推荐:关闭 RDB 持久化机制,开启 AOF

RDB

RDB是什么:

  1. RDB方式的持久化是通过快照(snapshortting)完成的,当符合一定条件时Redis会自动将内存中所有数据完整的压缩存储到硬盘上。

  2. RDB开启条件由2个参数 时间 和 改动次数构成。如:save 900 1

  3. RDB文件由2个参数 dir 和 dbfilename 分别指定目录 和 文件名

  4. RDB方式是Redis默认的持久化方式。

触发命令:

  1. save 命令(阻塞)

  2. bgsave 命令(fork过程阻塞)

主要触发方式:

  1. 自动触发规则(内部调用bgsave,不推荐开启)

  2. 全量复制(内部调用bgsave)

过程:

  1. 执行 save 或 bgsave 命令

  2. 生成新的 rdb 文件,如:temp-36985.rdb

  3. 覆盖 rdb 文件,如:dump-6379.rdb

优点:

  1. 启动速度快

  2. 占用空间小

缺点:

  1. 容易丢失数据

  2. 时间复杂度O(n)

关闭RDB方式:

redis-cli config set save ""

注意:

RDB并不能真正的关闭,在主从复制时主从都会生成RDB文件

AOF

AOF是什么:

  1. AOF是纯文本文件,会记录 Redis 的每次改动命令(不记录查询)。

  2. AOF开启条件:appendonly yes

  3. AOF文件由2个参数 dir 和 appendfilename 分别指定目录 和 文件名

  4. AOF方式 默认情况下Redis并没有开启。

由于每次改动都会记录,产生2个问题:

  1. 每次改动都写入硬盘,普通硬盘只能承受几百次qps。通过写入策略来调整

  2. 对同1个key执行几次操作就记录几次,冗余量特别大。通过 aof 文件重写来调整

AOF文件有3种写入策略:

  1. always(每次写入都会fsync同步到硬盘)

  2. everysec(默认,1s写1次)

  3. no(并非不写,交给系统控制预计30s写1次)

AOF重写:

  1. 重写方式

    1. 手动执行 bgrewriteaof 命令

    2. 自动触发规则(通过指定最小aof文件和aof增长率来自动内部调用 bgrewriteaof)

  2. 过程

    1. fork 出子进程

    2. 子进程执行 bgrewriteaof 命令

    3. 父进程将新接收的命令,同时写到 aof 文件和 aof_rewrite_buffer文件中。(在 aof 重写时,可配置关闭aof写入)

    4. 子进程将 aof_rewrite_buffer 文件追加到新 aof 文件中。

    5. 覆盖旧的 aof 文件

注意:

  1. 采用 everysec 方式,最多可能丢失 2s 的数据。

主从复制

为什么需要主从复制:

通过持久化保证 Redis 在服务器重启的情况下数据也不会丢失。但数据在一台服务器上,如果服务器的硬盘坏了,也会导致数据丢失。为了避免单点故障,Redis 提供了主从复制高可用方案。

主从复制结构:

  1. 1个 master 可以有多个 slave

  2. 1个 slave 只能有1个 master

  3. 数据流向单向 master -> slave

开启复制:

  1. 命令:--slaveof ip port

  2. 配置:slaveof ip port(默认配置都是master)

关闭复制:

slaveof no one

复制类型:

  1. 全量复制(首次 或者 网络断开时间比较长)

  2. 部分复制(在网络抖动一定范围的情况下,v2.8以上可配置复制缓存区repl-backlog-size)

全量复制过程:

  1. slave 节点 发起 psync runid offset:psync ? -1

  2. master 节点 返回 fullresync runid offset

  3. master 节点 bgsave 保存当前数据到 rdb

  4. master 节点 在此期间接收到新的数据存储到 buffer 中

  5. master 节点 send RDB、send buffer

  6. slave 节点 flush old data

  7. slave 节点 load RDB、load buffer

在master重启(master 的run_id更新)和slave重启(slave 的run_id丢失)时都会发生全量复制,通过 info server 可以查看run_id。

部分复制过程:

  1. slave 节点 发起 psync runid offset

  2. master 节点 确认 runid 和 offset没问题后,发送增量数据

  3. slave 节点 接收同步数据。

当全量复制完成 或 网络抖动一定范围 时,master 相当于 slave 的 client 进行增量更新数据。

Redis Sentinel

Redis-Sentinel是什么?

  1. Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案

  2. Redis-Sentinel本身也是一个独立运行的进程,它能监控多个 master-slave 集群,发现 master宕机 后能进行自动故障转移。

sentinel工作原理:

  1. 准备多个Redis Sentinel节点(建议至少3个节点,避免单点故障)

  2. 多个 Sentinel 节点发现并确认 master 主观下线

  3. 超过 quorum 个 sentinel 判定确认 客观下线

  4. 选出 1个 sentinel 节点作为领导

  5. 选出 1个 slave 节点作为 master

  6. 切换 slave 节点的 master 为新的 master

  7. 通知 client 主从变化

  8. 等待故障的 master 复活成为新的 slave

  9. Client 不直接连接 Redis 节点,应该连接 Sentinel 节点获取 Redis Info

2种下线判定:

  1. sdown(subjectively down,主观下线):每个 sentinel 判定 redis 节点下线。

  2. odown(objectively down,客观下线):超过 quorum 个 sentinel 判定 redis 节点下线。

启动方式:

  1. redis-sentinel /path/to/sentinel.conf

  2. redis-server /path/to/sentinel.conf --sentinel

三个定时任务:

  1. 每个 Sentinel 节点每秒通过 redis 的 __sentinel__:hello 发布一条消息,宣布自己的存在。同时也订阅来确定其他的 Sentinel 节点。

  2. 每个 Sentinel 节点每秒对其他 redis 节点执行 ping。确定是否下线。

  3. 每个 Sentinel 节点每10秒 对 master 和 slave 执行 info,确定 slaves。

配置模拟:

  1. 配置 Redis 开启主从复制

  2. 配置 Sentinel 监控主节点

echo "停止当前所有redis-server + redis-sentinel";
ps -x | grep redis | grep -v grep | awk '{print $1}' | xargs -r killecho "生成并启动3个 redis 配置";for port in 6379 6380 6381 ;doecho -e "daemonize yes\nport $port\npidfile /var/run/redis-$port.pid\nlogfile /var/log/redis/redis-$port.log\n" > /etc/redis/redis-$port.confif [ $port != 6379 ];thenecho "slaveof 127.0.0.1 6379" >> /etc/redis/redis-$port.conffiredis-server /etc/redis/redis-$port.confdoneecho "生成并启动3个 redis-sentinel 配置";for port in 26379 26380 26381 ;doecho -e "daemonize yes\nport $port\ndir /tmp\nsentinel monitor mymaster 127.0.0.1 6379 2\nsentinel down-after-milliseconds mymaster 3000\nsentinel parallel-syncs mymaster 1\nsentinel failover-timeout mymaster 60000\nlogfile /var/log/redis/sentinel-$port.log\n" > /etc/redis/redis-sentinel-$port.conf
redis-sentinel /etc/redis/redis-sentinel-$port.confdoneecho "结束";

常用的channel:

  • +switch-master:切换主节点

  • +convert-to-slave:切换从节点

  • +sdown:主观下线

.NET Core环境开发:

dotnet add package StackExchange.Redis

var options = new ConfigurationOptions()
{CommandMap = CommandMap.Sentinel,EndPoints = { { "192.168.0.51", 26379}, {"192.168.0.51", 26381}, {"192.168.0.51", 26380} },AllowAdmin = true,TieBreaker = "",ServiceName = "mymaster",SyncTimeout = 5000};var sentinelConn = ConnectionMultiplexer.Connect(options);var master = sentinelConn.GetServer("192.168.0.51",26381).SentinelGetMasterAddressByName("mymaster");// ...var conn = ConnectionMultiplexer.Connect(master);
sentinelConn.GetSubscriber().Subscribe("+switch-master", (channel, message) =>
{    // mymaster 192.168.0.51 6380 192.168.0.51 6381Console.WriteLine((string)message);    // ...conn = ConnectionMultiplexer.Connect(ip);conn.GetDatabase().StringSet("hello","故障切换后值");
});
sentinelConn.GetSubscriber().Subscribe("+convert-to-slave", (channel, message) =>
{    // slave 192.168.0.51:6379 192.168.0.51 6379 @ mymaster 192.168.0.51 6380Console.WriteLine((string)message);
});conn.GetDatabase().StringSet("hello","原始值");

注意:

  1. 所有Sentinel和Redis不能在同一个节点

Redis Cluster

实际上大部分场景下,Redis Sentinel已经足够好。请根据实际情况采用 Redis Cluster。

Redis Cluster 采用虚拟槽分区方式(16384个虚拟槽)。

原因:

  • 需要更高的qps(超过 10w/s)

  • 需要更高的数据量(超过 500G)

  • 需要更高的带宽(超过 1000M)

常用命令:

  1. redis-cli -h localhost -p 6382 cluster info:查看集群基本信息

  2. redis-cli -h localhost -p 6382 cluster slots:查看集群slot信息

  3. redis-cli -h localhost -p 6382 cluster nodes:查看集群node信息

  4. redis-cli -c:move自动跳转执行

  5. yum install -y redis-trib:官方提供了基于 ruby 的工具方便部署

搭建 Cluster 过程:

  1. 配置
    cluster-enabled:yes
    cluster-node-timeout 15000
    cluster-require-full-coverage no
    cluster-config-file node-${port}.conf

  2. meet
    redis-cli cluster meet ip port

  3. 分配槽(0-16383)
    redis-cli cluster addslots {0....5461}

  4. 分配主从(node-id)
    redis-cli cluster replicate {nodeid}

redis-cli 搭建:

echo "停止当前所有redis-server + redis-sentinel";
mkdir /etc/redis
ps -x | grep redis | grep -v grep | awk '{print $1}' | xargs -r killsleep 1echo "启动6个 redis + meet";for port in 7000 7001 7002 7003 7004 7005;doecho -e "daemonize yes\nport $port\npidfile /var/run/redis-$port.pid\nlogfile /var/log/redis/redis-$port.log\ncluster-enabled yes\ncluster-config-file nodes-$port.conf\ncluster-require-full-coverage no" > /etc/redis/redis-$port.conf
redis-server /etc/redis/redis-$port.confdonefor port in 7000 7001 7002 7003 7004 7005;doredis-cli -p $port FLUSHALLredis-cli -p $port cluster reset soft    if [ $port != 7000 ];thenredis-cli -p 7000 cluster meet 127.0.0.1 $portfidonesleep 1echo "分配 16383 槽";
redis-cli -p 7000 cluster addslots {0..5461}
redis-cli -p 7001 cluster addslots {5462..10922}
redis-cli -p 7002 cluster addslots {10922..16383}echo "配置 replication"redis-cli -p 7003 cluster replicate `redis-cli -p 7000 cluster nodes | grep myself | awk '{print $1}'`
redis-cli -p 7004 cluster replicate `redis-cli -p 7001 cluster nodes | grep myself | awk '{print $1}'`
redis-cli -p 7005 cluster replicate `redis-cli -p 7002 cluster nodes | grep myself | awk '{print $1}'`

redis-trib搭建:

  1. 准备节点

  2. 使用redis-trib搭建
    redis-trib create --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

redis-trib create 会自动meet、addslots、replicate。

配置

查看去除注释的配置:cat /etc/redis.conf | grep -v '^#' | grep -v '^$'

设置配置:config set key value

查询所有配置:config get *

基础配置

配置项默认值推荐值说明
daemonizenoyes(docker环境例外)是否以守护进程方式启动
port6379-redis服务监听端口
pidfile/var/run/redis.pid/var/run/redis-{port}.pidpid文件
logfile/var/log/redis/redis.log/var/log/redis/redis-{port}.log日志文件名:redis工作时产生的日志。
dir/var/lib/redis-rdb文件和aof文件目录。推荐使用大文件目录。(不指定则为当前目录)
protected-modeyes-限制为127.0.0.1访问。启用条件:没有bindIP 和 没有设置密码

RDB配置

配置项默认值推荐值说明
dbfilenamedump.rdbdump-{port}.rdbrdb文件名
rdbcompressionyesyes压缩格式
stop-writes-on-bgsave-erroryesyes出现错误时,停止新的写入
rdbchecksumyesyes数据完整性校验

AOF配置

配置项默认值推荐值说明
appendonlynoyes是否开启 aof 模式
appendfilename"appendonly.aof""appendonly-{port}.aof"aof文件名
appendfsynceveryseceverysecfsync方式
no-appendfsync-on-rewriteno(安全)yes(高性能)在 aof 重写时,是否停止fsync
auto-aof-rewrite-min-size64mb-aof文件重写的最小大小
auto-aof-rewrite-percentage100-aof文件增长率
aof-load-truncatedyesyes当 aof 文件不完整的时候,将完整的部分加载

主从复制配置

配置项默认值推荐值说明
slowlog-max-len1281000慢查询队列长度
slowlog-log-slow-than100001000(qps1w)慢查询阈值(单位:微秒)
slaveofip port-主从复制配置
slave-read-onlyyesyes从节点只读
repl-backlog-size104857610M复制缓存区,可以再原有基础上稍微增加

Sentinel配置

配置项默认值推荐值说明
daemonizenoyes是否以守护进程方式启动
port26379{port}sentinel监听端口
dir/tmp-工作目录
sentinel monitormymaster 127.0.0.1 6379 2-odown(objectively down,客观下线)规则:masterName ip port quorum
sentinel down-after-millisecondsmymaster 30000-sdown(subjectively down,主观下线)规则:masterName timeout(单位:毫秒)
sentinel parallel-syncsmymaster 1-并发同步数量
sentinel failover-timeoutmymaster 180000-多长时间内不再故障转移(单位:毫秒)
logfile/var/log/redis/sentinel.log/var/log/redis/sentinel-{port}.log日志文件

Cluster配置

配置项默认值推荐值说明
cluster-enablednoyes开启cluster模式
cluster-node-timeout15000-故障转移时间,主观下线超时时间
cluster-config-filenodes-{port}.confcluster配置
cluster-require-full-coverageyesnocluster 所有节点全部在线才提供服务

常见问题

redis是单线程吗?为什么这么快?

redis其实不是单线程(fsync,bgsave),一次只能执行一条命令。

慢查询

查询慢查询队列:slowlog get

客户端请求的生命周期:

  1. 发送命令

  2. 排队

  3. 执行命令

  4. 返回结果

慢查询发送在第三个阶段(执行命令),客户端超时不一定是慢查询。

fork

  1. fork本身是同步操作

  2. 内存越大耗时越长

  3. info:latest_fork_usec

规避全量复制

  • 首次全量复制:不可避免

  • runid 不匹配:故障转移

  • 复制缓冲区不足:配置repl_backlog_size调整大

常用命令

  • KEYS pattern :查询keys

  • DBSIZE :查询所有键的数量

  • EXISTS key :查询指定key是否存在

  • TYPE key :查询key的类型

  • DEL key :删除指定key

  • INFO :查看server 信息如:INFO memory

INFO 信息:

  • used_memory redis 当前使用的内存总量

  • used_memory_rss redis 当前使用的内存总量(包含内存碎片)

  • used_memory_peak redis 使用的内存总量峰值


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/319599.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

纪中A组模拟赛总结(2021.7.16)

成绩 rankrankranknamenamenamescorescorescoreT1T1T1T2T2T2T3T3T3T4T4T4525252lyflyflyf000000000000000 前言:暴零了,太棒了呀(^-^) 总结 开考后再四题里来回看,都没有找到解题方法(主要是有…

CF710F-String Set Queries【AC自动机,二进制分组】

正题 题目链接:https://www.luogu.com.cn/problem/CF710F 题目大意 TTT次操作 往集合中加入一个字符串往集合中删除一个字符串给出一个模式串求出现的集合里面的字符串个数 解题思路 删除的话改成加入一个权值为−1-1−1的字符串就是全都是加入操作了。 然后就可以像[SDOI2…

牛客网 【每日一题】8月5日题目精讲—蓝魔法师

来源:牛客网: 文章目录题目描述题解:代码:题目描述 “你,你认错人了。我真的,真的不是食人魔。”–蓝魔法师 给出一棵树,求有多少种删边方案,使得删后的图每个连通块大小小于等于k…

AtCoder Beginner Contest 178 总结

A - Not 签到题 #define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<iostream> #include<algorithm> using namespace std; const int N100010; int main() {IO;int T1;//cin>>T;while(T--){int x;cin>&g…

.NET Core 2.1.5和.NET Core SDK 2.1.403发布

2018 年 10 月 1 日&#xff0c;.NET Core 2.0 已宣布结束生命周期。这意味着 2.0 系列将不会提供更新。2.1 系列上月作为长期支持版发布&#xff0c;.NET Core 2.1.5 和 .NET Core SDK 2.1.403 版本包含许多问题修复。主要更新内容如下&#xff1a;CoreCLR2018-09-11 - [cb730…

P4859-已经没有什么好害怕的了【容斥,dp】

正题 题目链接:https://www.luogu.com.cn/problem/P4859 题目大意 两个长度为nnn的序列a,ba,ba,b两两匹配&#xff0c;求ai>bia_i>b_iai​>bi​的组数比ai<bia_i<b_iai​<bi​的组数多kkk的方案数。 保证输入数字两两不同 解题思路 其实就是求恰好有nk2\f…

(牛客网)树型dp

树型dp 视频链接 &#xff08;如果想购买网课&#xff0c;可以用我的邀请码&#xff09; 用我的链接购买&#xff0c;我再反你10&#xff0c;一共花54多值 购买链接 不放心可以先加我好友2830872914 总试题链接 文章目录树型dp例题NC15033 小G有一个大树NC511788 没有上司的舞…

Node 源项目定制化、打包并使用全过程讲解

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是全栈工…

纪中A组模拟赛总结(2021.7.17)

成绩 rankrankranknamenamenamescorescorescoreT1T1T1T2T2T2T3T3T3222222lyflyflyf999999000000总结 看到T1每一段有代价&#xff0c;每条边都至少要便利1次&#xff0c;感觉是树上线性规划&#xff08;昨天刚讲线性规划&#xff09;&#xff0c;觉得正着没那么好做&#xff0…

.Net Core中的Api版本控制

原文链接&#xff1a;API Versioning in .Net Core作者&#xff1a;Neel Bhatt简介Api的版本控制是Api开发中经常遇到的问题, 在大部分中大型项目都需要使用到Api的版本控制在本篇博客中&#xff0c;我们将说明一下如何在.Net Core Api项目中使用Api版本控制。本篇博客中测试项…

Educational Codeforces Round 95 (Rated for Div. 2)

昨天本来想打一下&#xff0c;但是今天早上课很早&#xff0c;就没有打&#xff0c;只是看了看前三个题写了个代码&#xff0c;今天中午课结束交了一下都AC了。y1s1 A题第一次写就出来了&#xff0c;但是答案一直不对&#xff0c;最后结果是样例错了-。- A - Buying Torches …

【每日一题】8月4日题目精讲—购物

来源&#xff1a;牛客网&#xff1a; 文章目录购物题目描述题解&#xff1a;代码&#xff1a;购物 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 32768K&#xff0c;其他语言65536K 64bit IO Format: %lld题目描述 在遥远的东方&#xff0c;有…

P4606-[SDOI2018]战略游戏【圆方树,虚树】

正题 题目链接:https://www.luogu.com.cn/problem/P4606 题目大意 给出nnn个点mmm条边的一张图&#xff0c;qqq次询问给出一个点集&#xff0c;询问有多少个点割掉后可以是点集中至少一个点对不连通。 解题思路 就是问圆方树上的虚树中的圆点数量&#xff0c;照着统计就好了…

【二分】雪(luogu 7405)

正题 luogu 7405 题目大意 坐标轴上有n个雪球&#xff0c;初始重量为0&#xff0c;每一条线段上有重量为1的雪&#xff0c;当雪球经过时&#xff0c;会加上这些雪&#xff0c;而地上就没有雪了 共有m个时刻&#xff0c;每个时刻会使所有雪球向左/右移动wiw_iwi​格&#xff…

Visual Studio 2017 与 Visual Studio for Mac 支持更新

微软在博客中简单介绍了关于 VS 2017 和 VS for Mac 项目的支持计划&#xff1a;https://blogs.msdn.microsoft.com/visualstudio/2018/10/05/visual-studio-2017-and-visual-studio-for-mac-support-updates/。微软表示&#xff0c;在目前努力开发 Visual Studio 2019 的同时&…

牛客练习赛 67——ST表

A.牛牛爱字符串 注意原字符串有空格&#xff0c;不要用cin #define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0) #pragma GCC optimize(2) #include<string> #include<iostream> #include<algorithm> using namespace std; int main() {IO;int …

【每日一题】8月3日题目精讲—小A的最短路

来源&#xff1a;牛客网&#xff1a; 文章目录题目描述题解&#xff1a;代码&#xff1a;时间限制&#xff1a;C/C 3秒&#xff0c;其他语言6秒 空间限制&#xff1a;C/C 262144K&#xff0c;其他语言524288K 64bit IO Format: %lld题目描述 小A这次来到一个景区去旅游&#xf…

Stack(nowcoder 11253-K)

正题 nowcoder 11253-K 题目大意 有n个数&#xff0c;依次加进栈中&#xff0c;每次加入前将栈顶比aia_iai​大的所有元素弹掉&#xff0c;加入后记bib_ibi​为栈的大小 现在给你b中的一些数&#xff0c;让你求a数组的一种合法方案&#xff0c;其中1~n在a中各出现了一次 解题…

P4424-[HNOI/AHOI2018]寻宝游戏【结论】

正题 题目链接:https://www.luogu.com.cn/problem/P4424 题目大意 nnn个mmm位二进制数&#xff0c;开始是一个000。 然后依次对所有二进制数进行nnn次andandand或者ororor操作。 qqq次询问给出二进制数rir_iri​&#xff0c;要求有多少种操作序列使得操作完后的数是rir_iri​…

ASP.NET Core中使用表达式树创建URL

当我们在ASP.NET Core中生成一个action的url会这样写&#xff1a;var url_urlHelper.Action("Index", "Home");这样的写法存在的问题在于我们传递了两个字符串类型的参数&#xff0c;而我们又无法避免对action和controller做重命名操作, 例如将index重命名…