V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
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 · 2017-01-18 22:27:51 +08:00 · 17787 次点击
    这是一个创建于 2866 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

    正解是队列

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

    @hosiet 重试一方面不优雅,另一方面会让性能更糟糕,堆积的重试操作多了之后,大家都抢不到锁。
    tomwei7
        10
    tomwei7  
       2017-01-19 11:36:47 +08:00
    加个队列吧, sqlite 不适合高并发的
    gdsagdada
        11
    gdsagdada  
       2017-01-19 11:50:02 +08:00
    # 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
        12
    gdsagdada  
       2017-01-19 11:52:15 +08:00
    楼主改 py2 了记得也贴下
    Actrace
        13
    Actrace  
       2017-01-19 13:21:28 +08:00
    sqlite 是针对小型的移动设备设计的数据库。。。楼主这是准备拿 100 台手机来做服务器集群吗。。
    thekoc
        14
    thekoc  
    OP
       2017-01-19 14:59:02 +08:00
    thekoc
        15
    thekoc  
    OP
       2017-01-19 15:00:06 +08:00
    @gdsagdada 我发现我插入数据的平均速度是要大于这种写法的插入速度的……所以最后会导致内存爆炸
    gdsagdada
        16
    gdsagdada  
       2017-01-19 17:06:29 +08:00
    @thekoc 内存爆了找你程序内存爆的原因,不报: datebase is locked 错误说明并发解决了
    gdsagdada
        17
    gdsagdada  
       2017-01-19 17:06:55 +08:00
    把你程序也贴出来大家观赏下[v2ex 滑稽]
    thekoc
        18
    thekoc  
    OP
       2017-01-19 17:45:02 +08:00
    @gdsagdada 我还没有试,我的意思是说我的平均插入速度要大于用这个方式做出来的函数的最高插入速度……所以无论如何还是会阻塞的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2423 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 15:59 · PVG 23:59 · LAX 07:59 · JFK 10:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.