基础分库

以下实例基于shardingsphere 4.1.0 + SpringBoot 2.2.5.RELEASE版本

依赖导入:

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.compile.sourceEncoding>UTF-8</project.compile.sourceEncoding>
<springboot.version>2.2.5.RELEASE</springboot.version>
<shardingsphere.version>4.1.0</shardingsphere.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${springboot.version}</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${springboot.version}</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${springboot.version}</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency> <dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency> </dependencies>

场景:通过id字段取余分片到两个数据库

  1. 引入依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
</dependency>
  1. 参数配置
spring.shardingsphere.datasource.names=ds0,ds1

spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/ds_0?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=0490218292 spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/ds_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=0490218292 spring.shardingsphere.sharding.tables.position.database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.position.database-strategy.inline.algorithm-expression=ds$->{id % 2}
  1. 测试插入数据
@Test
public void testAdd(){
for (int i = 0; i <= 20; i++) {
Position position=new Position();
position.setId((long) i);
position.setName("lagou"+i);
position.setSalary("1000");
position.setCity("beijing");
positionRepository.save(position);
}
}

主键生成使用雪花算法

  1. id需要设置IDENTITY
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
  1. 参数配置

增加id设置:

#id设置
spring.shardingsphere.sharding.tables.position.key-generator.column=id
spring.shardingsphere.sharding.tables.position.key-generator.type=SNOWFLAKE
  1. 测试
    @Test
public void testAdd(){
for (int i = 0; i <= 20; i++) {
Position position=new Position();
position.setName("lagou"+i);
position.setSalary("1000");
position.setCity("beijing");
positionRepository.save(position);
}
}

自定义主键生成器

  1. 自定义主键生成器类
public class MyCustomId implements ShardingKeyGenerator {

    @Override
public Comparable<?> generateKey() {
return System.currentTimeMillis()+new Random().nextInt(100000);
} @Override
public String getType() {
//自定义一个名称
return "MYID";
} @Override
public Properties getProperties() {
return null;
} @Override
public void setProperties(Properties properties) { } }
  1. 配置

在resources下创建META-INF/services目录,并创建一个文件,文件名为:org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator

里面写自定义主键生成器的全类名

  1. 配置生成器类型的地方改为和我们自定义的生成器的类型一致
#id设置
spring.shardingsphere.sharding.tables.position.key-generator.column=id
spring.shardingsphere.sharding.tables.position.key-generator.type=MYID

两表关联的分库

场景:职位表(position)和职位详情表(position_detail)是关联的两个表,关联关系是:position_detail.pid = position.id,那么我们期望在插入数据后,根据职位Id进行查询时能够只查询一个库,而不是笛卡尔积的进行查询。

  1. 完整的参数配置
spring.shardingsphere.datasource.names=ds0,ds1

spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/ds_0?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=0490218292 spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/ds_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=0490218292 #职位表设置
spring.shardingsphere.sharding.tables.position.database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.position.database-strategy.inline.algorithm-expression=ds$->{id % 2}
#id设置
spring.shardingsphere.sharding.tables.position.key-generator.column=id
spring.shardingsphere.sharding.tables.position.key-generator.type=SNOWFLAKE
#职位表详情设置
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.sharding-column=pid
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.algorithm-expression=ds$->{pid % 2}
#id设置
spring.shardingsphere.sharding.tables.position_detail.key-generator.column=id
spring.shardingsphere.sharding.tables.position_detail.key-generator.type=SNOWFLAKE

可以看出position的id的分片策略和position_detail的pid的分片策略一致。

2. 测试

@Test
public void testQueryPosition(){
Object positionAndDetailById = positionRepository.findPositionAndDetailById(730545854473043968L);
System.out.println(positionAndDetailById);
}

可以看出,只查询了一个库:

广播表设置

场景:城市表属于基础表,数据量不大,每个库都可以存一样的数据。

  1. 广播表配置
#广播表设置
spring.shardingsphere.sharding.broadcast-tables=city
spring.shardingsphere.sharding.tables.city.key-generator.column=id
spring.shardingsphere.sharding.tables.city.key-generator.type=SNOWFLAKE
  1. 测试
@Test
public void testAddCity(){
City city=new City();
city.setName("成都");
city.setProvince("四川");
cityRepository.save(city);
}

和之前的不同,这一条数据的插入,两个库都有。且ID也是一致的。

分库且分表

场景:我们有一个订单表,可以根据公司id(companyId)进行分库,然后在根据id进行分表。

  1. 参数配置
#订单表分库且分表
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.sharding-column=company_id
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.algorithm-expression=ds$->{company_id%2}
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.algorithm-expression=b_order${id % 2}
spring.shardingsphere.sharding.tables.b_order.actual-data-nodes=ds${0..1}.b_order${0..1}
#id设置
spring.shardingsphere.sharding.tables.b_order.key-generator.column=id
spring.shardingsphere.sharding.tables.b_order.key-generator.type=SNOWFLAKE
  1. 测试
    @Test
@Repeat(100)
public void testAddBOrder(){
BOrder bOrder=new BOrder();
bOrder.setDel(false);
bOrder.setCompanyId(new Random().nextInt(10));
bOrder.setPositionId(23);
bOrder.setUserId(22);
bOrder.setPublishUserId(11);
bOrder.setResumeType(1);
bOrder.setStatus("AUTO");
bOrder.setCreateTime(new Date());
bOrder.setOperateTime(new Date());
bOrder.setWorkYear("2");
bOrder.setName("lagou");
bOrder.setPositionName("Java");
bOrder.setResumeId(23443);
bOrderRepository.save(bOrder);
}

我们发现数据插入到了ds_0.b_order0、ds_0.b_order1、ds_1.b_order0、ds_1.b_order1四个node里面。

读写分离

  1. 参数配置
spring.shardingsphere.datasource.names=master,slave0

spring.shardingsphere.datasource.master.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/ds_0?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=0490218292 spring.shardingsphere.datasource.slave0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave0.jdbc-url=jdbc:mysql://localhost:3306/ds_0_slave?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=0490218292 #读写分离
spring.shardingsphere.masterslave.name=datasource
spring.shardingsphere.masterslave.master-data-source-name=master
spring.shardingsphere.masterslave.slave-data-source-names=slave0
#多个读库时的负载均衡策略
spring.shardingsphere.masterslave.load-balance-algorithm-type=ROUND_ROBIN
  1. 查询测试
    @Test
public void test(){
List<City> all = cityRepository.findAll();
all.forEach(x->System.out.println(x));
}

分库分表+读写分离的参数配置

#数据源
spring.shardingsphere.datasource.names=master0,slave0,slave1,master1,slave2,slave3 spring.shardingsphere.datasource.master0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master0.jdbc-url=jdbc:mysql://localhost:3306/master0?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.master0.username=root
spring.shardingsphere.datasource.master0.password=root spring.shardingsphere.datasource.slave0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave0.jdbc-url=jdbc:mysql://localhost:3306/slave0?useSSL=false
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=root spring.shardingsphere.datasource.slave1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave1.jdbc-url=jdbc:mysql://localhost:3306/slave1?useSSL=false
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=root spring.shardingsphere.datasource.master1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master1.jdbc-url=jdbc:mysql://localhost:3306/master1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.master1.username=root
spring.shardingsphere.datasource.master1.password=root spring.shardingsphere.datasource.slave2.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave2.jdbc-url=jdbc:mysql://localhost:3306/slave2?useSSL=false
spring.shardingsphere.datasource.slave2.username=root
spring.shardingsphere.datasource.slave2.password=root spring.shardingsphere.datasource.slave3.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave3.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave3.jdbc-url=jdbc:mysql://localhost:3306/slave3?useSSL=false
spring.shardingsphere.datasource.slave3.username=root
spring.shardingsphere.datasource.slave3.password=root #分库分表
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.sharding-column=company_id
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.algorithm-expression=master$->{company_id % 2}
spring.shardingsphere.sharding.tables.b_order.actual-data-nodes=master$->{0..1}.b_order$->{0..1}
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.algorithm-expression=b_order$->{id % 2} #读写分离
spring.shardingsphere.sharding.master-slave-rules.master0.master-data-source-name=master0
spring.shardingsphere.sharding.master-slave-rules.master0.slave-data-source-names=slave0, slave1
spring.shardingsphere.sharding.master-slave-rules.master1.master-data-source-name=master1
spring.shardingsphere.sharding.master-slave-rules.master1.slave-data-source-names=slave2, slave3

强制路由

在一些应用场景中,分片条件并不存在于SQL,而存在于外部业务逻辑。因此需要提供一种通过在外部业务代码中指定路由配置的一种方式,在ShardingSphere中叫做Hint。如果使用Hint指定了强制分片路由,那么SQL将会无视原有的分片逻辑,直接路由至指定的数据节点操作。

使用场景:

  • 数据分片操作,如果分片键没有在SQL或者数据表中,而是在业务逻辑代码中
  • 读写分离操作,如果需要强制在主库进行某些操作
  1. 自定义Hint实现类
public class MyHintShardingAlgorithm implements HintShardingAlgorithm<String> {

    @Override
public Collection<String> doSharding(Collection<String> collection, HintShardingValue<String> hintShardingValue) {
Collection<String> result=new ArrayList<>();
if(hintShardingValue.getValues().contains("master")){
((ArrayList<String>) result).add("master");
}else {
((ArrayList<String>) result).add("slave0");
}
return result;
}
}
  1. 配置自定义的Hint类
spring.shardingsphere.sharding.tables.city.database-strategy.hint.algorithm-class-name=com.mmc.sharding.hint.MyHintShardingAlgorithm

  1. 测试
    @Test
public void testHint(){
HintManager hintManager = HintManager.getInstance();
hintManager.addDatabaseShardingValue("city","master");
// hintManager.setMasterRouteOnly();
List<City> all = cityRepository.findAll();
all.forEach(x->System.out.println(x));
}

还可以使用hintManager.setMasterRouteOnly()指定仅路由到主库。

测试过程中发现Hint的自定义策略和读写分离配置有冲突。配置了读写分离后自定义Hint类不生效了,仅hintManager.setMasterRouteOnly()还可以用。

数据加密

脱敏配置分为如下几个:数据源配置,加密器配置,脱敏表配置以及查询属性配置,其详情如下图所示:

  • 数据源配置:指Datasource的配置信息
  • 加密器配置:指使用什么加密策略进行加解密。目前ShardingSphere内置了两种加解密策略AES、MD5
  • 脱敏表配置:指定哪个列用于存储密文数据,哪个列存明文数据,以及在应用里用哪个列(应用层sql里使用的列名)
  • 查询属性配置:当数据库同时存了明文和密文的时候,该属性开关用于决定是直接查询数据库表里的明文,还是查密文然后通过解密后返回。
  1. 先创建个表
CREATE TABLE `c_user` (
`Id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) DEFAULT NULL,
`pwd_plain` varchar(256) DEFAULT NULL,
`pwd_cipher` varchar(256) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 创建实体类
@Entity
@Table(name = "c_user")
public class CUser implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; @Column(name = "name")
private String name; /**
* 逻辑列名
*/
@Column(name = "password")
private String password;
}
  1. 参数配置
#定义数据表真实明文列
#spring.shardingsphere.encrypt.tables.c_user.columns.password.plain-column=pwd_plain
#定义数据表真实密文列
spring.shardingsphere.encrypt.tables.c_user.columns.password.cipher-column=pwd_cipher
#定义加密器,名称为lagou_pwd
spring.shardingsphere.encrypt.encryptors.lagou_pwd.type=aes
spring.shardingsphere.encrypt.encryptors.lagou_pwd.props.aes.key.value=1234
#指定加密器,password是逻辑列名,与实体类中的字段对应
spring.shardingsphere.encrypt.tables.c_user.columns.password.encryptor=lagou_pwd
  1. 测试
    @Test
public void testEncrypt(){
CUser cUser=new CUser();
cUser.setName("阿百川");
cUser.setPassword("123456");
cUserRepository.save(cUser);
} @Test
public void testQueryByPassword(){
List<CUser> byPassword = cUserRepository.findByPassword("123456");
System.out.println(byPassword);
}



数据库存放的已经是密文了,通过明文密码也可以查询到数据了。

分布式事务

仅仅需要在测试方法上加上两个注解:

    @Transactional(rollbackFor = Exception.class)
@ShardingTransactionType(TransactionType.XA)

TransactionType有XA、BASE、LOCAL三种

@Test
@Transactional(rollbackFor = Exception.class)
@ShardingTransactionType(TransactionType.XA)
public void testAddDetail(){
for (int i = 0; i <= 3; i++) {
Position position=new Position();
position.setName("lagou"+i);
position.setSalary("1000");
position.setCity("beijing");
positionRepository.save(position); if(i==3){
throw new RuntimeException();
}
PositionDetail positionDetail=new PositionDetail();
positionDetail.setPid(position.getId());
positionDetail.setDescription("详情");
positionDetailRepository.save(positionDetail);
}
}

Sharding JDBC案例实战的更多相关文章

  1. (升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)

    本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课 ...

  2. Spark Streaming 进阶与案例实战

    Spark Streaming 进阶与案例实战 1.带状态的算子: UpdateStateByKey 2.实战:计算到目前位置累积出现的单词个数写入到MySql中 1.create table CRE ...

  3. 《图解Spark:核心技术与案例实战》作者经验谈

    1,看您有维护博客,还利用业余时间著书,在技术输出.自我提升以及本职工作的时间利用上您有没有什么心得和大家分享?(也可以包含一些您写书的小故事.)回答:在工作之余能够写博客.著书主要对技术的坚持和热爱 ...

  4. 图解CSS3核心技术与案例实战(1)

    前言: 我买了一本<图解CSS3核心技术与案例实战>大漠写的,为了提高自己的自觉性呢,抓紧看书,把读书笔记放在这上面,跟大家一起分享,也为督促自己完成读书计划. 文末有微信公众号,感谢你的 ...

  5. kafka关于修改副本数和分区的数的案例实战(也可用作leader节点均衡案例)

    kafka关于修改副本数和分区的数的案例实战(也可用作leader节点均衡案例) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.关于topic分区数的修改 1>.创建1分 ...

  6. Java------------JVM(Java虚拟机)优化大全和案例实战

    JVM(Java虚拟机)优化大全和案例实战 堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Ge ...

  7. 《图解CSS3:核心技术与案例实战》

    <图解CSS3:核心技术与案例实战> 基本信息 作者: 大漠 丛书名: Web开发技术丛书 出版社:机械工业出版社 ISBN:9787111469209 上架时间:2014-7-2 出版日 ...

  8. 我的第一个上线小程序,案例实战篇二——LayaAir游戏开始界面开发

    不知不觉我的第一个小程序已经上线一周了,uv也稳定的上升着. 很多人说我的小程序没啥用,我默默一笑,心里说:“它一直敦促我学习,敦促我进步”.我的以一个小程序初衷是经验分享,目前先把经验分享到博客园, ...

  9. Spring boot项目集成Sharding Jdbc

    环境 jdk:1.8 framework: spring boot, sharding jdbc database: MySQL 搭建步骤 在pom 中加入sharding 依赖 <depend ...

随机推荐

  1. ctfhub 双写绕过 文件头检查

    双写绕过 进入环境 上传一句话木马 上传路径感觉不对检查源代码 从此处可以看出需要双写绕过 使用bp抓包 此处这样修改即可双写绕过 使用蚁剑连接 即可找到flag 文件头检查 进入环境 上传一句话木马 ...

  2. python学习笔记(四)——面向对象编程

    python 支持面向过程编程和面向对象编程. 传统面向过程编程,也叫函数式编程,通过我们的需求设计成一个一个的函数来完成,对一些小规模程序来说面向过程确实简单方便不少.而随着互联网的发展,对于一些大 ...

  3. ZEGO音视频服务的高可用架构设计与运营

    前言: ZEGO 即构科技作为一家实时音视频的提供商,系统稳定性直接影响用户的主观体验,如何保障服务高可用且用户体验最优是行业面临的挑战,本文结合实际业务场景进行思考,介绍 ZEGO 即构在高可用架构 ...

  4. 如何制作icon-font小图标

    1.首先可以去iconfont.cn阿里巴巴矢量字体库中下载你想要的图标(选择格式为SNG格式). 2.打开iconmoon这个网站(这个样子的),然后点击右上角那个Iconfont App如下图: ...

  5. JS:数组中push对象,覆盖问题

    发现将对象push进数组,后面的值会覆盖前面的值,最后输出的都是最后一次的值.其实这一切都是引用数据类型惹的祸.如果你也有类似问题,可以继续看下去哦.下面代码模拟:将json对象的每个键值对,单独搞成 ...

  6. canvas 实现 github404动态效果

    使用canvas来完成github404的动态效果 前几天使用css样式和js致敬了一下github404的类似界面,同时最近又接触了canvas,本着瞎折腾的想法,便借着之前的js的算法,使用can ...

  7. PAT B1014 福尔摩斯约会

    大侦探福尔摩斯接到一张奇怪的字条:我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm.大侦探很快就明白了,字条上奇 ...

  8. vue Element验证input提示

    <el-form-item prop="userName" class="userName_color"> <b>详细地址<i c ...

  9. python2.7安装pyinstaller

    python2.7直接安装pyinstaller会报错,版本4与python2不兼容,所以我们安装时需指定兼容的pyinstaller版本号.安装命令如下: pip2 install pyinstal ...

  10. 帝国cms插件 解决后台修改信息时内容关键字不替换的问题

    很多站长是不是发现了帝国cms增加信息时,是有关键词替换的,这样是有利于网站优化排名. 但是在后台格式化数据之后,再去进行修改之后,对不起,内容关键字就实效了. 针对这一问题,解决方案如下: 找到 / ...