V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yang2yang
V2EX  ›  程序员

问个线程相关的问题

  •  
  •   yang2yang ·
    yang2yang · 2016-09-22 14:13:18 +08:00 · 2533 次点击
    这是一个创建于 2976 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设是下面的情景中:

    我需要插入五百万条数据到数据库中,但是这些数据中有些是有重复的,但是我不要重复的数据纪录,所以程序中每次都需要判断查询是否有这个数据,也就是说是一条查询操作,然后在将没有的数据插入到数据库中,重复的数据跳过。在假设每一个查询操作需要 3s 的时间,插入操作需要 1s 的时间(假设,不要认真啊。。)

    那么,如果我使用两个线程,每个线程都有一个和数据库的连接,那么如果一个线程在查询的时候,那么是需要等待 3 秒的,那么这个查询操作是否是阻塞的呢?(第一个问题,是或不是我都想知道为什么)如果是阻塞的,那么是否操作系统会马上切换为其他线程,还是说会等待这个时间片到时间才切换?(第二个问题,无论哪个答案都想知道为什么)。

    最后一个问题是需要插入大量数据的时候,有哪些地方可以提高效率呢?这个问题有点宽泛,想答就答吧。。

    当然,有可能问题本身和我自己想的答案都有问题,也请指出来。

    诚心请各位 v 友们指点下。

    18 条回复    2016-09-23 14:09:50 +08:00
    zhicheng
        1
    zhicheng  
       2016-09-22 14:23:50 +08:00
    Google 唯一约束
    wizardoz
        2
    wizardoz  
       2016-09-22 14:27:52 +08:00
    让数据库自己处理冲突
    cxe2v
        3
    cxe2v  
       2016-09-22 14:47:16 +08:00
    把需要插入的数据存起来批量插入可以省去一部分时间
    xss
        4
    xss  
       2016-09-22 15:14:34 +08:00
    1. 阻塞,需要等 3 秒.

    2. 不一定会立刻切换,要看操作系统的线程切换策略.除非你有代码直接放弃了当前时间片,让出处理器.

    3. 参考一下 unique key,然后 try catch 住 duplicate 异常.
    warrenoo
        5
    warrenoo  
       2016-09-22 15:24:45 +08:00
    去重不需要依靠数据库的查询。可以在循环前对全部数据一次去重,或者依赖数据库的唯一约束在插入时进行验证。至于线程调度的问题,和你当前的问题关系不大。建议开 cpu 核数一致的线程数把所有数据分块去跑,砍掉过程中阻塞时间长的查询就可以了。
    yang2yang
        6
    yang2yang  
    OP
       2016-09-22 17:47:50 +08:00
    @warrenoo 嗯,如果使用多线程的话,那么是不是也可以节省时间,但是这个时间不是 I/O 阻塞的时间,而是占用了操作系统中其他服务的时间?
    warrenoo
        7
    warrenoo  
       2016-09-22 18:53:05 +08:00
    节省的是 i/o 阻塞时间。按你的假设,单个 insert 阻塞 1s 的话,单线程就要至少 500Ws, 如果是多线程,相当于数据库承载了并发请求,线程间不会相互阻塞,需要的 500W/线程数 s 。理论上线程数越多越好,但是也要考虑数据库负载并发的能力和线程调度的消耗,所以如果不需要挖掘极限性能的话,按核数开线程是比较稳妥的方案,当然得先确定你所说的线程是内核级的线程,不同语言的线程实现上是有差异的。
    dearmymy
        8
    dearmymy  
       2016-09-22 20:31:31 +08:00
    你这样去重你老板知道么。。。不会先去重后插入么
    yang2yang
        9
    yang2yang  
    OP
       2016-09-22 20:59:33 +08:00
    @warrenoo 为什么我还是觉得不是节省的是 I/O 阻塞时间呢。。
    假设有 a,b,c,d,e 五个线程,其中 a,b 是插入数据库的线程, c,d,e 是操作系统的其他线程。假设每个线程能分配 10 秒的时间片(当然,实际情况一定很复杂,简化模型), a 线程开始运行后到第 5 秒的时候,阻塞直到第 10 秒(因为按照调度算法是不会在一个线程阻塞的情况下,在时间片没到的情况下切换线程的,当然也有其他一些导致切换的原因,暂时不考虑),那么 a 时间片到了,之后切换成 b 线程,此时 b 还是重复 a 线程的路子,然后是操作系统其他线程 c,d,e 切换。那么这个模型中我使用的程序真正运行时间为 10 秒,但是我如果在开一个线程,是不是相当于又多了 5 秒,但是因为我再开了一个线程,也许会导致时间片的缩短(也许不会?),比如说为 8 秒,这样我的总时间就变多了?

    如果这样考虑的话,那么 I/O 阻塞的时间一点都没有节省呢?

    不知道我这个考虑中哪里有问题,还请解释一下。
    yang2yang
        10
    yang2yang  
    OP
       2016-09-22 21:00:31 +08:00
    @dearmymy 哈哈,老板不知道呢。。其实应该使用捕获异常的呢,已经改好了。
    zk8802
        11
    zk8802  
       2016-09-22 22:31:00 +08:00 via iPhone
    “因为按照调度算法是不会在一个线程阻塞的情况下,在时间片没到的情况下切换线程的”

    这句话好像不符合操作系统中线程调度算法的实际。楼主可以 Google 下操作系统的线程调度算法,或直接看看任何一本本科操作系统或计算机体系结构的教材。
    guyskk
        12
    guyskk  
       2016-09-23 00:09:18 +08:00 via Android
    用布隆过滤器去重后再插入
    hpeng
        13
    hpeng  
       2016-09-23 00:13:03 +08:00 via iPhone
    才五百万,临时表,存储过程,大概 10 分钟搞定了
    ryd994
        14
    ryd994  
       2016-09-23 06:58:09 +08:00
    先查询再插入…………你考虑竞争了么?开事务或者加锁的话,那就没啥多线程可言了……
    warrenoo
        15
    warrenoo  
       2016-09-23 11:00:04 +08:00
    @yang2yang linux 中, 进程的调度并不是时间片轮转这一种方式,而是综合了很多比较复杂的调度策略,推荐看一下深入理解 linux 内核的进程调度章节。所以[因为按照调度算法是不会在一个线程阻塞的情况下,在时间片没到的情况下切换线程的]这个并不成立。当然这些已经脱离了你的插入数据问题本身,建议设计实现方式的时候先不要过多考虑外部进程对当前程序的影响(这些操作系统完全可以处理), 只考虑从程序本身如何去优化。
    CRVV
        16
    CRVV  
       2016-09-23 11:01:35 +08:00
    好像只有 1 楼和 2 楼给出了正经的回答

    不就是这么两行么(PostgreSQL)
    CREATE TABLE xxx (id SERIAL PRIMARY KEY, v TEXT UNIQUE);
    INSERT INTO xxx VALUES (DEFAULT, 'xxyy') ON CONFLICT DO NOTHING;

    这种事情,既然要用数据库,那就用数据库现成的功能
    如果不用数据库的索引,那就自己把 500 万条数据里重复的都挑出来再往数据库里存

    从头到尾都和线程没什么关系
    yang2yang
        17
    yang2yang  
    OP
       2016-09-23 14:03:38 +08:00
    @warrenoo 恩,只是借这个问题加深一下操作系统中线程的理解,谢谢了。
    yang2yang
        18
    yang2yang  
    OP
       2016-09-23 14:09:50 +08:00
    @CRVV 厉害,原来除了再程序里面捕获异常,还可以这样。。直接让数据库处理这个逻辑。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5533 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 06:46 · PVG 14:46 · LAX 22:46 · JFK 01:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.