利用ShardingSphere(sharding-proxy)實現分庫分表,通過整合ZooKeeper,進行簡單配置文件修改輕鬆實現跨庫跨表相關操作,輕量級零侵入整合項目開發
準備工作
下載相關文件:https://download.csdn.net/download/u014374009/11837831
Linux和windows都可以使用!
1、解壓文件
找到下面的文件夾,並進入。
在conf目錄下進行配置。
修改下面兩個配置文件中的一個配置文件,當然也可以全部都進行配置,第一個配置文件主要是進行主從數據庫配置,第二個主要是進行數據庫分庫分表配置,也是本次主要演示的地方。
配置文件都很簡單,基本上就是數據庫連接信息。
在這裏先創建兩個數據庫,在每個數據庫下面分別建立四張表,相關的sql 建表語句在下載的文件中都有。
然後根據先前的配置文件,修改爲自己的數據庫連接配置信息。
接下來,配置server配置文件
其實主要就是把服務註冊到 ZooKeeper 上,注意ZK的默認端口是2181,如果自己修改了,要對應的修改,下面會介紹ZK的安裝配置。其次,就是配置用戶名和密碼,可以根據自己需要修改,這個在後面測試會用到。
下面是ZK的相關配置
在下載的文件夾中都有相關的文件。
進入conf目錄。
把 zoo_sample.cfg 複製一份,改名爲 zoo.cfg ,然後進行編輯。
主要配置一下數據和日誌存放路徑,這兒由於是windows系統,所以用了下面的路徑,另外端口號,可以根據自己需要進行修改,一般不修改。
然後保存之後,到 ZK 的bin目錄下,雙擊 zkServer.cmd 啓動 ZK。
接着到 sharding-proxy-3.0.0 的 bin 目錄下,雙擊 start.bat 啓動sharding-proxy服務。
啓動成功之後,會自動到 ZK 上註冊服務,並會返回 registered 等信息,同時也會顯示綁定的端口號,這個端口號就是後面數據庫連接測試的端口號。
用Navicat進行測試,根據先前的信息,進行填寫,端口號就是上面的,帳號密碼就是 配置文件中 的 root,root。
連接成功,此時,環境已經搭建完成了。
測試工作
用Navicat連接成功後,會發現 數據庫名字變爲 配置文件中的名字,同時,兩個數據庫,四張表,變爲一個數據庫 兩張表。
測試代碼,可以根據自己需要修改。
package com.sharding.demo.controller;
import com.sharding.demo.bean.Order;
import com.sharding.demo.bean.OrderItem;
import io.shardingsphere.core.keygen.DefaultKeyGenerator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.*;
/**
* @author: Frank
* @email [email protected]
* @date: 2019/10/9
* @time: 8:52
* @fuction: about the role of class.
*/
public class TestSharingDB {
private static Logger logger = LogManager.getLogger();
public static void main(String[] args) {
DefaultKeyGenerator key = new DefaultKeyGenerator();
int userId = 45;
Number orderIdKey = key.generateKey();
Long orderId = orderIdKey.longValue();
logger.info("分佈式主鍵orderId:" + orderId);
Number orderItemIdKey = key.generateKey();
Long orderItemId = orderItemIdKey.longValue();
logger.info("分佈式主鍵orderItemId:" + orderItemId);
Order order = new Order();
order.setUserId(userId);
order.setStatus("1");
order.setOrderId(orderId);
//Long returnOrderId = orderService.insert(order);
Connection newConn = null;
Statement newSmt=null;
try
{
Class.forName("com.mysql.jdbc.Driver");
newConn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3307/sharding_db?serverTimezone=UTC&useSSL=false","root", "root");
if (newConn != null) {
newSmt = newConn.createStatement();
String sql="insert into t_order values ("+order.getOrderId()+","+order.getUserId()+",'"+order.getStatus()+"')";
// System.out.println(sql);
newSmt.executeUpdate(sql);//DDL語句返回值爲0;
}
} catch (SQLException e1) {
e1.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
newSmt.close();
newConn.close();
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
//logger.info("插入成功後的returnOrderId:" + returnOrderId);
OrderItem item = new OrderItem();
// item.setOrderItemId(orderItemId);
item.setOrderId(orderId);
item.setUserId(userId);
item.setStatus("1");
//Long returnOrderItemId = orderItemService.insert(item);
try
{
Class.forName("com.mysql.jdbc.Driver");
newConn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3307/sharding_db?serverTimezone=UTC&useSSL=false","root", "root");
if (newConn != null) {
newSmt = newConn.createStatement();
//String sql="insert into t_order values ('"+item.getOrderId()+"','"+item.getUserId()+"','"+item.getOrderItemId()+"','"+item.getStatus()+"')";
String sql="insert into t_order_item values ("+item.getOrderId()+","+item.getUserId()+","+item.getOrderItemId()+")";
// System.out.println(sql);
newSmt.executeUpdate(sql);//DDL語句返回值爲0;
}
} catch (SQLException e1) {
e1.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
newSmt.close();
newConn.close();
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
//logger.info("插入成功後的returnOrderItemId:" + returnOrderItemId);
try
{
Class.forName("com.mysql.jdbc.Driver");
newConn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3307/sharding_db?serverTimezone=UTC&useSSL=false","root", "root");
if (newConn != null) {
newSmt = newConn.createStatement();
//String sql="insert into t_order values ('"+item.getOrderId()+"','"+item.getUserId()+"','"+item.getOrderItemId()+"','"+item.getStatus()+"')";
String sql="select * from t_order join t_order_item on t_order_item.order_id=t_order.order_id";
// System.out.println(sql);
ResultSet rs = newSmt.executeQuery(sql);
while(rs.next()){
Long id = rs.getLong(1);
Long name = rs.getLong(2);
String gender = rs.getString(3);
System.out.println("id:"+id+" 姓名:"+name+" 性別:"+gender);
}
}
} catch (SQLException e1) {
e1.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
newSmt.close();
newConn.close();
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
}
}
測試結果。
上面代碼多次執行以後,在 sqlyog中發現,數據分配到不同的表中。
在Navicat中查看,會發現,表的數據是 每個表的數據的並集,也就是把所有的數據整合在一起了。可以在所有 ORM 框架中(可通過DataSource選擇使用原生JDBC開發,或者在JPA、Hibernate或MyBatis中使用),對數據庫進行增刪查改了。
分佈式主鍵
傳統數據庫軟件開發中,主鍵自動生成技術是基本需求。而各個數據庫對於該需求也提供了相應的支持,比如MySQL的自增鍵,Oracle的自增序列等。 數據分片後,不同數據節點生成全局唯一主鍵是非常棘手的問題。同一個邏輯表內的不同實際表之間的自增鍵由於無法互相感知而產生重複主鍵。 雖然可通過約束自增主鍵初始值和步長的方式避免碰撞,但需引入額外的運維規則,使解決方案缺乏完整性和可擴展性。
Sharding-proxy介紹
Sharding-Proxy是ShardingSphere的第二個產品。 它定位爲透明化的數據庫代理端,提供封裝了數據庫二進制協議的服務端版本,用於完成對異構語言的支持。 目前先提供MySQL版本,它可以使用任何兼容MySQL協議的訪問客戶端(如:MySQL Command Client, MySQL Workbench等)操作數據,對DBA更加友好。
Atlas介紹
Atlas是由 Qihoo 360公司Web平臺部基礎架構團隊開發維護的一個基於MySQL協議的數據中間層項目。它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基礎上,修改了大量bug,添加了很多功能特性。目前該項目在360公司內部得到了廣泛應用,很多MySQL業務已經接入了Atlas平臺,每天承載的讀寫請求數達幾十億條。同時,有超過50家公司在生產環境中部署了Atlas,超過800人已加入,並且這些數字還在不斷增加。