47258


python bottleでsqlite3にアクセスして回答を保持する簡単なクイズアプリを作成しています。

下記のサンプルコードを1人で実行すると問題なく動作するのですが、複数人で実行すると一斉に回答した時に内部で`sqlite3.OperationalError`が発生し、回答者には`500
Internal Server Error`のエラー画面が表示されてしまいます。

sqlite3を数十人で一斉に書き込んでもロックしないようにするにはどのように設定するべきでしょうか。

Python 3.6.0  
bottle 0.12.13  
Windows 10 64bit / Windows Server 2012

**question.py**

[code]

    #coding:utf-8
    from bottle import run,route,template,redirect,request,get,post,static_file
    from datetime import datetime
    import sqlite3
    import os
    
    @route("/")
    def index():
        if not os.path.exists(db_name):
            init_table(db_name)
        question = [u'パンはパンでも', u'食べられないパンは', u'なーんだ？']
        answers = [[1, u'くさったパン'], [2, u'パンデミック'], [3, u'フライパン(英: a fried bread)']]
        return template("index",id=id,question=question,answers=answers)
    
    @route("/answer",method=["POST"])
    def answer():
        name = request.forms.user_name
        ans = request.forms.answer
        with sqlite3.connect(db_name) as conn:
            sql = "replace into answer values(?, ?)"
            conn.execute(sql, (name, ans))
            conn.commit()
        return redirect("/")
    
    def init_table(db_name):
        with sqlite3.connect(db_name) as conn:
            conn.execute('create table answer (name varchar(50) primary key, ans integer)')
    
    db_name = 'myquiz.db'
    run(host="localhost",port=8000,reloader=True)
    #run(host="192.168.X.X",port=8000,reloader=True)
    
[/code]

**index.html**

[code]

    <!DOCTYPE html>
    <html lang="jp">
    <head>
        <meta charset="UTF-8">
        <title>クイズアプリ</title>
    </head>
    <body>
        <div>
            % for line in question:
            {{line}}<br/>
            % end
        </div>
    
        <form method="POST" action="/answer">
            <table border="0">
                <tr>
                    <td align="right"><b> 名前：</b></td>
                    <td><input type="text" name="user_name" size="30" maxlength="20"></td>
                </tr>
            </table>
            <fieldset>
                  <legend>Answer</legend>
                  % for answer in answers:
                  <label><input type="radio" name="answer" value="{{answer[0]}}"/>{{answer[1]}}</label><br/>
                  % end
            </fieldset>
            <input class="sbutton" type="submit" value="回答する(SUBMIT)"/>
        </form>
    
    </body>
    </html>
    
[/code]

**コンソールに表示される内部エラー**

[code]

    Traceback (most recent call last):
      File "C:\Users\payaneco\AppData\Local\Programs\Python\Python36-32\lib\site-packages\bottle.py", line 862, in _handle
        return route.call(**args)
      File "C:\Users\payaneco\AppData\Local\Programs\Python\Python36-32\lib\site-packages\bottle.py", line 1740, in wrapper
        rv = callback(*a, **ka)
      File ".\question.py", line 21, in answer
        conn.execute(sql, (name, ans))
    sqlite3.OperationalError: database is locked
    
[/code]

**回答者に表示されるエラー**

[code]

    Error: 500 Internal Server Error
    Sorry, the requested URL 'http://localhost:8000/answer' caused an error:
    
    Internal Server Error
    
[/code]


CC BY-SA 4.0
2018-08-05T02:26:45.330
47249
2018-08-05T12:14:08.017
9820
question
python sqlite bottle
python bottle+sqlite3で複数ユーザの更新を受け付ける方法


`sqlite3`のトランザクションは非常にコストの高い処理です。`sqlite3`では、トランザクション中はファイル全体がロックされるため同時に複数の処理ができません。また、トランザクションは、マシンにトラブルがおきても処理結果が消えてしまないようにディスクへの書き込みが完了してからでないと通常は終了しません。要するに、一つずつ順番に処理をして、なおかつキャッシュを使わないで書き込みをするので時間がかかります。

そのため、`sqlite3`を数十人が一斉に書き込むような処理に使うのは向いていません。もし、RDBが必要なのであれば、PostgreSQLかMySQLを使うようにします。これらのデータベースではテーブルロックや行レベルロックが使えるので`sqlite3`よりは処理が高速になります。ただし、RDBではトランザクションの信頼性を重視しているので書き込みの処理はそれほど速くありません。

クイズアプリが社内や教室用で稀にデータが消えても大きな問題にならないのであれば、`Redis`を使うと非常に高速に処理できるし、インターネットで公開するアプリであれば、`Cloud
Firestore`のようなクラウドベースのNoSQLを使うと大量の書き込み処理に対応しやすいと思います。


CC BY-SA 4.0
2018-08-05T12:14:08.017
47258
2018-08-05T12:14:08.017
15171
47249
answer