java代码实现mysql分表操作(用户行为记录)
作者:Yang_薛
设置项目气动执行次方法(每天检查一次表记录)
public class DayInterval implements ServletContextListener{ private static SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void showDayTime() { Timer dTimer = new Timer(); dTimer.schedule(new TimerTask() { @Override public void run() { System.out.println("每日任务执行:"+simpleDateFormat.format(new Date())); LogTableCreate logTableCreate = new LogTableCreate(); Thread thread=new Thread(logTableCreate); thread.start(); } }, 1000 , 24* 60* 60 * 1000);//24* 60* 60 * 1000(第一次一秒后执行,以后每次一天后执行) } @Override public void contextDestroyed(ServletContextEvent arg0) { // showDayTime(); } @Override public void contextInitialized(ServletContextEvent arg0) { showDayTime(); } }
LogTableCreate 用来做表分表是否已经创建,如现在是9月在启动时检查是否存在当月表记录,不存在则创建存在则不创建,另外检查是否存在10月份表记录,不存在则创建(提前创建一个也空表,以此类推)。
拷贝代码修改createsql(建表sql),URL (数据库地址),USER (数据库连接用户),PASSWORD(数据库连接密码)
public class LogTableCreate extends TimerTask { private static final Log log = LogFactory.getLog(LogTableCreate.class); public static final String TBASENAME="tb_log"; private String createsql = " (\r\n" + " `ID` varchar(64) NOT NULL COMMENT '主键id',\r\n" + " `userid` varchar(255) DEFAULT NULL COMMENT '用户id',\r\n" + " `username` varchar(255) DEFAULT NULL COMMENT '用户姓名',\r\n" + " `useridcard` varchar(255) DEFAULT NULL COMMENT '用户身份证号码',\r\n" + " `realname` varchar(64) DEFAULT NULL COMMENT '真实姓名',\r\n" + " `logintime` varchar(255) DEFAULT NULL COMMENT '登录时间',\r\n" + " `exittime` varchar(64) DEFAULT NULL COMMENT '退出时间',\r\n" + " `ippath` varchar(255) DEFAULT NULL COMMENT 'ip地址',\r\n" + " `macpath` varchar(255) DEFAULT NULL COMMENT 'mac地址',\r\n" + " `usercreatedtime` varchar(255) DEFAULT NULL COMMENT '用户创建时间',\r\n" + " `userbusidaddress` varchar(255) DEFAULT NULL COMMENT '用户钱包地址',\r\n" + " `member` int(11) DEFAULT NULL COMMENT '是否是会员',\r\n" + " `membertype` int(11) DEFAULT NULL COMMENT '会员类型',\r\n" + " `spare1` varchar(255) DEFAULT NULL,\r\n" + " `spare2` varchar(255) DEFAULT NULL,\r\n" + " `spare3` varchar(255) DEFAULT NULL,\r\n" + " PRIMARY KEY (`ID`)\r\n" + ")"; private SimpleDateFormat sdyyyy = new SimpleDateFormat("yyyy"); private SimpleDateFormat sdmm = new SimpleDateFormat("MM"); private static final String URL = ""; private static final String USER = ""; private static final String PASSWORD = ""; //得到表名 public static String gettable() { Date date = new Date(); LogTableCreate logTableCreate=new LogTableCreate(); String yyyy = logTableCreate.sdyyyy.format(date); String mm = logTableCreate.sdmm.format(date); String nmm = logTableCreate.getNextMM(mm); return TBASENAME+yyyy+mm; } //得到下一个月 private String getNextMM(String mm){ String nmm = ""; int imm = Integer.parseInt(mm); if(imm>=12){ nmm = "01"; }else{ imm++; if(imm>9) nmm = ""+imm; else nmm = "0"+imm; } return nmm; } @Override public void run() { Date date = new Date(); String yyyy = sdyyyy.format(date); String mm = sdmm.format(date); String nmm = getNextMM(mm); String nyyyy = ""; if("01".equals(nmm)){ nyyyy = ""+(Integer.parseInt(yyyy)+1); }else{ nyyyy = yyyy; } log.info("日志表检查及创建:"+yyyy+" - "+mm+" | "+nyyyy+"-"+nmm); String temp = TBASENAME+yyyy+mm; //日志表名称 boolean has = false; try{ has = hasTable(temp); }catch(Exception e){ log.error("当前操作日志表是否存在判断时发生错误:"+e.getMessage()); return; } if(!has){ try{ createTable(temp); }catch(Exception e){ log.error("当前操作日志表创建时发生错误:"+e.getMessage()); return; } } temp = TBASENAME+nyyyy+nmm; has = false; try{ has = hasTable(temp); }catch(Exception e){ log.error("待用日志表是否存在判断时发生错误:"+e.getMessage()); return; } if(!has){ try{ createTable(temp); }catch(Exception e){ log.error("待用日志表创建时发生错误:"+e.getMessage()); return; } } log.info("日志表检查及创建结束"); } public boolean hasTable(String table) throws Exception{ Class.forName("com.mysql.jdbc.Driver"); //2. 获得数据库连接 Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); boolean state = false; DatabaseMetaData meta = conn.getMetaData(); ResultSet set; set = meta.getTables(null, null, table.toLowerCase(), null); while (set.next()) { state = true; break; } Statement stmt = null; try{ stmt = conn.createStatement(); }catch(Exception e){ log.error("检查日志表是否存在时发生错误:"+e.getMessage()); throw e; }finally{ if(stmt!=null) try { stmt.close(); } catch (Exception e) { //e.printStackTrace(); } } conn.close(); return state; } public void createTable(String table)throws Exception{ try{ Class.forName("com.mysql.jdbc.Driver"); //2. 获得数据库连接 Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); String sql = "create table "+table+createsql; Statement stmt = null; stmt = conn.createStatement(); stmt.execute(sql); }catch(Exception e){ log.error("初始化日志表时发生错误:"+e.getMessage()); throw e; } } }
补充:java水平分表_Java开发分库分表需要解决的问题及mycat是怎样实现分库分表的
引言
从字面上简单理解,就是把原本存储于一个库的数据分块存储到多个库上,把原本存储于一个表的数据分块存储到多个表上。
数据库中的数据量不一定是可控的,在未进行分库分表的情况下,随着时间和业务的发展,库中的表会越来越多,表中的数据量也会越来越大,相应地,数据操作,增删改查的开销也会越来越大;
另外,由于无法进行分布式式部署,而一台服务器的资源(CPU、磁盘、内存、IO等)是有限的,最终数据库所能承载的数据量、数据处理能力都将遭遇瓶颈。
分库分表的必要性
首先我们来了解一下为什么要做分库分表。在我们的业务(web应用)中,关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量、连接数、处理能力等都很有限,数据库本身的“有状态性”导致了它并不像Web和应用服务器那么容易扩展。那么在我们的业务中,是否真的有必要进行分库分表,就可以从上面几个条件来考虑。
单机储存容量。您的数据量是否在单机储存中碰到瓶颈。比如饿了么一天产生的用户行为数据就有24T,那么在传统的单机储存中肯定是不够的。
连接数、处理能力。在我们的用户量达到一定程度时,特定时间的并发量又成了一个大问题,在一个高并发的网站中秒级数十万的并发量都是很正常的。在普通的单机数据库中秒级千次的操作问题都很大。
所以在我们进行分库分表之前我们最好考虑一下,我们的数据量是不是够大,并发量是不是够大。如果您的回答是肯定的,那我们就开始做吧。
事务问题
解决事务问题目前有两种可行的方案:分布式事务和通过应用程序与数据库共同控制实现事务下面对两套方案进行一个简单的对比。
方案一:使用分布式事务
优点:交由数据库管理,简单有效
缺点:性能代价高,特别是shard越来越多时
方案二:由应用程序和数据库共同控制
原理:将一个跨多个数据库的分布式事务分拆成多个仅处 于单个数据库上面的小事务,并通过应用程序来总控 各个小事务。
优点:性能上有优势
缺点:需要应用程序在事务控制上做灵活设计。如果使用 了spring的事务管理,改动起来会面临一定的困难。
分库分表的实施策略。
分库分表有垂直切分和水平切分两种。
3.1 何谓垂直切分,即将表按照功能模块、关系密切程度划分出来,部署到不同的库上。
例如,我们会建立定义数据库workDB、商品数据库payDB、用户数据库userDB、日志数据库logDB等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等。
3.2 何谓水平切分,当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,例如userID散列,进行划分,然后存储到多个结构相同的表,和不同的库上。
例如,我们的userDB中的用户数据表中,每一个表的数据量都很大,就可以把userDB切分为结构相同的多个userDB:part0DB、part1DB等,再将userDB上的用户数据表userTable,切分为很多userTable:userTable0、userTable1等,然后将这些表按照一定的规则存储到多个userDB上。
3.3 应该使用哪一种方式来实施数据库分库分表,这要看数据库中数据量的瓶颈所在,并综合项目的业务类型进行考虑。
如果数据库是因为表太多而造成海量数据,并且项目的各项业务逻辑划分清晰、低耦合,那么规则简单明了、容易实施的垂直切分必是首选。
而如果数据库中的表并不多,但单表的数据量很大、或数据热度很高,这种情况之下就应该选择水平切分,水平切分比垂直切分要复杂一些,它将原本逻辑上属于一体的数据进行了物理分割,除了在分割时要对分割的粒度做好评估,考虑数据平均和负载平均,后期也将对项目人员及应用程序产生额外的数据管理负担。
在现实项目中,往往是这两种情况兼而有之,这就需要做出权衡,甚至既需要垂直切分,又需要水平切分。我们的游戏项目便综合使用了垂直与水平切分,我们首先对数据库进行垂直切分,然后,再针对一部分表,通常是用户数据表,进行水平切分。
mycat是怎样实现分库分表的?mycat里面通过定义路由规则来实现分片表(路由规则里面会定义分片字段,以及分片算法)。分片算法有多种,你所说的hash是其中一种,还有取模、按范围分片等等。在mycat里面,会对所有传递的sql语句做路由处理(路由处理的依据就是表是否分片,如果分片,那么需要依据分片字段和对应的分片算法来判断sql应该传递到哪一个、或者哪几个、又或者全部节点去执行)
总结
以上就是我对Java开发分库分表需要解决的问题及mycat是怎样实现分库分表的 问题及其优化总结,如有错误或未考虑完全的地方,望不吝赐教。