Android数据库设计——3,自动化 分库分表
Posted 夜辉疾风
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android数据库设计——3,自动化 分库分表相关的知识,希望对你有一定的参考价值。
分库、分表
用户量大的时候必须去分库分表,分库分表也需要自动化
分库
前置工作
/**
* Describe:修改注解,增加主键标识
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField
String value();//表列名
boolean isPrimary() default false;//是否是主键
public class BaseDao<T> implements IBaseDao<T>
//获得当前表的唯一标识,一般为主键
//这里查询到当前表主键对应的值,传递进来的entityClass是带有唯一标识的,这里将它找出来
public String getPrimary(T entity)
for (Map.Entry<String, Field> entry : cacheMap.entrySet())
Field cacheField = entry.getValue();
DbField dbField = cacheField.getAnnotation(DbField.class);
//当前表列名是主键
if (dbField != null && dbField.isPrimary())
try
//得到主键值
return cacheField.get(entity).toString();
catch (Exception e)
e.printStackTrace();
return "";
/**
* Describe:数据库分库的工厂类
*/
public class BaseDaoSubFactory extends BaseDaoFactory
//单例
private static final BaseDaoSubFactory instance = new BaseDaoSubFactory();
public static BaseDaoSubFactory getInstance()
return instance;
//数据库文件统一存放路径
private static final String def_db_path = "data/data/" + BuildConfig.APPLICATION_ID;
//数据库连接池,缓存分库的连接池
private Map<String, BaseDao> dbGroup = Collections.synchronizedMap(new HashMap<String, BaseDao>());
/**
* 获得分库的操作dao
*
* @param daoClass dao的class对象
* @param entityClass 对应表的class对象
* @param <T> 继承basedao的dao对象的类型
* @param <M> 传递进dao的表对应的java实体类的类型
* @param m 传递进dao的表对应的java实体类,必须包含唯一约束的主键
* @return 数据库分库操作dao对象
*/
public <T extends BaseDao<M>, M> T getSubDao(Class<T> daoClass, Class<M> entityClass, M m)
//在主数据库里先获取到basedao对象
T baseDao = BaseDaoSubFactory.getInstance().getBaseDao(daoClass, entityClass);
//在主数据库里获取到了对象才能创建分库
if (baseDao != null)
//创建分库数据库
String dbPath = getSeparateTablePath(baseDao, m);
//如果缓存有,则直接取缓存中的数据库操作对象
if (dbGroup.get(dbPath) != null)
return (T) dbGroup.get(dbPath);
//没有缓存,则创建
SQLiteDatabase sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
try
//获得dao的对象,并初始化
baseDao = daoClass.newInstance();
baseDao.init(sqLiteDatabase, entityClass);
//添加缓存
dbGroup.put(dbPath, baseDao);
catch (Exception e)
e.printStackTrace();
return baseDao;
return null;
//创建当前用户的分数据库路径
public <M> String getSeparateTablePath(BaseDao<M> baseDao, M m)
String separateTablePath = "";
//创建分库的唯一标识
String primary = baseDao.getPrimary(m);
//获取到了唯一标识,创建数据库文件
if (!TextUtils.isEmpty(primary))
File file = new File(def_db_path);
boolean iscreate = true;
//不存在默认的数据库则创建
if (!file.exists())
//创建成功
if (!file.mkdirs())
iscreate = false;
if (iscreate)
//如果数据库根目录存在,则返回分库路径
//这样,每一个表都有一个单独的数据库。例如,用户表,根据用户id将每个用户分到独立的数据库中,这个数据库中只存在当前用户的一些列表信息
separateTablePath = file.getAbsolutePath() + "/" + baseDao.getTableName() + "_" + primary + ".db";
return separateTablePath;
注意:
对于前端应用来说,最好的方式就是一个用户一个数据库,以为前端用户少,一个用户一个库便于操作。
对于服务器来说,则需要定制一套规则。例如,一千万用户一个库,1-10000000id的用户分为库0,以此类推。那么这一千万用户就有两个id,主库id(随着用户量的增长而增长,0~+∞)、分库id(0~10000000,以此类推),并且主库id和分库id不可以一起查,因为他们之间是没有关联的。
分表
当我们的数据量上升到一定大小的时候,操作速度就会明显受影响,所以要根据有规律的唯一标识符按一定规则来分表,规则是灵活的。
例如:
某一个用户操作表,需要根据用户id来查到该用户在这张表中的操作记录。
如果这张操作表记录了非常多的用户操作,那么我们就可以根据一定的特性来进行分表。
分表的本质和分库一致。
例如,操作表中,用户id是唯一的或者操作id是唯一的。那么我们就可以根据唯一标识符来进行分表。
比如我们根据用户id结尾的数字就可以分出十张表。0-9的十张操作表。这十张表分别存用户id结尾为0-9的操作记录。
如果十张表不够用,则我们可以将0-9的id转成16进制,这样就可以有十六张表。
如果十六张表不够用,则我们可以根据用户id的后两位,也就是00-99分成10张表。
以此类推。
这,就是分表。
/**
* Describe:用户头像表,有一百张,根据用户id的后两位数来分
*/
@DbTable("tb_user_img")
public class UserImg
private Long imgId;
@DbField("_id")
private Long userId;//用户id,更严格的说,这里应该指向user表的id,它是一个外键
private String time;//创建时间
private String imgPath;//头像路径
//获得用户id的后两位
public int getUserIdSuffix()
if (userId < 100)
return userId.intValue();
else
String idStr = userId + "";
return Integer.parseInt(idStr.substring(idStr.length() + 2));
/**
* Describe:用户头像,分了一百张,规则是用户id的后两位00-99
*/
public class PhotoDao extends BaseDao<UserImg>
//重写初始化方法,在初始化数据库操作对象的时候创建一百张表
@Override
public void init(SQLiteDatabase sqLiteDatabase, Class<UserImg> entityClass)
// super.init(sqLiteDatabase,entityClass);
this.sqLiteDatabase = sqLiteDatabase;
this.entityClass = entityClass;
if (!isInit)
//通过反射和注解获得表名
//自动建表
//如果没有添加注解,则通过反射去拿表明
DbTable dbTable = entityClass.getAnnotation(DbTable.class);
if (dbTable != null)
//注解拿表名
this.tableName = dbTable.value();
else
//反射拿表名
this.tableName = entityClass.getSimpleName();
//这里开始建一百张表
for (int i = 0; i < 100; i++)
//创建表的sql语句
String createTableSql = getCreateTableSql(tableName + getTableSuffix(i));
this.sqLiteDatabase.execSQL(createTableSql);
initCacheMap();
isInit = true;
//实际查询,根据用户id先定位到哪张表再查询
@Override
public List<UserImg> queryByWhere(UserImg where)
//准备好条件语句
Map<String, String> values = getValues(where);
Condition condition = new Condition(values);
String photoName = tableName + getTableSuffix(where.getUserIdSuffix());
Cursor cursor = sqLiteDatabase.query(photoName, null, condition.whereClause, condition.whereArgs, null, null, null, null);
List<UserImg> resultList = getResult(cursor, where);
cursor.close();
return resultList;
//处理分表的后缀
private String getTableSuffix(int i)
return (i < 10 ? "0" : "") + i;
以上是关于Android数据库设计——3,自动化 分库分表的主要内容,如果未能解决你的问题,请参考以下文章