`

Redis上踩过的一些坑-美团

 
阅读更多

上上周和同事(龙哥)参加了360组织的互联网技术训练营第三期,美团网的DBA负责人侯军伟给大家介绍了美团网在redis上踩得一些坑,讲的都是干货和坑。

    分为5个部分:

   二、redis bgrewriteaof问题

   三、redis内存占用飙升

   四、redis内存使用优化 

   五、redis cluster遇到的一些问题 

 

 一、周期性出现connect timeout

1. 背景:
      大部分互联网公司都会有Mysql或者Oracle的DBA,但是在Nosql方面一般不会设置专门的DBA。不过对于一些知名的互联网公司来说,Nosql的使用量是巨大的,所以通常让Mysql的DBA或者单独聘请工程师来维护一些Nosql数据库,比如:
      Redis, Hbase, Memcache(其实严格讲不是nosql), Mongodb, Cassandra。从讲座看美团网应该是有专职的Redis DBA。所以作为业务开发人员不需要自己安装、配置、运维Redis,只需要找Redis DBA来申请就可以了。
      这里为了简化说明:Redis DBA提供的服务叫做Redis云,业务开发人员叫做业务端(redis的使用者)
      
   2. 现象:
       业务端在使用redis云提供的redis服务后,经常出现connect timeout:
Java代码    收藏代码
  1. redis.clients.jedis.exceptions.JedisConnectionException  
  2. java.net.SocketException  
  3. java.net.SocketTimeoutException:connect time out  
   
   3. 分析和怀疑:
   业务端一般认为redis出现问题,就是redis云有问题,人的“正常”思维:看别人错误容易,发现自己难,扯多了, 出现这个有很多原因:
   (1). 网络原因:比如是否存在跨机房、网络割接等等。
   (2). 慢查询,因为redis是单线程,如果有慢查询的话,会阻塞住之后的操作。 
   (3). value值过大?比如value几十兆,当然这种情况比较少,其实也可以看做是慢查询的一种
   (4). aof重写/rdb fork发生?瞬间会堵一下Redis服务器。
   (5). 其他..................
 
   4. 查询原因
   演讲者一开始怀疑是网络问题,但是并未发现问题,观察各种对比图表,tcp listenOverFlow和timeout经常周期出现。(赞一下这个监控,我们监控现在还没有这个层面的)
   有关listenOverFlow:
查看现有的连接数是否大于设置的backlog,如果大于就丢弃,并相应的参数值加1。其中backlog是由程序和系统参数net.core.somaxconn共同设置,当backlog的值大于系统设置的net.core.somaxconn时则取net.core.somaxconn的值,否则取程序设置的backlog值。这种出错的方式也被记录在TcpListenOverflows中(其只记录了连接个数不足而产生溢出错误的次数!)。
   觉得可能和TCP相关,于是分析了Tcp三次握手:最后一次握手客户端的请求会进入服务器端的一个队列(可以认为是下三图)中,如果这个队列满了,就会发生上面的异常。(accept)
  (1) TCP三次握手: 
   
  (2) redis客户端与redis服务器交互的过程(本质就是TCP请求)
  (3) I/O 多路复用程序通过队列向文件事件分派器传送套接字的过程
    
   (4) 和redis有什么关系呢?
        由于Redis的单线程模型(对命令的处理和连接的处理都是在一个线程中),如果存在慢查询的话,会出现上面的这种情况,造成新的accept的连接进不了队列。
    
    如果上面的图没法理解的话,看看这张图:
      
 
   5. 解决方法:
    (1) 对慢查询进行持久化,比如定时存放到mysql之类。(redis的慢查询只是一个list,超过list设置的最大值,会清除掉之前的数据,也就是看不到历史)
    (2) 对慢查询进行报警(频率、数量、时间)等等因素
    (3) 打屁股,哈哈:
     
     (4) 其实应该做的是:对业务端进行培训,告诉他们一下redis开发的坑,redis不是万金油,这个和Mysql DBA要培训Mysql使用者一样,否则防不胜防。
      比如他执行了 monitor, keys *, flushall, drop table, update table set a=1; 这种也是防不胜防的( 当然也可以做限制,利用rename-command一个随机数),但是提高工程师的水平才是关键。
      
 

 二、redis bgrewriteaof问题

一、背景

1. AOF:

    Redis的AOF机制有点类似于Mysql binlog,是Redis的提供的一种持久化方式(另一种是RDB),它会将所有的写命令按照一定频率(no, always, every seconds)写入到日志文件中,当Redis停机重启后恢复数据库。

     

 

2. AOF重写:

     (1) 随着AOF文件越来越大,里面会有大部分是重复命令或者可以合并的命令(100次incr = set key 100)

     (2) 重写的好处:减少AOF日志尺寸,减少内存占用,加快数据库恢复时间。

    

 

 

 

二、单机多实例可能存在Swap和OOM的隐患:

    由于Redis的单线程模型,理论上每个redis实例只会用到一个CPU, 也就是说可以在一台多核的服务器上部署多个实例(实际就是这么做的)。但是Redis的AOF重写是通过fork出一个Redis进程来实现的,所以有经验的Redis开发和运维人员会告诉你,在一台服务器上要预留一半的内存(防止出现AOF重写集中发生,出现swap和OOM)。

    

 

 

 

三、最佳实践

1. meta信息:作为一个redis云系统,需要记录各个维度的数据,比如:业务组、机器、实例、应用、负责人多个维度的数据,相信每个Redis的运维人员都应该有这样的持久化数据(例如Mysql),一般来说还有一些运维界面,为自动化和运维提供依据

    例如如下:

 

 

    

 

2. AOF的管理方式:

 (1) 自动:让每个redis决定是否做AOF重写操作(根据auto-aof-rewrite-percentage和auto-aof-rewrite-min-size两个参数):

  

  

 (2) crontab: 定时任务,可能仍然会出现多个redis实例,属于一种折中方案。

 

 (3) remote集中式:

       最终目标是一台机器一个时刻,只有一个redis实例进行AOF重写。

       具体做法其实很简单,以机器为单位,轮询每个机器的实例,如果满足条件就运行(比如currentSize和baseSize满足什么关系)bgrewriteaof命令。

       期间可以监控发生时间、耗时、频率、尺寸的前后变化            

 

策略 优点 缺点
自动 无需开发

1. 有可能出现(无法预知)上面提到的Swap和OOM


2. 出了问题,处理起来其实更费时间。

AOF控制中心(remote集中式)

1. 防止上面提到Swap和OOM。


2. 能够收集更多的数据(aof重写的发生时间、耗时、频率、尺寸的前后变化),更加有利于运维和定位问题(是否有些机器的实例需要拆分)。

控制中心需要开发。

 

 

一台机器轮询执行bgRewriteAof代码示例:

 

Java代码    收藏代码
  1. package com.sohu.cache.inspect.impl;  
  2.   
  3. import com.sohu.cache.alert.impl.BaseAlertService;  
  4. import com.sohu.cache.entity.InstanceInfo;  
  5. import com.sohu.cache.inspect.InspectParamEnum;  
  6. import com.sohu.cache.inspect.Inspector;  
  7. import com.sohu.cache.util.IdempotentConfirmer;  
  8. import com.sohu.cache.util.TypeUtil;  
  9. import org.apache.commons.collections.MapUtils;  
  10. import org.apache.commons.lang.StringUtils;  
  11. import redis.clients.jedis.Jedis;  
  12.   
  13. import java.util.Collections;  
  14. import java.util.LinkedHashMap;  
  15. import java.util.List;  
  16. import java.util.Map;  
  17. import java.util.concurrent.TimeUnit;  
  18.   
  19.   
  20. public class RedisIsolationPersistenceInspector extends BaseAlertService implements Inspector {  
  21.   
  22.     public static final int REDIS_DEFAULT_TIME = 5000;  
  23.   
  24.     @Override  
  25.     public boolean inspect(Map<InspectParamEnum, Object> paramMap) {  
  26.         // 某台机器和机器下所有redis实例  
  27.         final String host = MapUtils.getString(paramMap, InspectParamEnum.SPLIT_KEY);  
  28.         List<InstanceInfo> list = (List<InstanceInfo>) paramMap.get(InspectParamEnum.INSTANCE_LIST);  
  29.         // 遍历所有的redis实例  
  30.         for (InstanceInfo info : list) {  
  31.             final int port = info.getPort();  
  32.             final int type = info.getType();  
  33.             int status = info.getStatus();  
  34.             // 非正常节点  
  35.             if (status != 1) {  
  36.                 continue;  
  37.             }  
  38.             if (TypeUtil.isRedisDataType(type)) {  
  39.                 Jedis jedis = new Jedis(host, port, REDIS_DEFAULT_TIME);  
  40.                 try {  
  41.                     // 从redis info中索取持久化信息  
  42.                     Map<String, String> persistenceMap = parseMap(jedis);  
  43.                     if (persistenceMap.isEmpty()) {  
  44.                         logger.error("{}:{} get persistenceMap failed", host, port);  
  45.                         continue;  
  46.                     }  
  47.                     // 如果正在进行aof就不做任何操作,理论上要等待它完毕,否则  
  48.                     if (!isAofEnabled(persistenceMap)) {  
  49.                         continue;  
  50.                     }  
  51.                     // 上一次aof重写后的尺寸和当前aof的尺寸  
  52.                     long aofCurrentSize = MapUtils.getLongValue(persistenceMap, "aof_current_size");  
  53.                     long aofBaseSize = MapUtils.getLongValue(persistenceMap, "aof_base_size");  
  54.                     // 阀值大于60%  
  55.                     long aofThresholdSize = (long) (aofBaseSize * 1.6);  
  56.                     double percentage = getPercentage(aofCurrentSize, aofBaseSize);  
  57.                     // 大于60%且超过60M  
  58.                     if (aofCurrentSize >= aofThresholdSize && aofCurrentSize > (64 * 1024 * 1024)) {  
  59.                         // bgRewriteAof 异步操作。  
  60.                         boolean isInvoke = invokeBgRewriteAof(jedis);  
  61.                         if (!isInvoke) {  
  62.                             logger.error("{}:{} invokeBgRewriteAof failed", host, port);  
  63.                             continue;  
  64.                         } else {  
  65.                             logger.warn("{}:{} invokeBgRewriteAof started percentage={}", host, port, percentage);  
  66.                         }  
  67.                         // 等待Aof重写成功(bgRewriteAof是异步操作)  
  68.                         while (true) {  
  69.                             try {  
  70.                                 // before wait 1s  
  71.                                 TimeUnit.SECONDS.sleep(1);  
  72.                                 Map<String, String> loopMap = parseMap(jedis);  
  73.                                 Integer aofRewriteInProgress = MapUtils.getInteger(loopMap, "aof_rewrite_in_progress"null);  
  74.                                 if (aofRewriteInProgress == null) {  
  75.                                     logger.error("loop watch:{}:{} return failed", host, port);  
  76.                                     break;  
  77.                                 } else if (aofRewriteInProgress <= 0) {  
  78.                                     // bgrewriteaof Done  
  79.                                     logger.warn("{}:{} bgrewriteaof Done lastSize:{}Mb,currentSize:{}Mb", host, port,  
  80.                                             getMb(aofCurrentSize),  
  81.                                             getMb(MapUtils.getLongValue(loopMap, "aof_current_size")));  
  82.                                     break;  
  83.                                 } else {  
  84.                                     // wait 1s  
  85.                                     TimeUnit.SECONDS.sleep(1);  
  86.                                 }  
  87.                             } catch (Exception e) {  
  88.                                 logger.error(e.getMessage(), e);  
  89.                             }  
  90.                         }  
  91.                     } else {  
  92.                         if (percentage > 50D) {  
  93.                             long currentSize = getMb(aofCurrentSize);  
  94.                             logger.info("checked {}:{} aof increase percentage:{}% currentSize:{}Mb", host, port,  
  95.                                     percentage, currentSize > 0 ? currentSize : "<1");  
  96.                         }  
  97.                     }  
  98.                 } finally {  
  99.                     jedis.close();  
  100.                 }  
  101.             }  
  102.         }  
  103.         return true;  
  104.     }  
  105.   
  106.     private long getMb(long bytes) {  
  107.         return (long) (bytes / 1024 / 1024);  
  108.     }  
  109.   
  110.     private boolean isAofEnabled(Map<String, String> infoMap) {  
  111.         Integer aofEnabled = MapUtils.getInteger(infoMap, "aof_enabled"null);  
  112.         return aofEnabled != null && aofEnabled == 1;  
  113.     }  
  114.   
  115.     private double getPercentage(long aofCurrentSize, long aofBaseSize) {  
  116.         if (aofBaseSize == 0) {  
  117.             return 0.0D;  
  118.         }  
  119.         String format = String.format("%.2f", (Double.valueOf(aofCurrentSize - aofBaseSize) * 100 / aofBaseSize));  
  120.         return Double.parseDouble(format);  
  121.     }  
  122.   
  123.     private Map<String, String> parseMap(final Jedis jedis) {  
  124.         final StringBuilder builder = new StringBuilder();  
  125.         boolean isInfo = new IdempotentConfirmer() {  
  126.             @Override  
  127.             public boolean execute() {  
  128.                 String persistenceInfo = null;  
  129.                 try {  
  130.                     persistenceInfo = jedis.info("Persistence");  
  131.                 } catch (Exception e) {  
  132.                     logger.warn(e.getMessage() + "-{}:{}", jedis.getClient().getHost(), jedis.getClient().getPort(),  
  133.                             e.getMessage());  
  134.                 }  
  135.                 boolean isOk = StringUtils.isNotBlank(persistenceInfo);  
  136.                 if (isOk) {  
  137.                     builder.append(persistenceInfo);  
  138.                 }  
  139.                 return isOk;  
  140.             }  
  141.         }.run();  
  142.         if (!isInfo) {  
  143.             logger.error("{}:{} info Persistence failed", jedis.getClient().getHost(), jedis.getClient().getPort());  
  144.             return Collections.emptyMap();  
  145.         }  
  146.         String persistenceInfo = builder.toString();  
  147.         if (StringUtils.isBlank(persistenceInfo)) {  
  148.             return Collections.emptyMap();  
  149.         }  
  150.         Map<String, String> map = new LinkedHashMap<String, String>();  
  151.         String[] array = persistenceInfo.split("\r\n");  
  152.         for (String line : array) {  
  153.             String[] cells = line.split(":");  
  154.             if (cells.length > 1) {  
  155.                 map.put(cells[0], cells[1]);  
  156.             }  
  157.         }  
  158.   
  159.         return map;  
  160.     }  
  161.   
  162.     public boolean invokeBgRewriteAof(final Jedis jedis) {  
  163.         return new IdempotentConfirmer() {  
  164.             @Override  
  165.             public boolean execute() {  
  166.                 try {  
  167.                     String response = jedis.bgrewriteaof();  
  168.                     if (response != null && response.contains("rewriting started")) {  
  169.                         return true;  
  170.                     }  
  171.                 } catch (Exception e) {  
  172.                     String message = e.getMessage();  
  173.                     if (message.contains("rewriting already")) {  
  174.                         return true;  
  175.                     }  
  176.                     logger.error(message, e);  
  177.                 }  
  178.                 return false;  
  179.             }  
  180.         }.run();  
  181.     }  
  182. }  

 

 

 

 

 

附图一张:

 

 

 

 

 三、redis内存占用飙升

 一、现象:
    redis-cluster某个分片内存飙升,明显比其他分片高很多,而且持续增长。并且主从的内存使用量并不一致。
  
二、分析可能原因:
 1.  redis-cluster的bug (这个应该不存在)
 2. 客户端的hash(key)有问题,造成分配不均。(redis使用的是crc16, 不会出现这么不均的情况)
 3. 存在个别大的key-value: 例如一个包含了几百万数据set数据结构(这个有可能)
 4. 主从复制出现了问题。
 5. 其他原因
 
三、调查原因:
 1. 经查询,上述1-4都不存在
 2. 观察info信息,有一点引起了怀疑: client_longes_output_list有些异常。
3. 于是理解想到服务端和客户端交互时,分别为每个客户端设置了输入缓冲区和输出缓冲区,这部分如果很大的话也会占用Redis服务器的内存。
  
从上面的client_longest_output_list看,应该是输出缓冲区占用内存较大,也就是有大量的数据从Redis服务器向某些客户端输出。
于是使用client list命令(类似于mysql processlist) redis-cli -h host -p port client list | grep -v "omem=0",来查询输出缓冲区不为0的客户端连接,于是查询到祸首monitor,于是豁然开朗.
  
monitor的模型是这样的,它会将所有在Redis服务器执行的命令进行输出,通常来讲Redis服务器的QPS是很高的,也就是如果执行了monitor命令,Redis服务器在Monitor这个客户端的输出缓冲区又会有大量“存货”,也就占用了大量Redis内存。
 
  
四、紧急处理和解决方法
进行主从切换(主从内存使用量不一致),也就是redis-cluster的fail-over操作,继续观察新的Master是否有异常,通过观察未出现异常。
查找到真正的原因后,也就是monitor,关闭掉monitor命令的进程后,内存很快就降下来了。
 
五、 预防办法:
1. 为什么会有monitor这个命令发生,我想原因有两个:
(1). 工程师想看看究竟有哪些命令在执行,就用了monitor
(2). 工程师对于redis学习的目的,因为进行了redis的托管,工程师只要会用redis就可以了,但是作为技术人员都有学习的好奇心和欲望。
2. 预防方法:
(1) 对工程师培训,讲一讲redis使用过程中的坑和禁忌
(2) 对redis云进行介绍,甚至可以让有兴趣的同学参与进来
(3) 针对client做限制,但是官方也不建议这么做,官方的默认配置中对于输出缓冲区没有限制。
Java代码    收藏代码
  1. client-output-buffer-limit normal 0 0 0  
(4) 密码:redis的密码功能较弱,同时多了一次IO
(5) 修改客户端源代码,禁止掉一些危险的命令(shutdown, flushall, monitor, keys *),当然还是可以通过redis-cli来完成
(6) 添加command-rename配置,将一些危险的命令(flushall, monitor, keys * , flushdb)做rename,如果有需要的话,找到redis的运维人员处理
Java代码    收藏代码
  1. rename-command FLUSHALL "随机数"  
  2. rename-command FLUSHDB "随机数"  
  3. rename-command KEYS "随机数"  
 
六、模拟实验:
1.  开启一个空的Redis(最简,直接redis-server)
Java代码    收藏代码
  1. redis-server  
    初始化内存使用量如下:
Java代码    收藏代码
  1. # Memory  
  2. used_memory:815072  
  3. used_memory_human:795.97K  
  4. used_memory_rss:7946240  
  5. used_memory_peak:815912  
  6. used_memory_peak_human:796.79K  
  7. used_memory_lua:36864  
  8. mem_fragmentation_ratio:9.75  
  9. mem_allocator:jemalloc-3.6.0  
    client缓冲区:
Java代码    收藏代码
  1. # Clients  
  2. connected_clients:1  
  3. client_longest_output_list:0  
  4. client_biggest_input_buf:0  
  5. blocked_clients:0  
 
2. 开启一个monitor:
Java代码    收藏代码
  1. redis-cli -h 127.0.0.1 -p 6379 monitor  
3. 使用redis-benchmark:
Java代码    收藏代码
  1. redis-benchmark -h 127.0.0.1 -p 6379 -c 500 -n 200000  
4. 观察
(1) info memory:内存一直增加,直到benchmark结束,monitor输出完毕,但是used_memory_peak_human(历史峰值)依然很高--观察附件中日志
(2)info clients: client_longest_output_list: 一直在增加,直到benchmark结束,monitor输出完毕,才变为0 --观察附件中日志
(3)redis-cli -h host -p port client list | grep "monitor" omem一直很高,直到benchmark结束,monitor输出完毕,才变为0 --观察附件中日志
监控脚本:
Java代码    收藏代码
  1. while [ 1 == 1 ]  
  2. do  
  3. now=$(date "+%Y-%m-%d_%H:%M:%S")  
  4. echo "=========================${now}==============================="  
  5. echo " #Client-Monitor"  
  6. redis-cli -h 127.0.0.1 -p 6379 client list | grep monitor  
  7. redis-cli -h 127.0.0.1 -p 6379 info clients  
  8. redis-cli -h 127.0.0.1 -p 6379 info memory  
  9. #休息100毫秒  
  10. usleep 100000  
  11. done  
 完整的日志文件:
 
一、背景: 选择合适的使用场景
   很多时候Redis被误解并乱用了,造成的Redis印象:耗内存、价格成本很高:
   1. 为了“赶时髦”或者对于Mysql的“误解”在一个并发量很低的系统使用Redis,将原来放在Mysql数据全部放在Redis中。
     ----(Redis比较适用于高并发系统,如果是一些复杂Mis系统,用Redis反而麻烦,因为单从功能讲Mysql要更为强大,而且Mysql的性能其实已经足够了。)
   2. 觉得Redis就是个KV缓存
     -----(Redis支持多数据结构,并且具有很多其他丰富的功能)
   3. 喜欢做各种对比,比如Mysql, Hbase, Redis等等
    -----(每种数据库都有自己的使用场景,比如Hbase吧,我们系统的个性化数据有1T,此时放在Redis根本就不合适,而是将一些热点数据放在Redis)
    总之就是在合适的场景,选择合适的数据库产品。
  附赠两个名言:
Evan Weaver, Twitter, March 2009 写道
Everything runs from memory in Web 2.0!
Tim Gray 写道
Tape is Dead, Disk is Tape, Flash is Disk, RAM Locality is king. 
(磁带已死,磁盘是新磁带,闪存是新磁盘,随机存储器局部性是为王道)
  
二、一次string转化为hash的优化
1. 场景:
    用户id: userId,
    用户微博数量:weiboCount    
userId(用户id) weiboCount(微博数)
1 2000
2

10

3

288

.... ...
1000000 1000
 
2. 实现方法:
(1) 使用Redis字符串数据结构, userId为key, weiboCount作为Value
(2) 使用Redis哈希结构,hashkey只有一个, key="allUserWeiboCount",field=userId,fieldValue= weiboCount
(3) 使用Redis哈希结构,  hashkey为多个, key=userId/100, field=userId%100, fieldValue= weiboCount
前两种比较容易理解,第三种方案解释一下:每个hashKey存放100个hash-kv,field=userId%100,也就是
userId hashKey field
1 0 1
2 0

2

3 0

3

... .... ...
99 0 99
100 1 0
101 1 1
.... ... ...
9999 99 99
100000 1000 0

 

3. 获取方法:

 

Java代码    收藏代码
  1. #获取userId=5003用户的微博数  
  2. (1) get 5003  
  3. (2) hget allUserWeiboCount 5003  
  4. (3) hget 50 3  

 

 

4. 内存占用量对比(100万用户 userId:1~1000000) 

  

Java代码    收藏代码
  1. #方法一 Memory  
  2. used_memory:85999592  
  3. used_memory_human:82.02M  
  4. used_memory_rss:96043008  
  5. used_memory_peak:85999592  
  6. used_memory_peak_human:82.02M  
  7. used_memory_lua:36864  
  8. mem_fragmentation_ratio:1.12  
  9. mem_allocator:jemalloc-3.6.0  
  10.   
  11. #方法二 Memory  
  12. used_memory:101665632  
  13. used_memory_human:96.96M  
  14. used_memory_rss:110702592  
  15. used_memory_peak:101665632  
  16. used_memory_peak_human:96.96M  
  17. used_memory_lua:36864  
  18. mem_fragmentation_ratio:1.09  
  19. mem_allocator:jemalloc-3.6.0  
  20.   
  21.   
  22. #方法三 Memory  
  23. used_memory:9574136  
  24. used_memory_human:9.13M  
  25. used_memory_rss:17285120  
  26. used_memory_peak:101665632  
  27. used_memory_peak_human:96.96M  
  28. used_memory_lua:36864  
  29. mem_fragmentation_ratio:1.81  
  30. mem_allocator:jemalloc-3.6.0  

 

  内存使用量:

  

5. 导入数据代码(不考虑代码优雅性,单纯为了测试,勿喷)
 
Java代码    收藏代码
  1. package com.carlosfu.redis;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.HashMap;  
  5. import java.util.List;  
  6. import java.util.Map;  
  7. import java.util.Random;  
  8.   
  9. import org.junit.Test;  
  10.   
  11. import redis.clients.jedis.Jedis;  
  12.   
  13. /** 
  14.  * 一次string-hash优化 
  15.  * @author carlosfu 
  16.  * @Date 2015-11-8 
  17.  * @Time 下午7:27:45 
  18.  */  
  19. public class TestRedisMemoryOptimize {  
  20.       
  21.     private final static int TOTAL_USER_COUNT = 1000000;  
  22.   
  23.     /** 
  24.      * 纯字符串 
  25.      */  
  26.     @Test  
  27.     public void testString() {  
  28.         Jedis jedis = null;  
  29.         try {  
  30.             jedis = new Jedis("127.0.0.1"6379);  
  31.             List<String> kvsList = new ArrayList<String>(200);  
  32.             for (int i = 1; i <= TOTAL_USER_COUNT; i++) {  
  33.                 String userId = String.valueOf(i);  
  34.                 kvsList.add(userId);  
  35.                 String weiboCount = String.valueOf(new Random().nextInt(100000));  
  36.                 kvsList.add(weiboCount);  
  37.                 if (i % 2000 == 0) {  
  38.                     System.out.println(i);  
  39.                     jedis.mset(kvsList.toArray(new String[kvsList.size()]));  
  40.                     kvsList = new ArrayList<String>(200);  
  41.                 }  
  42.             }  
  43.         } catch (Exception e) {  
  44.             e.printStackTrace();  
  45.         } finally {  
  46.             if (jedis != null) {  
  47.                 jedis.close();  
  48.             }  
  49.         }  
  50.     }  
  51.       
  52.       
  53.       
  54.     /** 
  55.      * 纯hash 
  56.      */  
  57.     @Test  
  58.     public void testHash() {  
  59.         String hashKey = "allUserWeiboCount";  
  60.           
  61.         Jedis jedis = null;  
  62.         try {  
  63.             jedis = new Jedis("127.0.0.1"6379);  
  64.             Map<String,String> kvMap = new HashMap<String, String>();  
  65.             for (int i = 1; i <= TOTAL_USER_COUNT; i++) {  
  66.                 String userId = String.valueOf(i);  
  67.                 String weiboCount = String.valueOf(new Random().nextInt(100000));  
  68.                 kvMap.put(userId, weiboCount);  
  69.                 if (i % 2000 == 0) {  
  70.                     System.out.println(i);  
  71.                     jedis.hmset(hashKey, kvMap);  
  72.                     kvMap = new HashMap<String, String>();  
  73.                 }  
  74.             }  
  75.         } catch (Exception e) {  
  76.             e.printStackTrace();  
  77.         } finally {  
  78.             if (jedis != null) {  
  79.                 jedis.close();  
  80.             }  
  81.         }  
  82.     }  
  83.       
  84.     /** 
  85.      * segment hash 
  86.      */  
  87.     @Test  
  88.     public void testSegmentHash() {  
  89.         int segment = 100;  
  90.         Jedis jedis = null;  
  91.         try {  
  92.             jedis = new Jedis("127.0.0.1"6379);  
  93.             Map<String,String> kvMap = new HashMap<String, String>();  
  94.             for (int i = 1; i <= TOTAL_USER_COUNT; i++) {  
  95.                 String userId = String.valueOf(i % segment);  
  96.                 String weiboCount = String.valueOf(new Random().nextInt(100000));  
  97.                 kvMap.put(userId, weiboCount);  
  98.                 if (i % segment == 0) {  
  99.                     System.out.println(i);  
  100.                     int hash = (i-1) / segment;  
  101.                     jedis.hmset(String.valueOf(hash), kvMap);  
  102.                     kvMap = new HashMap<String, String>();  
  103.                 }  
  104.             }  
  105.         } catch (Exception e) {  
  106.             e.printStackTrace();  
  107.         } finally {  
  108.             if (jedis != null) {  
  109.                 jedis.close();  
  110.             }  
  111.         }  
  112.     }  
  113.   
  114. }  
 
 
三、结果对比
 redis核心对象 数据类型 + 编码方式 + ptr  分段hash也不会造成drift
方案 优点 缺点
string

直观、容易理解


    
           
    1. 内存占用较大

           
    1. key值分散、不变于计算整体

        
hash

直观、容易理解、整合整体


    
           
    1. 内存占用大

           
    1. 一个key占用过大内存,如果是redis-cluster会出 现data drift

        

 

segment-hash

内存占用量小,虽然理解不够直观,但是总体上是最优的。

理解不够直观。

 
四、结论:
   在使用Redis时,要选择合理的数据结构解决实际问题,那样既可以提高效率又可以节省内存。所以此次优化方案三为最佳。
 
附图一张:redis其实是一把瑞士军刀:
 
 
 

  

由于演讲时间有限,有关Redis-Cluster,演讲者没做太多介绍,简单的介绍了一些Redis-Cluster概念作用和遇到的两个问题,我们在Redis-Cluster也有很多运维经验,将来的文章会介绍。

 

但是讲演者反复强调,不要听信网上对于Redis-Cluster的毁谤(实践出真知),对于这一点我很赞同,我们从Redis-Cluster beta版 RC1~4 到现在的3.0-release均没有遇到什么大问题(线上维护600个实例)。

 

一、Redis-Cluster

有关Redis-Cluster的详细介绍有很多这里就不多说了,可以参考:

1. redis-cluster研究和使用

2. Redis Cluster 3.0.5集群实践

3. 本博客的一些Redis-Cluster的介绍(未更新完毕)

4. Redis设计与实现那本书(作者:黄建宏):非常的推荐看这本书。

总之Redis-Cluster是一个无中心的分布式Redis存储架构,解决了Redis高可用、可扩展等问题。

 

 

 

 

二、两个问题:

 

1. Redis-Cluster主从节点不要在同一个机器部署

   (1) 以我们的经验看redis实例本身基本不会挂掉,通常是机器出了问题(断电、机器故障)、甚至是机架、机柜出了问题,造成Redis挂掉。

   (2) 如果Redis-Cluster的主从都在一个机器上,那么如果这台机器挂了,主从全部挂掉,高可用就无法实现。(如果full converage=true,也就意味着整个集群挂掉)

   (3) 通常来讲一对主从所在机器:不跨机房、要跨机架、可以在一个机柜。

 

2. Redis-Cluster误判节点fail进行切换

   (1) Redis-Cluster是无中心的架构,判断节点失败是通过仲裁的方式来进行(gossip和raft),也就是大部分节点认为一个节点挂掉了,就会做fail判定。

   (2) 如果某个节点在执行比较重的操作(flushall, slaveof等等)(可能短时间redis客户端连接会阻塞(redis单线程))或者由于网络原因,造成其他节点认为它挂掉了,会做fail判定。

   (3) Redis-Cluster提供了cluster-node-timeout这个参数(默认15秒),作为fail依据(如果超过15秒还是没反应,就认为是挂掉了),具体可以参考这篇文章:Redis-Cluster的FailOver失败案例分析

        以我们的经验看15秒完全够用。

   

 

三、未来要介绍的问题:

 

1. Redis-Cluster客户端实现Mget操作。

2. Redis-Cluster--Too many Cluster redirections异常

3. Redis-Cluster无底洞问题解析。

4. 两个Redis-Cluster集群,meet操作问题后的恶果。

5. Redis-Cluster配置之full converage问题。

6. Redis-Cluster故障转移测试

7. Redis-Cluster常用运维技巧。

8. Redis-Cluster一键开通。

9. Redis-Cluster客户端jedis详解。

 

四、附赠一些不错的资料:

  1.  Redis-Cluster的FailOver失败案例分析
  2.  Redis Cluster 迁移遇到的各种坑及解决方案
  3.  Redis Cluster架构优化
  4.  Redis常见集群方案、Codis实践及与Twemproxy比较
  5.  Redis Cluster架构优化
  6. 【运维实践】鱼与熊掌:使用redis-cluster需要注意些什么?
  7.  Docker及和Redis Cluster的化学反应(上)By 芒果TV
  8.  Docker及和Redis Cluster的化学反应(下)By 芒果TV
  9.  Redis cluster使用经验——网易有道
  10.  Redis Cluster浅析和Bada对比
  11.  互联网Redis应用场景探讨
  12.  Redis集群技术及Codis实践
  13.  谈Twitter的百TB级Redis缓存实践
  14.  Hadoop、Spark、HBase与Redis的适用性讨论
  15. Codis作者黄东旭细说分布式Redis架构设计和踩过的那些坑们

 

 

分享到:
评论

相关推荐

    美团在Redis上踩过的一些坑-3.redis内存占用飙升

    在使用Redis的过程中,他们遇到了一些问题,特别是关于Redis内存占用飙升的问题。下面我们将深入探讨这个问题以及可能的解决方案。 Redis内存占用飙升的原因多种多样,可能是由于以下几点: 1. **数据结构不当**:...

    美团在Redis上踩过的一些坑-目录(本人非美团)

    NULL 博文链接:https://carlosfu.iteye.com/blog/2254154

    Apollo 7.0行为预测模块升级:轨迹交互与评估器设计详解及其应用

    内容概要:本文详细解析了Apollo 7.0行为预测模块的关键升级点,主要包括新增的Inter-TNT模式、VECTORNET_EVALUATOR以及JOINTLY_PREDICTION_PLANNING_EVALUATOR。这些组件通过引入轨迹交互模拟、动态归一化、联合预测规划等创新机制,显著提高了障碍物轨迹预测的准确性和场景适应性。特别是在处理复杂交通场景如高速公路变道、十字路口交汇时表现出色。此外,文中还介绍了增量式特征更新机制的应用,有效减少了CPU占用,提升了系统的实时性能。 适用人群:适用于对自动驾驶技术感兴趣的开发者、研究人员和技术爱好者,尤其是那些希望深入了解Apollo平台行为预测模块工作原理的人群。 使用场景及目标:①帮助读者理解Apollo 7.0行为预测模块的技术细节;②指导开发者如何利用这些新技术提升自动驾驶系统的预测精度;③为研究者提供有价值的参考资料,促进相关领域的进一步探索。 其他说明:文章不仅提供了详细的代码解读,还包括了实际应用场景中的效果对比,使读者能够全面掌握新旧版本之间的差异。同时,附带的思维导图有助于快速理清各个子模块之间的调用关系和数据流向。

    基于S7-200 PLC与MCGS组态的智能交通灯控制系统设计及应用

    内容概要:本文详细介绍了利用西门子S7-200 PLC和MCGS组态软件构建智能交通灯控制系统的方法。首先阐述了系统的硬件配置,包括选用的PLC型号、输入输出设备及其具体的功能分配。接着深入探讨了梯形图编程的核心逻辑,如定时器嵌套、车流量检测与响应机制,确保红绿灯能够根据实际情况灵活调整。此外还讲解了MCGS组态界面的设计要点,通过图形化方式呈现交通状况并提供人机交互功能。最后分享了一些实际调试过程中遇到的问题及解决方案。 适合人群:从事工业自动化领域的工程师和技术人员,特别是对PLC编程和组态软件有一定了解的人群。 使用场景及目标:适用于城市交通管理部门或相关科研机构进行智能交通系统的研究与开发;旨在提高道路交叉口的通行效率,减少拥堵现象。 其他说明:文中不仅提供了详细的理论指导,还包括了许多实践经验教训,对于初学者来说非常有价值。同时提到一些进阶话题,如加入V2V通信模块的可能性,为未来研究指出了方向。

    光伏特性曲线建模:基于Matlab与Simulink的分布式光伏系统仿真

    内容概要:本文详细介绍了光伏特性曲线模型的基本概念及其在Matlab和Simulink中的实现方法。首先阐述了光伏电池的电流-电压(I-V)和功率-电压(P-V)曲线的基础理论,包括理想二极管方程及相关参数的意义。接着展示了如何使用Matlab编写代码来计算并绘制简单的I-V曲线,随后探讨了Simulink环境下构建光伏特性曲线模型的方法,强调了图形化界面的优势。此外,还讨论了分布式光伏系统的特点,通过修改基础模型以适应多电池串联或并联系统的需求。文中不仅提供了具体的代码实例,还分享了一些实用的经验和技术细节,如温度系数、辐照度变化对模型的影响等。 适合人群:从事光伏系统研究的技术人员、高校相关专业师生、对光伏建模感兴趣的工程爱好者。 使用场景及目标:①理解和掌握光伏电池的工作原理及其数学模型;②学会使用Matlab和Simulink进行光伏特性曲线的建模与仿真;③能够分析不同环境条件下光伏系统的性能表现,为优化设计提供依据。 其他说明:文章中包含了大量详细的代码片段和操作指南,有助于读者快速上手实践。同时提醒读者关注模型参数的选择与调整,确保仿真结果贴近实际情况。

    Bergsoft NextSuite (VCL) v6.40.0 for Delphi & CB 6-12 Athens Full Source.7z

    BergSoft NextSuite 是一个强大的 Delphi 和 C++ Builder 组件套件。NextGrid 是一个易于使用的组件,具有设计时(带可视化列编辑器)和运行时的方法和属性理解。NextGrid 具有卓越的 StringGrid 功能和标准的 Delphi ListView。NextDBGrid 是一个基于著名的 NextGrid 组件的强大 Delphi 数据网格和 C++ Builder。

    中职计算机软件工程.pdf

    中职计算机软件工程.pdf

    基于Verilog的FPGA高性能伺服驱动系统实现:电流环、坐标变换、SVPWM及编码器协议

    内容概要:本文详细介绍了如何利用Verilog语言在FPGA平台上实现高性能伺服驱动系统。主要内容涵盖多个关键模块,包括电流环、坐标变换、速度环、位置环、电机反馈接口、SVPWM生成和编码器协议。每个模块都通过具体的Verilog代码片段展示了其功能和实现方式。电流环部分重点讲解了电流反馈和电压输出的计算;坐标变换部分讨论了从三相静止坐标系到两相旋转坐标系的转换;速度环和位置环则采用了PID控制算法实现对电机的速度和位置的精确控制;电机反馈接口和编码器协议确保了电机位置信息的准确获取;SVPWM模块生成了高效的三相PWM波形。这些模块共同协作,实现了对电机的高效、精准控制。 适合人群:具备一定硬件开发基础,特别是熟悉FPGA和Verilog编程的技术人员,以及从事电机控制和伺服系统开发的研究人员。 使用场景及目标:适用于需要深入了解和掌握FPGA平台上的伺服控制系统设计的专业人士。主要目标是帮助读者理解各模块的工作原理及其在实际应用中的实现方法,提升他们在伺服驱动系统设计方面的能力。 阅读建议:由于涉及大量具体代码和技术细节,建议读者在阅读过程中结合实际电路图和仿真工具进行理解和验证。此外,可以尝试自己动手实现部分模块,以便更好地掌握相关技术和优化设计。

    ffmepg windows 下载详细教程2025年(最新)

    ffmepg windows 下载详细教程2025年(最新)

    COMSOL模拟实现偏振无关BIC超表面的设计与验证

    内容概要:本文探讨了一种新型的超表面设计,能够在保持结构对称性的同时实现偏振无关的连续域束缚态(BIC)。传统的BIC设计通常需要破坏结构对称性,从而导致偏振依赖的问题。新的设计方案通过调整几何参数和模式耦合,使得不同偏振模式能够自然耦合并形成稳定的BIC。文中详细介绍了使用COMSOL进行仿真的步骤,包括参数扫描、模式特征分析以及实验验证。结果显示,新机制不仅能在较宽的偏振范围内保持高Q因子,而且对制造误差具有较高的容忍度。 适合人群:从事光学、电磁学研究的专业人士,尤其是对超表面设计和BIC感兴趣的科研人员。 使用场景及目标:适用于需要高精度、高稳定性和宽偏振适应性的应用场景,如LiDAR系统、光电探测、生化传感等领域。目标是提供一种创新的设计思路和技术实现路径,突破传统BIC设计的局限。 其他说明:文中提供了详细的MATLAB和COMSOL代码片段,帮助读者理解和复现实验结果。此外,强调了新机制在实际制备中的优势,特别是对制造误差的高容忍度。

    永磁同步电机MTPA与弱磁控制技术详解及其工程实现

    内容概要:本文详细探讨了永磁同步电机(PMSM)控制系统中的关键技术,尤其是最大转矩电流比(MTPA)控制和弱磁控制。首先介绍了MTPA的基本原理,包括基于查表法和公式的实现方式,以及应对温度变化引起的参数漂移的方法。接着讨论了速度环PI控制器的设计,强调了防积分饱和机制的重要性。对于弱磁控制,则着重讲解了电压极限圆的概念及其在过调制情况下的应用,同时提供了具体的Python和C语言代码示例。此外,还涉及到了SVPWM过调制处理的技术细节,如调制比超过1后的波形调整策略。最后分享了一些实际工程项目中的经验教训和技术挑战。 适合人群:从事电机控制领域的工程师、研究人员以及相关专业的学生。 使用场景及目标:帮助读者深入了解PMSM控制系统的内部运作机制,掌握MTPA和弱磁控制的具体实现方法,提高解决实际问题的能力。 其他说明:文中引用了多篇学术文献作为理论支持,并附上了大量源代码片段供参考学习。

    MiniTool重点技术共享Windows数据恢复软件.doc

    MiniTool重点技术共享Windows数据恢复软件.doc

    高速数据采集领域中ADS54J60 FMC子卡的硬件设计与FPGA实现

    内容概要:本文详细介绍了ADS54J60高速采集卡FMC子卡的设计与实现。该子卡支持4通道16位1G采样率,涵盖了硬件架构设计(原理图、PCB布局)、FPGA源码实现(Verilog代码)等方面。硬件方面,着重讨论了电源管理、时钟分配、信号完整性等问题;FPGA部分,则展示了ADC控制逻辑、数据同步及传输优化的具体实现方法。此外,文中还分享了许多实践经验,如电源纹波控制、LVDS接口配置、数据同步算法等,帮助开发者避免常见陷阱。 适合人群:从事高速数据采集系统的硬件工程师、FPGA开发人员、嵌入式系统设计师。 使用场景及目标:适用于需要高性能数据采集的应用场合,如通信系统、雷达信号处理等。目标是帮助读者掌握ADS54J60 FMC子卡的设计与实现,从而加速项目开发进程。 其他说明:文中提供的设计文件和代码可以直接用于制板生产,大大缩短了从设计到应用的时间。同时,作者还分享了一些实用技巧和经验教训,有助于提高系统的稳定性和性能。

    【Linux摄像头驱动开发】从原理到实战:V4L2框架与USB摄像头工作流程详解及开发指南

    内容概要:本文详细介绍了Linux摄像头驱动的工作原理及其开发流程。首先解释了摄像头驱动的重要性,它是Linux系统与摄像头硬件交互的桥梁,使系统能够识别并操作摄像头。接着深入探讨了V4L2框架作为Linux摄像头驱动的核心,它为视频设备提供了标准化接口,简化了应用与硬件间的交互。文章还具体分析了USB摄像头的工作流程,包括图像捕捉、信号转换、数据传输等环节。开发指南部分则强调了前期准备的重要性,如理解Linux内核架构、USB子系统原理及掌握C语言编程技能。随后阐述了开发步骤,涵盖编写内核模块、注册USB驱动程序以及适配不同摄像头。最后讨论了常见问题及解决方案,如驱动加载失败和图像显示异常,并展望了Linux摄像头驱动在未来智能安防和物联网等领域的应用前景。 适用人群:对Linux系统有一定了解,尤其是对设备驱动开发感兴趣的开发者和技术爱好者。 使用场景及目标:①帮助读者理解Linux摄像头驱动的工作原理,包括V4L2框架和USB摄像头的数据传输过程;②指导读者进行Linux摄像头驱动的开发,从前期准备到具体实现步骤;③解决开发过程中可能出现的常见问题,如驱动加载失败和图像显示异常。 其他说明:本文不仅提供了理论知识,还结合实际案例详细讲解了开发流程中的各个环节,旨在帮助读者更好地掌握Linux摄像头驱动的开发技巧,同时展望了其未来在智能安防和物联网等领域的应用潜力。

    MATLAB仿真中光伏板至蓄电池充电的Buck电路设计与优化

    内容概要:本文详细介绍了利用MATLAB进行光伏板向蓄电池充电仿真的全过程。主要内容涵盖光伏电池模型建立、Buck电路设计及其参数选择、PWM信号生成、闭环控制系统设计等方面。文中不仅提供了具体的MATLAB代码示例,还深入探讨了如何通过调整电感、电容值及PWM占空比等参数来优化充电效果,确保输出电压稳定在10.8-14.4V之间,并能提供80A的大电流。此外,文章还讨论了针对不同充电阶段采用不同的充电策略,如强充、缓充和浮充,以保护蓄电池免受过充损害。 适合人群:从事电力电子、新能源技术研究的专业人士,尤其是那些对光伏系统有兴趣的技术人员。 使用场景及目标:适用于需要理解和掌握光伏板向蓄电池充电原理和技术细节的人群。目标是帮助读者学会构建完整的充电系统仿真模型,理解各部件的工作机制,并掌握优化方法。 其他说明:文中提到的一些具体数值和参数设置基于特定应用场景,实际应用时可根据实际情况进行适当调整。同时,文中提供的MATLAB代码片段可以直接应用于MATLAB环境,方便读者动手实践。

    APITable-Typescript资源

    vika.cnAirtable

    COMSOL变压器模型:时域与频域分析及磁致伸缩、噪声和洛伦兹力的多物理场仿真

    内容概要:本文详细介绍了如何使用 COMSOL Multiphysics 对变压器进行时域和频域分析,探讨了磁致伸缩、噪声和洛伦兹力的影响。文中通过具体的代码示例展示了如何设置时域和频域的边界条件,定义磁致伸缩系数,计算洛伦兹力,并通过多物理场耦合模拟变压器的振动和噪声。此外,还讨论了一些常见的仿真技巧和注意事项,如相位对齐、材料非线性特性和边界条件设置等。 适合人群:从事电力系统研究、变压器设计和仿真的工程师和技术人员。 使用场景及目标:适用于希望深入了解变压器内部物理机制及其对外界因素响应的专业人士。通过掌握这些方法,可以优化变压器设计,减少噪声,提升电力系统的稳定性和可靠性。 其他说明:文章不仅提供了理论背景,还给出了实用的代码片段和仿真技巧,帮助读者更好地理解和应用 COMSOL 进行变压器建模。

    2001-2022年分析师盈余预测质量,分析师预测偏差-误差和分析师预测分歧度(方法一)

    分析师预测偏差/分析师预测误差/分析师预测准确度/分析师盈余预测误差/分析师盈余 预测准确度 分析师预测分歧度/分析师盈余预测分歧度 方法一,分母为实际每股盈余( 此帖) 方法一,分母为实际每股盈余 分析师预测偏差(FERROR)是指分析师的盈 余预测值与实际盈余值的平均偏差 分析师预测分歧度(FDISP1和FDISP2)是 指每个分析师最近一次盈余预测值的标准差 本文参考周国开等的度量方法,首先剔除了分 析师预测公布日晚于年报公布日的样本,如果同一分析师在一年内对同一家公同发布了多份 预测,则仅保留该分析师在那年的最后一次预测值样本;其次剔除了每股实际收益和每股预 测收益缺失的样本;最后运用公式(1)和公式度量分析师预测偏差,运用公式(2)和公 式(3)度量分析师预测分歧度。 其中: FEPSit为i公司当年的分析师预测每股 盈余 Mean(FEPSi,t)为公司i第t年的所有证券分析师最近一次每股盈余预 测的平均值 Std(FEPSi,t)为公司i第t年的所有证券分析师最近一次每股盈 余预测的标准差 MEPSit为i公司当年的实际每股盈余 样本选择:全部A股200 1-2022年数

    永磁同步电机滑模观测器无感控制技术解析及其应用

    内容概要:本文深入探讨了永磁同步电机(PMSM)滑模观测器无感控制技术。首先介绍了滑模观测器的基本原理,通过构建观测器估计电机的状态变量,特别是转子位置和速度。文中展示了滑模观测器的C语言和MATLAB代码实现,详细解释了滑模控制律、符号函数的作用以及如何通过滑模面获取转子位置和速度。接着讨论了滑模观测器在实际应用中的优缺点,如低成本、高可靠性和抗扰动能力强,但也存在抖振等问题。针对这些问题,提出了改进措施,如引入滤波器和平滑处理方法。最后,通过具体案例展示了滑模观测器在工业现场的实际效果,强调了其在复杂环境下的稳定性和鲁棒性。 适合人群:从事电机控制系统研究与开发的技术人员,尤其是对永磁同步电机无感控制感兴趣的工程师。 使用场景及目标:适用于需要高精度、低成本电机控制的场合,如电动汽车、智能家居等领域。目标是掌握滑模观测器的工作原理和技术实现,提高电机控制系统的性能和可靠性。 其他说明:本文提供了详细的代码示例和调试技巧,帮助读者更好地理解和应用滑模观测器技术。同时,文中还分享了一些实际工程中的经验和教训,有助于解决实际问题。

    电机设计领域:基于Ansys Maxwell与OptiSlang的永磁同步电机多目标尺寸优化解决方案

    内容概要:本文详细介绍了利用Ansys Maxwell和OptiSlang进行永磁同步电机多目标尺寸优化的方法和技术细节。首先,通过参数化建模将电机的关键尺寸(如磁钢宽度、槽开口宽度、气隙长度)设为变量,实现自动化调整。接着,利用OptiSlang设置多目标优化,包括最小化转矩脉动、最大化效率以及最小化有效材料质量,并加入必要的约束条件(如平均转矩和温升)。文中展示了具体的优化流程,包括参数空间采样、参数耦合设置、异常处理等。此外,还讨论了一些实际应用中的注意事项,如参数范围的安全余量、网格剖分的稳定性等。最终,通过帕累托前沿分析得到了多个优化设计方案,验证了多参数联动优化的有效性和优越性。 适合人群:从事电机设计、电磁场仿真、优化算法等领域工作的工程师和技术人员。 使用场景及目标:适用于需要对永磁同步电机进行多目标尺寸优化的设计项目,旨在提高电机性能(如效率、转矩)、降低成本、优化材料使用等。 其他说明:文章提供了丰富的实战经验和技巧,帮助读者更好地理解和应用多目标优化方法。同时,强调了参数化建模和多参数联动的重要性,避免了传统单目标优化的局限性。

Global site tag (gtag.js) - Google Analytics