首页 > 代码库 > net.sz.framework 框架 轻松搭建数据服务中心----读写分离数据一致性,滑动缓存

net.sz.framework 框架 轻松搭建数据服务中心----读写分离数据一致性,滑动缓存

前言

  前文讲述了net.sz.framework 框架的基础实现功能,本文主讲 net.sz.framework.db 和 net.sz.framework.szthread;

net.sz.framework.db 是 net.sz.framework 底层框架下的orm框架,仿照翻译了hibernate实现功能,虽然不足hibernate强大;但在于其功能实现单一高效和高可控性;

net.sz.framework.szthread 是 net.sz.framework 底层框架下的线程控制中心和线程池概念;

以上就不在赘述,前面的文章已经将结果了;

 

叙述

  无论你是做何种软件开发,都离不开数据;

数据一般我们都会有两个问题一直在脑后徘徊,那就是读和写的问题;

一般正常情况下数据我们可能出现的存储源是数据库(mysql,sqlserver,sqlite,Nosql等)、文件数据库(excel,xml,cvs等)

无论是合作数据格式都只是在意数据的存储;保证数据不丢失等情况;

那么我们为了数据的读取和写入高效会想尽办法去处理数据,已达到我们需求范围类的数据最高效最稳当的方式;

今天我们准备的是 orm框架下面的 SqliteDaoImpl 对 sqlite数据源 进行测试和代码设计;换其他数据源也是大同小异;

准备工作

新建项目 maven java项目 net.sz.dbserver 

我们在项目下面创建model、cache、db、main这几个包;

技术分享

然后在 model 包 下面创建 ModelTest 类

技术分享
 1 package net.sz.dbserver.model; 2  3 import javax.persistence.Id; 4 import net.sz.framework.szlog.SzLogger; 5  6 /** 7  * 8  * <br> 9  * author 失足程序员<br>10  * blog http://www.cnblogs.com/ty408/<br>11  * mail 492794628@qq.com<br>12  * phone 13882122019<br>13  */14 public class ModelTest {15 16     private static SzLogger log = SzLogger.getLogger();17 18     /*主键ID*/19     @Id20     private long Id;21     /*内容*/22     private String name;23 24 25     public ModelTest() {26     }27 28     public long getId() {29         return Id;30     }31 32     public void setId(long Id) {33         this.Id = Id;34     }35 36     public String getName() {37         return name;38     }39 40     public void setName(String name) {41         this.name = name;42     }43 44 }
View Code

 

然后在db包下面建立dbmanager类;

 1 package net.sz.dbserver.db; 2  3 import java.sql.Connection; 4 import java.util.ArrayList; 5 import net.sz.dbserver.model.ModelTest; 6 import net.sz.framework.db.Dao; 7 import net.sz.framework.db.SqliteDaoImpl; 8 import net.sz.framework.szlog.SzLogger; 9 import net.sz.framework.utils.PackageUtil;10 11 /**12  *13  * <br>14  * author 失足程序员<br>15  * blog http://www.cnblogs.com/ty408/<br>16  * mail 492794628@qq.com<br>17  * phone 13882122019<br>18  */19 public class DBManager {20 21     private static SzLogger log = SzLogger.getLogger();22     private static final DBManager IN_ME = new DBManager();23 24     public static DBManager getInstance() {25         return IN_ME;26     }27 28     Dao dao = null;29 30     public DBManager() {31         try {32             /*不使用连接池,显示执行sql语句的数据库操作*/33             this.dao = new SqliteDaoImpl("/home/sqlitedata/testdb.dat", true);34         } catch (Exception e) {35             log.error("创建数据库连接", e);36         }37     }38 39     /**40      * 检查并创建数据表结构41      */42     public void checkTables() {43         /*创建连接,并自动释放*/44         try (Connection con = this.dao.getConnection()) {45             String packageName = "net.sz.dbserver.model";46             /*获取包下面所有类*/47             ArrayList<Class<?>> tables = PackageUtil.getClazzs(packageName);48             if (tables != null) {49                 for (Class<?> table : tables) {50                     /*检查是否是需要创建的表*/51                     if (this.dao.checkClazz(table)) {52                         /*创建表结构*/53                         this.dao.createTable(con, table);54                     }55                 }56             }57         } catch (Exception e) {58             log.error("创建表抛异常", e);59         }60     }61 62 }

 

我们在dbmanager类里面通过SqliteDaoImpl 类创建了sqlite数据库支持的类似于hibernate的辅助;

在checktables下面会查找我们项目包下面所有类型,并且创建数据表;如果表存在就更新表结构(sqlite特性,不会更新表结构);

我们在checktables函数下面做到了对连接的复用情况;创建后并自动释放代码

接下来main包里面创建主函数启动类

技术分享
 1 package net.sz.dbserver.main; 2  3 import net.sz.dbserver.db.DBManager; 4 import net.sz.framework.szlog.SzLogger; 5  6 /** 7  * 8  * <br> 9  * author 失足程序员<br>10  * blog http://www.cnblogs.com/ty408/<br>11  * mail 492794628@qq.com<br>12  * phone 13882122019<br>13  */14 public class MainManager {15 16     private static SzLogger log = SzLogger.getLogger();17 18     public static void main(String[] args) {19         log.error("创建数据库,创建数据表结构");20         DBManager.getInstance().checkTables();21     }22 23 }
View Code

 

以上代码我们完成了数据库文件和数据表的创建

 1 --- exec-maven-plugin:1.2.1:exec (default-cli) @ net.sz.dbserver --- 2 设置系统字符集sun.stdout.encoding:utf-8 3 设置系统字符集sun.stderr.encoding:utf-8 4 日志级别:DEBUG 5 输出文件日志目录:../log/sz.log 6 是否输出控制台日志:true 7 是否输出文件日志:true 8 是否使用双缓冲输出文件日志:true 9 [04-07 10:56:38:198:ERROR:MainManager.main():19] 创建数据库,创建数据表结构10 [04-07 10:56:38:521:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.model.ModelTest 字段:log is transient or static or final;11 [04-07 10:56:38:538:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 无此表 12 [04-07 10:56:38:561:ERROR:SqliteDaoImpl.createTable():200] 13 表:14  create table if not exists `ModelTest` (15      `Id` bigint not null primary key,16      `name` varchar(255) null17 ); 18 创建完成;

 

这里的步骤在之前文章《存在即合理,重复轮子orm java版本》里面有详细介绍,不过当前版本和当时文章版本又有更多优化和改进;

准备测试数据

 1        /*创建支持id*/ 2         GlobalUtil.setServerID(1); 3         for (int i = 0; i < 10; i++) { 4             ModelTest modelTest = new ModelTest(); 5             /*获取全局唯一id*/ 6             modelTest.setId(GlobalUtil.getId()); 7             /*设置参数*/ 8             modelTest.setName("123"); 9 10             try {11                 DBManager.getInstance().getDao().insert(modelTest);12             } catch (Exception e) {13                 log.error("写入数据失败", e);14             }15         }    

 

 输出

技术分享
 1 --- exec-maven-plugin:1.2.1:exec (default-cli) @ net.sz.dbserver --- 2 设置系统字符集sun.stdout.encoding:utf-8 3 设置系统字符集sun.stderr.encoding:utf-8 4 日志级别:DEBUG 5 输出文件日志目录:../log/sz.log 6 是否输出控制台日志:true 7 是否输出文件日志:true 8 是否使用双缓冲输出文件日志:true 9 [04-07 11:13:17:904:ERROR:MainManager.main():21] 创建数据库,创建数据表结构10 [04-07 11:13:18:203:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.model.ModelTest 字段:log is transient or static or final;11 [04-07 11:13:18:215:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 12 [04-07 11:13:18:216:ERROR:SqliteDaoImpl.existsColumn():130] 数据库:null 表:ModelTest 映射数据库字段:ModelTest 检查结果:已存在,将不会修改13 [04-07 11:13:18:216:ERROR:SqliteDaoImpl.createTable():168] 表:ModelTest 字段:Id 映射数据库字段:Id 存在,将不会修改,14 [04-07 11:13:18:216:ERROR:SqliteDaoImpl.existsColumn():130] 数据库:null 表:ModelTest 映射数据库字段:ModelTest 检查结果:已存在,将不会修改15 [04-07 11:13:18:216:ERROR:SqliteDaoImpl.createTable():168] 表:ModelTest 字段:name 映射数据库字段:name 存在,将不会修改,16 [04-07 11:13:18:245:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 17 [04-07 11:13:18:245:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest18 [04-07 11:13:18:246:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@4bf558aa 添加数据 表:ModelTest 结果 影响行数:119 [04-07 11:13:18:272:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 20 [04-07 11:13:18:272:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest21 [04-07 11:13:18:273:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@2d38eb89 添加数据 表:ModelTest 结果 影响行数:122 [04-07 11:13:18:295:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 23 [04-07 11:13:18:296:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest24 [04-07 11:13:18:297:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@5fa7e7ff 添加数据 表:ModelTest 结果 影响行数:125 [04-07 11:13:18:319:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 26 [04-07 11:13:18:319:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest27 [04-07 11:13:18:320:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@4629104a 添加数据 表:ModelTest 结果 影响行数:128 [04-07 11:13:18:343:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 29 [04-07 11:13:18:343:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest30 [04-07 11:13:18:344:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@27f8302d 添加数据 表:ModelTest 结果 影响行数:131 [04-07 11:13:18:368:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 32 [04-07 11:13:18:368:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest33 [04-07 11:13:18:369:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@4d76f3f8 添加数据 表:ModelTest 结果 影响行数:134 [04-07 11:13:18:391:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 35 [04-07 11:13:18:391:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest36 [04-07 11:13:18:392:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@2d8e6db6 添加数据 表:ModelTest 结果 影响行数:137 [04-07 11:13:18:415:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 38 [04-07 11:13:18:415:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest39 [04-07 11:13:18:416:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@23ab930d 添加数据 表:ModelTest 结果 影响行数:140 [04-07 11:13:18:438:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 41 [04-07 11:13:18:439:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest42 [04-07 11:13:18:440:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@4534b60d 添加数据 表:ModelTest 结果 影响行数:143 [04-07 11:13:18:461:ERROR:SqliteDaoImpl.existsTable():110] 表:ModelTest 检查结果: 已存在 44 [04-07 11:13:18:462:ERROR:Dao.insert():1023] 执行 insert into `ModelTest` (`Id`, `name`) values (?, ? ) 添加数据 表:ModelTest45 [04-07 11:13:18:463:ERROR:Dao.insert():1067] 执行 org.sqlite.jdbc4.JDBC4PreparedStatement@3fa77460 添加数据 表:ModelTest 结果 影响行数:1
View Code

 

 重构modeltest类

首先在cache包下面创建CacheBase类实现缓存的基本参数

 1 package net.sz.dbserver.cache; 2  3 import javax.persistence.Id; 4 import net.sz.framework.util.AtomInteger; 5  6 /** 7  * 8  * <br> 9  * author 失足程序员<br>10  * blog http://www.cnblogs.com/ty408/<br>11  * mail 492794628@qq.com<br>12  * phone 13882122019<br>13  */14 public class CacheBase {15 16     /*主键ID*/17     @Id18     protected long Id;19 20     /*编辑状态 是 transient 字段,不会更新到数据库的*/21     private volatile transient boolean edit;22     /*版本号 是 transient 字段,不会更新到数据库的*/23     private volatile transient AtomInteger versionId;24     /*创建时间*/25     private volatile transient long createTime;26     /*最后获取缓存时间*/27     private volatile transient long lastGetCacheTime;28 29     public CacheBase() {30     }31 32     /**33      * 创建34      */35     public void createCache() {36         edit = false;37         versionId = new AtomInteger(1);38         createTime = System.currentTimeMillis();39         lastGetCacheTime = System.currentTimeMillis();40     }41 42     public long getId() {43         return Id;44     }45 46     public void setId(long Id) {47         this.Id = Id;48     }49 50     public boolean isEdit() {51         return edit;52     }53 54     public void setEdit(boolean edit) {55         this.edit = edit;56     }57 58     public AtomInteger getVersionId() {59         return versionId;60     }61 62     public void setVersionId(AtomInteger versionId) {63         this.versionId = versionId;64     }65 66     public long getCreateTime() {67         return createTime;68     }69 70     public void setCreateTime(long createTime) {71         this.createTime = createTime;72     }73 74     public long getLastGetCacheTime() {75         return lastGetCacheTime;76     }77 78     public void setLastGetCacheTime(long lastGetCacheTime) {79         this.lastGetCacheTime = lastGetCacheTime;80     }81 82     /**83      * 拷贝数据84      *85      * @param cacheBase86      */87     public void copy(CacheBase cacheBase) {88         this.Id = cacheBase.Id;89     }90 91 }

 

在cachebase类中,我创建了copy函数用来赋值新数据的;

通过这个类型,我们可以做到定时缓存,滑动缓存效果;

增加版号的作用在于,更新操作标识,是否是编辑状态也是用作更新标识;

于此同时我们把原 ModelTest 唯一键、主键 id 移动到了 cachebase 父类中

修改modeltest类继承cachebase;

1 public class ModelTest extends CacheBase

 改造一下dbmanager

 1     Dao readDao = null; 2     Dao writeDao = null; 3  4     public Dao getReadDao() { 5         return readDao; 6     } 7  8     public Dao getWriteDao() { 9         return writeDao;10     }11 12     public DBManager() {13         try {14             /*不使用连接池,显示执行sql语句的数据库操作*/15             this.readDao = new SqliteDaoImpl("/home/sqlitedata/testdb.dat", true);16             /*不使用连接池,显示执行sql语句的数据库操作*/17             this.writeDao = new SqliteDaoImpl("/home/sqlitedata/testdb.dat", true);18         } catch (Exception e) {19             log.error("创建数据库连接", e);20         }21     }

 

加入读取数据库连接、写入数据库连接;

CacheManager

在cache包下面建立cachemanager类;

cachemanager 类型是我们具体和重点思路;

构建了读取,并加入缓存集合;

构建了更新并写入数据库;

同时读取和更新都保证线程安全性特点;

  1 package net.sz.dbserver.cache;  2   3 import java.util.concurrent.ConcurrentHashMap;  4 import net.sz.dbserver.db.DBManager;  5 import net.sz.framework.szlog.SzLogger;  6   7 /**  8  *  9  * <br> 10  * author 失足程序员<br> 11  * blog http://www.cnblogs.com/ty408/<br> 12  * mail 492794628@qq.com<br> 13  * phone 13882122019<br> 14  */ 15 public class CacheManager { 16  17     private static SzLogger log = SzLogger.getLogger(); 18     private static final CacheManager IN_ME = new CacheManager(); 19  20     public static CacheManager getInstance() { 21         return IN_ME; 22     } 23     /*缓存集合*/ 24     final ConcurrentHashMap<Long, CacheBase> cacheMap = new ConcurrentHashMap<>(); 25  26     /** 27      * 获取一条数据,这里我只是测试,提供思路, 28      * <br> 29      * 所以不会去考虑list等情况; 30      * <br> 31      * 需要的话可以自行修改 32      * 33      * @param <T> 34      * @param clazz 35      * @param id 36      * @return 37      */ 38     public <T extends CacheBase> T getCacheBase(Class<T> clazz, long id) { 39         CacheBase cacheBase = null; 40         cacheBase = cacheMap.get(id); 41         if (cacheBase == null) { 42             try { 43                 /*先读取数据库*/ 44                 cacheBase = DBManager.getInstance().getReadDao().getObjectByWhere(clazz, "where id=@id", id); 45                 /*加入同步操作*/ 46                 synchronized (cacheMap) { 47                     /*这个时候再次读取缓存,防止并发*/ 48                     CacheBase tmp = cacheMap.get(id); 49                     /*双重判断*/ 50                     if (tmp == null) { 51                         /*创建缓存标识*/ 52                         cacheBase.createCache(); 53                         /*加入缓存信息*/ 54                         cacheMap.put(id, cacheBase); 55                     } else { 56                         cacheBase = tmp; 57                     } 58                 } 59             } catch (Exception e) { 60                 log.error("读取数据异常", e); 61             } 62         } 63  64         if (cacheBase != null) { 65             /*更新最后获取缓存的时间*/ 66             cacheBase.setLastGetCacheTime(System.currentTimeMillis()); 67         } 68  69         return (T) cacheBase; 70     } 71  72     /** 73      * 更新缓存数据同时更新数据库数据 74      * 75      * @param <T> 76      * @param t 77      * @return 78      */ 79     public <T extends CacheBase> boolean updateCacheBase(T t) { 80         if (t == null) { 81             throw new UnsupportedOperationException("参数 T 为 null"); 82         } 83         try { 84             CacheBase cacheBase = null; 85             cacheBase = cacheMap.get(t.getId()); 86             /*理论上,控制得当这里是不可能为空的*/ 87             if (cacheBase != null) { 88                 /*理论上是能绝对同步的,你也可以稍加修改*/ 89                 synchronized (cacheBase) { 90                     /*验证编辑状态和版号,保证写入数据是绝对正确的*/ 91                     if (cacheBase.isEdit() 92                             && cacheBase.getVersionId() == t.getVersionId()) { 93                         /*拷贝最新数据操作*/ 94                         cacheBase.copy(t); 95                         /*写入数据库,用不写入还是同步写入,看自己需求而一定*/ 96                         DBManager.getInstance().getWriteDao().update(cacheBase); 97                         /*保证写入数据库后进行修改 对版本号进行加一操作*/ 98                         cacheBase.getVersionId().changeZero(1); 99                         /*设置最新的最后访问时间*/100                         cacheBase.setLastGetCacheTime(System.currentTimeMillis());101                         /*修改编辑状态*/102                         cacheBase.setEdit(false);103                         log.error("数据已修改,最新版号:" + cacheBase.getVersionId());104                         return true;105                     } else {106                         log.error("版本已经修改无法进行更新操作");107                         throw new UnsupportedOperationException("版本已经修改无法进行更新操作");108                     }109                 }110             } else {111                 log.error("缓存不存在无法修改数据");112                 throw new UnsupportedOperationException("缓存不存在无法修改数据");113             }114         } catch (Exception e) {115             throw new UnsupportedOperationException("更新数据异常", e);116         }117     }118 119     /**120      * 获取独占编辑状态121      *122      * @param id123      * @return124      */125     public boolean updateEdit(long id) {126         CacheBase t = null;127         t = cacheMap.get(id);128         if (t == null) {129             throw new UnsupportedOperationException("未找到数据源");130         }131         return updateEdit(t);132     }133 134     /**135      * 获取独占编辑状态136      *137      * @param t138      * @return139      */140     public boolean updateEdit(CacheBase t) {141         if (t == null) {142             throw new UnsupportedOperationException("参数 T 为 null");143         }144         if (!t.isEdit()) {145             synchronized (t) {146                 if (!t.isEdit()) {147                     /*同步后依然需要双重判定*/148                     t.setEdit(true);149                     return true;150                 }151             }152         }153         return false;154     }155 156 }

 

可能有人要问, 为啥要加锁,加版号或者加编辑状态;

我们先看一张图片

技术分享

 当同一份数据,展示给客户端(web,多线程等)的时候,同时进行获取,进行编辑,我们不可能每次都需要去调用独占编辑;

那么问题来了我们就拿modeltest的name字段说明,当前等于123,当client1和client2都表示数据的名字错误了需要修改成789;

那么在写入数据库的时候总会有先后顺序,那么后面的很可能就覆盖了前面的修改,

我们假如client1先提交,把name字段改为456,这时候client2提交了,789就直接覆盖了456字段,

程序根本不知道字段的覆盖了,也不知道哪一个是正确的;

所以我加入了编辑状态和版号验证;当然你也可以根据你的需求来进行修改

 1 package net.sz.dbserver.main; 2  3 import net.sz.dbserver.cache.CacheManager; 4 import net.sz.dbserver.db.DBManager; 5 import net.sz.dbserver.model.ModelTest; 6 import net.sz.framework.szlog.SzLogger; 7 import net.sz.framework.utils.GlobalUtil; 8  9 /**10  *11  * <br>12  * author 失足程序员<br>13  * blog http://www.cnblogs.com/ty408/<br>14  * mail 492794628@qq.com<br>15  * phone 13882122019<br>16  */17 public class MainManager {18 19     private static SzLogger log = SzLogger.getLogger();20 21     public static void main(String[] args) {22 23         log.error("创建数据库,创建数据表结构");24         DBManager.getInstance().checkTables();25         /*创建支持id*/26         GlobalUtil.setServerID(1);27         ModelTest modelTest = new ModelTest();28         /*获取全局唯一id*/29         modelTest.setId(GlobalUtil.getId());30         /*设置参数*/31         modelTest.setName("123");32 33         /*创建测试数据先修改数据库*/34         try {35             DBManager.getInstance().getReadDao().insert(modelTest);36         } catch (Exception e) {37             log.error("写入数据失败", e);38         }39 40         /*打印一次id*/41         log.error("modelTest.getId()=" + modelTest.getId());42 43         for (int i = 0; i < 3; i++) {44             new Thread(() -> {45                 try {46                     /*上面的写入数据是为了获取这个id,保证测试代码编辑功能*/47                     ModelTest cacheBase = CacheManager.getInstance().getCacheBase(ModelTest.class, modelTest.getId());48                     if (cacheBase != null) {49                         log.error("成功获得数据");50                         /*独占编辑状态你可以不需要*/51                         if (CacheManager.getInstance().updateEdit(cacheBase)) {52                             log.error("成功获得编辑状态");53                             /*为了模拟并发,我们采用id,保证唯一的数据查看到底谁写入成功*/54                             cacheBase.setName(GlobalUtil.getId() + "");55                             CacheManager.getInstance().updateCacheBase(cacheBase);56                             log.error("modelTest.getName()=" + cacheBase.getName());57                         } else {58                             log.error("获取编辑状态失败");59                         }60                     }61                 } catch (Exception e) {62                     log.error("更新数据异常", e);63                 }64             }).start();65         }66 67     }68 69 }

 

在mainmanager类main函数测试里面加入3个线程模拟并发状态

技术分享 

正常添加的测试数据

 1 [04-07 13:50:50:514:ERROR:MainManager.main():23] 创建数据库,创建数据表结构 2 [04-07 13:50:50:937:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.model.ModelTest 字段:log is transient or static or final; 3 [04-07 13:50:50:952:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.cache.CacheBase 字段:edit is transient or static or final; 4 [04-07 13:50:50:952:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.cache.CacheBase 字段:versionId is transient or static or final; 5 [04-07 13:50:50:952:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.cache.CacheBase 字段:createTime is transient or static or final; 6 [04-07 13:50:50:952:ERROR:Dao.getColumns():532] 类:net.sz.dbserver.cache.CacheBase 字段:lastGetCacheTime is transient or static or final; 7 [04-07 13:51:37:591:ERROR:MainManager.main():41] modelTest.getId()=7040713505000100000 8 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():49] 成功获得数据 9 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():49] 成功获得数据10 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():49] 成功获得数据11 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():52] 成功获得编辑状态12 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():58] 获取编辑状态失败13 [04-07 13:51:45:392:ERROR:MainManager.lambda$main$0():58] 获取编辑状态失败14 [04-07 13:51:45:428:ERROR:CacheManager.updateCacheBase():101] 数据已修改,最新版号:215 [04-07 13:51:45:428:ERROR:MainManager.lambda$main$0():56] modelTest.getName()=7040713514500100000

技术分享

修改后的数据;

保证了并发写入、修改的问题,保证了数据的一致性;

实现滑动缓存

在cache包下面建立里CheckCacheTimer定时器类

 1 package net.sz.dbserver.cache; 2  3 import java.util.HashMap; 4 import java.util.Map; 5 import net.sz.framework.szlog.SzLogger; 6 import net.sz.framework.szthread.TimerTaskModel; 7  8 /** 9  *10  * <br>11  * author 失足程序员<br>12  * blog http://www.cnblogs.com/ty408/<br>13  * mail 492794628@qq.com<br>14  * phone 13882122019<br>15  */16 public class CheckCacheTimer extends TimerTaskModel {17 18     private static SzLogger log = SzLogger.getLogger();19 20     public CheckCacheTimer(int intervalTime) {21         super(intervalTime);22     }23 24     @Override25     public void run() {26         /*考虑缓存的清理的都放在这里、当然有很多值的注意细节有待细化*/27         HashMap<Long, CacheBase> tmp = new HashMap(CacheManager.getInstance().cacheMap);28         for (Map.Entry<Long, CacheBase> entry : tmp.entrySet()) {29             Long key = entry.getKey();30             CacheBase value =http://www.mamicode.com/ entry.getValue();31             if (!value.isEdit()) {32                 /*如果数据不在编辑状态、且30分钟无访问清理*/33                 if (System.currentTimeMillis() - value.getLastGetCacheTime() > 30 * 60 * 1000) {34                     synchronized (CacheManager.getInstance().cacheMap) {35                         if (!value.isEdit()) {36                             /*如果数据不在编辑状态、且30分钟无访问清理*/37                             if (System.currentTimeMillis() - value.getLastGetCacheTime() > 30 * 60 * 1000) {38                                 CacheManager.getInstance().cacheMap.remove(value.getId());39                             }40                         }41                     }42                 }43             }44         }45     }46 }

 

在cachemanager类构造函数加入

1     public CacheManager() {2         /*创建一秒钟检查的定时器*/3         ThreadPool.addTimerTask(ThreadPool.GlobalThread, new CheckCacheTimer(1000));4     }

 

滑动缓存就构建完成了,

这里就不在测试了,理论就是这么个理论;思路就是这么个思路;

脱离数据源的单纯缓存器

改造CacheBase类

技术分享
  1 package net.sz.net.sz.framework.caches;  2   3 import net.sz.framework.util.AtomInteger;  4   5 /**  6  *  7  * <br>  8  * author 失足程序员<br>  9  * blog http://www.cnblogs.com/ty408/<br> 10  * mail 492794628@qq.com<br> 11  * phone 13882122019<br> 12  */ 13 public abstract class CacheBase { 14  15     /*编辑状态 */ 16     private volatile transient boolean edit; 17     /*版本号 */ 18     private volatile transient AtomInteger versionId; 19     /*创建时间*/ 20     private volatile transient long createTime; 21     /*最后获取缓存时间*/ 22     private volatile transient long lastGetCacheTime; 23     /*true 表示是滑动缓存*/ 24     private volatile transient boolean slide; 25     /*清理时间*/ 26     private volatile transient long clearTime; 27  28     public CacheBase() { 29     } 30  31     /** 32      * 创建 33      * @param slide 34      * @param clearTime 35      */ 36     protected CacheBase(boolean slide, long clearTime) { 37         this.slide                      = slide; 38         this.clearTime                  = clearTime; 39     } 40  41     /** 42      * 43      */ 44     protected void createCache(){ 45         this.edit                       = false; 46         this.versionId                  = new AtomInteger(1); 47         this.createTime                 = System.currentTimeMillis(); 48         this.lastGetCacheTime           = System.currentTimeMillis(); 49     } 50  51         /** 52      * 53      */ 54     protected void copyCache(CacheBase tmp){ 55         this.edit                       = tmp.edit; 56         this.versionId                  = tmp.getVersionId(); 57         this.createTime                 = tmp.getClearTime(); 58         this.lastGetCacheTime           = System.currentTimeMillis(); 59     } 60  61     public boolean isEdit() { 62         return edit; 63     } 64  65     public void setEdit(boolean edit) { 66         this.edit = edit; 67     } 68  69     public AtomInteger getVersionId() { 70         return versionId; 71     } 72  73     public void setVersionId(AtomInteger versionId) { 74         this.versionId = versionId; 75     } 76  77     public long getCreateTime() { 78         return createTime; 79     } 80  81     public void setCreateTime(long createTime) { 82         this.createTime = createTime; 83     } 84  85     public long getLastGetCacheTime() { 86         return lastGetCacheTime; 87     } 88  89     public void setLastGetCacheTime(long lastGetCacheTime) { 90         this.lastGetCacheTime = lastGetCacheTime; 91     } 92  93     public boolean isSlide() { 94         return slide; 95     } 96  97     public void setSlide(boolean slide) { 98         this.slide = slide; 99     }100 101     public long getClearTime() {102         return clearTime;103     }104 105     public void setClearTime(long clearTime) {106         this.clearTime = clearTime;107     }108 109 }
View Code

 

改造CacheManager类

技术分享
  1 package net.sz.net.sz.framework.caches;  2   3 import java.util.concurrent.ConcurrentHashMap;  4 import net.sz.framework.szlog.SzLogger;  5 import net.sz.framework.szthread.ThreadPool;  6   7 /**  8  *  9  * <br> 10  * author 失足程序员<br> 11  * blog http://www.cnblogs.com/ty408/<br> 12  * mail 492794628@qq.com<br> 13  * phone 13882122019<br> 14  */ 15 public class CacheManager { 16  17     private static SzLogger log = SzLogger.getLogger(); 18  19     /*缓存集合*/ 20     final ConcurrentHashMap<String, CacheBase> cacheMap = new ConcurrentHashMap<>(); 21  22     public CacheManager() { 23         /*创建一秒钟检查的定时器*/ 24         ThreadPool.addTimerTask(ThreadPool.GlobalThread, new CheckCacheTimer(1000, this)); 25     } 26  27     /** 28      * 默认30分钟清理的滑动缓存、如果存在缓存键、将不再添加 29      * 30      * @param key 31      * @param object 32      * @return 33      */ 34     public boolean addCache(String key, CacheBase object) { 35         return addCache(key, object, 30 * 60 * 1000); 36     } 37  38     /** 39      * 默认滑动缓存、如果存在缓存键、将不再添加 40      * 41      * @param key 42      * @param object 43      * @param clearTime 滑动缓存的清理时间 44      * @return 45      */ 46     public boolean addCache(String key, CacheBase object, long clearTime) { 47         return addCache(key, object, clearTime, true); 48     } 49  50     /** 51      * 默认滑动缓存、如果存在缓存键、将不再添加 52      * 53      * @param key 54      * @param object 55      * @param clearTime 清理缓存的间隔时间 56      * @param isSlide true表示滑动缓存, 57      * @return 58      */ 59     public boolean addCache(String key, CacheBase object, long clearTime, boolean isSlide) { 60         return addCache(key, object, clearTime, isSlide, false); 61     } 62  63     /** 64      * 65      * @param key 66      * @param object 67      * @param clearTime 清理缓存的间隔时间 68      * @param isSlide true表示滑动缓存, 69      * @param put true 表示强制添加数据集合,及已经存在键数据 70      * @return 71      */ 72     public boolean addCache(String key, CacheBase object, long clearTime, boolean isSlide, boolean put) { 73         if (put) { 74             object.createCache(); 75             cacheMap.put(key, object); 76             if (log.isDebugEnabled()) { 77                 log.debug("强制添加缓存键=" + key); 78             } 79             return true; 80         } else if (!cacheMap.containsKey(key)) { 81             /*加入同步操作*/ 82             synchronized (key) { 83                 if (!cacheMap.containsKey(key)) { 84                     object.createCache(); 85                     cacheMap.put(key, object); 86                     return true; 87                 } else { 88                     if (log.isDebugEnabled()) { 89                         log.debug("数据已经添加,不能再次添加"); 90                     } 91                 } 92             } 93         } else { 94             if (log.isDebugEnabled()) { 95                 log.debug("数据已经添加,不能再次添加"); 96             } 97         } 98         return false; 99     }100 101     public boolean removeCache(String key) {102         cacheMap.remove(key);103         return true;104     }105 106     public CacheBase getCache(String key) {107         return getCache(key, CacheBase.class);108     }109 110     /**111      * 获取一条数据,这里我只是测试,提供思路,112      * <br>113      * 所以不会去考虑list等情况;114      * <br>115      * 需要的话可以自行修改116      *117      * @param <T>118      * @param clazz119      * @param key120      * @return121      */122     public <T extends CacheBase> T getCache(String key, Class<T> clazz) {123         CacheBase cacheBase = null;124         cacheBase = cacheMap.get(key);125         if (cacheBase != null) {126             /*更新最后获取缓存的时间*/127             cacheBase.setLastGetCacheTime(System.currentTimeMillis());128         }129         return (T) cacheBase;130     }131 132     /**133      * 更新缓存数据同时更新数据库数据134      *135      * @param key136      * @param object137      * @return138      */139     public boolean updateCacheBase(String key, CacheBase object) {140         if (object == null) {141             throw new UnsupportedOperationException("参数 object 为 null");142         }143         CacheBase cacheBase = null;144         cacheBase = cacheMap.get(key);145         /*理论上,控制得当这里是不可能为空的*/146         if (cacheBase != null) {147             /*理论上是能绝对同步的,你也可以稍加修改*/148             synchronized (key) {149                 /*验证编辑状态和版号,保证写入数据是绝对正确的*/150                 if (cacheBase.getVersionId() == object.getVersionId()) {151                     /*拷贝最新数据操作*/152                     cacheMap.put(key, object);153                     /*保证写入数据库后进行修改 对版本号进行加一操作*/154                     object.getVersionId().changeZero(1);155                     /*设置最新的最后访问时间*/156                     object.setLastGetCacheTime(System.currentTimeMillis());157                     /*修改编辑状态*/158                     object.setEdit(false);159                     if (log.isDebugEnabled()) {160                         log.debug("数据已修改,最新版号:" + object.getVersionId());161                     }162                     return true;163                 } else {164                     if (log.isDebugEnabled()) {165                         log.debug("版本已经修改无法进行更新操作");166                     }167                     throw new UnsupportedOperationException("版本已经修改无法进行更新操作");168                 }169             }170         } else {171             if (log.isDebugEnabled()) {172                 log.debug("缓存不存在无法修改数据");173             }174             throw new UnsupportedOperationException("缓存不存在无法修改数据");175         }176     }177 178     /**179      * 获取独占编辑状态180      *181      * @param key182      * @return183      */184     public boolean updateEdit(String key) {185         CacheBase cacheBase = null;186         cacheBase = cacheMap.get(key);187         if (cacheBase == null) {188             throw new UnsupportedOperationException("未找到数据源");189         }190         return updateEdit(key, cacheBase);191     }192 193     /**194      * 获取独占编辑状态195      *196      * @param key197      * @param cacheBase198      * @return199      */200     public boolean updateEdit(String key, CacheBase cacheBase) {201         if (cacheBase == null) {202             throw new UnsupportedOperationException("参数 cacheBase 为 null");203         }204         if (!cacheBase.isEdit()) {205             synchronized (key) {206                 if (!cacheBase.isEdit()) {207                     /*同步后依然需要双重判定*/208                     cacheBase.setEdit(true);209                     /*设置最新的最后访问时间*/210                     cacheBase.setLastGetCacheTime(System.currentTimeMillis());211                     return true;212                 }213             }214         }215         return false;216     }217 218 }
View Code

 

改造CheckCacheTimer类

技术分享
 1 package net.sz.net.sz.framework.caches; 2  3 import java.util.HashMap; 4 import java.util.Map; 5 import net.sz.framework.szlog.SzLogger; 6 import net.sz.framework.szthread.TimerTaskModel; 7  8 /** 9  *10  * <br>11  * author 失足程序员<br>12  * blog http://www.cnblogs.com/ty408/<br>13  * mail 492794628@qq.com<br>14  * phone 13882122019<br>15  */16 public class CheckCacheTimer extends TimerTaskModel {17 18     private static SzLogger log = SzLogger.getLogger();19     private final CacheManager cacheManager;20 21     public CheckCacheTimer(int intervalTime, CacheManager cacheManager) {22         super(intervalTime);23         this.cacheManager = cacheManager;24     }25 26     @Override27     public void run() {28         /*考虑缓存的清理的都放在这里、当然有很多值的注意细节有待细化*/29         HashMap<String, CacheBase> tmp = new HashMap(this.cacheManager.cacheMap);30         for (Map.Entry<String, CacheBase> entry : tmp.entrySet()) {31             String key = entry.getKey();32             CacheBase value =http://www.mamicode.com/ entry.getValue();33             /*理论上,这里是能够保证绝对缓存,同步*/34             if (!value.isEdit()) {35                 /*滑动缓存清理*/36                 if (value.isSlide() && System.currentTimeMillis() - value.getLastGetCacheTime() < value.getClearTime()) {37                     continue;38                 }39                 /*固定缓存清理*/40                 if (!value.isSlide() && System.currentTimeMillis() - value.getCreateTime() < value.getClearTime()) {41                     continue;42                 }43                 this.cacheManager.removeCache(key);44             }45         }46     }47 }
View Code

 

脱离了数据源的缓存器;

求大神指教了;如果觉得可以点个推荐;技术分享

觉得不好请手下留情不要点击反对哦,技术分享

 

net.sz.framework 框架 轻松搭建数据服务中心----读写分离数据一致性,滑动缓存