序)沉迷遊戲,只因不安於現狀卻又沒法改變已逝的過去sql
昨天下午開始作一些細化的東西,其中就包括了用戶註冊的時候,系統隨機分配一個暱稱,這樣的狀況幾乎在全部遊戲中均可見到,不算什麼稀奇的東西,策劃要求的是隻有漢字,必須看着像名字,不能重複,這樣我最開始想的直接隨機產生x個漢字的想法看來就太簡單了,而且不少遊戲的隨機暱稱看着還像模像樣,確實是一個名字。數據庫
另外在這裏還有個細節問題,當用戶打開註冊界面選擇角色的時候從服務器得到了惟一性的角色名後,該角色名則不能再分配給其餘人,當用戶註冊後則與之綁定,若是用戶選擇刷新從新獲取角色名,則以前角色名如何處置?固然有兩種方案:服務器
1)直接丟掉,只要分配了即便用戶沒有綁定該角色名也廢棄 併發
2)丟回池裏,可再分配給其餘人app
第一種狀況使用率過低,當太多用戶不停刷的時候,使用率更低了,因而我以爲第二種比較好,鑑於名字要像模像樣這樣的狀況看來必須走詞庫進行組合了,固然百家姓要拉下來,因而我本身動手整理了以下三個文件:dom
依次爲:姓氏 ----> 女性角色名 ----> 男性角色名ide
之因此要把男女分開,那是由於男性總是分配到柔柔,瑤瑤,這樣的暱稱,估計瞬間就會開口大罵,爲了我少挨點罵,因而把他們分開了,另外因爲詞庫比較大,組合後的數據更加大,這樣的分開也可以提升速度,在剛剛開始的時候我犯了一個很大的錯誤,那就是把問題想簡單了,我最開始的想法是:測試
1)根據性別從woman.txt或者man.txt裏隨機選擇一個名字,再隨機在姓氏文件中選取一個姓氏,組合成名字this
2)判斷該角色名是否已經被註冊,若是已經被註冊,跳轉到1spa
3)判斷該角色是否存在於Set集合(個Set集合用來保存當前已經分配出去的,可是用戶還未綁定的角色名)
4)丟入Set集合,發給客戶端
5)用戶刷新角色則將該角色從Set移除,以便複用
作完後客戶端測試了從功能上講沒啥問題,可是作完後立刻本身就發現這裏有很大的問題,首先走了詞庫,有IO問題,這裏的詞庫男女不一樣的角色名分別各有100萬左右,其次當用戶數量太大的時候,剩下的未使用的角色名被抽中的機率會變得很小很小,越靠後分配時間越久,特別是詞庫快要耗盡的時候,剩下未分配的名稱幾乎不會再被分配到,甚至會死在這裏,想到這裏瞬間精神了,粗心害死人啊。
因而打算重寫這一塊,首先不能使用隨機數,隨機就會出問題,另外由於詞庫的大小,正式上線的時候根據詞庫組合出來的用戶名可能會有上千萬,所以這樣的數據確定不能直接丟內存,因而個人大概處理方式以下:
1)分別建立男性角色名錶與女性角色名錶,將全部暱稱初始化到數據庫,做爲一個池。
2)分別建立男性角色名視圖與女性角色名視圖
3)服務器啓動後初始化角色名錶的數據,保證裏面都是沒有被分配的數據
4)初始化男性與女性暱稱訪問遊標,遊標與數據庫裏的行index對應
5)每次用戶請求隨機暱稱的時候將遊標的位置下移一位,取出裏面的值發給客戶端
6)即便用戶不使用該名字,要求刷新名字,遊標依舊保持下移
這樣遊標始終取一下個值,則確定不會重複,省掉了取出了還要和數據庫比對的狀況,而且這裏的遊標必定不能後退,不然就會出現一個用戶刷新暱稱不停在兩個暱稱中切換的狀況,這樣估計就要罵娘了,這樣作了後還有幾個問題就是如何保證使用率,就是遊標移過了,可是該暱稱沒有被使用的數據,可以再次被分配到,因而個人想法是刷新數據表,重置遊標位置。天天或者每週刷新一次表,重置遊標位置爲1,這樣則可合理使用裏面的數據。
這裏還要處理一些其餘併發問題,好比遊標移動要保證惟一,另外刷新後臺數據表的時候不能讓遊標移動,不然會出現髒數據。
實現取隨機暱稱的代碼:
private volatile int currentMaleRoleNameCursorIndex = 1; private volatile int currentFemaleRoleNameCursorIndex = 1; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); private boolean isRefurbish = false; public static final int DEFAULT_INDEX_VALUE = 1; private interface SexType { int WOMAN = 0; int MAN = 1; } public String getRandomRoleName(int sex) { try { lock.lock(); while (isRefurbish) { condition.await(); } String name = null; switch (sex) { case SexType.MAN: name = this.jdbcAccess.findString("select * from v_male_role_name_table where index = ?", currentMaleRoleNameCursorIndex, sex); currentMaleRoleNameCursorIndex += 1; break; case SexType.WOMAN: name = this.jdbcAccess.findString("select * from v_female_role_name_table where index = ?", currentFemaleRoleNameCursorIndex, sex); currentFemaleRoleNameCursorIndex += 1; break; default: break; } return StringUtils.hasText(name) ? name : "notFound"; } catch (Exception e) { e.printStackTrace(); return "error"; } finally { lock.unlock(); } }
Quartz中的任務天天刷新池裏的數據:
@Inject private RandomHan randomHan; private static class RoleItems { private int id; private String name; public static RoleItems setValues(ResultSet resultSet) throws SQLException { RoleItems item = new RoleItems(); item.id = resultSet.getInt("id"); item.name = resultSet.getString("name"); return item; } } public void refurbishRandomNameTable() { randomHan.setRefurbish(true); List<RoleItems> cacheMaleNameItems = this.jdbcAccess.find("select t.id, t.name from v_male_role_name_table where 1 = 1 and index <= ?", new RowMapper<RoleItems>() { @Override public RoleItems mapRow(ResultSet resultSet, int rowNum) throws SQLException { return RoleItems.setValues(resultSet); } }, randomHan.getCurrentMaleRoleNameCursorIndex()); for (RoleItems item : cacheMaleNameItems) { if (isExistRoleName(item.name)) { this.jdbcAccess.execute("delete from male_role_name_table where id = ?", item.id); } } randomHan.setCurrentMaleRoleNameCursorIndex(RandomHan.DEFAULT_INDEX_VALUE); List<RoleItems> cacheFemaleNameItems = this.jdbcAccess.find("select t.id, t.name from v_female_role_name_table where 1 = 1 and index <= ?", new RowMapper<RoleItems>() { @Override public RoleItems mapRow(ResultSet resultSet, int rowNum) throws SQLException { return RoleItems.setValues(resultSet); } }, randomHan.getCurrentMaleRoleNameCursorIndex()); for (RoleItems item : cacheFemaleNameItems) { if (isExistRoleName(item.name)) { this.jdbcAccess.execute("delete from female_role_name_table where id = ?", item.id); } } randomHan.setCurrentFemaleRoleNameCursorIndex(RandomHan.DEFAULT_INDEX_VALUE); randomHan.setRefurbish(false); } private boolean isExistRoleName(String nickName) { return this.jdbcAccess.findInteger("select count(*) from player_role where nick_name = ?", nickName) > 0; }
數據庫基本操做本身從新封裝了下,以下:
public class JDBCAccess { private final Logger logger = LoggerFactory.getLogger(JDBCAccess.class); private JdbcTemplate jdbcTemplate; public <T> List<T> find(String sql, RowMapper<T> rowMapper, Object... params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.query(sql, params, rowMapper); } finally { logger.debug("find, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public <T> T findUniqueResult(String sql, RowMapper<T> rowMapper, Object... params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.queryForObject(sql, params, rowMapper); } finally { logger.debug("findUniqueResult, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public int findInteger(String sql, Object... params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.queryForInt(sql, params); } finally { logger.debug("findInteger, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public boolean findBooleanFiled(String sql, Object... params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.queryForObject(sql, params, new RowMapper<Boolean>() { @Override public Boolean mapRow(ResultSet resultSet, int rowNum) throws SQLException { return resultSet.getBoolean(1); } }); } catch(Exception e) { return false; } finally { logger.debug("findString, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public Long findLongFiled(String sql, Object... params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.queryForObject(sql, params, new RowMapper<Long>() { @Override public Long mapRow(ResultSet resultSet, int rowNum) throws SQLException { return resultSet.getLong(1); } }); } finally { logger.debug("findString, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public int findIntegerFiled(String sql, Object... params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.queryForObject(sql, params, new RowMapper<Integer>() { @Override public Integer mapRow(ResultSet resultSet, int rowNum) throws SQLException { return resultSet.getInt(1); } }); } catch (Exception e) { return -1; } finally { logger.debug("findString, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public String findString(String sql, Object... params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.queryForObject(sql, params, new RowMapper<String>() { @Override public String mapRow(ResultSet resultSet, int rowNum) throws SQLException { return resultSet.getString(1); } }); } finally { logger.debug("findString, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public int execute(String sql, Object... params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.update(sql, params); } finally { logger.debug("execute, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public int[] batchExecute(String sql, List<Object[]> params) { StopWatch watch = new StopWatch(); try { return jdbcTemplate.batchUpdate(sql, params); } finally { logger.debug("batchExecute, sql={}, params={}, elapsedTime={}", new Object[]{sql, params, watch.elapsedTime()}); } } public void setDataSource(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } }
這裏還有一個問題,那就是用戶刷新暱稱後應該是不同的姓氏的暱稱,這個可使用遊標解決,也但是在初始化數據裏作手腳,不過再說吧,總以爲這個東西本身也搞的很挫了,先在這裏記錄下吧。。。