热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SQLite做为本地缓存应注意的几大方面

今天我们要介绍的就是如何利用SQLite作为本地缓存的方法。其拥有适应于本地数据缓存和应用程序等诸多优点。AD:【线下活动】三大新锐HTML5企业汇聚51CTO—大话移动前端技术今天看到了园友陆敏计

今天我们要介绍的就是如何利用SQLite作为本地缓存的方法。其拥有适应于本地数据缓存和应用程序等诸多优点。

AD:【线下活动】三大新锐HTML 5企业汇聚51CTO—大话移动前端技术

今天看到了园友陆敏计的一篇文章<>, 写到了SQLite的诸多优点,尤其适应于本地数据缓存和应用程序。

转自陆兄的内容,来夸夸Sqlite:

SQLite官方网站: http://www.sqlite. org/ 时第一眼看到关于SQLite的特性。

1. ACID事务

2. 零配置 – 无需安装和管理配置

3. 储存在单一磁盘文件中的一个完整的数据库

4. 数据库文件可以在不同字节顺序的机器间自由的共享

5. 支持数据库大小至2TB

6. 足够小, 大致3万行C代码, 250K

7. 比一些流行的数据库在大部分普通数据库操作要快

8. 简单, 轻松的API

9. 包含TCL绑定, 同时通过Wrapper支持其他语言的绑定

10. 良好注释的源代码, 并且有着90%以上的测试覆盖率

11. 独立: 没有额外依赖

12. Source完全的Open, 你可以用于任何用途, 包括出售它

13. 支持多种开发语言,C, PHP, Perl, Java, ASP .NET,Python

正好前一段时间我做了这方面的应用,我就结合陆兄的这篇文章,谈谈我在Sqlite本地缓存业务数据时的经验,给大家借鉴一下。我开发时比较仓促,很多地方请大家多提意见。

解决的问题

首先介绍我用Sqlite解决的实际问题是什么?

问题1:某个功能的数据需要连接一个远程数据库查询速度很慢,查一次数据不容易,希望能够重复利用之前查过的数据集。

问题2:非常大的数据量比如几千万甚至几亿条数据,一次性读取到DataTable中,会内存溢出的,所以在第一次分析时就是通过Reader的方式,分析完一条后并不在内存中保存,但是紧接着用户的第二次分析、第三次分析还是要用到的第一次分析的数据,如果我们重新查询一次远程服务器,效率可想而知啊。

结合上面的2个问题,为了解决效率问题和数据重复利用度,减少数据库服务器的压力,我才用Sqlite缓存数据(当然这不是唯一也不是最好的解决方案) 。

优化SQLiteHelper

陆兄的SQLiteHelper类我增加了几个有用的方法:

第一个方法是GetSchema,得到某个表的表结构。

    
  1. /// <summary>     
  2. /// 查询数据库中的所有数据类型信息     
  3. /// summary>     
  4. /// <returns>returns>     
  5. public DataTable GetSchema()  
  6. {  
  7.     using (SQLiteConnection connection = new SQLiteConnection(connectionString))  
  8.     {  
  9.         connection.Open();  
  10.         DataTable data = connection.GetSchema("TABLES");  
  11.         connection.Close();  
  12.         //foreach (DataColumn column in data.Columns)     
  13.         //{     
  14.         //    Console.WriteLine(column.ColumnName);     
  15.         //}     
  16.         return data;  
  17.     }  

第二个方法是IsTableExist,判断SQLite数据库重某个表是否存在 。

    
  1. /// <summary>     
  2. /// 判断SQLite数据库表是否存在    
  3. /// summary>     
  4. /// <param name="dbPath">要创建的SQLite数据库文件路径param>     
  5. public bool IsTableExist(string tableName)  
  6. {  
  7.     using (SQLiteConnection connection = new SQLiteConnection(connectionString))  
  8.     {  
  9.         connection.Open();  
  10.         using (SQLiteCommand command = new SQLiteCommand(connection))  
  11.         {  
  12. command.CommandText = "SELECT COUNT(*) FROM sqlite_master where type='table' and name='" + tableName + "'";  
  13.             int iaaa = Convert.ToInt32(command.ExecuteScalar());  
  14.             if (Convert.ToInt32(command.ExecuteScalar()) == 0)  
  15.             {  
  16.                 return false;  
  17.             }  
  18.             else  
  19.             {  
  20.                 return true;  
  21.             }  
  22.         }  
  23.     }  

第三个方法是Query,执行查询语句,返回DataSet

    
  1. /// <summary> 
  2. /// 执行查询语句,返回DataSet  
  3. /// summary> 
  4. /// <param name="SQLString">查询语句param> 
  5. /// <returns>DataSetreturns> 
  6. public DataSet Query(string SQLString)  
  7. {  
  8.     using (SQLiteConnection connection = new SQLiteConnection(connectionString))  
  9.     {  
  10.         DataSet ds = new DataSet();  
  11.         try  
  12.         {  
  13.             connection.Open();  
  14.             SQLiteDataAdapter command = new SQLiteDataAdapter(SQLString, connection);  
  15.             command.Fill(ds, "ds");  
  16.         }  
  17.         catch (System.Data.SQLite.SQLiteException ex)  
  18.         {  
  19.             throw new Exception(ex.Message);  
  20.         }  
  21.         return ds;  
  22.     }  

构建缓存对象模型和缓存控制器

每一块缓存对象,在数据库中会产生一个表,而表名称是有缓存控制器自动生成的,访问缓存的工作全部交由缓存控制器完成,通过缓存项的ID和ModuleKey来访问。

在Sqlite中还需要一个系统表来维护每个缓存项和实际缓存存储表之间的对应关系,我们称之为配置表,它将在缓存控制器创建Sqlite缓存数据库文件时创建。

配置表共有以下几个字段,分别和缓存对象模型CdlCacheItem类映射:

列名称 说明
Id 缓存的唯一数字编号
ModuleKey 缓存模块名称,一个模块可以有多个缓存数据,ID可以区分。实际应用时,某个功能时会经常缓存数据的,所以通过ModuleKey就可以得到这个功能所有的缓存列表,然后选定其中的部分缓存来进行使用。
Comments 缓存说明
TableName 缓存数据存储的数据表名称
AddDate 缓存时间戳

创建数据库的方法如下

    
  1. static void CreateDB()  
  2. {  
  3.     //总共有ID、ModuleKey、Comments、AddDate这几列  
  4.     string sql = "CREATE TABLE SYSCDLTABLES(ID 
  5. INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,MODULEKEY VARCHAR(200),
  6. COMMENTS VARCHAR(500),TABLENAME VARCHAR(100),ADDDATE DATETIME)";  
  7.     SQLiteDBHelper.CreateDB(CACHEFILEPATH, sql);  

每个缓存项(缓存对象模型)定义如下,和配置表对应:

    
  1. /// <summary> 
  2. /// 缓存项对象  
  3. /// summary> 
  4. /// <Author>Tecky LeeAuthor> 
  5. /// <Date>2011-1-11 15:11Date> 
  6. public class CdlCacheItem  
  7. {  
  8.     int m_id;  
  9.  
  10.     public int Id  
  11.     {  
  12.         get { return m_id; }  
  13.         set { m_id = value; }  
  14.     }  
  15.     string m_moduleKey;  
  16.  
  17.     public string ModuleKey  
  18.     {  
  19.         get { return m_moduleKey; }  
  20.         set { m_moduleKey = value; }  
  21.     }  
  22.     string m_comments;  
  23.  
  24.     public string Comments  
  25.     {  
  26.         get { return m_comments; }  
  27.         set { m_comments = value; }  
  28.     }  
  29.     string m_tableName;  
  30.  
  31.     public string TableName  
  32.     {  
  33.         get { return m_tableName; }  
  34.         set { m_tableName = value; }  
  35.     }  
  36.     DateTime m_timestamp;  
  37.  
  38.     public DateTime Timestamp  
  39.     {  
  40.         get { return m_timestamp; }  
  41.         set { m_timestamp = value; }  
  42.     }  

下面是控制器的接口定义:

    
  1. public interface ICdlCacheController  
  2.     {  
  3.         void BeginLoadRow();  
  4.         void EndLoadRow();  
  5.         System.Collections.Generic.IList GetCdlCacheItems(string moduleKey);  
  6.         CdlCacheItem GetCdlCacheItems(int id);  
  7.         void LoadRow(System.Data.DataRow row, string tableName);  
  8.         void LoadRow(IEnumerable<object> row, string tableName);  
  9.         string LoadTable(System.Data.DataTable dt, string moduleKey, string comments);  
  10.         System.Data.Common.DbDataReader QueryCdlTableReader(CdlCacheItem item);  
  11.         System.Data.DataTable QueryCdlTables(CdlCacheItem item);  
  12.         System.Data.DataTable QueryCdlTables(string sql);  
  13.         void RemoveAllTables();  
  14.         void RemoveCdlTables(string moduleKey);  
  15.         void RemoveCdlTables(System.Collections.Generic.IList items);  
  16.         void RemoveCdlTables(CdlCacheItem item);  
  17.         void RemoveCdlTables(int id);  
  18.     } 

上面的函数下面来做个说明:

1、BeginLoadRow、LoadRow和EndLoadRow,三个函数组为了在我们查询主数据库时使用Reader方式读取数据时,可以一条条将数据同时存放在缓存中。

2、RemoveAllTables和RemoveCdlTables是用来删除缓存项的。

3、GetCdlCacheItems,通过moduleKey得到多个缓存项。比如用户想基于这几天内保存的某个功能的数据做一次快速分析,那么我们就可以通过这个函数得到缓存列表,由用户选择列表中的一个来继续。

4、QueryCdlTableReader,得到某个缓存数据的Reader对象,这样可以一行行的分析,一次读出大数据量的数据到DataTable中,内存可能会溢出的。

5、QueryCdlTables,将某个缓存项查询并装载到DataTable中。

提高缓存数据写入效率

Sqlite在保存数据的时候,比如一次保存一个亿条的数据,一条条插入效率非常低下,网上也有人对其进行讨论。

效率低下的主要原因在于IO操作次数过于频繁,所以在LoadTable或者是使用BeginLoadRow·EndLoadRow的时候,使用了事务来减少数据提交的次数,结果保存的效率非常的高,我测试的结果是400万条数据查询,只需要几十秒钟,这点时间相对于重新查一次远程服务器那是可以忽略了。

下面给出BeginLoadRow和EndLoadRow的具体代码(只有在EndRow的时候才会提交一次数据):

    
  1. SQLiteConnection m_connection;  
  2. SQLiteCommand m_command;  
  3. DbTransaction m_transaction;  
  4. public void BeginLoadRow()  
  5. {  
  6.     m_connection = new SQLiteConnection("Data Source=" + CACHEFILEPATH);  
  7.  
  8.     m_connection.Open();  
  9.     m_transaction = m_connection.BeginTransaction();  
  10.     m_command = new SQLiteCommand(m_connection);  
  11. }  
  12. public void EndLoadRow()  
  13. {  
  14.     try  
  15.     {  
  16.         if (m_command != null)  
  17.             m_command.Dispose();  
  18.  
  19.         if (m_transaction != null)  
  20.         {  
  21.             m_transaction.Commit();  
  22.         }  
  23.  
  24.         if (m_connection != null)  
  25.         {  
  26.             m_connection.Close();  
  27.             m_connection.Dispose();  
  28.         }  
  29.     }  
  30.     catch (System.Exception ex)  
  31.     {  
  32.         LogHandle.Error(ex);  
  33.     }  

LoadTable函数内部也是调用BeginLoadRow·EndLoadRow模式来完成的。

数据库文件如何创建:

Sqlite数据库文件如果不存在,在执行sql语句的时候,会自动根据ConnetionString中指定的位置创建数据库文件,默认创建的空数据库只有4K。

其他有待讨论的问题:

1、我是将所有的缓存做到一个数据库文件中了,实际应用根据业务的不同,可以一份缓存数据一个文件也是很好管理的,维护也方便,资源管理器中就可以拷贝删除等。

2、当我们存储一亿条数据到Sqlite的时候,因为Sqlite没有压缩数据,结果数据库文件就可以会有好几个G(这也不一定,适合数据库字段的多少,字段类型有关的)。

文件太大就消耗了磁盘空间,而且用户或者程序如果不及时清理的,可能会耗尽磁盘空间。

这里就必须建立一个机制,检查sqlite的缓存并及时清理,或者设置缓存应用的上限,当达到上限后自动根据时间戳清理历史缓存。

原文链接:http://www.cnblogs.com/TeckyLi/archive/2011/02/17/1957317.html


推荐阅读
  • SQLite3是一个广泛使用的数据库,从linux,windows到安卓都有SQLite的应用。本文介绍SQLite3在windows上的编译。SQLite3提供了多种源代码的下载 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 有没有人用过sqlite?关于tablehasnocolumnnamedcolumn插入数据的时候报上边的错。问题是我明明有这一列。直接在sqlitedevoloper里执 ... [详细]
  • Easyui + asp.net mvc + sqlite 开发教程(录屏)适合入门
    第一节:前言(技术简介)EasyUI是一套js的前端框架利用它可以快速的开发出好看的前端系统web它是在jquery的框架基础上面现在越来越多的企业用它来开发web系统 ... [详细]
  • 州的先生(https:zmister.com)在很多项目中都有使用到SQLite数据库作为数据存储的工具,其中包括一些桌面图形界面程序和线上的Web应用程序。至今为止,它们都运行良 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • 我正在一个涉及SQLite的项目中,我只有一个数据库文件,现在我正在测试我的应 ... [详细]
  • 在Android Studio中查看SQLite数据库
    原来查看数据库内容,我们一般都是将数据库文件从手机导出,再用专门的软件打开查看,比较繁琐。最近发现了一个比较方便的方法:使用工具stetho。使用方式在gradle中配置depen ... [详细]
  • Python使用SQLite1.sqlite3的安装python2.5.x以上版本默认自带sqlite3模块。2.链接sqlite3数据库```#导入sqlite3模块import ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有