我是靠谱客的博主 想人陪金针菇,最近开发中收集的这篇文章主要介绍2021-11-16 - Redis - 个人实践与踩坑汇总1.文档阅读2.整理输出2.2 搭建集群,主从复制2.3 如何将测试环境的redis数据,迁移拷贝到本地redis服务中2.4 关于设置自增操作【或者其他操作】和过期时间一起操作时,可能会导致key永久不过期。2.5 并发访问控制  -- 针对单一用户,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1.应用场景

主要用于收集个人Redis相关操作,临时记录存放。后续整理输出为blog。

2.学习/操作

抛弃时间的人,时间也抛弃他。——莎士比亚

1.文档阅读

判断Redis有序集合中是否存在某个成员的方法

08 | 数据库优化方案(一):查询请求增加时,如何做主从分离?-极客时间

2.整理输出

2.1 判断Redis有序集合中是否存在某个成员的方法

方法一

有序集合中,redis没有命令直接判断有序集合中是否存在某个成员,但可以借助ZLEXCOUNT命令实现:zlexcount 命令 -- Redis中国用户组(CRUG)

ZLEXCOUNT key min max

  • 有序集合中成员名称 min 和 max 之间的成员数量; Integer类型。

命令使用示例如下:

127.0.0.1:6379> zrevrange zsetkey 0 -1
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
127.0.0.1:6379> zlexcount zsetkey [a [a
(integer) 1 # 存在
127.0.0.1:6379> zlexcount zsetkey [m [m
(integer) 0 # 不存在

php代码示例如下:

public function checkExists($zsetkey, $member, $redis)
{
    $ret = intval($redis->zLexCount($zsetkey, '['.$member, '['.$member));
    return $ret > 0 ? true : false;
}

方法二

使用redis有序集合的ZSCORE命令实现:zscore 命令 -- Redis中国用户组(CRUG)

ZSCORE key member

  • 返回有序集key中,成员member的score值。
  • 如果member元素不是有序集key的成员,或key不存在,返回nil。

命令使用示例如下:

127.0.0.1:6379> zadd myzset 1 "one"
(integer) 1
127.0.0.1:6379> zscore myzset "one"
"1"
127.0.0.1:6379> zscore myzset "two"
nil

php代码示例如下:

public function checkExists($zsetkey, $member, $redis)
{
    $ret = $redis->zScore($zsetkey, $member);
    return false === $ret ? false : true;
}

2.2 搭建集群,主从复制

单机多实例模式 // 一般用于本地开发环境以及学习redis集群和主从同步

多机多实例模式 // 实际生产环境使用

【狂神说Java】Redis最新超详细版教程通俗易懂_哔哩哔哩_bilibili

2.3 如何将测试环境的redis数据,迁移拷贝到本地redis服务中

三种redis数据导出导入方式 - 无敌仙人掌 - 博客园

如何高效地向Redis插入大量的数据 - iVictor - 博客园

目前个人的思路:

1. 搜索,和使用目前市面上已有的第三方库/工具进行完成

2. 另外自己动手写一个工具/方法,使用代码去同步

3. 最原始的一种方法,就是手动去设置,但是并不是设置所有的数据,而且只设置自己当前开发中需要的数据 -- 这种方式,最简单也是目前比较推荐。

扩展

对于线上ES等数据存储的服务,当访问线上时常不稳定时,也可以使用这种方式作为一种临时方案,而又暂时没有更好的解决办法时,对于个人而言,这种方式不失为一种临时有效的方案。

2.4 关于设置自增操作【或者其他操作】和过期时间一起操作时,可能会导致key永久不过期。

访问频率控制——防止恶意用户频繁访问_LemmonTreelss的博客-CSDN博客_访问频率。-- 尚未验证 -- TBD

20220120 周四 成都

原因:

因为exsits与inc两个操作不能保证原子性,两者必要要么一起执行,要么都不执行,中间不允许有其他操作。

这里的if判断仅仅是针对上面的出现的不能保证原子性的时候的折中而采用的处理方式 -- 其实属于异常情况

异常情况与处理:

判断过期时间,是否为-1,代表永久有效,则重新设置过期时间。

另外可以采用的方式:

采用lua脚本,但是会麻烦一些,结合此处的业务重要性,或许没那么重要。

2.5 并发访问控制  -- 针对单一用户

<?php


namespace appcomponentsredis;

use appcomponentsLimit;
use appcomponentsRedisClient;

class ApiLimitRedis
{

    const key_sms_ip = "limit:sms:ip:";
    const key_sms_country = "limit:sms:country:";
    const key_sms_ip_whitelist = "limit:sms:ip:whitelist:";
    const key_user_follow_pop_up_limit = "limit:follow:";

    public static $redis_client;

    private static $_redis_pool = 'api-limit';

    public static function LimitRedisKey($user_id, $path){
        return "limit:" . $user_id . ":" . $path;
    }


    public static function LimitSmsPhoneKey($cellphone){
        $time =  date("Y-m-d");
        return "limit:sms:phone:" .$time .":". $cellphone;
    }

    private static function _getInstance()
    {
        if (self::$redis_client instanceof RedisClient) {
            return self::$redis_client;
        }
        self::$redis_client = new RedisClient(self::$_redis_pool);
        return self::$redis_client;
    }

    public static function SetApiLimit($user_id, $path, $ts = 1)
    {
        $key = self::LimitRedisKey($user_id, $path);
        return self::_getInstance()->set($key, 1, ["NX", "EX" => $ts]);
    }

    public static function ExistApiLimit($user_id, $path)
    {
        $key = self::LimitRedisKey($user_id, $path);
        return self::_getInstance()->exists($key);
    }

    // 短信ip限制
    public static function SmsIpLimit($ip){
        $key = self::key_sms_ip . $ip;
        $limitNum = self::_getInstance()->incr($key, 1);
        if($limitNum == 1){
            self::_getInstance()->expire($key, Limit::SmsIpExpire);
        }
        return $limitNum;
    }

    public static function SetSmsIpWhitelist($ip){
        $key = self::key_sms_ip_whitelist . $ip;
        $ip = self::_getInstance()->get($key);
        if (!$ip) {
            self::_getInstance()->set($key, "1");
        }
        self::_getInstance()->expireat($key, strtotime('tomorrow'));

        return true;
    }

    public static function GetSmsIpWhitelist($ip)
    {
        return self::_getInstance()->get(self::key_sms_ip_whitelist . $ip);
    }


    // 短信phone限制
    public static function SmsPhoneLimit($phone){
        $key = self::LimitSmsPhoneKey($phone);
        $limitNum = self::_getInstance()->incr($key, 1);
        if($limitNum == 1){
            self::_getInstance()->expire($key, 115200);
        }
        return $limitNum;
    }


    /**
     * @param $country_code
     * @return mixed
     */
    public static function SmsCountryMaxLimit($country_code)
    {
        $Ymd = date("Y-m-d", time() + 28800);
        $key = self::key_sms_country . $Ymd . ":" . $country_code;
        return self::_getInstance()->incr($key, 1);
    }


    public static function CheckSmsCountryMaxLimit($country_code)
    {
        $Ymd = date("Y-m-d", time() + 28800);
        $key = self::key_sms_country . $Ymd . ":" . $country_code;
        return self::_getInstance()->get($key);
    }


    /**
     * Used for follow/operation
     * Limit for showing the banner for an hour if the user plays with follow button of a specific streamer
     * @param string $user_guid
     * @param string $streamer_guid
     * @return mixed
     */
    public static function SetFollowUpcomingLsLimit(string $user_guid, string $streamer_guid) {
        $key = self::key_user_follow_pop_up_limit . $user_guid . ':' . $streamer_guid;
        $limitNum = self::_getInstance()->incr($key, 1);
        if($limitNum == 1){
            self::_getInstance()->expire($key, Limit::FollowPopupExpire);
        }
        return $limitNum;
    }

    /**
     *  Used for follow/operation
     *  Limit for showing the banner for an hour if the user plays with follow button of a specific streamer
     * @param string $user_guid
     * @param string $streamer_guid
     * @return mixed
     */
    public static function checkIfExistFollowUpcomingLsLimit(string $user_guid, string $streamer_guid)
    {
        $key = self::key_user_follow_pop_up_limit . $user_guid . ':' . $streamer_guid;
        return self::_getInstance()->exists($key);
    }

}

2.6  Redis支持查看单个key占用内存情况 / 以及获取配置信息

同时验证bitmap的最大支持的offset,以及占用最大的内存情况

memory usage some.key // 单位是字节

127.0.0.1:6379> setbit test.max.offset-1 536870911 1

(integer) 0

127.0.0.1:6379> getbit test.max.offset-1 536870911

(integer) 1

127.0.0.1:6379> memory usage test.max.offset-1

(integer) 67125320

127.0.0.1:6379> getbit test.max.offset-1 4294967296

(error) ERR bit offset is not an integer or out of range

127.0.0.1:6379> getbit test.max.offset-1 4294967295

(integer) 0

127.0.0.1:6379> getbit test.max.offset-1 4294967295

(integer) 0

127.0.0.1:6379> setbit test.max.offset-1 4294967295 1

(integer) 0

127.0.0.1:6379> getbit test.max.offset-1 4294967295

(integer) 1

127.0.0.1:6379> memory usage test.max.offset-1

(integer) 537935944

127.0.0.1:6379>

 CONFIG 命令查看或设置配置项

 CONFIG get * // 所有的
 CONFIG get param_name
 CONFIG set param_name value (设置param_name=value)

实践 -- 本地redis服务,默认配置

127.0.0.1:6379> config get *

  1) "rdbchecksum"

  2) "yes"

  3) "daemonize"

  4) "no"

  5) "io-threads-do-reads"

  6) "no"

  7) "lua-replicate-commands"

  8) "yes"

  9) "always-show-logo"

10) "no"

11) "protected-mode"

12) "yes"

13) "rdbcompression"

14) "yes"

15) "rdb-del-sync-files"

16) "no"

17) "activerehashing"

18) "yes"

19) "stop-writes-on-bgsave-error"

20) "yes"

21) "set-proc-title"

22) "yes"

23) "dynamic-hz"

24) "yes"

25) "lazyfree-lazy-eviction"

26) "no"

27) "lazyfree-lazy-expire"

28) "no"

29) "lazyfree-lazy-server-del"

30) "no"

31) "lazyfree-lazy-user-del"

32) "no"

33) "lazyfree-lazy-user-flush"

34) "no"

35) "repl-disable-tcp-nodelay"

36) "no"

37) "repl-diskless-sync"

38) "no"

39) "gopher-enabled"

40) "no"

41) "aof-rewrite-incremental-fsync"

42) "yes"

43) "no-appendfsync-on-rewrite"

44) "no"

45) "cluster-require-full-coverage"

46) "yes"

47) "rdb-save-incremental-fsync"

48) "yes"

49) "aof-load-truncated"

50) "yes"

51) "aof-use-rdb-preamble"

52) "yes"

53) "cluster-replica-no-failover"

54) "no"

55) "cluster-slave-no-failover"

56) "no"

57) "replica-lazy-flush"

58) "no"

59) "slave-lazy-flush"

60) "no"

61) "replica-serve-stale-data"

62) "yes"

63) "slave-serve-stale-data"

64) "yes"

65) "replica-read-only"

66) "yes"

67) "slave-read-only"

68) "yes"

69) "replica-ignore-maxmemory"

70) "yes"

71) "slave-ignore-maxmemory"

72) "yes"

73) "jemalloc-bg-thread"

74) "yes"

75) "activedefrag"

76) "no"

77) "syslog-enabled"

78) "no"

79) "cluster-enabled"

80) "no"

81) "appendonly"

82) "no"

83) "cluster-allow-reads-when-down"

84) "no"

85) "crash-log-enabled"

86) "yes"

87) "crash-memcheck-enabled"

88) "yes"

89) "use-exit-on-panic"

90) "no"

91) "disable-thp"

92) "yes"

93) "cluster-allow-replica-migration"

94) "yes"

95) "replica-announced"

96) "yes"

97) "aclfile"

98) ""

99) "unixsocket"

100) ""

101) "pidfile"

102) ""

103) "replica-announce-ip"

104) ""

105) "slave-announce-ip"

106) ""

107) "masteruser"

108) ""

109) "cluster-announce-ip"

110) ""

111) "syslog-ident"

112) "redis"

113) "dbfilename"

114) "dump.rdb"

115) "appendfilename"

116) "appendonly.aof"

117) "server_cpulist"

118) ""

119) "bio_cpulist"

120) ""

121) "aof_rewrite_cpulist"

122) ""

123) "bgsave_cpulist"

124) ""

125) "ignore-warnings"

126) ""

127) "proc-title-template"

128) "{title} {listen-addr} {server-mode}"

129) "masterauth"

130) ""

131) "requirepass"

132) ""

133) "supervised"

134) "no"

135) "syslog-facility"

136) "local0"

137) "repl-diskless-load"

138) "disabled"

139) "loglevel"

140) "notice"

141) "maxmemory-policy"

142) "noeviction"

143) "appendfsync"

144) "everysec"

145) "oom-score-adj"

146) "no"

147) "acl-pubsub-default"

148) "allchannels"

149) "sanitize-dump-payload"

150) "no"

151) "databases"

152) "16"

153) "port"

154) "6379"

155) "io-threads"

156) "1"

157) "auto-aof-rewrite-percentage"

158) "100"

159) "cluster-replica-validity-factor"

160) "10"

161) "cluster-slave-validity-factor"

162) "10"

163) "list-max-ziplist-size"

164) "-2"

165) "tcp-keepalive"

166) "300"

167) "cluster-migration-barrier"

168) "1"

169) "active-defrag-cycle-min"

170) "1"

171) "active-defrag-cycle-max"

172) "25"

173) "active-defrag-threshold-lower"

174) "10"

175) "active-defrag-threshold-upper"

176) "100"

177) "lfu-log-factor"

178) "10"

179) "lfu-decay-time"

180) "1"

181) "replica-priority"

182) "100"

183) "slave-priority"

184) "100"

185) "repl-diskless-sync-delay"

186) "5"

187) "maxmemory-samples"

188) "5"

189) "maxmemory-eviction-tenacity"

190) "10"

191) "timeout"

192) "0"

193) "replica-announce-port"

194) "0"

195) "slave-announce-port"

196) "0"

197) "tcp-backlog"

198) "511"

199) "cluster-announce-bus-port"

200) "0"

201) "cluster-announce-port"

202) "0"

203) "cluster-announce-tls-port"

204) "0"

205) "repl-timeout"

206) "60"

207) "repl-ping-replica-period"

208) "10"

209) "repl-ping-slave-period"

210) "10"

211) "list-compress-depth"

212) "0"

213) "rdb-key-save-delay"

214) "0"

215) "key-load-delay"

216) "0"

217) "active-expire-effort"

218) "1"

219) "hz"

220) "10"

221) "min-replicas-to-write"

222) "0"

223) "min-slaves-to-write"

224) "0"

225) "min-replicas-max-lag"

226) "10"

227) "min-slaves-max-lag"

228) "10"

229) "maxclients"

230) "10000"

231) "active-defrag-max-scan-fields"

232) "1000"

233) "slowlog-max-len"

234) "128"

235) "acllog-max-len"

236) "128"

237) "lua-time-limit"

238) "5000"

239) "cluster-node-timeout"

240) "15000"

241) "slowlog-log-slower-than"

242) "10000"

243) "latency-monitor-threshold"

244) "0"

245) "proto-max-bulk-len"

246) "536870912"

247) "stream-node-max-entries"

248) "100"

249) "repl-backlog-size"

250) "1048576"

251) "maxmemory"

252) "0"

253) "hash-max-ziplist-entries"

254) "512"

255) "set-max-intset-entries"

256) "512"

257) "zset-max-ziplist-entries"

258) "128"

259) "active-defrag-ignore-bytes"

260) "104857600"

261) "hash-max-ziplist-value"

262) "64"

263) "stream-node-max-bytes"

264) "4096"

265) "zset-max-ziplist-value"

266) "64"

267) "hll-sparse-max-bytes"

268) "3000"

269) "tracking-table-max-keys"

270) "1000000"

271) "client-query-buffer-limit"

272) "1073741824"

273) "repl-backlog-ttl"

274) "3600"

275) "auto-aof-rewrite-min-size"

276) "67108864"

277) "logfile"

278) ""

279) "watchdog-period"

280) "0"

281) "dir"

282) "/Users/kumu/Documents/github/ningxiaofa"

283) "save"

284) "3600 1 300 100 60 10000"

285) "client-output-buffer-limit"

286) "normal 0 0 0 slave 268435456 67108864 60 pubsub 33554432 8388608 60"

287) "unixsocketperm"

288) "0"

289) "slaveof"

290) ""

291) "notify-keyspace-events"

292) ""

293) "bind"

294) ""

295) "oom-score-adj-values"

296) "0 200 800"

127.0.0.1:6379>

Redis MEMORY USAGE 命令 | 程序员笔记

2.7 启动Redis 服务,以及客户端访问的配置

二、Redis启动、停止、Redis命令行的操作 - 钧天府人 - 博客园

2.8 redis批量删除keys

   方式一:
    笨方法:
        依次指定多个 key:
        DEL key1 key2 key3 ...

    
   方式二:
    在方式一的基础上,使用xargs传参
        先查询,在删除,使用xargs传参(xargs可以将管道或标准输入(stdin)数据转换成命令行参数),先执行查询语句,在将查询出来的key值,当作del的参数去删除。
        redis-cli -n 7 KEYS *platform_config_key* | xargs redis-cli -n 7 del
        例如批量删除包含 platform_config_key 字符串的keys
        [root@ip-172-31-24-254 ~]# redis-cli -n 7 KEYS *platform_config_key* | xargs redis-cli -n 7 del
        (integer) 10
        [root@ip-172-31-24-254 ~]# 

        redis-cli -h 127.0.0.1 -p 6379 -a xxxxxx -n 1
        参数解释:
        -h <hostname> Server hostname (default: 127.0.0.1). 指定ip
        -p <port> Server port (default: 6379).指定端口,默认是6379
        -a <password> Password to use when connecting to the server. 连接密码
        -n <db> Database number.指定的数据库

  

 方式三:
    使用 Lua 脚本
        EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 pattern

    例如,要删除以 user: 开头的所有 key,可以执行以下命令:
        EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 user:*

        Localhost-Docker:0>keys *
        1) "ning1"
        2) "ning"

        Localhost-Docker:0>EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 ning*
        "2"
        Localhost-Docker:0>
 

后续补充

...

3.问题/补充

1. 之前发生过的一个问题,用Redis主从同步,写入Redis的数据量太大,没加频次控制,导致每秒几十万写入,主从延迟过大,运维频频报警,在主库不挂掉的情况下,这样大量写入会不会造成数据丢失?

作者回复: 之前遇到过 如果主从延迟很大,数据会堆积到redis主库的发送缓冲区,会导致主库oom

2. 网友 - LONGER

14 | 高性能数据库集群:读写分离-极客时间

目前还在用单机一直在扛着,目前数据量在百万万,在不停的优化,建立冗余等方式,还在保持着一个较快的查询速度,因为业务查询的关系,多表之间的关联,聚合,很难避免,一直想引用缓存,但是查询的条件太多,很动态,就不知道如何设计缓存,类似于京东筛选物品,多品类,多维度筛选,不知道大牛有何高见

作者回复: 按照2-8原则,选出占访问量80%的前20%的请求条件缓存,因为大部分人的查询不会每次都非常多条件,以手机为例,查询苹果加华为的可能占很大一部分

3. RESP 也可以通过抓包的方式来具体查看格式,使用 tcpdump 进行抓包得到的文件,使用 wireshark 进行查看,也比较直观。

4.参考

判断Redis有序集合中是否存在某个成员的方法 - ZYallers的个人空间 - OSCHINA - 中文开源技术交流社区

14 | 高性能数据库集群:读写分离-极客时间

后续补充

...

最后

以上就是想人陪金针菇为你收集整理的2021-11-16 - Redis - 个人实践与踩坑汇总1.文档阅读2.整理输出2.2 搭建集群,主从复制2.3 如何将测试环境的redis数据,迁移拷贝到本地redis服务中2.4 关于设置自增操作【或者其他操作】和过期时间一起操作时,可能会导致key永久不过期。2.5 并发访问控制  -- 针对单一用户的全部内容,希望文章能够帮你解决2021-11-16 - Redis - 个人实践与踩坑汇总1.文档阅读2.整理输出2.2 搭建集群,主从复制2.3 如何将测试环境的redis数据,迁移拷贝到本地redis服务中2.4 关于设置自增操作【或者其他操作】和过期时间一起操作时,可能会导致key永久不过期。2.5 并发访问控制  -- 针对单一用户所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部