| 
                         2.4.1 结论 
    - 多线程设置是决定DDL、DML、WAL(包括SHM)操作是否线程安全的设置
 
    - 多线程设置与读写(连接)分离没有任何关系,并不是实现读写(连接)分离的必要条件(很多人对这一点有误解)
 
 
3,性能优化tips 
3.1 合理使用事务 
由#2.2的分析可知,写操作会在RESERVED状态下将数据更改、b-tree的更改、日志等写入page  cache,并最终flush到数据库文件中;使用事务的话,只需要一次对DB文件的flush操作,同时也不会对其他连接的读写操作阻塞;对比以下两种数据写入方式(这里以统一存储提供的API为例),实测耗时有十几倍的差距(当然对于频繁的读操作,使用事务可以减事务状态的切换,也会有一点点性能提升): 
- // batch insert in transaction with 1000000 records 
 - // 
 - AliDBExecResult* execResult = NULL; 
 - _database->InTransaction([&]() -> bool {    // in transaction 
 -   auto statement = _database->PrepareStatement("INSERT INTO table VALUES(?, ?)"); 
 -   for (auto record : records) {    // bind 1000000 records 
 -     // bind record 
 -     ... 
 -     ... 
 -     statement->AddBatch(); 
 -   } 
 -   auto result = statement->ExecuteUpdate(); 
 -   return result->is_success_; 
 - }); 
 -  
 -  
 - // batch insert with 1000000 records, no transaction 
 - // 
 - auto statement = _database->PrepareStatement("INSERT INTO table VALUES(?, ?)"); 
 - for (auto record : records) {    // bind 1000000 records 
 -   // bind record 
 -   ... 
 -   ... 
 -   statement->ExecuteUpdate(); 
 - } 
 
  
3.2 启用WAL + 读写(连接)分离 
启用WAL之后,数据库大部分写操作变成了串行写(对WAL文件的串行操作),对写入性能提升有非常大的帮助;同时读写操作可以互相完全不阻塞(如#2.3所述)。上述两点比较好的解释了启用WAL带来的提升;同时推荐一个写连接  + 多个读连接的模型,如下图所示: 
 
3.2.1 读写连接分离的细节 
    - 读操作使用不同的连接并发执行,可以完全避免由于显式事务、写操作之间的锁竞争带来的死锁
 
    - 所有的写操作、显式事务操作都使用同一个连接,且所有的写操作、显式事务操作都串行执行
 
 
可以完全避免由于显式事务、写操作之间的锁竞争带来的死锁,如#2.2.1提到的死锁的例子 
并发写并不能有效的提高写入效率,参考如下伪代码,哪段执行更快? 
- // two transactions:  
 - void Transaction_1() { 
 -         connection_->Exec("BEGIN"); 
 -       connection_->Exec("insert into table(value) values('xxxx')"); 
 -       connection_->Exec("COMMIT"); 
 - } 
 -  
 - void Transaction_2() { 
 -         connection_->Exec("BEGIN"); 
 -       connection_->Exec("insert into table(value) values('xxxx')"); 
 -       connection_->Exec("COMMIT"); 
 - } 
 -  
 - // code fragment 1: concurrent transaction 
 - thread1.RunBlock([]() -> void { 
 -       for (int i=0; i< 100000; i++) { 
 -             Transaction_1(); 
 -     } 
 - }); 
 -  
 - thread2.RunBlock([]() -> void { 
 -       for (int i=0; i< 100000; i++) { 
 -             Transaction_2(); 
 -     } 
 - }); 
 -  
 - thread1.Join(); thread2.join(); 
 -  
 - // code fragment 2: serial transaction 
 - for (int i=0; i< 100000; i++) { 
 -   Transaction_1(); 
 - } 
 - for (int i=0; i< 100000; i++) { 
 -   Transaction_2(); 
 - } 
 
  
3.3 针对具体业务场景,设置合适的WAL SIZE 
如#2.3提到,过大的WAL文件,会让查找操作从B-Tree查找退化成线性查找(WAL中page连续存储);但大的WAL文件对写操作较友好。对于大记录的写入操作,较大的wal  size会有效提高写入效率,同时不会影响查询效率 
3.4 针对业务场景分库分表 
分库分表可以有效提高数据操作的并发度;但同时过多的表会影响数据库文件的加载速度。现在数据库方向的很多研究包括Auto sharding, paxos  consensus, 存储和计算的分离等;Auto 
application-awared optimization,Auto hardware-awared optimization,machine 
learning based optimization也是不错的方向。 
3.5 其他 
包括WAL checkpoint策略、WAL size优化、page size优化等,均需要根据具体的业务场景设置。 
4,常见问题 & 误区 
4.1 线程安全设置及误区 
sqlites configuration options:  https://sqlite.org/c3ref/c_config_getmalloc.html 
按照sqlite文档,sqlite线程安全模式有以下三种: 
SQLITE_CONFIG_SINGLETHREAD(单线程模式) 
This option sets the threading mode to Single-thread. In other words, it  disables all mutexing and puts SQLite into a mode where it can only be used by a  single thread. 
SQLITE_CONFIG_MULTITHREAD(多线程模式)                         (编辑:91站长网) 
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! 
                     |