• 请不要在回答技术问题时复制粘贴 AI 生成的内容
shuangdeyu
V2EX  ›  程序员

redis 并发下写入数据丢失

  •  
  •   shuangdeyu · Aug 31, 2020 · 4459 views
    This topic created in 2106 days ago, the information mentioned may be changed or developed.

    不懂就问,使用 redis 的 list 类型做消息队列,通过这个队列将要写到 mysql 的数据延后批量写入

    遇到的问题是,使用 jmeter 测并发的时候发现,10000 线程的时候,写到 redis 的数据会丢失,而且丢失数量是不规则的; 5000 线程以内则一切正常,主要想知道这是什么原因?单机如何去优化?替代方案比如 MQ 暂时不考虑。

    使用语言是 Go,代码如下,其实也很简单,只是往 redis 队列中推入数据,用 Redis Manager 观察:

    func Testt(c *gin.Context) {
        // 这是测试数据,数据长度小得到的也是一样的结果
        //x := `{"Dateline":"2020-08-31 13:34:59","Error":200002,"Id":"ee0da19f-eb05-4728-8e23-107336043ede","Ip":"192.168.2.150","Method":"POST","Resp":"null"}`
    
        // 这是封装的 LPush 函数
        dbhelper.Lpush("testttt", "_api", "1")
    
        c.JSON(200, gin.H{
            "e":   0,
            "msg": "success",
        })
    }
    

    10000 线程的测试结果,只存进去 1542 个数据:

    5000 线程的测试结果,5000 个数据全部写进队列了:

    Supplement 1  ·  Sep 1, 2020

    感谢大家的回答,问题找到了,测试代码没有严谨对待,没有抛出错误信息,饶了弯路 =_= ,一直在钻redis配置的牛角尖,加上后抛出的错误是"connection pool timeout",然后修改代码,在建立连接客户端的时候在"redis.Options"中配置连接池大小等属性后问题解决,因为默认的"PoolSize"只有10,一个进程内10000的并发量自然是不够用了。

    配置连接数上也发现了问题,修改"redis.conf"连接数后发现数量怎么都不会增加,因为之前有解决mysql连接数问题的经验,很快就找到还有一个隐藏的"limit.conf"中一个"LimitNOFILE"参数限制了最大连接数,修改后连接数修改就能生效了。

    12 replies    2020-09-01 10:01:05 +08:00
    useben
        1
    useben  
       Aug 31, 2020
    查看下 redis 连接是否有错误, 调大连接数
    saturn7
        2
    saturn7  
       Aug 31, 2020
    看 demo 代码象只是复用单实例连接到 Redis Server,典型的写 PHP 思维。解决要用连接池 + 并发锁。
    micean
        3
    micean  
       Aug 31, 2020
    lpush 的结果不观察一下吗?
    JJstyle
        4
    JJstyle  
       Aug 31, 2020
    @saturn7 楼主需要能并发插入数据,你给加一个并发锁,是要解决什么问题?
    fanpei0121
        5
    fanpei0121  
       Aug 31, 2020
    经测试 并发 10 万条 redis 插入,没有漏数据,测试代码不要太在意细节。


    func Test(c *gin.Context) {
    // 这是测试数据,数据长度小得到的也是一样的结果
    x := `{"Dateline":"2020-08-31 13:34:59","Error":200002,"Id":"ee0da19f-eb05-4728-8e23-107336043ede","Ip":"192.168.2.150","Method":"POST","Resp":"null"}`

    // 这是封装的 LPush 函数
    rdb := redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
    Password: "",
    DB: 0,
    })
    dataChan := make(chan string, 1000)

    go func() {
    for i := 1; i <= 100000; i++ {
    dataChan <- x
    }
    }()

    for j := 1; j < 20; j++ {
    go func() {
    for {
    data := <-dataChan
    err := rdb.RPush("test111", data).Err()
    logs.Error(err)
    }
    }()
    }

    c.JSON(200, gin.H{
    "e": 0,
    "msg": "success",
    })
    }
    huntcool001
        6
    huntcool001  
       Aug 31, 2020
    没看明白. 你是怎么知道 lpush 结果正常的.. redis 返回值是什么, 异常有 catch 住打印没?


    另外,消息队列最好用 Redis Stream 来做.
    Citrus
        7
    Citrus  
       Aug 31, 2020 via iPhone
    你写请求发出去就不管了是咋知道这成功了呢?
    90928yao
        8
    90928yao  
       Aug 31, 2020
    打个 redis 返回啊。肯定很多报错 拿不到链接
    th00000
        9
    th00000  
       Aug 31, 2020
    猜测一下: Redis 的写入是单线程的, 假设你的 dbhelper 是有缓存的, 每次去写的时候会等待前面写入之后再去写
    但是你并发比较高, 导致前面的这个 dbhelper 还没来得及写入, 就被拎出来又存了数据, 继续排队, 导致只有最终数据写进去了
    ZehaiZhang
        10
    ZehaiZhang  
       Aug 31, 2020
    之前 trycatch 发现了一些因为数据格式问题导致的 push 失败
    PiersSoCool
        11
    PiersSoCool  
       Aug 31, 2020
    大哥你这用异步跑,又没有同步措施,协程没跑完,主进程就退出了,肯定有问题啊
    securityCoding
        12
    securityCoding  
       Sep 1, 2020
    @PiersSoCool 加个 waitGroup 或者 chan
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   905 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 57ms · UTC 22:36 · PVG 06:36 · LAX 15:36 · JFK 18:36
    ♥ Do have faith in what you're doing.