diff --git a/source.html b/source.html index 744f0cd7b..d288c6ed6 100644 --- a/source.html +++ b/source.html @@ -197,7 +197,7 @@ @Id @GeneratedValue @Column(comment = "UUID") - private String seqid = ""; //UUID + private String loginid = ""; //UUID @Column(updatable = false, comment = "C端用户ID") private long userid; //C端用户ID @@ -252,7 +252,135 @@ } } -

         以上范例是用户登陆记录的分表分库策略,一年一个库,一个库中365张表,每天一个表。由于分表是根据记录的创建时间作为策略分表,因此策略接口中的public String getTable(String table, Serializable primary) 方法不能实现,因为根据主键id无法确定到数据库表。

+

         以上范例是用户登陆记录的分表分库策略,一年一个库,一个库中365张表,每天一个表。由于分表是根据记录的创建时间作为策略分表,因此策略接口中的public String getTable(String table, Serializable primary) 方法无法实现,根据主键id无法判断出数据所在的数据库表。若有根据ID获取单条记录的需求,则需要变更loginid的生成规则,可以改成UUID+createtime,如下:

+
@DistributeTable(strategy = LoginRecord.TableStrategy.class)
+public class LoginRecord extends BaseEntity {
+
+    @Id
+    @Column(comment = "主键ID; 值=UUID+create36time")
+    private String loginid = ""; //主键ID; 值=UUID+create36time
+
+    @Column(updatable = false, comment = "C端用户ID")
+    private long userid; //C端用户ID
+
+    @Column(updatable = false, comment = "登录网络类型; wifi/4g/3g")
+    private String netmode = ""; //登录网络类型; wifi/4g/3g
+
+    @Column(updatable = false, comment = "APP版本信息")
+    private String appversion = ""; //APP版本信息
+
+    @Column(updatable = false, comment = "APP操作系统信息")
+    private String appos = ""; //APP操作系统信息
+
+    @Column(updatable = false, comment = "登录时客户端信息")
+    private String loginagent = ""; //登录时客户端信息
+
+    @Column(updatable = false, comment = "登录时的IP")
+    private String loginaddr = ""; //登录时的IP
+
+    @Column(updatable = false, comment = "创建时间")
+    private long createtime; //创建时间
+
+    /** 以下省略getter setter方法 */
+   
+
+    //创建对象
+    public static void main(String[] args) throws Throwable {
+        LoginRecord record = new LoginRecord();
+        long now = System.currentTimeMillis();
+        record.setCreatetime(now); //设置创建时间
+        String create36time = Long.toString(now, 36); //时间的36进制
+        if (create36time.length() < 9) create36time = "0" + create36time; //当前时间值的36进制只可能是8位或9位,不足9位填充0
+        record.setLoginid(Utility.uuid() + create36time);  //主键的生成规则
+        //....  填充其他字段
+        source.insert(record);
+    }
+
+    public static class TableStrategy implements DistributeTableStrategy<LoginRecord> {
+
+        private static final String dayformat = "%1$tY%1$tm%1$td";
+
+        private static final String yearformat = "%1$tY";
+
+        //过滤查询时调用本方法
+        @Override
+        public String getTable(String table, FilterNode node) {
+            Serializable day = node.findValue("#day");
+            if (day != null) getTable(table, (Integer) day, 0L); //存在#day参数则直接使用day值
+            Serializable time = node.findValue("#createtime");  //存在createtime则使用最小时间,且createtime的范围必须在一天内,因为本表以天为单位建表
+            return getTable(table, 0, (time == null ? 0L : (time instanceof Range ? ((Range.LongRange) time).getMin() : (Long) time)));
+        }
+
+        //创建或单个查询时调用本方法
+        @Override
+        public String getTable(String table, LoginRecord bean) {
+            return getTable(table, 0, bean.getCreatetime());
+        }
+
+        //根据主键ID查询单个记录时调用本方法
+        @Override
+        public String getTable(String table, Serializable primary) {
+            String loginid = (String) primary;
+            String create36time = loginid.substring(loginid.length() - 9); //固定最后9位为创建时间的36进制值
+            return getTable(table, 0, Long.parseLong(create36time, 36));
+        }
+
+        private String getTable(String table, int day, long createtime) {
+            int pos = table.indexOf('.');
+            String year = (day > 0 ? "" + day / 10000 : String.format(yearformat, createtime));
+            return "platf_login_" + year + "." + table.substring(pos + 1) + "_" + (day > 0 ? day : String.format(dayformat, createtime));
+        }
+    }
+}
+                
+

         如上范例,若需要分表策略的三个接口均得到实现,需要对主键ID的生成规则进行一定的设计。并不是所有的数据表都需要进行全量查询或单个记录查询,开发人员可根据使用场景来设计分表策略和主键生成规则。事实上用户登录记录很少场景需要查询单个记录的, 但是常见的场景是查询单个用户的登录列表。上面的范例无法满足查询单个用户的登录信息需求,而分表策略又只能根据一种规则生成,因此需要按用户维度存在另外一张表中。

+
@DistributeTable(strategy = LoginUserRecord.TableStrategy.class)
+public class LoginUserRecord extends BaseEntity {
+
+    @Id
+    @GeneratedValue
+    @Column(comment = "UUID")
+    private String seqid = ""; //UUID
+
+    @Column(updatable = false, comment = "C端用户ID")
+    private long userid; //C端用户ID
+
+    @Column(comment = "LoginRecord主键")
+    private String loginid = ""; //LoginRecord主键
+
+    @Column(updatable = false, comment = "创建时间")
+    private long createtime; //创建时间
+
+    /** 以下省略getter setter方法 */
+
+    public static class TableStrategy implements DistributeTableStrategy<LoginUserRecord> {
+
+        @Override
+        public String getTable(String table, LoginUserRecord bean) {
+            return getTable(table, bean.getUserid());
+        }
+
+        @Override
+        public String getTable(String table, FilterNode node) {
+            Serializable id = node.findValue("userid");
+            if (id != null) return getTable(table, id);
+            return getHashTable(table, (Integer) node.findValue("#hash"));
+        }
+
+        @Override
+        public String getTable(String table, Serializable userid) {
+            return getHashTable(table, (int) (((Long) userid) % 100));
+        }
+
+        private String getHashTable(String table, int hash) {
+            int pos = table.indexOf('.');
+            return "platf_login." + table.substring(pos + 1) + "_" + (hash > 9 ? hash : ("0" + hash));
+        }
+
+    }
+}
+                
+

         如上,表LoginUserRecord只存储用户ID与登录信息ID的关联关系,以用户ID取模100进行hash存储,获取用户登录列表时,先查询LoginUserRecord一页的数据,再根据loginid查询LoginRecord实体。常见的分表策略是时间和主键hash,例如用户信息表采用主键hash分表:

@DistributeTable(strategy = UserDetail.TableStrategy.class)
 public class UserDetail extends BaseEntity {
 
@@ -304,7 +432,7 @@
 
     }
 }
-                
+

         如上,用户表以userid取模100进行hash分表,若需要提供根据手机号查询单个用户信息,则需要另外存在一个用户ID对应手机号码的关系表,同样可以以手机号后两位数字为hash存储。

CacheSource 入门

         CacheSource同Memcached类似,像一个带有过期功能地Map容器,存放key-value数据。常见的使用场景就是存放HTTP的Session信息。Redkale把用户会话信息数据当做业务数据处理,而不是接入层的数据。WebSocket的连接态数据也是用CacheSource存储。key为WebSocket的groupid,value为WebSocket服务端节点的IP地址列表。