当前位置: 首页 > 科技观察

本文了解Redis在大数据下的应用

时间:2023-03-19 10:00:16 科技观察

Redis在大数据下的应用一、Redis客户端的区别1.1Redis普通客户端目前市面上比较流行的客户端有jedis、lettuce、redissonjedis客户端连接方式是基于TCP的阻塞方式lettucelettuce是基于netty多路复用的异步非阻塞方式(目前业界解决高并发大数据问题的思路)redisson用的比上面两种少。在并发量不多的情况下,两者的性能可能差不多,jedis的性能可能比lettuce好,但是当并发量增加的时候,jedis的超时错误会增加,而lettuce只是增加了平均值响应时间和最大响应时间,而生菜是以稳定为基础的。1.2epoll模型——为什么单线程redis快Redis内部使用epoll模型来提高链接处理能力。传统的TCP链接和epoll模型的本质区别在于TCP链接存在链接数瓶颈。随着连接数的增加,响应速度会明显变慢。epoll可以支持更大的连接数,对性能影响不大2.大数据下的redis存储方案2.1分片模式分片模式就是部署多个redis节点,然后客户端确定数据分片规则,常见的分片规则是hash分片基于节点的数量。优点:服务端不需要进行繁琐的配置,路由规则由客户端决定。缺点:缺点很明显,如果多个节点中有一个挂了,这部分数据会丢失,因为客户端还是会为每个节点分配一个连接,客户端在配置的时候要注意IP列表的顺序分片节点IP。.建议:如果shard节点比较少,可以使用shards来适当分散压力配置示例:spring:remote:ecredis:type:shardinguri:-192.168.1.3:6379-192.168.1.4:6379-192.168.1.5:6379-192.168.1.6:6379-192.168.1.7:6379db:1maxIdle:10minIdle:5maxActive:10password:GpG4fZoxsp7cTB5fkeyPrefix:'ERP:EXPORT-CENTER:'2.2Redis2.8版本引入了sentinel机制,sentinel的概念应运而生,并且sentinel是自动化的故障恢复不需要关心IP有没有变化。优点:哨兵模式建立在主从模式的基础上,主从模式的所有优点在哨兵模式中都有。主从可自动切换,系统更健壮,可用性更高。Sentinel会不断检查主服务器和从服务器是否正常运行。当被监控的Redis服务器出现问题时,Sentinel通过API脚本向管理员或其他应用程序发送通知。缺点:Redis难以支持在线扩展。对于集群来说,当容量达到上限时,在线扩容会变得非常复杂。spring:redis:password:123456sentinel:master:masternodes:47.98.217.106:26379,47.98.217.109:26380,47.98.217.109:26381timeout:20000database:0jedis:pool:max-active:300max-wait:-1max-idle-idle:202.3rediscluster集群使用数据分片共享数据,提供数据复制和故障转移功能,包括哨兵模式的所有功能。优点:数据按槽分布存储。访问任意一个master节点,都可以获得任意shard上的数据。可以扩展任意一个master节点或者增加一个新的master节点时,数据会自动分片并同步迁移(rediscluster重新分片由redis内部的redis-trib进行),服务器不需要离线。如果每个master都采用master-slave模式,那么当master出现故障时,下面的slave会选举一个新的master缺点:需要使用Ruby进行部署,配置相当繁琐,维护不便配置示例:spring:redis:password:cluster:nodes:192.168.1.3:6379,192.168.1.4:6379,192.168.1.5:6379max-redirects:3lettuce:pool:max-idle:16max-active:32min-idle:82.4cachecloudcachecloud是一个解决方案,实现各种类型(RedisStandalone、RedisSentinel、RedisCluster)的自动化部署,解决Redis实例碎片化现象,提供完善的统计、监控、运维功能,降低运维成本和误操作,提高机器利用率,灵活提供伸缩的优点:配置更简单,集群节点不再由客户端维护,配置一个域自动获取节点列表配置示例:spring:domain:cachecloud.server1.com:8080remote:ecredis:appid:2type:clouduri:db:1maxIdle:10minIdle:5maxActive:10password:GpG4fZoxsp7cTB5fkeyPrefix:'ERP:EXPORT-CENTER:'应用案例:2.5redis存储方案选择throughputlessdatavolume,datasecurityis不高:单机模式或sharding模式吞吐量大数据量大,数据安全性高:Sentinel模式和集群模式吞吐数据量大,数据安全性高,扩展性强:集群模式3,性能优化3.1日志优化Redis日志存储模式分为两种:RDB和AOF,RDB是实时写入磁盘,AOF是延迟批量写入磁盘RDB模式:优点:实时存储日志,数据恢复更有优势缺点:频繁的磁盘IO会影响redisAOF方式吞吐:优点:定时批量刷新日志到磁盘,适合高吞吐场景,对redis性能影响小缺点:如果redis一次性挂掉某些时刻内存中的数据可能会丢失,故障恢复时这部分数据无法恢复模式选择:如果吞吐量小,可以使用RDB。如果吞吐量大,可以选择AOF来提升性能。根据具体场景选择AOF配置有两种方式:ppendonlyyes#aof文件名设置appendfilename"appendonly-${port}.aof"#配置选择appendfsynceverysecdir/bigdiskpath#不开启aof重写,因为太耗性能currentredisvalue优化指令减少磁盘空间和压力,但是因为需要判断merge逻辑,会有很大的性能开销,一般不开启aofrewrite#假设服务器在钥匙清单;127.0.0.1:6379>RPUSHlist“A”“B”(整数)2127.0.0.1:6379>RPUSHlist“C”(整数)3127.0.0.1:6379>RPUSHlist“D”“E”(整数)5127.0.0.1:6379>LPOPlist“A”127.0.0.1:6379>LPOPlist“B”127.0.0.1:6379>RPUSHlist“F”“G”(整数)5127.0.0.1:6379>LRANGElist0-11)“C”2)“D”3)"E"4)"F"5)"G"127.0.0.1:6379>普通AOF会将之前的6次写入命令保存到日志中,AOFrewrite会先去redis中获取list的值,并找到就是["C","D","E","F","G"],然后生成一个RPUSH列表"C""D""E""F""G"来替换之前的6项3.2缓存更新策略redisdefault下面是使用LRU策略,因为内存有限,但是如果一直往redis写数据,肯定不可能把所有的数据都存到内存中(1)noeviction:如果内存使用达到maxmemory,客户端会继续写数据,然后直接向客户端报错(2)allkeys-lru:就是我们的经常说LRU算法是去掉最近最少使用的key(最常用的)对应的数据(3)volatile-lru:也是采用LRU算法,但是只针对那些有指定time-to-live的key(TTL)Cleanup(4)allkeys-random:随机选择一些key删除(5)volatile-random:随机选择一些设置了TTL的key删除(6)volatile-ttl:删除一些key,选择那些TTL除对于LRU,时间比较短的key也可以通过scan的方式轮询ttl来清理。3.3代码中使用redis的一些建议避免使用keys*等模糊查询,会阻塞当前线程,使用scan来处理。redis客户端建议不要使用redisdesktopmanagerStringcursor=ScanParams.SCAN_POINTER_START;ScanParamsscanParams=newScanParams();//匹配表达式scanParams.match("key*");//每次扫描的次数scanParams.count(1000);while(true){ScanResult<String>result=jedis.scan(cursor,scanParams);cursor=result.getStringCursor();if("0".equals(cursor)){break;}}hgetall也要避免,改用hscan,但是如果通过RedisTemplate回调使用hscan,要注意资源的释放,否则会出现请求达到一定次数后无法发起请求的问题(客户端挂了)ifset同时设置expire过期时间,不要先设置再过期,同样的需求应该使用原子操作setkeyvalue[EXseconds][PXmilliseconds][NX|XX]在redis的多个版本中写入不同格式的数据会造成兼容性问题。可以使用type命令做兼容性处理,监控等旧数据不存在后再去掉判断逻辑。Stringtype=jedis.type("a");if("string".equalsIgnoreCase(type)){//dosomething}elseif("list".equalsIgnoreCase(type)){//dosomething}如果redis中的数据需要去重,可以使用set或者hashmap,hashmap的性能更高,但是除了hashmap数据结构的维护之外还有更多的数据。之前测试过hashmap存储了100B的数据,但是实际用量可能是200B~300B甚至更多,数据多的时候集合的性能会降低。一个更低的建议:数据少的时候用set,数据多的时候用hashmap,但是要注意尽量减少存储内容的长度。比如{"source":"order"}可以改成{"s":1}来去重不建议使用list进行操作,因为每次判断都需要从list中取数据,然后添加进去,在多线程运行下仍然可能出现重复问题(比如两个线程同时运行lrange)//在多线程模式下,会有问题//假设线程A和线程B执行lrangeList<String>list=jedis.lrange("a",0,-1);if(!list.contains("bbb")){jedis.lpush("bbb");}如果有很多命令一次处理,流水线的性能更好。List可以结合lpush/rpop,rpush/lpop实现队列功能,但是不建议把list当作MQ的功能,因为没有记录的状态,无法追踪数据处理情况关于redis分布式锁,目前流行的实现方式没有完美的解决方案,使用lua脚本的版本也不完美。如果需求允许延迟或者在一定时间内不允许多次执行,setnx设置过期时间是最好的解决方案4.故障转移和数据迁移4.1数据迁移方案旧节点被新节点替换,新旧密钥兼容。新节点作为旧节点的slave节点,数据自动同步后,移除旧节点。不建议使用代码迁移,因为不同的业务数据结构在很多不同类型的节点之间可能有不同的迁移方式。如果单个节点迁移到分片集群,唯??一的办法就是使用migration如果新业务会使用新key,为了保留旧key,可以开两个连接池,一个给新key,一个给新keyoldkey,这样当旧key失效的时候,旧key的连接就会被移除,可以完全迁移到新key上。业务的动态扩展必须在集群模式下进行。也可以使用cachecloud,数据会自动同步到各个节点。在数据迁移过程中,即使正在迁移访问的某个key,数据仍然可以使用。如果正常返回,不用担心迁移过程对数据访问的影响。4.2故障转移对客户端的影响。虽然rediscluster模式可以在master节点失效时自动从slave中选举一个节点为主,但是和jedisclient类似。不支持故障转移,即当集群中某个节点发生故障正在切换时,如果客户端正在访问故障节点,此时集群故障转移还没有完成,客户端会报错。如果需要,客户端也可以支持故障转移,需要修改jedis客户端源码实现