推荐学习书目
› Learn Python the Hard Way
Python Sites
› PyPI - Python Package Index
› http://diveintopython.org/toc/index.html
› Pocoo
值得关注的项目
› PyPy
› Celery
› Jinja2
› Read the Docs
› gevent
› pyenv
› virtualenv
› Stackless Python
› Beautiful Soup
› 结巴中文分词
› Green Unicorn
› Sentry
› Shovel
› Pyflakes
› pytest
Python 编程
› pep8 Checker
Styles
› PEP 8
› Google Python Style Guide
› Code Style from The Hitchhiker's Guide
thekoc
V2EX  ›  Python

sqlite3 多线程高并发的访问如何处理?

  •  
  •   thekoc · Jan 18, 2017 · 19090 views
    This topic created in 3576 days ago, the information mentioned may be changed or developed.

    用得是自带的 sqlite3 库,可能有 100 个左右的线程吧,目前是每个线程拥有一个单独的连接,所有连接的插入间隔大约是 0.00x 秒,会出现 datebase is locked 错误,请问怎么处理呢?

    18 replies  •  2017-01-19 17:45:02 +08:00
    mansur
    mansur
    Jan 18, 2017
    1
    弄一个队列写吧
    murmur
    murmur
    Jan 18, 2017
    2
    我怎么印象中 sqlite 用排队+锁的机制比多线程还要好一点
    100 线程的话为啥不考虑 mysql 啊
    hosiet
    hosiet
    Jan 18, 2017 via Android
    3
    写入发现被锁了就循环多试几次嘛,可以给个重试上限(例如五次,或者没上限也行)以及重试时间间隔等等。

    不过我记得好像有阻塞的写法吧,就是完成写入才返回那种。不知道你用的是什么编程语言?
    thekoc
    thekoc
    OP
    Jan 18, 2017
    4
    @murmur 因为想尽量用标准库完成。。
    thekoc
    thekoc
    OP
    Jan 18, 2017
    5
    @hosiet Python
    hosiet
    hosiet
    Jan 18, 2017 via Android
    6
    糟糕没看节点( Python )。 pysqlite 不支持多线程共享连接是个问题。
    clino
    clino
    Jan 18, 2017 via Android
    7
    gevent 并发,这样同时只有一个读写 sqlite
    thekoc
    thekoc
    OP
    Jan 18, 2017
    8
    @hosiet 是支持的,要开启一个选项
    zwh8800
    zwh8800
    Jan 19, 2017
    9
    sqlite 并发不行的, sqlite 的事务就是锁表,你无论开几个线程,只要访问的是同一张表,最后在 sqlite 那里都会被锁,实际上最后都是顺序执行的。

    正解是队列

    <del>更正确的解是换数据库</del>

    @hosiet 重试一方面不优雅,另一方面会让性能更糟糕,堆积的重试操作多了之后,大家都抢不到锁。
    tomwei7
    tomwei7
    Jan 19, 2017
    10
    加个队列吧, sqlite 不适合高并发的
    gdsagdada
    gdsagdada
    Jan 19, 2017
    11
    # coding:utf-8
    import sqlite3
    import queue, os


    def singleton(cls):
    instances = {}

    def _singleton(*args, **kw):
    if cls not in instances:
    instances[cls] = cls(*args, **kw)
    return instances[cls]

    return _singleton


    @singleton
    class SQLiteUtil(object):
    __queue_conn = queue.Queue(maxsize=1)
    __path = None

    def __init__(self, path):
    self.__path = path
    print('path:', self.__path)
    self.__create_conn()

    def __create_conn(self):
    conn = sqlite3.connect(self.__path, check_same_thread=False)
    self.__queue_conn.put(conn)

    def __close(self, cursor, conn):
    if cursor is not None:
    cursor.close()
    if conn is not None:
    cursor.close()
    self.__create_conn()

    def execute_query(self, sql, params):
    conn = self.__queue_conn.get()
    cursor = conn.cursor()
    value = None
    try:
    records = None
    if not params is None:
    records = cursor.execute(sql, params).fetchall()
    else:
    records = cursor.execute(sql).fetchall()
    field = [i[0] for i in cursor.description]
    value = [dict(zip(field, i)) for i in records]
    finally:
    self.__close(cursor, conn)
    return value

    def executescript(self, sql):
    conn = self.__queue_conn.get()
    cursor = conn.cursor()
    try:
    cursor.executescript(sql)
    conn.commit()
    except Exception as e:
    conn.rollback()
    raise
    finally:
    self.__close(cursor, conn)

    def execute_update(self, sql, params):
    return self.execute_update_many([sql], [params])

    def execute_update_many(self, sql_list, params_list):
    conn = self.__queue_conn.get()
    cursor = conn.cursor()
    count = 0
    try:
    for index in range(len(sql_list)):
    sql = sql_list[index]
    params = params_list[index]
    if not params is None:
    count += cursor.execute(sql, params).rowcount
    else:
    count += cursor.execute(sql).rowcount
    conn.commit()
    except Exception as e:
    conn.rollback()
    raise
    finally:
    self.__close(cursor, conn)
    return count


    '''
    example:

    one = SQLiteUtil('xxx.sqlite')

    rst = one.execute_query('select * from website', None)
    for line in rst:
    print(line.get('id'), line.get('url'), line.get('content'))


    print(one.execute_update('update website set content = \'2222222\' where id = ?', ('1',)))
    print(one.execute_update('update website set content = \'2222222\' where id = \'1\'', None))


    print('update many')
    count = one.execute_update_many(
    [
    'update website set content = \'一\' where id = \'1\'',
    'update website set content = \'二\' where id = \'2\'',
    'update website set content = 1 where id = \'3\''
    ],
    [None, None, None]
    )
    print('count:', count)
    '''

    python3 的 用 py2 自己改改,可抗千万级并发[v2ex 滑稽专属表情]
    gdsagdada
    gdsagdada
    Jan 19, 2017
    12
    楼主改 py2 了记得也贴下
    Actrace
    Actrace
    Jan 19, 2017
    13
    sqlite 是针对小型的移动设备设计的数据库。。。楼主这是准备拿 100 台手机来做服务器集群吗。。
    thekoc
    thekoc
    OP
    Jan 19, 2017
    15
    @gdsagdada 我发现我插入数据的平均速度是要大于这种写法的插入速度的……所以最后会导致内存爆炸
    gdsagdada
    gdsagdada
    Jan 19, 2017
    16
    @thekoc 内存爆了找你程序内存爆的原因,不报: datebase is locked 错误说明并发解决了
    gdsagdada
    gdsagdada
    Jan 19, 2017
    17
    把你程序也贴出来大家观赏下[v2ex 滑稽]
    thekoc
    thekoc
    OP
    Jan 19, 2017
    18
    @gdsagdada 我还没有试,我的意思是说我的平均插入速度要大于用这个方式做出来的函数的最高插入速度……所以无论如何还是会阻塞的。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   5137 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 00:13 · PVG 08:13 · LAX 16:13 · JFK 19:13
    ♥ Do have faith in what you're doing.